Skip to content

Commit

Permalink
feat(IBA): IBA::normalize() - Normalize 3D vectors (i.e., divide by t…
Browse files Browse the repository at this point in the history
…heir length) textures. (#3945)

Normalize 3D vectors (i.e., divide by their length) textures.

inCenter and outCenter define 0.0 coordinate in input and output 
3D vectors encodings (e.g. 0.0f for [-1,1] or 0.5f for [0,1] encoding).
 
scale defines the scale factor to apply to the normalized vectors 
(i.e. 1.0f for a full range normalization, 0.5f for 0.0-1.0 range
encoding).

Default values are for [-1,1] encoding and full range normalization.

Functions required 3 or 4 channels. If destination have no alpha
channel,
but source have, alpha channel will be dropped.

## Tests

```
int main(int argc, const char* argv[])
{
    ImageBuf input(argv[1]);
    ImageBuf output;

    bool ok = ImageBufAlgo::normalize(output, input, 0.0f, 0.0f, 1.0f);
    if (ok)
    {
		ok = output.write(argv[2]);
    }
    else {
        std::cerr << "Could not normalize image: " << argv[1] << std::endl;
        return -1;
    }
    return 0;
}
```

---------

Signed-off-by: ssh4net <[email protected]>
Signed-off-by: Anton Dukhovnikov <[email protected]>
Signed-off-by: Vlad Erium <[email protected]>
Signed-off-by: Larry Gritz <[email protected]>
Co-authored-by: Anton Dukhovnikov <[email protected]>
Co-authored-by: Larry Gritz <[email protected]>
  • Loading branch information
3 people committed Aug 26, 2023
1 parent a5c7a0c commit b445bc6
Show file tree
Hide file tree
Showing 19 changed files with 216 additions and 2 deletions.
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;
}



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

0 comments on commit b445bc6

Please sign in to comment.