diff --git a/src/libOpenImageIO/imagebufalgo_test.cpp b/src/libOpenImageIO/imagebufalgo_test.cpp index 940e2a8ff5..cdc545c9c8 100644 --- a/src/libOpenImageIO/imagebufalgo_test.cpp +++ b/src/libOpenImageIO/imagebufalgo_test.cpp @@ -641,6 +641,41 @@ test_zover() +// Test ImageBuf::resample +void +test_resample() +{ + std::cout << "test resample\n"; + + // Timing + Benchmarker bench; + bench.units(Benchmarker::Unit::ms); + + ImageSpec spec_hd_rgba_f(1920, 1080, 4, TypeFloat); + ImageSpec spec_hd_rgba_u8(1920, 1080, 4, TypeUInt8); + ImageBuf buf_hd_rgba_f(spec_hd_rgba_f); + ImageBuf buf_hd_rgba_u8(spec_hd_rgba_u8); + float red_rgba[] = { 1.0, 0.0, 0.0, 1.0 }; + ImageBufAlgo::fill(buf_hd_rgba_f, red_rgba); + ImageBufAlgo::fill(buf_hd_rgba_u8, red_rgba); + ImageBuf smallf(ImageSpec(1024, 512, 4, TypeFloat)); + ImageBuf smallu8(ImageSpec(1024, 512, 4, TypeUInt8)); + bench(" IBA::resample HD->1024x512 rgba f->f interp ", + [&]() { ImageBufAlgo::resample(smallf, buf_hd_rgba_f, true); }); + bench(" IBA::resample HD->1024x512 rgba f->u8 interp ", + [&]() { ImageBufAlgo::resample(smallu8, buf_hd_rgba_f, true); }); + bench(" IBA::resample HD->1024x512 rgba u8->u8 interp ", + [&]() { ImageBufAlgo::resample(smallu8, buf_hd_rgba_u8, true); }); + bench(" IBA::resample HD->1024x512 rgba f->f no interp ", + [&]() { ImageBufAlgo::resample(smallf, buf_hd_rgba_f, false); }); + bench(" IBA::resample HD->1024x512 rgba f->u8 no interp ", + [&]() { ImageBufAlgo::resample(smallu8, buf_hd_rgba_f, false); }); + bench(" IBA::resample HD->1024x512 rgba u8->u8 no interp ", + [&]() { ImageBufAlgo::resample(smallu8, buf_hd_rgba_u8, false); }); +} + + + // Tests ImageBufAlgo::compare void test_compare() @@ -1581,6 +1616,7 @@ main(int argc, char** argv) test_over(TypeFloat); test_over(TypeHalf); test_zover(); + test_resample(); test_compare(); test_isConstantColor(); test_isConstantChannel(); diff --git a/src/libOpenImageIO/imagebufalgo_xform.cpp b/src/libOpenImageIO/imagebufalgo_xform.cpp index 0abbb1ace8..beb7d1a700 100644 --- a/src/libOpenImageIO/imagebufalgo_xform.cpp +++ b/src/libOpenImageIO/imagebufalgo_xform.cpp @@ -1070,6 +1070,35 @@ ImageBufAlgo::fit(const ImageBuf& src, KWArgs options, ROI roi, int nthreads) +// This operates just like the internals of ImageBuf::interppixel(), but +// reuses the provided iterator to avoid the overhead of constructing a new +// one each time. This speeds it up by 20x! The iterator `it` must already be +// associated with `img`, but it need not be positioned correctly. +template +static bool +interppixel(const ImageBuf& img, ImageBuf::ConstIterator& it, float x, + float y, span pixel, ImageBuf::WrapMode wrap) +{ + int n = std::min(int(pixel.size()), img.spec().nchannels); + float* localpixel = OIIO_ALLOCA(float, n * 4); + float* p[4] = { localpixel, localpixel + n, localpixel + 2 * n, + localpixel + 3 * n }; + x -= 0.5f; + y -= 0.5f; + int xtexel, ytexel; + float xfrac, yfrac; + xfrac = floorfrac(x, &xtexel); + yfrac = floorfrac(y, &ytexel); + it.rerange(xtexel, xtexel + 2, ytexel, ytexel + 2, 0, 1, wrap); + for (int i = 0; i < 4; ++i, ++it) + for (int c = 0; c < n; ++c) + p[i][c] = it[c]; //NOSONAR + bilerp(p[0], p[1], p[2], p[3], xfrac, yfrac, n, pixel.data()); + return true; +} + + + template static bool resample_(ImageBuf& dst, const ImageBuf& src, bool interpolate, ROI roi, @@ -1080,7 +1109,6 @@ resample_(ImageBuf& dst, const ImageBuf& src, bool interpolate, ROI roi, const ImageSpec& srcspec(src.spec()); const ImageSpec& dstspec(dst.spec()); int nchannels = src.nchannels(); - bool deep = src.deep(); // Local copies of the source image window, converted to float float srcfx = srcspec.full_x; @@ -1109,25 +1137,10 @@ resample_(ImageBuf& dst, const ImageBuf& src, bool interpolate, ROI roi, float s = (x - dstfx + 0.5f) * dstpixelwidth; float src_xf = srcfx + s * srcfw; int src_x = ifloor(src_xf); - if (deep) { - srcpel.pos(src_x, src_y, 0); - int nsamps = srcpel.deep_samples(); - OIIO_DASSERT(nsamps == out.deep_samples()); - if (!nsamps || nsamps != out.deep_samples()) - continue; - for (int c = 0; c < nchannels; ++c) { - if (dstspec.channelformat(c) == TypeDesc::UINT32) - for (int samp = 0; samp < nsamps; ++samp) - out.set_deep_value( - c, samp, srcpel.deep_value_uint(c, samp)); - else - for (int samp = 0; samp < nsamps; ++samp) - out.set_deep_value(c, samp, - srcpel.deep_value(c, samp)); - } - } else if (interpolate) { + if (interpolate) { // Non-deep image, bilinearly interpolate - src.interppixel(src_xf, src_yf, pel, ImageBuf::WrapClamp); + interppixel(src, srcpel, src_xf, src_yf, pel, + ImageBuf::WrapClamp); for (int c = roi.chbegin; c < roi.chend; ++c) out[c] = pel[c]; } else { @@ -1144,6 +1157,88 @@ resample_(ImageBuf& dst, const ImageBuf& src, bool interpolate, ROI roi, +static bool +resample_deep(ImageBuf& dst, const ImageBuf& src, bool interpolate, ROI roi, + int nthreads) +{ + // If it's deep, figure out the sample allocations first, because + // it's not thread-safe to do that simultaneously with copying the + // values. + const ImageSpec& srcspec(src.spec()); + const ImageSpec& dstspec(dst.spec()); + float srcfx = srcspec.full_x; + float srcfy = srcspec.full_y; + float srcfw = srcspec.full_width; + float srcfh = srcspec.full_height; + float dstpixelwidth = 1.0f / dstspec.full_width; + float dstpixelheight = 1.0f / dstspec.full_height; + ImageBuf::ConstIterator srcpel(src, roi); + ImageBuf::Iterator dstpel(dst, roi); + for (; !dstpel.done(); ++dstpel, ++srcpel) { + float s = (dstpel.x() - dstspec.full_x + 0.5f) * dstpixelwidth; + float t = (dstpel.y() - dstspec.full_y + 0.5f) * dstpixelheight; + int src_y = ifloor(srcfy + t * srcfh); + int src_x = ifloor(srcfx + s * srcfw); + srcpel.pos(src_x, src_y, 0); + dstpel.set_deep_samples(srcpel.deep_samples()); + } + + OIIO_ASSERT(src.deep() == dst.deep()); + ImageBufAlgo::parallel_image(roi, nthreads, [&](ROI roi) { + const ImageSpec& srcspec(src.spec()); + const ImageSpec& dstspec(dst.spec()); + int nchannels = src.nchannels(); + + // Local copies of the source image window, converted to float + float srcfx = srcspec.full_x; + float srcfy = srcspec.full_y; + float srcfw = srcspec.full_width; + float srcfh = srcspec.full_height; + + float dstfx = dstspec.full_x; + float dstfy = dstspec.full_y; + float dstfw = dstspec.full_width; + float dstfh = dstspec.full_height; + float dstpixelwidth = 1.0f / dstfw; + float dstpixelheight = 1.0f / dstfh; + + ImageBuf::Iterator out(dst, roi); + ImageBuf::ConstIterator srcpel(src); + for (int y = roi.ybegin; y < roi.yend; ++y) { + // s,t are NDC space + float t = (y - dstfy + 0.5f) * dstpixelheight; + // src_xf, src_xf are image space float coordinates + float src_yf = srcfy + t * srcfh; + // src_x, src_y are image space integer coordinates of the floor + int src_y = ifloor(src_yf); + for (int x = roi.xbegin; x < roi.xend; ++x, ++out) { + float s = (x - dstfx + 0.5f) * dstpixelwidth; + float src_xf = srcfx + s * srcfw; + int src_x = ifloor(src_xf); + srcpel.pos(src_x, src_y, 0); + int nsamps = srcpel.deep_samples(); + OIIO_DASSERT(nsamps == out.deep_samples()); + if (!nsamps || nsamps != out.deep_samples()) + continue; + for (int c = 0; c < nchannels; ++c) { + if (dstspec.channelformat(c) == TypeDesc::UINT32) + for (int samp = 0; samp < nsamps; ++samp) + out.set_deep_value(c, samp, + srcpel.deep_value_uint(c, samp)); + else + for (int samp = 0; samp < nsamps; ++samp) + out.set_deep_value(c, samp, + srcpel.deep_value(c, samp)); + } + } + } + }); + + return true; +} + + + bool ImageBufAlgo::resample(ImageBuf& dst, const ImageBuf& src, bool interpolate, ROI roi, int nthreads) @@ -1155,27 +1250,7 @@ ImageBufAlgo::resample(ImageBuf& dst, const ImageBuf& src, bool interpolate, return false; if (dst.deep()) { - // If it's deep, figure out the sample allocations first, because - // it's not thread-safe to do that simultaneously with copying the - // values. - const ImageSpec& srcspec(src.spec()); - const ImageSpec& dstspec(dst.spec()); - float srcfx = srcspec.full_x; - float srcfy = srcspec.full_y; - float srcfw = srcspec.full_width; - float srcfh = srcspec.full_height; - float dstpixelwidth = 1.0f / dstspec.full_width; - float dstpixelheight = 1.0f / dstspec.full_height; - ImageBuf::ConstIterator srcpel(src, roi); - ImageBuf::Iterator dstpel(dst, roi); - for (; !dstpel.done(); ++dstpel, ++srcpel) { - float s = (dstpel.x() - dstspec.full_x + 0.5f) * dstpixelwidth; - float t = (dstpel.y() - dstspec.full_y + 0.5f) * dstpixelheight; - int src_y = ifloor(srcfy + t * srcfh); - int src_x = ifloor(srcfx + s * srcfw); - srcpel.pos(src_x, src_y, 0); - dstpel.set_deep_samples(srcpel.deep_samples()); - } + return resample_deep(dst, src, interpolate, roi, nthreads); } bool ok;