Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imagebuf docs examples: c++ examples, outfile, run script, and doc (#3992) #4020

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Comment on lines +377 to +378
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the C++ code, you called this BEGIN/END-imagebuf-get-pixel-avg

: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::
Copy link
Collaborator

@lgritz lgritz Oct 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be

Suggested change
.. tabs::
.. tabs:

In Sphinx rst, a :: ending a text sentence means "the next indented region is code". So if we replace the ":: followed by indented region" with a .. tab:: section, we don't want that treated as a code example, we want it interpreted as commands.


.. 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's important not to us dedent here, because the code you're cutting from already starts in column 0.


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`_ ::
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another spot where the :: needs to change to : because we replaced intended code itself with .. tabs:: commands.


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::
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
chapter). You could rewrite the example even more simply::
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;
}
Loading