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

IBA::normalize() - Normalize 3D vectors (i.e., divide by their length) textures. #3945

Merged
merged 12 commits into from
Aug 26, 2023
3 changes: 3 additions & 0 deletions src/cmake/compiler.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ if (CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_APPLECLANG)
# Don't warn about using unknown preprocessor symbols in `#if`
add_compile_options ("-Wno-expansion-to-defined")
endif ()
if (CMAKE_GENERATOR MATCHES "Xcode")
add_compile_options ("-Wno-shorten-64-to-32")
endif ()
endif ()

if (CMAKE_COMPILER_IS_GNUCC AND NOT (CMAKE_COMPILER_IS_CLANG OR CMAKE_COMPILER_IS_APPLECLANG))
Expand Down
28 changes: 28 additions & 0 deletions src/include/OpenImageIO/imagebufalgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,34 @@ ImageBuf OIIO_API pow (const ImageBuf &A, cspan<float> B,
bool OIIO_API pow (ImageBuf &dst, const ImageBuf &A, cspan<float> B,
ROI roi={}, int nthreads=0);

/// Normalize a 3D vector texture (i.e., divide each pixel by its length).
/// This function assumes a 3-channel image that represents a 3-vector, or a
/// 4-channel image that represents a 3-vector plus an alpha value. If an
/// alpha channel is present, its value is merely copied, and is not part of
/// the normalization computation. If the destination has no alpha channel but
/// the sources do, the alpha channel will be dropped.
///
/// `inCenter` and `outCenter` define the pixel value that corresponds to a
/// 0.0 vector value for input and output, respectively. `scale` defines the
/// scale factor to apply to the normalized vectors.
///
/// Thus, if the input image encodes vector components into [0,1] range pixel
/// values so that a pixel value 0.5 indicates a 0-length vector, then you
/// should use `inCenter=0.5`, whereas if they are already using the full
/// range (0.0 is encoded as 0.0), then you want `inCenter=0.0`. Similarly, if
/// you want the output normalized vectors to be in the range [0,1], use
/// `outCenter=0.5` and `scale=0.5`, but if you want them to be in the range
/// [-1,1], use `outCenter=0.0` and `scale=1.0` (this probably will only work
/// if you intend to write the results in `float` or `half` format).
///
bool OIIO_API normalize(ImageBuf& dst, const ImageBuf& A, float inCenter=0.0f,
float outCenter=0.0f, float scale=1.0f,
ROI roi={}, int nthreads=0);

ImageBuf OIIO_API normalize(const ImageBuf& A, float inCenter=0.0f,
float outCenter=0.0, float scale=1.0f,
ROI roi={}, int nthreads=0);


/// Converts a multi-channel image into a one-channel image via a weighted
/// sum of channels:
Expand Down
65 changes: 65 additions & 0 deletions src/libOpenImageIO/imagebufalgo_pixelmath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,71 @@ ImageBufAlgo::pow(const ImageBuf& A, cspan<float> b, ROI roi, int nthreads)
return result;
}

template<class Rtype>
static bool
normalize_impl(ImageBuf& R, const ImageBuf& A, float inCenter, float outCenter,
float scale, ROI roi, int nthreads)
{
ImageBufAlgo::parallel_image(roi, nthreads, [&](ROI roi) {
ImageBuf::ConstIterator<Rtype> a(A, roi);
for (ImageBuf::Iterator<Rtype> r(R, roi); !r.done(); ++r, ++a) {
float x = a[0] - inCenter;
float y = a[1] - inCenter;
float z = a[2] - inCenter;

float length = std::sqrt(x * x + y * y + z * z);

float s = (length > 0.0f) ? scale / length : 0.0f;

r[0] = x * s + outCenter;
r[1] = y * s + outCenter;
r[2] = z * s + outCenter;

if (R.spec().nchannels == 4) {
r[3] = a[3];
}
}
});
return true;
}

bool
ImageBufAlgo::normalize(ImageBuf& dst, const ImageBuf& src, float inCenter,
float outCenter, float scale, ROI roi, int nthreads)
{
if (!ImageBufAlgo::IBAprep(roi, &dst, &src))
return false;

if (src.spec().nchannels != 3 && src.spec().nchannels != 4) {
src.errorfmt("normalize can only handle 3- or 4-channel images");
return false;
}
if (src.spec().nchannels < dst.spec().nchannels) {
dst.errorfmt(
"destination buffer can`t have more channels than the source");
return false;
}

bool ok;
OIIO_DISPATCH_COMMON_TYPES(ok, "normalize", normalize_impl,
dst.spec().format, dst, src, inCenter, outCenter,
scale, roi, nthreads);

return ok;
}

