Skip to content

Commit

Permalink
imagebuf docs examples: c++ examples, outfile, run script, and doc
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelRCarroll-Intel committed Oct 13, 2023
1 parent 5731449 commit f9a8b45
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 92 deletions.
122 changes: 31 additions & 91 deletions src/doc/imagebuf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -369,57 +369,28 @@ For the remainder of this section, we will assume that you have a
Example: Visiting all pixels to compute an average color
--------------------------------------------------------

.. code-block:: cpp
.. tabs::

.. tab:: C++
.. literalinclude:: ../../testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp
:language: c++
:start-after: BEGIN-imagebuf-get-pixel-average
:end-before: END-imagebuf-get-pixel-average
:dedent: 4

void print_channel_averages (const std::string &filename)
{
// Set up the ImageBuf and read the file
ImageBuf buf (filename);
bool ok = buf.read (0, 0, true, TypeDesc::FLOAT); // Force a float buffer
if (! ok)
return;
// Initialize a vector to contain the running total
int nc = buf.nchannels();
std::vector<float> total (n, 0.0f);
// Iterate over all pixels of the image, summing channels separately
for (ImageBuf::ConstIterator<float> it (buf); ! it.done(); ++it)
for (int c = 0; c < nc; ++c)
total[c] += it[c];
// Print the averages
imagesize_t npixels = buf.spec().image_pixels();
for (int c = 0; c < nc; ++c)
std::cout << "Channel " << c << " avg = " (total[c] / npixels) << "\n";
}
.. _sec-make-black:

Example: Set all pixels in a region to black
--------------------------------------------

.. code-block:: cpp

bool make_black (ImageBuf &buf, ROI region)
{
if (buf.spec().format != TypeDesc::FLOAT)
return false; // Assume it's a float buffer
// Clamp the region's channel range to the channels in the image
roi.chend = std::min (roi.chend, buf.nchannels);
// Iterate over all pixels in the region...
for (ImageBuf::Iterator<float> it (buf, region); ! it.done(); ++it) {
if (! it.exists()) // Make sure the iterator is pointing
continue; // to a pixel in the data window
for (int c = roi.chbegin; c < roi.chend; ++c)
it[c] = 0.0f; // clear the value
}
return true;
}
.. tabs::

.. tab:: C++
.. literalinclude:: ../../testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp
:language: c++
:start-after: BEGIN-imagebuf-set-region-black
:end-before: END-imagebuf-set-region-black
:dedent: 4

Dealing with buffer data types
==============================
Expand Down Expand Up @@ -474,37 +445,14 @@ Strategy 2: Template your iterating functions based on buffer type
Consider the following alternate version of the `make_black` function
from Section `Example: Set all pixels in a region to black`_ ::

template<typename BUFT>
static bool make_black_impl (ImageBuf &buf, ROI region)
{
// Clamp the region's channel range to the channels in the image
roi.chend = std::min (roi.chend, buf.nchannels);
// Iterate over all pixels in the region...
for (ImageBuf::Iterator<BUFT> it (buf, region); ! it.done(); ++it) {
if (! it.exists()) // Make sure the iterator is pointing
continue; // to a pixel in the data window
for (int c = roi.chbegin; c < roi.chend; ++c)
it[c] = 0.0f; // clear the value
}
return true;
}
bool make_black (ImageBuf &buf, ROI region)
{
if (buf.spec().format == TypeDesc::FLOAT)
return make_black_impl<float> (buf, region);
else if (buf.spec().format == TypeDesc::HALF)
return make_black_impl<half> (buf, region);
else if (buf.spec().format == TypeDesc::UINT8)
return make_black_impl<unsigned char> (buf, region);
else if (buf.spec().format == TypeDesc::UINT16)
return make_black_impl<unsigned short> (buf, region);
else {
buf.error ("Unsupported pixel data format %s", buf.spec().format);
return false;
}
}
.. tabs::

.. tab:: C++
.. literalinclude:: ../../testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp
:language: c++
:start-after: BEGIN-imagebuf-iterator-template
:end-before: END-imagebuf-iterator-template
:dedent: 4

In this example, we make an implementation that is templated on the buffer
type, and then a wrapper that calls the appropriate template specialization
Expand All @@ -515,22 +463,14 @@ In fact, :file:`imagebufalgo_util.h` provides a macro to do this (and
several variants, which will be discussed in more detail in the next
chapter). You could rewrite the example even more simply::

#include <OpenImageIO/imagebufalgo_util.h>
template<typename BUFT>
static bool make_black_impl (ImageBuf &buf, ROI region)
{
... same as before ...
}
bool make_black (ImageBuf &buf, ROI region)
{
bool ok;
OIIO_DISPATCH_COMMON_TYPES (ok, "make_black", make_black_impl,
buf.spec().format, buf, region);
return ok;
}
.. tabs::

