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

Refactor IBA::resample() #1882

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
79 changes: 79 additions & 0 deletions src/libOpenImageIO/imagebufalgo_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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[])
Expand Down Expand Up @@ -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 ()
{
Expand Down Expand Up @@ -941,6 +1019,7 @@ main (int argc, char **argv)
test_mul ();
test_mad ();
test_over ();
test_resample ();
test_compare ();
test_isConstantColor ();
test_isConstantChannel ();
Expand Down
167 changes: 115 additions & 52 deletions src/libOpenImageIO/imagebufalgo_xform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<DSTTYPE> out (dst, roi);
ImageBuf::ConstIterator<SRCTYPE> 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<DSTTYPE> out (dst, roi);
ImageBuf::ConstIterator<SRCTYPE> 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<DSTTYPE,DSTTYPE> out (dst, roi);
ImageBuf::ConstIterator<SRCTYPE,DSTTYPE> 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<float> srcpel (src, roi);
ImageBuf::Iterator<float> 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<float> out (dst, roi);
ImageBuf::ConstIterator<float> srcpel (src);
for (int y = roi.ybegin; y < roi.yend; ++y) {
// s,t are NDC space
float t = (y-dstfy+0.5f)*dstpixelheight;
Expand All @@ -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));
}
}
}
Expand All @@ -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<float> srcpel (src, roi);
ImageBuf::Iterator<float> 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;
Expand Down