ImageBuf
ImageBufAlgo::normalize(const ImageBuf& A, float inCenter, float outCenter,
float scale, ROI roi, int nthreads)
{
ImageBuf result;
bool ok = ImageBufAlgo::normalize(result, A, inCenter, outCenter, scale,
roi, nthreads);
if (!ok && !result.has_error())
result.errorfmt("normalize error");
return result;
}
ssh4net marked this conversation as resolved.
Show resolved Hide resolved



template<class D, class S>
Expand Down
16 changes: 14 additions & 2 deletions src/oiiotool/oiiotool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4655,8 +4655,6 @@ OIIOTOOL_OP(unsharp, 1, [&](OiiotoolOp& op, span<ImageBuf*> img) {
threshold);
});



UNARY_IMAGE_OP(laplacian, ImageBufAlgo::laplacian); // --laplacian
UNARY_IMAGE_OP(fft, ImageBufAlgo::fft); // --fft
UNARY_IMAGE_OP(ifft, ImageBufAlgo::ifft); // --ifft
Expand All @@ -4665,6 +4663,17 @@ UNARY_IMAGE_OP(unpolar, ImageBufAlgo::polar_to_complex); // --unpolar



// --normalize
OIIOTOOL_OP(normalize, 1, [&](OiiotoolOp& op, span<ImageBuf*> img) {
float inCenter = op.options().get_float("incenter", 0.0f);
float outCenter = op.options().get_float("outcenter", 0.0f);
float scale = op.options().get_float("scale", 1.0f);
return ImageBufAlgo::normalize(*img[0], *img[1], inCenter, outCenter,
scale);
});



// --fixnan
void
action_fixnan(Oiiotool& ot, cspan<const char*> argv)
Expand Down Expand Up @@ -6864,6 +6873,9 @@ Oiiotool::getargs(int argc, char* argv[])
ap.arg("--laplacian")
.help("Laplacian filter the image")
.OTACTION(action_laplacian);
ap.arg("--normalize")
.help("Normalize the image (options: incenter=0.5, outcenter=0.5, scale=0.5)")
.OTACTION(action_normalize);
ap.arg("--fft")
.help("Take the FFT of the image")
.OTACTION(action_fft);
Expand Down
29 changes: 29 additions & 0 deletions src/python/py_imagebufalgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,28 @@ IBA_complex_to_polar_ret(const ImageBuf& src, ROI roi, int nthreads)



bool
IBA_normalize(ImageBuf& dst, const ImageBuf& src, float inCenter,
float outCenter, float scale, ROI roi, int nthreads)
{
py::gil_scoped_release gil;
return ImageBufAlgo::normalize(dst, src, inCenter, outCenter, scale, roi,
nthreads);
}



ImageBuf
IBA_normalize_ret(const ImageBuf& src, float inCenter, float outCenter,
float scale, ROI roi, int nthreads)
{
py::gil_scoped_release gil;
return ImageBufAlgo::normalize(src, inCenter, outCenter, scale, roi,
nthreads);
}



bool
IBA_fillholes_pushpull(ImageBuf& dst, const ImageBuf& src, ROI roi,
int nthreads)
Expand Down Expand Up @@ -2959,6 +2981,13 @@ declare_imagebufalgo(py::module& m)
"mode"_a = ImageBufAlgo::NONFINITE_BOX3,
"roi"_a = ROI::All(), "nthreads"_a = 0)

.def_static("normalize", &IBA_normalize, "dst"_a, "src"_a,
"inCenter"_a = 0.0f, "outCenter"_a = 0.0f, "scale"_a = 1.0f,
"roi"_a = ROI::All(), "nthreads"_a = 0)
.def_static("normalize", &IBA_normalize_ret, "src"_a,
"inCenter"_a = 0.0f, "outCenter"_a = 0.0f, "scale"_a = 1.0f,
"roi"_a = ROI::All(), "nthreads"_a = 0)

