diff --git a/src/libOpenImageIO/imagebufalgo_test.cpp b/src/libOpenImageIO/imagebufalgo_test.cpp index 67cbbaf982..5294bdcdea 100644 --- a/src/libOpenImageIO/imagebufalgo_test.cpp +++ b/src/libOpenImageIO/imagebufalgo_test.cpp @@ -55,6 +55,51 @@ static bool verbose = false; static bool wedge = false; static int threadcounts[] = { 1, 2, 4, 8, 12, 16, 20, 24, 28, 32, 64, 128, 1024, 1<<30 }; +namespace { + +// Some pre-made specs and buffers -- make these once to unclutter the +// test functions themselves. +ImageSpec spec_2x2_f (2, 2, 1, TypeFloat); +ImageSpec spec_2x2_rgb_f (2, 2, 3, TypeFloat); +ImageSpec spec_1k_rgb_f (1024, 1024, 3, TypeFloat); +ImageSpec spec_1k_rgb_u8 (1024, 1024, 3, TypeUInt8); +ImageSpec spec_1k_rgb_u16 (1024, 1024, 3, TypeUInt16); +ImageSpec spec_1k_rgba_f (1024, 1024, 4, TypeFloat); +ImageSpec spec_1k_rgba_h (1024, 1024, 4, TypeHalf); +ImageSpec spec_1k_rgba_u8 (1024, 1024, 4, TypeUInt8); +ImageSpec spec_1k_rgba_u16 (1024, 1024, 4, TypeUInt16); +ImageSpec spec_hd_rgb_f (1920, 1080, 3, TypeFloat); +ImageSpec spec_hd_rgb_h (1920, 1080, 3, TypeHalf); +ImageSpec spec_hd_rgb_u8 (1920, 1080, 3, TypeUInt8); +ImageSpec spec_hd_rgb_u16 (1920, 1080, 3, TypeUInt16); +ImageSpec spec_hd_rgba_f (1920, 1080, 4, TypeFloat); +ImageSpec spec_hd_rgba_h (1920, 1080, 4, TypeHalf); +ImageSpec spec_hd_rgba_u8 (1920, 1080, 4, TypeUInt8); +ImageSpec spec_hd_rgba_u16 (1920, 1080, 4, TypeUInt16); + +ImageBuf buf_2x2_f (spec_2x2_f); +ImageBuf buf_2x2_rgb (spec_2x2_rgb_f); +ImageBuf buf_1k_rgb_f (spec_1k_rgb_f); +ImageBuf buf_1k_rgb_u8 (spec_1k_rgb_u8); +ImageBuf buf_1k_rgb_u16 (spec_1k_rgb_u16); +ImageBuf buf_1k_rgba_f (spec_1k_rgba_f); +ImageBuf buf_1k_rgba_h (spec_1k_rgba_h); +ImageBuf buf_1k_rgba_u8 (spec_1k_rgba_u8); +ImageBuf buf_1k_rgba_u16(spec_1k_rgba_u16); +ImageBuf buf_hd_rgb_f (spec_hd_rgb_f); +ImageBuf buf_hd_rgb_h (spec_hd_rgb_h); +ImageBuf buf_hd_rgb_u8 (spec_hd_rgb_u8); +ImageBuf buf_hd_rgb_u16 (spec_hd_rgb_u16); +ImageBuf buf_hd_rgba_f (spec_hd_rgba_f); +ImageBuf buf_hd_rgba_h (spec_hd_rgba_h); +ImageBuf buf_hd_rgba_u8 (spec_hd_rgba_u8); +ImageBuf buf_hd_rgba_u16(spec_hd_rgba_u16); + +// Some colors +float red_rgba[] = { 1.0, 0.0, 0.0, 1.0 }; +} + + static void getargs (int argc, char *argv[]) @@ -559,6 +604,39 @@ void test_over () +// Test ImageBuf::resample +void test_resample () +{ + std::cout << "test resample\n"; + + // Timing + Benchmarker bench; + 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::resize 2k->1k rgba f->f interp ", [&](){ + ImageBufAlgo::resample (smallf, buf_hd_rgba_f, true); + }); + bench (" IBA::resize 2k->1k rgba f->u8 interp ", [&](){ + ImageBufAlgo::resample (smallu8, buf_hd_rgba_f, true); + }); + bench (" IBA::resize 2k->1k rgba u8->u8 interp ", [&](){ + ImageBufAlgo::resample (smallu8, buf_hd_rgba_u8, true); + }); + bench (" IBA::resize 2k->1k rgba f->f no interp ", [&](){ + ImageBufAlgo::resample (smallf, buf_hd_rgba_f, false); + }); + bench (" IBA::resize 2k->1k rgba f->u8 no interp ", [&](){ + ImageBufAlgo::resample (smallu8, buf_hd_rgba_f, false); + }); + bench (" IBA::resize 2k->1k rgba u8->u8 no interp ", [&](){ + ImageBufAlgo::resample (smallu8, buf_hd_rgba_u8, false); + }); +} + + + // Tests ImageBufAlgo::compare void test_compare () { @@ -941,6 +1019,7 @@ main (int argc, char **argv) test_mul (); test_mad (); test_over (); + test_resample (); test_compare (); test_isConstantColor (); test_isConstantChannel (); diff --git a/src/libOpenImageIO/imagebufalgo_xform.cpp b/src/libOpenImageIO/imagebufalgo_xform.cpp index 91ffc0637c..96e5d0a86a 100644 --- a/src/libOpenImageIO/imagebufalgo_xform.cpp +++ b/src/libOpenImageIO/imagebufalgo_xform.cpp @@ -501,12 +501,10 @@ static bool resample_ (ImageBuf &dst, const ImageBuf &src, bool interpolate, ROI roi, int nthreads) { - ImageBufAlgo::parallel_image (roi, nthreads, [&](ROI roi){ + ASSERT (!src.deep() && !dst.deep()); + ImageBufAlgo::parallel_image (roi, nthreads, [&,interpolate](ROI roi){ const ImageSpec &srcspec (src.spec()); const ImageSpec &dstspec (dst.spec()); - int nchannels = src.nchannels(); - bool deep = src.deep(); - ASSERT (deep == dst.deep()); // Local copies of the source image window, converted to float float srcfx = srcspec.full_x; @@ -520,10 +518,107 @@ resample_ (ImageBuf &dst, const ImageBuf &src, bool interpolate, float dstfh = dstspec.full_height; float dstpixelwidth = 1.0f / dstfw; float dstpixelheight = 1.0f / dstfh; - float *pel = ALLOCA (float, nchannels); - ImageBuf::Iterator out (dst, roi); - ImageBuf::ConstIterator srcpel (src); + if (interpolate) { + int nchannels = src.nchannels(); + float *pel = ALLOCA (float, nchannels); + float *localpixel = ALLOCA (float, nchannels*4); + float *p[4] = { localpixel, localpixel+nchannels, localpixel+2*nchannels, localpixel+3*nchannels }; + ImageBuf::Iterator out (dst, roi); + ImageBuf::ConstIterator it (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; + float yy = src_yf - 0.5f; + int ytexel; + float yfrac = floorfrac (yy, &ytexel); + for (int x = roi.xbegin; x < roi.xend; ++x, ++out) { + float s = (x-dstfx+0.5f)*dstpixelwidth; + float src_xf = srcfx + s * srcfw; + // Non-deep image, bilinearly interpolate + + // src.interppixel (src_xf, src_yf, pel); + + float xx = src_xf - 0.5f; + int xtexel; + float xfrac = floorfrac (xx, &xtexel); + it.rerange (xtexel, xtexel+2, ytexel, ytexel+2, 0, 1); + for (int i = 0; i < 4; ++i, ++it) + for (int c = 0; c < nchannels; ++c) + p[i][c] = it[c]; + bilerp (p[0], p[1], p[2], p[3], xfrac, yfrac, nchannels, pel); + + for (int c = roi.chbegin; c < roi.chend; ++c) + out[c] = pel[c]; + } + } + + } else { // vvv NO interpolate case + 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); + // Non-deep image, just copy closest pixel + srcpel.pos (src_x, src_y, 0); + for (int c = roi.chbegin; c < roi.chend; ++c) + out[c] = srcpel[c]; + } + } + } + }); + return true; +} + + + +static bool +resample_deep (ImageBuf &dst, const ImageBuf &src, bool interpolate, + ROI roi, int nthreads) +{ + ASSERT (src.deep() && 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 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::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 ()); + } + + ImageBufAlgo::parallel_image (roi, nthreads, [=,&dst,&src](ROI roi){ + int nchannels = src.nchannels(); + const ImageSpec &dstspec (dst.spec()); + 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; @@ -535,30 +630,18 @@ resample_ (ImageBuf &dst, const ImageBuf &src, bool interpolate, 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(); - ASSERT (nsamps == out.deep_samples()); - if (! nsamps) - 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) { - // Non-deep image, bilinearly interpolate - src.interppixel (src_xf, src_yf, pel); - for (int c = roi.chbegin; c < roi.chend; ++c) - out[c] = pel[c]; - } else { - // Non-deep image, just copy closest pixel - srcpel.pos (src_x, src_y, 0); - for (int c = roi.chbegin; c < roi.chend; ++c) - out[c] = srcpel[c]; + srcpel.pos (src_x, src_y, 0); + int nsamps = srcpel.deep_samples(); + ASSERT (nsamps == out.deep_samples()); + if (! nsamps) + 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)); } } } @@ -579,27 +662,7 @@ ImageBufAlgo::resample (ImageBuf &dst, const ImageBuf &src, 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;