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

feat(IBA): IBA::scale() #4541

Merged
merged 7 commits into from
Dec 1, 2024
Merged
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
Binary file added src/doc/figures/scale.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions src/doc/imagebufalgo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1329,6 +1329,38 @@ Image arithmetic

|

.. doxygenfunction:: scale(const ImageBuf &A, const ImageBuf &B, KWArgs options = {}, ROI roi = {}, int nthreads = 0)
..

Result-as-parameter version:

.. doxygenfunction:: scale(ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, KWArgs options = {}, ROI roi = {}, int nthreads = 0)

Examples:

.. tabs::

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

.. tab:: Python
.. literalinclude:: ../../testsuite/docs-examples-python/src/docs-examples-imagebufalgo.py
:language: py
:start-after: BEGIN-imagebufalgo-scale
:end-before: END-imagebufalgo-scale
:dedent: 4

.. code-tab:: bash oiiotool

# Pixel-by-pixel multiplication of all channels of one image by the only channel of another image
oiiotool a.exr mono.exr --scale -o scale.exr

|

.. doxygenfunction:: mul(Image_or_Const A, Image_or_Const B, ROI roi = {}, int nthreads = 0)
..

Expand Down
21 changes: 21 additions & 0 deletions src/doc/oiiotool.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2507,6 +2507,27 @@ current top image.
Include/exclude subimages (see :ref:`sec-oiiotool-subimage-modifier`).


.. option:: --scale

Replace the *two* top images with a new image that is the pixel-by-pixel
multiplicative product of those images. One of the images must have a single
channel, that channel's pixel value is used to scale all channels of the
other image by.


Example::

# Apply vertical gradient
oiiotool tahoe.jpg --pattern fill:top=0.5:bottom=1 512x384 1 --scale -o scale.jpg
..

.. image:: figures/tahoe-small.jpg
:width: 2.0 in
.. image:: figures/scale.jpg
:width: 2.0 in
|


.. option:: --mul
--mulc <value>
--mulc <value0,value1,value2...>
Expand Down
17 changes: 17 additions & 0 deletions src/doc/pythonbindings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2833,6 +2833,23 @@ Image arithmetic



.. py:method:: ImageBuf ImageBufAlgo.scale (A, B, roi=ROI.All, nthreads=0)
bool ImageBufAlgo.scale (dst, A, B, roi=ROI.All, nthreads=0)

Per-pixel multiply all channels of one image by the single channle of the
other image. One of the input images must have only one channel.

Example:

.. code-block:: python

# Scale one image by the other
buf = ImageBufAlgo.scale (ImageBuf("a.exr"), ImageBuf("mono.exr"))

# Scale one image by the other, in place
ImageBufAlgo.scale (buf, buf, ImageBuf("mono.exr"))


.. py:method:: ImageBuf ImageBufAlgo.mul (A, B, roi=ROI.All, nthreads=0)
bool ImageBufAlgo.mul (dst, A, B, roi=ROI.All, nthreads=0)

Expand Down
15 changes: 15 additions & 0 deletions src/include/OpenImageIO/imagebufalgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,21 @@ ImageBuf OIIO_API abs (const ImageBuf &A, ROI roi={}, int nthreads=0);
bool OIIO_API abs (ImageBuf &dst, const ImageBuf &A, ROI roi={}, int nthreads=0);


/// Compute per-pixel product `A * B`, returning the result image. At least
/// one of `A` and `B` must be a single channel image, whose value is used to
/// scale all channels of the other image.
///
/// @param options
/// Optional ParamValue's that may control the reconstruction.
/// (Reserved for future expansion.)
///
ImageBuf OIIO_API scale (const ImageBuf &A, const ImageBuf &B,
KWArgs options = {}, ROI roi={}, int nthreads=0);
/// Write to an existing image `dst` (allocating if it is uninitialized).
bool OIIO_API scale (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B,
KWArgs options = {}, ROI roi={}, int nthreads=0);


/// Compute per-pixel product `A * B`, returning the result image.
///
/// Either both `A` and `B` are images, or one is an image and the other is
Expand Down
59 changes: 59 additions & 0 deletions src/libOpenImageIO/imagebufalgo_muldiv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,65 @@
OIIO_NAMESPACE_BEGIN


template<class Rtype, class Atype, class Btype>
static bool
scale_impl(ImageBuf& R, const ImageBuf& A, const ImageBuf& B, ROI roi,
int nthreads)
{
ImageBufAlgo::parallel_image(roi, nthreads, [&](ROI roi) {
ImageBuf::Iterator<Rtype> r(R, roi);
ImageBuf::ConstIterator<Atype> a(A, roi);
ImageBuf::ConstIterator<Btype> b(B, roi);
for (; !r.done(); ++r, ++a, ++b)
for (int c = roi.chbegin; c < roi.chend; ++c)
r[c] = a[c] * b[0];
});
return true;
}