.. tab:: C++
.. literalinclude:: ../../testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp
:language: c++
:start-after: BEGIN-imagebuf-disptach
:end-before: END-imagebuf-dispatch
:dedent: 4

This other type-dispatching helper macros will be discussed in more
detail in Chapter :ref:`chap-imagebufalgo`.

10 changes: 10 additions & 0 deletions testsuite/docs-examples-cpp/ref/out.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
findaverages.exr : 640 x 480, 3 channel, float openexr
SHA-1: E8D3C267CBC4BBDE41343DCA008773A3E55B8159
set-region-black.exr : 640 x 480, 3 channel, float openexr
SHA-1: 7006ED82385E7BCD1FB338B9D3DBFF754541E980
set-region-black-template-dispatch.exr : 640 x 480, 3 channel, half openexr
SHA-1: FEB8B385D8784C4A16F3164D9B1A0F9113D64C86
set-region-black-template-float.exr : 640 x 480, 3 channel, float openexr
SHA-1: 7006ED82385E7BCD1FB338B9D3DBFF754541E980
set-region-black-template-uint8.exr : 640 x 480, 3 channel, half openexr
SHA-1: 9E09138C84FC4FDA52A6D76AED751B6551BB3417
error: Uninitialized input image
error: Uninitialized input image
zero1.exr : 512 x 512, 3 channel, float openexr
Expand Down
6 changes: 6 additions & 0 deletions testsuite/docs-examples-cpp/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
# hashes merely check that the images don't change, but saves us the space
# of checking in a full copy of the image if it's not needed.
hashes = [
# Outputs from the ImageBuf chapter:
"findaverages.exr",
"set-region-black.exr",
"set-region-black-template-dispatch.exr",
"set-region-black-template-float.exr",
"set-region-black-template-uint8.exr",
# Outputs from the ImageBufAlgo chapter:
"zero1.exr",
"zero2.exr",
Expand Down
205 changes: 204 additions & 1 deletion testsuite/docs-examples-cpp/src/docs-examples-imagebuf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,215 @@ void example1()
//
///////////////////////////////////////////////////////////////////////////

#include <OpenImageIO/imagebuf.h>

// BEGIN-imagebuf-get-pixel-avg
void print_channel_averages(const std::string& filename)
{
// Set up the ImageBuf and read the file
ImageBuf buf(filename);
bool ok = buf.read(0, 0, true, TypeDesc::FLOAT); // Force a float buffer
if (!ok)
return;

// Initialize a vector to contain the running total
int nc = buf.nchannels();
std::vector<float> total(nc, 0.0f);

// Iterate over all pixels of the image, summing channels separately
for (ImageBuf::ConstIterator<float> it(buf); !it.done(); ++it)
for (int c = 0; c < nc; ++c)
total[c] += it[c];

// Print the averages
imagesize_t npixels = buf.spec().image_pixels();
for (int c = 0; c < nc; ++c)
std::cout << "Channel " << c << " avg = " << (total[c] / npixels)
<< "\n";
}

// END-imagebuf-get-pixel-avg

void print_channel_averages_example()
{
const std::string filename = "findaverages.exr";
int x_sz = 640;
int y_sz = 480;
ImageBuf A(ImageSpec(x_sz, y_sz, 3, TypeDesc::FLOAT));
for (int i = 0; i < x_sz; ++i)
for (int j = 0; j < y_sz; ++j) {
// Create a square RGB gradient so determining an average is interesting
A.setpixel(i, j, 0,
cspan<float>(
{ powf(float(i) / (x_sz - 1), 2.0f),
powf(float(j) / (y_sz - 1), 2.0f),
powf(float(i * j) / (x_sz * y_sz - 1), 2.0f) }));
}
if (!A.write(filename)) {
std::cout << "error: " << A.geterror() << "\n";
} else {
print_channel_averages(filename);
}
}

// BEGIN-imagebuf-set-region-black
bool make_black(ImageBuf& buf, ROI region)
{
if (buf.spec().format != TypeDesc::FLOAT)
return false; // Assume it's a float buffer

// Clamp the region's channel range to the channels in the image
region.chend = std::min(region.chend, buf.nchannels());
// Iterate over all pixels in the region...
for (ImageBuf::Iterator<float> it(buf, region); !it.done(); ++it) {
if (!it.exists()) // Make sure the iterator is pointing
continue; // to a pixel in the data window
for (int c = region.chbegin; c < region.chend; ++c)
it[c] = 0.0f; // clear the value
}
return true;
}
// END-imagebuf-set-region-black

void make_black_example()
{
int x_sz = 640;
int y_sz = 480;
ImageBuf A(ImageSpec(x_sz, y_sz, 3, TypeDesc::FLOAT));
for (int i = 0; i < x_sz; ++i)
for (int j = 0; j < y_sz; ++j) {
// Create RGB gradient so region changing is easy to see
A.setpixel(i, j, 0,
cspan<float>({ float(i) / (x_sz - 1),
float(j) / (y_sz - 1),
float(i * j) / (x_sz * y_sz - 1) }));
}
// A rectangular region straddling the middle of the image above
ROI region(x_sz / 4, x_sz * 3 / 4, y_sz / 4, y_sz * 3 / 4, 0, 1, 0, 3);
if (make_black(A, region)) {
A.write("set-region-black.exr");
} else {
std::cout << "error: buffer is not a float buffer\n";
}
}



// BEGIN-imagebuf-iterator-template
#include <OpenImageIO/half.h>
template<typename BUFT>
static bool make_black_impl(ImageBuf& buf, ROI region)
{
// Clamp the region's channel range to the channels in the image
region.chend = std::min(region.chend, buf.nchannels());

// Iterate over all pixels in the region...
for (ImageBuf::Iterator<BUFT> it(buf, region); !it.done(); ++it) {
if (!it.exists()) // Make sure the iterator is pointing
continue; // to a pixel in the data window
for (int c = region.chbegin; c < region.chend; ++c)
it[c] = 0.0f; // clear the value
}
return true;
}

bool make_black_templated(ImageBuf& buf, ROI region)
{
if (buf.spec().format == TypeDesc::FLOAT)
return make_black_impl<float>(buf, region);
else if (buf.spec().format == TypeDesc::HALF)
return make_black_impl<half>(buf, region);
else if (buf.spec().format == TypeDesc::UINT8)
return make_black_impl<unsigned char>(buf, region);
else if (buf.spec().format == TypeDesc::UINT16)
return make_black_impl<unsigned short>(buf, region);
else {
buf.errorf("Unsupported pixel data format %s",
buf.spec().format.c_str());
return false;
}
}
// END-imagebuf-iterator-template

void make_black_template_example()
{
int x_sz = 640;
int y_sz = 480;
ImageBuf A(ImageSpec(x_sz, y_sz, 3, TypeDesc::FLOAT));
for (int i = 0; i < x_sz; ++i)
for (int j = 0; j < y_sz; ++j) {
// Create RGB gradient so region changing is easy to see
A.setpixel(i, j, 0,
cspan<float>({ float(i) / (x_sz - 1),
float(j) / (y_sz - 1),
float(i * j) / (x_sz * y_sz - 1) }));
}
// A rectangular region straddling the middle of the image above
ROI region(x_sz / 4, x_sz * 3 / 4, y_sz / 4, y_sz * 3 / 4, 0, 1, 0, 3);
if (make_black_templated(A, region)) {
A.write("set-region-black-template-float.exr");
} else {
std::cout << "error: " << A.geterror() << "\n";
}

ImageBuf B(ImageSpec(x_sz, y_sz, 3, TypeDesc::UINT8));
for (int i = 0; i < x_sz; ++i)
for (int j = 0; j < y_sz; ++j) {
// Create RGB gradient so region changing is easy to see
B.setpixel(i, j, 0,
cspan<float>({ float(i) / (x_sz - 1),
float(j) / (y_sz - 1),
float(i * j) / (x_sz * y_sz - 1) }));
}
// A rectangular region straddling the middle of the image above
if (make_black_templated(B, region)) {
B.write("set-region-black-template-uint8.exr");
} else {
std::cout << "error: " << B.geterror() << "\n";
}
}

// BEGIN-imagebuf-dispatch
#include <OpenImageIO/imagebufalgo_util.h>
bool make_black_dispatch(ImageBuf& buf, ROI region)
{
bool ok;
OIIO_DISPATCH_COMMON_TYPES(ok, "make_black_dispatch", make_black_impl,
buf.spec().format, buf, region);
return ok;
}
// END-imagebuf-dispatch

void make_black_dispatch_example()
{
int x_sz = 640;
int y_sz = 480;
ImageBuf A(ImageSpec(x_sz, y_sz, 3, TypeDesc::UINT16));
for (int i = 0; i < x_sz; ++i)
for (int j = 0; j < y_sz; ++j) {
// Create RGB gradient so region changing is easy to see
A.setpixel(i, j, 0,
cspan<float>({ float(i) / (x_sz - 1),
float(j) / (y_sz - 1),
float(i * j) / (x_sz * y_sz - 1) }));
}
// A rectangular region straddling the middle of the image above
ROI region(x_sz / 4, x_sz * 3 / 4, y_sz / 4, y_sz * 3 / 4, 0, 1, 0, 3);
if (make_black_templated(A, region)) {
A.write("set-region-black-template-dispatch.exr");
} else {
std::cout << "error: " << A.geterror() << "\n";
}
}

int main(int /*argc*/, char** /*argv*/)
{
// Each example function needs to get called here, or it won't execute
// as part of the test.
example1();
// example1();
print_channel_averages_example();
make_black_example();
make_black_template_example();
make_black_dispatch_example();
return 0;
}

0 comments on commit f9a8b45

Please sign in to comment.