.def_static("fillholes_pushpull", &IBA_fillholes_pushpull, "dst"_a,
"src"_a, "roi"_a = ROI::All(), "nthreads"_a = 0)
.def_static("fillholes_pushpull", &IBA_fillholes_pushpull_ret, "src"_a,
Expand Down
Binary file added testsuite/common/vectorschart_raw.tif
Binary file not shown.
Binary file added testsuite/common/vectorschart_raw_xyza.exr
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ Comparing "resample.tif" and "../../../testsuite/oiiotool-xform/ref/resample.tif
PASS
Comparing "fit.tif" and "../../../testsuite/oiiotool-xform/ref/fit.tif"
PASS
Comparing "normalize_uiui.tif" and "ref/normalize_uiui.tif"
PASS
Comparing "normalize_uifl.exr" and "ref/normalize_uifl.exr"
PASS
Comparing "normalize_flfl.exr" and "ref/normalize_flfl.exr"
PASS
Comparing "normalize_flui.tif" and "ref/normalize_flui.tif"
PASS
Comparing "normalize_flui_na.tif" and "ref/normalize_flui_na.tif"
PASS
Comparing "bsplinekernel.exr" and "../../../testsuite/oiiotool/ref/bsplinekernel.exr"
PASS
Comparing "bspline-blur.tif" and "../../../testsuite/oiiotool/ref/bspline-blur.tif"
Expand Down
10 changes: 10 additions & 0 deletions testsuite/python-imagebufalgo/ref/out-freetype2.4.11.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ Comparing "resample.tif" and "../../../testsuite/oiiotool-xform/ref/resample.tif
PASS
Comparing "fit.tif" and "../../../testsuite/oiiotool-xform/ref/fit.tif"
PASS
Comparing "normalize_uiui.tif" and "ref/normalize_uiui.tif"
PASS
Comparing "normalize_uifl.exr" and "ref/normalize_uifl.exr"
PASS
Comparing "normalize_flfl.exr" and "ref/normalize_flfl.exr"
PASS
Comparing "normalize_flui.tif" and "ref/normalize_flui.tif"
PASS
Comparing "normalize_flui_na.tif" and "ref/normalize_flui_na.tif"
PASS
Comparing "bsplinekernel.exr" and "../../../testsuite/oiiotool/ref/bsplinekernel.exr"
PASS
Comparing "bspline-blur.tif" and "../../../testsuite/oiiotool/ref/bspline-blur.tif"
Expand Down
10 changes: 10 additions & 0 deletions testsuite/python-imagebufalgo/ref/out-python2-alt.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ Comparing "resample.tif" and "../../../testsuite/oiiotool-xform/ref/resample.tif
PASS
Comparing "fit.tif" and "../../../testsuite/oiiotool-xform/ref/fit.tif"
PASS
Comparing "normalize_uiui.tif" and "ref/normalize_uiui.tif"
PASS
Comparing "normalize_uifl.exr" and "ref/normalize_uifl.exr"
PASS
Comparing "normalize_flfl.exr" and "ref/normalize_flfl.exr"
PASS
Comparing "normalize_flui.tif" and "ref/normalize_flui.tif"
PASS
Comparing "normalize_flui_na.tif" and "ref/normalize_flui_na.tif"
PASS
Comparing "bsplinekernel.exr" and "../../../testsuite/oiiotool/ref/bsplinekernel.exr"
PASS
Comparing "bspline-blur.tif" and "../../../testsuite/oiiotool/ref/bspline-blur.tif"
Expand Down
10 changes: 10 additions & 0 deletions testsuite/python-imagebufalgo/ref/out-python3-freetype2.4.11.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ Comparing "resample.tif" and "../../../testsuite/oiiotool-xform/ref/resample.tif
PASS
Comparing "fit.tif" and "../../../testsuite/oiiotool-xform/ref/fit.tif"
PASS
Comparing "normalize_uiui.tif" and "ref/normalize_uiui.tif"
PASS
Comparing "normalize_uifl.exr" and "ref/normalize_uifl.exr"
PASS
Comparing "normalize_flfl.exr" and "ref/normalize_flfl.exr"
PASS
Comparing "normalize_flui.tif" and "ref/normalize_flui.tif"
PASS
Comparing "normalize_flui_na.tif" and "ref/normalize_flui_na.tif"
PASS
Comparing "bsplinekernel.exr" and "../../../testsuite/oiiotool/ref/bsplinekernel.exr"
PASS
Comparing "bspline-blur.tif" and "../../../testsuite/oiiotool/ref/bspline-blur.tif"
Expand Down
10 changes: 10 additions & 0 deletions testsuite/python-imagebufalgo/ref/out-python3.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ Comparing "resample.tif" and "../../../testsuite/oiiotool-xform/ref/resample.tif
PASS
Comparing "fit.tif" and "../../../testsuite/oiiotool-xform/ref/fit.tif"
PASS
Comparing "normalize_uiui.tif" and "ref/normalize_uiui.tif"
PASS
Comparing "normalize_uifl.exr" and "ref/normalize_uifl.exr"
PASS
Comparing "normalize_flfl.exr" and "ref/normalize_flfl.exr"
PASS
Comparing "normalize_flui.tif" and "ref/normalize_flui.tif"
PASS
Comparing "normalize_flui_na.tif" and "ref/normalize_flui_na.tif"
PASS
Comparing "bsplinekernel.exr" and "../../../testsuite/oiiotool/ref/bsplinekernel.exr"
PASS
Comparing "bspline-blur.tif" and "../../../testsuite/oiiotool/ref/bspline-blur.tif"
Expand Down
10 changes: 10 additions & 0 deletions testsuite/python-imagebufalgo/ref/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ Comparing "resample.tif" and "../../../testsuite/oiiotool-xform/ref/resample.tif
PASS
Comparing "fit.tif" and "../../../testsuite/oiiotool-xform/ref/fit.tif"
PASS
Comparing "normalize_uiui.tif" and "ref/normalize_uiui.tif"
PASS
Comparing "normalize_uifl.exr" and "ref/normalize_uifl.exr"
PASS
Comparing "normalize_flfl.exr" and "ref/normalize_flfl.exr"
PASS
Comparing "normalize_flui.tif" and "ref/normalize_flui.tif"
PASS
Comparing "normalize_flui_na.tif" and "ref/normalize_flui_na.tif"
PASS
Comparing "bsplinekernel.exr" and "../../../testsuite/oiiotool/ref/bsplinekernel.exr"
PASS
Comparing "bspline-blur.tif" and "../../../testsuite/oiiotool/ref/bspline-blur.tif"
Expand Down
3 changes: 3 additions & 0 deletions testsuite/python-imagebufalgo/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
"contrast-sigmoid5.tif",
"saturate-0.tif", "saturate-2.tif",
"resize.tif", "resample.tif", "fit.tif",
"normalize_uiui.tif", "normalize_uifl.exr",
"normalize_flfl.exr", "normalize_flui.tif",
"normalize_flui_na.tif",
"bsplinekernel.exr", "bspline-blur.tif", "tahoe-median.tif",
"dilate.tif", "erode.tif",
"unsharp.tif", "unsharp-median.tif", "tahoe-laplacian.tif",
Expand Down
17 changes: 17 additions & 0 deletions testsuite/python-imagebufalgo/src/test_imagebufalgo.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,23 @@ def test_iba (func, *args, **kwargs) :
b = test_iba (ImageBufAlgo.invert, a)
write (b, "invert.tif", oiio.UINT8)

# normalize
a = ImageBuf (OIIO_TESTSUITE_ROOT+"/common/vectorschart_raw.tif")
b = test_iba (ImageBufAlgo.normalize, a, 0.5, 0.5, 0.5)
write (b, "normalize_uiui.tif", oiio.UINT16)
b = test_iba (ImageBufAlgo.normalize, a, 0.5, 0.0, 1.0)
write (b, "normalize_uifl.exr", oiio.HALF)

a = ImageBuf (OIIO_TESTSUITE_ROOT+"/common/vectorschart_raw_xyza.exr")
b = test_iba (ImageBufAlgo.normalize, a, 0.0, 0.0, 1.0)
write (b, "normalize_flfl.exr", oiio.HALF)
b = test_iba (ImageBufAlgo.normalize, a, 0.0, 0.5, 0.5)
write (b, "normalize_flui.tif", oiio.UINT16)
b = ImageBuf()
b.specmod().nchannels = 3
b = test_iba (ImageBufAlgo.normalize, a, 0.0, 0.5, 0.5)
write (b, "normalize_flui_na.tif", oiio.UINT16)

# pow
b = ImageBufAlgo.pow (gray128, 2)
write (b, "cpow1.exr")
Expand Down
Loading