bool
ImageBufAlgo::scale(ImageBuf& dst, const ImageBuf& A, const ImageBuf& B,
KWArgs options, ROI roi, int nthreads)
{
pvt::LoggedTimer logtime("IBA::scale");
bool ok = false;
if (B.nchannels() == 1) {
if (IBAprep(roi, &dst, &A, &B))
OIIO_DISPATCH_COMMON_TYPES3(ok, "scale", scale_impl,
dst.spec().format, A.spec().format,
B.spec().format, dst, A, B, roi,
nthreads);
} else if (A.nchannels() == 1) {
if (IBAprep(roi, &dst, &A, &B))
OIIO_DISPATCH_COMMON_TYPES3(ok, "scale", scale_impl,
dst.spec().format, B.spec().format,
A.spec().format, dst, B, A, roi,
nthreads);
} else {
dst.errorfmt(
"ImageBufAlgo::scale(): one of the arguments must be a single channel image.");
}

return ok;
}



ImageBuf
ImageBufAlgo::scale(const ImageBuf& A, const ImageBuf& B, KWArgs options,
ROI roi, int nthreads)
{
ImageBuf result;
bool ok = scale(result, A, B, options, roi, nthreads);
if (!ok && !result.has_error())
result.errorfmt("ImageBufAlgo::scale() error");
return result;
}



