static bool circular_shift_ (ImageBuf &dst, const ImageBuf &src, int xshift, int yshift, int zshift, ROI dstroi, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( boost::bind(circular_shift_<DSTTYPE,SRCTYPE>, boost::ref(dst), boost::cref(src), xshift, yshift, zshift, dstroi, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case int width = dstroi.width(), height = dstroi.height(), depth = dstroi.depth(); ImageBuf::ConstIterator<SRCTYPE,DSTTYPE> s (src, roi); ImageBuf::Iterator<DSTTYPE,DSTTYPE> d (dst); for ( ; ! s.done(); ++s) { int dx = s.x() + xshift; OIIO::wrap_periodic (dx, dstroi.xbegin, width); int dy = s.y() + yshift; OIIO::wrap_periodic (dy, dstroi.ybegin, height); int dz = s.z() + zshift; OIIO::wrap_periodic (dz, dstroi.zbegin, depth); d.pos (dx, dy, dz); if (! d.exists()) continue; for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = s[c]; } return true; }
static bool fill_corners_ (ImageBuf &dst, const float *topleft, const float *topright, const float *bottomleft, const float *bottomright, ROI origroi, ROI roi=ROI(), int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(fill_corners_<T>, OIIO::ref(dst), topleft, topright, bottomleft, bottomright, origroi, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case float w = std::max (1, origroi.width() - 1); float h = std::max (1, origroi.height() - 1); for (ImageBuf::Iterator<T> p (dst, roi); !p.done(); ++p) { float u = (p.x() - origroi.xbegin) / w; float v = (p.y() - origroi.ybegin) / h; for (int c = roi.chbegin; c < roi.chend; ++c) p[c] = bilerp (topleft[c], topright[c], bottomleft[c], bottomright[c], u, v); } return true; }
static bool clamp_ (ImageBuf &dst, const float *min, const float *max, bool clampalpha01, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( boost::bind(clamp_<D>, boost::ref(dst), min, max, clampalpha01, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator<D> d (dst, roi); ! d.done(); ++d) { for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = OIIO::clamp<float> (d[c], min[c], max[c]); } int a = dst.spec().alpha_channel; if (clampalpha01 && a >= roi.chbegin && a < roi.chend) { for (ImageBuf::Iterator<D> d (dst, roi); ! d.done(); ++d) { d[a] = OIIO::clamp<float> (d[a], 0.0f, 1.0f); } } return true; }
static bool noise_gaussian_ (ImageBuf &dst, float mean, float stddev, bool mono, int seed, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(noise_gaussian_<T>, OIIO::ref(dst), mean, stddev, mono, seed, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator<T> p (dst, roi); !p.done(); ++p) { int x = p.x(), y = p.y(), z = p.z(); float n = 0.0; for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == roi.chbegin || !mono) n = mean + stddev * hashnormal (x, y, z, c, seed); p[c] = p[c] + n; } } return true; }
static bool paste_ (ImageBuf &dst, ROI dstroi, const ImageBuf &src, ROI srcroi, int nthreads) { // N.B. Punt on parallelizing because of the subtle interplay // between srcroi and dstroi, the parallel_image idiom doesn't // handle that especially well. And it's not worth customizing for // this function which is inexpensive and not commonly used, and so // would benefit little from parallelizing. We can always revisit // this later. But in the mean time, we maintain the 'nthreads' // parameter for uniformity with the rest of IBA. int src_nchans = src.nchannels (); int dst_nchans = dst.nchannels (); ImageBuf::ConstIterator<S,D> s (src, srcroi); ImageBuf::Iterator<D,D> d (dst, dstroi); for ( ; ! s.done(); ++s, ++d) { if (! d.exists()) continue; // Skip paste-into pixels that don't overlap dst's data for (int c = srcroi.chbegin, c_dst = dstroi.chbegin; c < srcroi.chend; ++c, ++c_dst) { if (c_dst >= 0 && c_dst < dst_nchans) d[c_dst] = c < src_nchans ? s[c] : D(0); } } return true; }
static bool div_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( boost::bind(div_impl<Rtype,Atype,Btype>, boost::ref(R), boost::cref(A), boost::cref(B), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator<Rtype> r (R, roi); ImageBuf::ConstIterator<Atype> a (A, roi); ImageBuf::ConstIterator<Btype> b (B, roi); for ( ; !r.done(); ++r, ++a, ++b) for (int c = roi.chbegin; c < roi.chend; ++c) { float v = b[c]; r[c] = (v == 0.0f) ? 0.0f : (a[c] / v); } return true; }
static bool noise_salt_ (ImageBuf &dst, float saltval, float saltportion, bool mono, int seed, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(noise_salt_<T>, OIIO::ref(dst), saltval, saltportion, mono, seed, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator<T> p (dst, roi); !p.done(); ++p) { int x = p.x(), y = p.y(), z = p.z(); float n = 0.0; for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == roi.chbegin || !mono) n = hashrand (x, y, z, c, seed); if (n < saltportion) p[c] = saltval; } } return true; }
static int action_flip (int argc, const char *argv[]) { if (ot.postpone_callback (1, action_flip, argc, argv)) return 0; ot.read (); ImageRecRef A = ot.pop(); ot.push (new ImageRec (*A, ot.allsubimages ? -1 : 0, ot.allsubimages ? -1 : 0, true, false)); int subimages = ot.curimg->subimages(); for (int s = 0; s < subimages; ++s) { int miplevels = ot.curimg->miplevels(s); for (int m = 0; m < miplevels; ++m) { const ImageBuf &Aib ((*A)(s,m)); ImageBuf &Rib ((*ot.curimg)(s,m)); ImageBuf::ConstIterator<float> a (Aib); ImageBuf::Iterator<float> r (Rib); int nchans = Rib.nchannels(); int firstscanline = Rib.ymin(); int lastscanline = Rib.ymax(); for ( ; ! r.done(); ++r) { a.pos (r.x(), lastscanline - (r.y() - firstscanline)); for (int c = 0; c < nchans; ++c) r[c] = a[c]; } } } return 0; }
static bool transpose_ (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( boost::bind(transpose_<DSTTYPE,SRCTYPE>, boost::ref(dst), boost::cref(src), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::ConstIterator<SRCTYPE,DSTTYPE> s (src, roi); ImageBuf::Iterator<DSTTYPE,DSTTYPE> d (dst); for ( ; ! s.done(); ++s) { d.pos (s.y(), s.x(), s.z()); if (! d.exists()) continue; for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = s[c]; } return true; }
static bool render_box_ (ImageBuf &dst, array_view<const float> color, ROI roi=ROI(), int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(render_box_<T>, OIIO::ref(dst), color, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case float alpha = 1.0f; if (dst.spec().alpha_channel >= 0 && dst.spec().alpha_channel < int(color.size())) alpha = color[dst.spec().alpha_channel]; else if (int(color.size()) == roi.chend+1) alpha = color[roi.chend]; if (alpha == 1.0f) { for (ImageBuf::Iterator<T> r (dst, roi); !r.done(); ++r) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = color[c]; } else { for (ImageBuf::Iterator<T> r (dst, roi); !r.done(); ++r) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = color[c] + r[c] * (1.0f-alpha); // "over" } return true; }
static inline void zero_ (ImageBuf &buf) { int chans = buf.nchannels(); for (ImageBuf::Iterator<T> pixel (buf); pixel.valid(); ++pixel) for (int i = 0; i < chans; ++i) pixel[i] = 0; }
static inline void setpixel_ (ImageBuf &buf, int x, int y, int z, const float *data, int chans) { ImageBuf::Iterator<T> pixel (buf, x, y, z); if (pixel.valid()) { for (int i = 0; i < chans; ++i) pixel[i] = data[i]; } }
static inline void transfer_pixels_ (ImageBuf &buf, ColorTransfer *tfunc) { for (ImageBuf::Iterator<T> pixel (buf); pixel.valid(); ++pixel) { convert_types (buf.spec().format, pixel.rawptr(), buf.spec().format, pixel.rawptr(), buf.nchannels(), tfunc, buf.spec().alpha_channel, buf.spec().z_channel); } }
static bool flop_ (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ImageBuf::ConstIterator<S, D> s (src, roi); ImageBuf::Iterator<D, D> d (dst, roi); for ( ; ! d.done(); ++d) { s.pos (roi.xend-1 - (d.x() - roi.xbegin), d.y(), d.z()); for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = s[c]; } return true; }
static bool pow_impl (ImageBuf &R, const ImageBuf &A, const float *b, ROI roi, int nthreads) { ImageBufAlgo::parallel_image (roi, nthreads, [&](ROI roi){ ImageBuf::ConstIterator<Atype> a (A, roi); for (ImageBuf::Iterator<Rtype> r (R, roi); !r.done(); ++r, ++a) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = pow (a[c], b[c]); }); return true; }
static bool absdiff_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, ROI roi, int nthreads) { ImageBufAlgo::parallel_image (roi, nthreads, [&](ROI roi){ ImageBuf::Iterator<Rtype> r (R, roi); ImageBuf::ConstIterator<Atype> a (A, roi); ImageBuf::ConstIterator<Btype> b (B, roi); for ( ; !r.done(); ++r, ++a, ++b) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = std::abs (a[c] - b[c]); }); return true; }
static bool checker_ (ImageBuf &dst, Dim3 size, const float *color1, const float *color2, Dim3 offset, ROI roi, int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(checker_<T>, OIIO::ref(dst), size, color1, color2, offset, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator<T> p (dst, roi); !p.done(); ++p) { int xtile = (p.x()-offset.x)/size.x; xtile += (p.x()<offset.x); int ytile = (p.y()-offset.y)/size.y; ytile += (p.y()<offset.y); int ztile = (p.z()-offset.z)/size.z; ztile += (p.z()<offset.z); int v = xtile + ytile + ztile; if (v & 1) for (int c = roi.chbegin; c < roi.chend; ++c) p[c] = color2[c]; else for (int c = roi.chbegin; c < roi.chend; ++c) p[c] = color1[c]; } return true; }
static bool rotate270_ (ImageBuf &dst, const ImageBuf &src, ROI dst_roi, int nthreads) { ROI dst_roi_full = dst.roi_full(); ImageBuf::ConstIterator<S, D> s (src); ImageBuf::Iterator<D, D> d (dst, dst_roi); for ( ; ! d.done(); ++d) { s.pos (dst_roi_full.yend - d.y() - 1, d.x(), d.z()); for (int c = dst_roi.chbegin; c < dst_roi.chend; ++c) d[c] = s[c]; } return true; }
static bool flip_ (ImageBuf &dst, const ImageBuf &src, ROI dst_roi, int nthreads) { ROI src_roi_full = src.roi_full(); ROI dst_roi_full = dst.roi_full(); ImageBuf::ConstIterator<S, D> s (src); ImageBuf::Iterator<D, D> d (dst, dst_roi); for ( ; ! d.done(); ++d) { int yy = d.y() - dst_roi_full.ybegin; s.pos (d.x(), src_roi_full.yend-1 - yy, d.z()); for (int c = dst_roi.chbegin; c < dst_roi.chend; ++c) d[c] = s[c]; } return true; }
static bool channel_sum_ (ImageBuf &dst, const ImageBuf &src, const float *weights, ROI roi, int nthreads) { ImageBufAlgo::parallel_image (roi, nthreads, [&](ROI roi){ ImageBuf::Iterator<D> d (dst, roi); ImageBuf::ConstIterator<S> s (src, roi); for ( ; !d.done(); ++d, ++s) { float sum = 0.0f; for (int c = roi.chbegin; c < roi.chend; ++c) sum += s[c] * weights[c]; d[0] = sum; } }); return true; }
static bool convolve_ (ImageBuf &dst, const ImageBuf &src, const ImageBuf &kernel, bool normalize, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( boost::bind(convolve_<DSTTYPE,SRCTYPE>, boost::ref(dst), boost::cref(src), boost::cref(kernel), normalize, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case float scale = 1.0f; if (normalize) { scale = 0.0f; for (ImageBuf::ConstIterator<float> k (kernel); ! k.done(); ++k) scale += k[0]; scale = 1.0f / scale; } float *sum = ALLOCA (float, roi.chend); ROI kroi = get_roi (kernel.spec()); ImageBuf::Iterator<DSTTYPE> d (dst, roi); ImageBuf::ConstIterator<SRCTYPE> s (src, roi, ImageBuf::WrapClamp); for ( ; ! d.done(); ++d) { for (int c = roi.chbegin; c < roi.chend; ++c) sum[c] = 0.0f; for (ImageBuf::ConstIterator<float> k (kernel, kroi); !k.done(); ++k) { float kval = k[0]; s.pos (d.x() + k.x(), d.y() + k.y(), d.z() + k.z()); for (int c = roi.chbegin; c < roi.chend; ++c) sum[c] += kval * s[c]; } for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = scale * sum[c]; } return true; }
static bool transpose_ (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { ImageBufAlgo::parallel_image (roi, nthreads, [&](ROI roi){ ImageBuf::ConstIterator<SRCTYPE,DSTTYPE> s (src, roi); ImageBuf::Iterator<DSTTYPE,DSTTYPE> d (dst); for ( ; ! s.done(); ++s) { d.pos (s.y(), s.x(), s.z()); if (! d.exists()) continue; for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = s[c]; } }); return true; }
static bool mul_impl (ImageBuf &R, const float *val, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( boost::bind(mul_impl<Rtype>, boost::ref(R), val, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } ImageBuf::Iterator<Rtype> r (R, roi); for (ImageBuf::Iterator<Rtype> r (R, roi); !r.done(); ++r) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = r[c] * val[c]; return true; }
static bool fill_const_ (ImageBuf &dst, const float *values, ROI roi=ROI(), int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( OIIO::bind(fill_const_<T>, OIIO::ref(dst), values, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case for (ImageBuf::Iterator<T> p (dst, roi); !p.done(); ++p) for (int c = roi.chbegin; c < roi.chend; ++c) p[c] = values[c]; return true; }
static bool rangeexpand_ (ImageBuf &R, bool useluma, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( boost::bind(rangeexpand_<Rtype>, boost::ref(R), useluma, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } const ImageSpec &Rspec (R.spec()); int alpha_channel = Rspec.alpha_channel; int z_channel = Rspec.z_channel; if (roi.nchannels() < 3 || (alpha_channel >= roi.chbegin && alpha_channel < roi.chbegin+3) || (z_channel >= roi.chbegin && z_channel < roi.chbegin+3)) { useluma = false; // No way to use luma } ImageBuf::Iterator<Rtype> r (R, roi); for (ImageBuf::Iterator<Rtype> r (R, roi); !r.done(); ++r) { if (useluma) { float luma = 0.21264f * r[roi.chbegin] + 0.71517f * r[roi.chbegin+1] + 0.07219f * r[roi.chbegin+2]; if (fabsf(luma) <= 1.0f) continue; // Not HDR, no range compression needed float scale = rangeexpand (luma) / luma; for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == alpha_channel || c == z_channel) continue; r[c] = r[c] * scale; } } else { for (int c = roi.chbegin; c < roi.chend; ++c) { if (c == alpha_channel || c == z_channel) continue; r[c] = rangeexpand (r[c]); } } } return true; }
static bool pow_impl (ImageBuf &R, const ImageBuf &A, const float *b, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( boost::bind(pow_impl<Rtype,Atype>, boost::ref(R), boost::cref(A), b, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } ImageBuf::ConstIterator<Atype> a (A, roi); for (ImageBuf::Iterator<Rtype> r (R, roi); !r.done(); ++r, ++a) for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = pow (a[c], b[c]); return true; }
static bool clamp_ (ImageBuf &dst, const ImageBuf &src, const float *min, const float *max, bool clampalpha01, ROI roi, int nthreads) { ImageBufAlgo::parallel_image (roi, nthreads, [&](ROI roi){ ImageBuf::ConstIterator<S> s (src, roi); for (ImageBuf::Iterator<D> d (dst, roi); ! d.done(); ++d, ++s) { for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = OIIO::clamp<float> (s[c], min[c], max[c]); } int a = src.spec().alpha_channel; if (clampalpha01 && a >= roi.chbegin && a < roi.chend) { for (ImageBuf::Iterator<D> d (dst, roi); ! d.done(); ++d) d[a] = OIIO::clamp<float> (d[a], 0.0f, 1.0f); } }); return true; }
bool ImageBufAlgo::make_kernel (ImageBuf &dst, string_view name, float width, float height, float depth, bool normalize) { int w = std::max (1, (int)ceilf(width)); int h = std::max (1, (int)ceilf(height)); int d = std::max (1, (int)ceilf(depth)); // Round up size to odd w |= 1; h |= 1; d |= 1; ImageSpec spec (w, h, 1 /*channels*/, TypeDesc::FLOAT); spec.depth = d; spec.x = -w/2; spec.y = -h/2; spec.z = -d/2; spec.full_x = spec.x; spec.full_y = spec.y; spec.full_z = spec.z; spec.full_width = spec.width; spec.full_height = spec.height; spec.full_depth = spec.depth; dst.reset (spec); if (Filter2D *filter = Filter2D::create (name, width, height)) { // Named continuous filter from filter.h for (ImageBuf::Iterator<float> p (dst); ! p.done(); ++p) p[0] = (*filter)((float)p.x(), (float)p.y()); delete filter; } else if (name == "binomial") { // Binomial filter float *wfilter = ALLOCA (float, width); for (int i = 0; i < width; ++i) wfilter[i] = binomial (width-1, i); float *hfilter = (height == width) ? wfilter : ALLOCA (float, height); if (height != width) for (int i = 0; i < height; ++i) hfilter[i] = binomial (height-1, i); float *dfilter = ALLOCA (float, depth); if (depth == 1) dfilter[0] = 1; else for (int i = 0; i < depth; ++i) dfilter[i] = binomial (depth-1, i); for (ImageBuf::Iterator<float> p (dst); ! p.done(); ++p) p[0] = wfilter[p.x()-spec.x] * hfilter[p.y()-spec.y] * dfilter[p.z()-spec.z]; } else {
// Tests ImageBufAlgo::compare void test_compare () { std::cout << "test compare\n"; // Construct two identical 50% grey images const int WIDTH = 10, HEIGHT = 10, CHANNELS = 3; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); ImageBuf A (spec); ImageBuf B (spec); const float grey[CHANNELS] = { 0.5, 0.5, 0.5 }; ImageBufAlgo::fill (A, grey); ImageBufAlgo::fill (B, grey); // Introduce some minor differences const int NDIFFS = 10; ImageBuf::Iterator<float> a (A); for (int i = 0; i < NDIFFS && a.valid(); ++i, ++a) { for (int c = 0; c < CHANNELS; ++c) a[c] = a[c] + 0.01f * i; } // We expect the differences to be { 0, 0.01, 0.02, 0.03, 0.04, 0.05, // 0.06, 0.07, 0.08, 0.09, 0, 0, ...}. const float failthresh = 0.05; const float warnthresh = 0.025; ImageBufAlgo::CompareResults comp; ImageBufAlgo::compare (A, B, failthresh, warnthresh, comp); // We expect 5 pixels to exceed the fail threshold, 7 pixels to // exceed the warn threshold, the maximum difference to be 0.09, // and the maximally different pixel to be (9,0). // The total error should be 3 chans * sum{0.01,...,0.09} / (pixels*chans) // = 3 * 0.45 / (100*3) = 0.0045 std::cout << "Testing comparison: " << comp.nfail << " failed, " << comp.nwarn << " warned, max diff = " << comp.maxerror << " @ (" << comp.maxx << ',' << comp.maxy << ")\n"; std::cout << " mean err " << comp.meanerror << ", RMS err " << comp.rms_error << ", PSNR = " << comp.PSNR << "\n"; OIIO_CHECK_EQUAL (comp.nfail, 5); OIIO_CHECK_EQUAL (comp.nwarn, 7); OIIO_CHECK_EQUAL_THRESH (comp.maxerror, 0.09, 1e-6); OIIO_CHECK_EQUAL (comp.maxx, 9); OIIO_CHECK_EQUAL (comp.maxy, 0); OIIO_CHECK_EQUAL_THRESH (comp.meanerror, 0.0045, 1.0e-8); }
static int action_sub (int argc, const char *argv[]) { if (ot.postpone_callback (2, action_sub, argc, argv)) return 0; ImageRecRef B (ot.pop()); ImageRecRef A (ot.pop()); ot.read (A); ot.read (B); ot.push (new ImageRec (*A, ot.allsubimages ? -1 : 0, ot.allsubimages ? -1 : 0, true, false)); int subimages = ot.curimg->subimages(); for (int s = 0; s < subimages; ++s) { int miplevels = ot.curimg->miplevels(s); for (int m = 0; m < miplevels; ++m) { const ImageBuf &Aib ((*A)(s,m)); const ImageBuf &Bib ((*B)(s,m)); if (! same_size (Aib, Bib)) { // FIXME: some day, there should be options of combining // differing images somehow. std::cerr << "oiiotool: " << argv[0] << " could not combine images of differing sizes\n"; continue; } ImageBuf &Rib ((*ot.curimg)(s,m)); ImageBuf::ConstIterator<float> a (Aib); ImageBuf::ConstIterator<float> b (Bib); ImageBuf::Iterator<float> r (Rib); int nchans = Rib.nchannels(); for ( ; ! r.done(); ++r) { a.pos (r.x(), r.y()); b.pos (r.x(), r.y()); for (int c = 0; c < nchans; ++c) r[c] = a[c] - b[c]; } } } return 0; }