// Tests ImageBuf construction from application buffer void ImageBuf_test_appbuffer () { const int WIDTH = 8; const int HEIGHT = 8; const int CHANNELS = 1; static float buf[HEIGHT][WIDTH] = { { 0, 0, 0, 0, 1, 0, 0, 0 }, { 0, 0, 0, 1, 0, 1, 0, 0 }, { 0, 0, 1, 0, 0, 0, 1, 0 }, { 0, 1, 0, 0, 0, 0, 0, 1 }, { 0, 0, 1, 0, 0, 0, 1, 0 }, { 0, 0, 0, 1, 0, 1, 0, 0 }, { 0, 0, 0, 0, 1, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } }; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); ImageBuf A (spec, buf); // Make sure A now points to the buffer OIIO_CHECK_EQUAL ((void *)A.pixeladdr (0, 0, 0), (void *)buf); // write it A.write ("A.tif"); // Read it back and make sure it matches the original ImageBuf B ("A.tif"); for (int y = 0; y < HEIGHT; ++y) for (int x = 0; x < WIDTH; ++x) OIIO_CHECK_EQUAL (A.getchannel (x, y, 0, 0), B.getchannel (x, y, 0, 0)); }
void test_compute () { double time; ROI roi (0, xres, 0, yres, 0, 1, 0, channels); std::cout << "Test straightforward as 1D array of float: "; ImageBufAlgo::zero (imgR); time = time_trial (std::bind (test_arrays, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); // imgR.write ("ref.exr"); std::cout << "Test array iterated like an image: "; ImageBufAlgo::zero (imgR); time = time_trial (std::bind (test_arrays_like_image, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array iterated like an image, multithreaded: "; ImageBufAlgo::zero (imgR); time = time_trial (std::bind (test_arrays_like_image_multithread_wrapper, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array as 1D, using SIMD: "; ImageBufAlgo::zero (imgR); time = time_trial (std::bind (test_arrays_simd4, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array iterated like an image, using SIMD: "; ImageBufAlgo::zero (imgR); time = time_trial (std::bind (test_arrays_like_image_simd, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array iterated like an image, using SIMD, multithreaded: "; ImageBufAlgo::zero (imgR); time = time_trial (std::bind (test_arrays_like_image_simd_multithread_wrapper, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test ImageBufAlgo::mad 1 thread: "; ImageBufAlgo::zero (imgR); time = time_trial (std::bind (test_IBA, roi, 1), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test ImageBufAlgo::mad multi-thread " << numthreads << ": "; ImageBufAlgo::zero (imgR); time = time_trial (std::bind (test_IBA, roi, numthreads), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); }
void test_compute() { Benchmarker bench; bench.iterations(iterations); bench.trials(ntrials); bench.work(xres * yres * channels); bench.units(Benchmarker::Unit::ms); ROI roi(0, xres, 0, yres, 0, 1, 0, channels); ImageBufAlgo::zero(imgR); bench("1D array loop", test_arrays, roi); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 2), 0.50, 0.001); // imgR.write ("ref.exr"); ImageBufAlgo::zero(imgR); bench("iterated as image", test_arrays_like_image, roi); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 2), 0.50, 0.001); ImageBufAlgo::zero(imgR); bench("iterated as image, threaded", [&]() { ImageBufAlgo::parallel_image(roi, test_arrays_like_image); }); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 2), 0.50, 0.001); ImageBufAlgo::zero(imgR); bench("1D array loop, SIMD", test_arrays_simd4, roi); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 2), 0.50, 0.001); ImageBufAlgo::zero(imgR); bench("iterated as image, SIMD", test_arrays_like_image_simd, roi); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 2), 0.50, 0.001); ImageBufAlgo::zero(imgR); bench("iterated as image, SIMD, threaded", [&]() { ImageBufAlgo::parallel_image(roi, test_arrays_like_image_simd); }); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 2), 0.50, 0.001); ImageBufAlgo::zero(imgR); bench("IBA::mad 1 thread", test_IBA, roi, 1); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 2), 0.50, 0.001); ImageBufAlgo::zero(imgR); bench("IBA::mad threaded", test_IBA, roi, numthreads); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH(imgR.getchannel(xres / 2, yres / 2, 0, 2), 0.50, 0.001); }
int ImageBufAlgo::compare_Yee (const ImageBuf &img0, const ImageBuf &img1, CompareResults &result, float luminance, float fov, ROI roi, int nthreads) { if (! roi.defined()) roi = roi_union (get_roi(img0.spec()), get_roi(img1.spec())); roi.chend = std::max (roi.chend, roi.chbegin+3); // max of 3 channels result.maxerror = 0; result.maxx=0, result.maxy=0, result.maxz=0, result.maxc=0; result.nfail = 0, result.nwarn = 0; int nscanlines = roi.height() * roi.depth(); bool luminanceOnly = false; // assuming colorspaces are in Adobe RGB (1998), convert to LAB // paste() to copy of up to 3 channels, converting to float, and // ending up with a 0-origin image. End up with an LAB image in // aLAB, and a luminance image in aLum. ImageSpec spec (roi.width(), roi.height(), 3 /*chans*/, TypeDesc::FLOAT); ImageBuf aLAB (spec); ImageBufAlgo::paste (aLAB, 0, 0, 0, 0, img0, roi, nthreads); AdobeRGBToXYZ (aLAB, ROI::All(), nthreads); // contains XYZ now ImageBuf aLum; int channelorder[] = { 1 }; // channel to copy ImageBufAlgo::channels (aLum, aLAB, 1, channelorder); ImageBufAlgo::mul (aLum, aLum, luminance, ROI::All(), nthreads); XYZToLAB (aLAB, ROI::All(), nthreads); // now it's LAB // Same thing for img1/bLAB/bLum ImageBuf bLAB (spec); ImageBufAlgo::paste (bLAB, 0, 0, 0, 0, img1, roi, nthreads); AdobeRGBToXYZ (bLAB, ROI::All(), nthreads); // contains XYZ now ImageBuf bLum; ImageBufAlgo::channels (bLum, bLAB, 1, channelorder); ImageBufAlgo::mul (bLum, bLum, luminance, ROI::All(), nthreads); XYZToLAB (bLAB, ROI::All(), nthreads); // now it's LAB // Construct Gaussian pyramids (not really pyramids, because they all // have the same resolution, but really just a bunch of successively // more blurred images). GaussianPyramid la (aLum); GaussianPyramid lb (bLum); float num_one_degree_pixels = (float) (2 * tan(fov * 0.5 * M_PI / 180) * 180 / M_PI); float pixels_per_degree = roi.width() / num_one_degree_pixels; unsigned int adaptation_level = 0; for (int i = 0, npixels = 1; i < PYRAMID_MAX_LEVELS && npixels <= num_one_degree_pixels; ++i, npixels *= 2) adaptation_level = i; float cpd[PYRAMID_MAX_LEVELS]; cpd[0] = 0.5f * pixels_per_degree; for (int i = 1; i < PYRAMID_MAX_LEVELS; ++i) cpd[i] = 0.5f * cpd[i - 1]; float csf_max = contrast_sensitivity (3.248f, 100.0f); float F_freq[PYRAMID_MAX_LEVELS - 2]; for (int i = 0; i < PYRAMID_MAX_LEVELS - 2; ++i) F_freq[i] = csf_max / contrast_sensitivity (cpd[i], 100.0f); for (int y = 0; y < nscanlines; ++y) { for (int x = 0; x < roi.width(); ++x) { float contrast[PYRAMID_MAX_LEVELS - 2]; float sum_contrast = 0; for (int i = 0; i < PYRAMID_MAX_LEVELS - 2; i++) { float n1 = fabsf (la.value(x,y,i) - la.value(x,y,i+1)); float n2 = fabsf (lb.value(x,y,i) - lb.value(x,y,i+1)); float numerator = std::max (n1, n2); float d1 = fabsf (la.value(x,y,i+2)); float d2 = fabsf (lb.value(x,y,i+2)); float denominator = std::max (std::max (d1, d2), 1.0e-5f); contrast[i] = numerator / denominator; sum_contrast += contrast[i]; } if (sum_contrast < 1e-5) sum_contrast = 1e-5f; float F_mask[PYRAMID_MAX_LEVELS - 2]; float adapt = la.value(x,y,adaptation_level) + lb.value(x,y,adaptation_level); adapt *= 0.5f; if (adapt < 1e-5) adapt = 1e-5f; for (int i = 0; i < PYRAMID_MAX_LEVELS - 2; i++) F_mask[i] = mask(contrast[i] * contrast_sensitivity(cpd[i], adapt)); float factor = 0; for (int i = 0; i < PYRAMID_MAX_LEVELS - 2; i++) factor += contrast[i] * F_freq[i] * F_mask[i] / sum_contrast; factor = Imath::clamp (factor, 1.0f, 10.0f); float delta = fabsf (la.value(x,y,0) - lb.value(x,y,0)); bool pass = true; // pure luminance test delta /= tvi(adapt); if (delta > factor) { pass = false; } else if (! luminanceOnly) { // CIE delta E test with modifications float color_scale = 1.0f; // ramp down the color test in scotopic regions if (adapt < 10.0f) { color_scale = 1.0f - (10.0f - color_scale) / 10.0f; color_scale = color_scale * color_scale; } float da = aLAB.getchannel(x,y,0,1) - bLAB.getchannel(x,y,0,1); // diff in A float db = aLAB.getchannel(x,y,0,2) - bLAB.getchannel(x,y,0,2); // diff in B da = da * da; db = db * db; delta = (da + db) * color_scale; if (delta > factor) pass = false; } if (!pass) { ++result.nfail; if (factor > result.maxerror) { result.maxerror = factor; result.maxx = x; result.maxy = y; // result.maxz = z; } } } } return result.nfail; }
bool ImageBufAlgo::deepen (ImageBuf &dst, const ImageBuf &src, float zvalue, ROI roi, int nthreads) { if (src.deep()) { // For some reason, we were asked to deepen an already-deep image. // So just copy it. return dst.copy (src); // FIXME: once paste works for deep files, this should really be // return paste (dst, roi.xbegin, roi.ybegin, roi.zbegin, roi.chbegin, // src, roi, nthreads); } // Construct an ideal spec for dst, which is like src but deep. const ImageSpec &srcspec (src.spec()); int nc = srcspec.nchannels; int zback_channel = -1; ImageSpec force_spec = srcspec; force_spec.deep = true; force_spec.set_format (TypeDesc::FLOAT); force_spec.channelformats.clear(); for (int c = 0; c < nc; ++c) { if (force_spec.channelnames[c] == "Z") force_spec.z_channel = c; else if (force_spec.channelnames[c] == "Zback") zback_channel = c; } bool add_z_channel = (force_spec.z_channel < 0); if (add_z_channel) { // No z channel? Make one. force_spec.z_channel = force_spec.nchannels++; force_spec.channelnames.push_back ("Z"); } if (! IBAprep (roi, &dst, &src, NULL, &force_spec, IBAprep_SUPPORT_DEEP)) return false; if (! dst.deep()) { dst.error ("Cannot deepen to a flat image"); return false; } float *pixel = OIIO_ALLOCA (float, nc); // First, figure out which pixels get a sample and which do not for (int z = roi.zbegin; z < roi.zend; ++z) for (int y = roi.ybegin; y < roi.yend; ++y) for (int x = roi.xbegin; x < roi.xend; ++x) { bool has_sample = false; src.getpixel (x, y, z, pixel); for (int c = 0; c < nc; ++c) if (c != force_spec.z_channel && c != zback_channel && pixel[c] != 0.0f) { has_sample = true; break; } if (! has_sample && ! add_z_channel) for (int c = 0; c < nc; ++c) if ((c == force_spec.z_channel || c == zback_channel) && (pixel[c] != 0.0f && pixel[c] < 1e30)) { has_sample = true; break; } if (has_sample) dst.set_deep_samples (x, y, z, 1); } dst.deep_alloc (); // Now actually set the values for (int z = roi.zbegin; z < roi.zend; ++z) for (int y = roi.ybegin; y < roi.yend; ++y) for (int x = roi.xbegin; x < roi.xend; ++x) { if (dst.deep_samples (x, y, z) == 0) continue; for (int c = 0; c < nc; ++c) dst.set_deep_value (x, y, z, c, 0 /*sample*/, src.getchannel (x, y, z, c)); if (add_z_channel) dst.set_deep_value (x, y, z, nc, 0, zvalue); } bool ok = true; // FIXME -- the above doesn't split into threads. Someday, it should // be refactored like this: // OIIO_DISPATCH_COMMON_TYPES2 (ok, "deepen", deepen_, // dst.spec().format, srcspec.format, // dst, src, add_z_channel, z, roi, nthreads); return ok; }