template<class Rtype, class Atype, class Btype>
static bool
mul_impl(ImageBuf& R, const ImageBuf& A, const ImageBuf& B, ROI roi,
Expand Down
4 changes: 4 additions & 0 deletions src/oiiotool/oiiotool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3090,6 +3090,7 @@ BINARY_IMAGE_OP(add, ImageBufAlgo::add); // --add
BINARY_IMAGE_OP(sub, ImageBufAlgo::sub); // --sub
BINARY_IMAGE_OP(mul, ImageBufAlgo::mul); // --mul
BINARY_IMAGE_OP(div, ImageBufAlgo::div); // --div
BINARY_IMAGE_OP(scale, ImageBufAlgo::scale); // --scale
BINARY_IMAGE_OP(absdiff, ImageBufAlgo::absdiff); // --absdiff

BINARY_IMAGE_COLOR_OP(addc, ImageBufAlgo::add, 0); // --addc
Expand Down Expand Up @@ -6644,6 +6645,9 @@ Oiiotool::getargs(int argc, char* argv[])
ap.arg("--csub %s:VAL")
.hidden() // Deprecated synonym
.OTACTION(action_subc);
ap.arg("--scale")
.help("Scale all channels of one image by the single channel of another image")
.OTACTION(action_scale);
ap.arg("--mul")
.help("Multiply two images")
.OTACTION(action_mul);
Expand Down
21 changes: 21 additions & 0 deletions src/python/py_imagebufalgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,22 @@ IBA_abs_ret(const ImageBuf& A, ROI roi = ROI::All(), int nthreads = 0)



bool
IBA_scale_images(ImageBuf& dst, const ImageBuf& A, const ImageBuf& B,
ROI roi = ROI::All(), int nthreads = 0)
{
py::gil_scoped_release gil;
return ImageBufAlgo::scale(dst, A, B, {}, roi, nthreads);
}

ImageBuf
IBA_scale_images_ret(const ImageBuf& A, const ImageBuf& B, ROI roi = ROI::All(),
int nthreads = 0)
{
py::gil_scoped_release gil;
return ImageBufAlgo::scale(A, B, {}, roi, nthreads);
}

bool
IBA_mul_color(ImageBuf& dst, const ImageBuf& A, py::object values_tuple,
ROI roi = ROI::All(), int nthreads = 0)
Expand Down Expand Up @@ -2625,6 +2641,11 @@ declare_imagebufalgo(py::module& m)
.def_static("abs", &IBA_abs_ret, "A"_a, "roi"_a = ROI::All(),
"nthreads"_a = 0)

.def_static("scale", &IBA_scale_images, "dst"_a, "A"_a, "B"_a,
"roi"_a = ROI::All(), "nthreads"_a = 0)
.def_static("scale", &IBA_scale_images_ret, "A"_a, "B"_a,
"roi"_a = ROI::All(), "nthreads"_a = 0)

.def_static("mul", &IBA_mul_images, "dst"_a, "A"_a, "B"_a,
"roi"_a = ROI::All(), "nthreads"_a = 0)
.def_static("mul", &IBA_mul_color, "dst"_a, "A"_a, "B"_a,
Expand Down
1 change: 1 addition & 0 deletions testsuite/docs-examples-cpp/ref/out-arm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ example_add
example_sub
example_absdiff
example_abs
example_scale
example_mul
example_div
example_fixNonFinite
Expand Down
1 change: 1 addition & 0 deletions testsuite/docs-examples-cpp/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ example_add
example_sub
example_absdiff
example_abs
example_scale
example_mul
example_div
example_fixNonFinite
Expand Down
2 changes: 2 additions & 0 deletions testsuite/docs-examples-cpp/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
command += run_app("cmake -E copy " + test_source_dir + "/../common/unpremult.tif unpremult.tif")
command += run_app("cmake -E copy " + test_source_dir + "/../common/bayer.png bayer.png")

command += oiio_app("oiiotool") + "--pattern fill:top=0:bottom=1 256x256 1 -o mono.exr > out.txt ;"

# Copy the grid to both a tiled and scanline version
command += oiio_app("iconvert") + "../common/grid.tif --scanline scanline.tif > out.txt ;"
command += oiio_app("iconvert") + "../common/grid.tif --tile 64 64 tiled.tif > out.txt ;"
Expand Down
14 changes: 14 additions & 0 deletions testsuite/docs-examples-cpp/src/docs-examples-imagebufalgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,19 @@ void example_abs()
Abs.write("abs.exr");
}

void example_scale()
{
print("example_scale\n");
// BEGIN-imagebufalgo-scale
// Pixel-by-pixel multiplication of all channels of A by the single channel of B
ImageBuf A("A.exr");
ImageBuf B("mono.exr");
ImageBuf Product = ImageBufAlgo::scale(A, B);

// END-imagebufalgo-scale
Product.write("scale.exr");
}

void example_mul()
{
print("example_mul\n");
Expand Down Expand Up @@ -725,6 +738,7 @@ int main(int /*argc*/, char** /*argv*/)
example_sub();
example_absdiff();
example_abs();
example_scale();
example_mul();
example_div();

Expand Down
1 change: 1 addition & 0 deletions testsuite/docs-examples-python/ref/out-arm.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ example_add
example_sub
example_absdiff
example_abs
example_scale
example_mul
example_div
example_fixNonFinite
Expand Down
1 change: 1 addition & 0 deletions testsuite/docs-examples-python/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ example_add
example_sub
example_absdiff
example_abs
example_scale
example_mul
example_div
example_fixNonFinite
Expand Down
2 changes: 2 additions & 0 deletions testsuite/docs-examples-python/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
command += run_app("cmake -E copy " + test_source_dir + "/../common/unpremult.tif unpremult.tif")
command += run_app("cmake -E copy " + test_source_dir + "/../common/bayer.png bayer.png")

command += oiio_app("oiiotool") + "--pattern fill:top=0:bottom=1 256x256 1 -o mono.exr > out.txt ;"

# Copy the grid to both a tiled and scanline version
command += oiio_app("iconvert") + "../common/grid.tif --scanline scanline.tif > out.txt ;"
command += oiio_app("iconvert") + "../common/grid.tif --tile 64 64 tiled.tif > out.txt ;"
Expand Down
11 changes: 11 additions & 0 deletions testsuite/docs-examples-python/src/docs-examples-imagebufalgo.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,16 @@ def example_abs():
# END-imagebufalgo-absolute
Abs.write("abs.exr", "half")

def example_scale():
print("example_scale")
# BEGIN-imagebufalgo-scale
# Pixel-by-pixel multiplication of all channels of one image A by the single channel of the other image
A = ImageBuf("A.exr")
B = ImageBuf("mono.exr")
Product = ImageBufAlgo.scale (A, B)
#END-imagebufalgo-scale
Product.write("scale.exr", "half")

def example_mul():
print("example_mul")
# BEGIN-imagebufalgo-mul
Expand Down Expand Up @@ -626,6 +636,7 @@ def example_make_texture():
example_sub()
example_absdiff()
example_abs()
example_scale()
example_mul()
example_div()

Expand Down
5 changes: 5 additions & 0 deletions testsuite/oiiotool/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
+ " --sub -d half -o sub.exr")
command += oiiotool ("--pattern constant:color=.1,.2,.3 64x64+0+0 3 "
+ " --subc 0.1,0.1,0.1 -d half -o subc.exr")

# Test -- scale
command += oiiotool ("--pattern fill:topleft=0,0,1:topright=0,1,0:bottomleft=1,0,1:bottomright=1,1,0 64x64 3"
+ " --pattern fill:top=0:bottom=1 64x64 1"
+ " --scale -o scale.exr")

# test --mul of images
command += oiiotool ("grey64.exr -pattern constant:color=1.5,1,0.5 64x64 3 --mul -o mul.exr")
Expand Down
9 changes: 9 additions & 0 deletions testsuite/python-imagebufalgo/src/test_imagebufalgo.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,15 @@ def test_iba (func, *args, **kwargs) :
b = test_iba (ImageBufAlgo.absdiff, a, (0.2,0.2,0.2))
write (b, "absdiff.exr", oiio.HALF)
a = ImageBuf()

# scale
a = ImageBuf(ImageSpec(128, 128, 3, oiio.HALF))
ImageBufAlgo.fill(a, topleft = (0, 0, 1), topright = (0, 1, 0),
bottomleft = (1, 0, 1), bottomright = (1, 1, 0))
b = ImageBuf(ImageSpec(128, 128, 1, oiio.HALF))
ImageBufAlgo.fill(a, top = 0, bottom = 1)
b = test_iba(ImageBufAlgo.scale, a, b)
a = ImageBuf()

# mul
b = ImageBufAlgo.mul (gray128, 1.5)
Expand Down
Loading