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; }
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 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 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 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 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; }
void test_paste () { std::cout << "test paste\n"; // Create the source image, make it a gradient ImageSpec Aspec (4, 4, 3, TypeDesc::FLOAT); ImageBuf A (Aspec); for (ImageBuf::Iterator<float> it (A); !it.done(); ++it) { it[0] = float(it.x()) / float(Aspec.width-1); it[1] = float(it.y()) / float(Aspec.height-1); it[2] = 0.1f; } // Create destination image -- black it out ImageSpec Bspec (8, 8, 3, TypeDesc::FLOAT); ImageBuf B (Bspec); float gray[3] = { .1, .1, .1 }; ImageBufAlgo::fill (B, gray); // Paste a few pixels from A into B -- include offsets ImageBufAlgo::paste (B, 2, 2, 0, 1 /* chan offset */, A, ROI(1, 4, 1, 4)); // Spot check float a[3], b[3]; B.getpixel (1, 1, 0, b); OIIO_CHECK_EQUAL (b[0], gray[0]); OIIO_CHECK_EQUAL (b[1], gray[1]); OIIO_CHECK_EQUAL (b[2], gray[2]); B.getpixel (2, 2, 0, b); A.getpixel (1, 1, 0, a); OIIO_CHECK_EQUAL (b[0], gray[0]); OIIO_CHECK_EQUAL (b[1], a[0]); OIIO_CHECK_EQUAL (b[2], a[1]); B.getpixel (3, 4, 0, b); A.getpixel (2, 3, 0, a); OIIO_CHECK_EQUAL (b[0], gray[0]); OIIO_CHECK_EQUAL (b[1], a[0]); OIIO_CHECK_EQUAL (b[2], a[1]); }
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; }
static bool crop_ (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads=1) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( boost::bind(crop_<D,S>, boost::ref(dst), boost::cref(src), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::ConstIterator<S,D> s (src, roi); ImageBuf::Iterator<D,D> d (dst, roi); for ( ; ! d.done(); ++d, ++s) { for (int c = roi.chbegin; c < roi.chend; ++c) d[c] = s[c]; } return true; }
bool ImageBufAlgo::histogram_draw (ImageBuf &R, const std::vector<imagesize_t> &histogram) { // Fail if there are no bins to draw. int bins = histogram.size(); if (bins == 0) { R.error ("There are no bins to draw, the histogram is empty"); return false; } // Check R and modify it if needed. int height = R.spec().height; if (R.spec().format != TypeDesc::TypeFloat || R.nchannels() != 1 || R.spec().width != bins) { ImageSpec newspec = ImageSpec (bins, height, 1, TypeDesc::FLOAT); R.reset ("dummy", newspec); } // Fill output image R with white color. ImageBuf::Iterator<float, float> r (R); for ( ; ! r.done(); ++r) r[0] = 1; // Draw histogram left->right, bottom->up. imagesize_t max = *std::max_element (histogram.begin(), histogram.end()); for (int b = 0; b < bins; b++) { int bin_height = (int) ((float)histogram[b]/(float)max*height + 0.5f); if (bin_height != 0) { // Draw one bin at column b. for (int j = 1; j <= bin_height; j++) { int row = height - j; r.pos (b, row); r[0] = 0; } } } return true; }
static bool mad_implf (ImageBuf &R, const ImageBuf &A, const float *b, const float *c, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( boost::bind(mad_implf<Rtype,Atype>, boost::ref(R), boost::cref(A), b, c, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator<Rtype> r (R, roi); ImageBuf::ConstIterator<Atype> a (A, roi); for ( ; !r.done(); ++r, ++a) for (int ch = roi.chbegin; ch < roi.chend; ++ch) r[ch] = a[ch] * b[ch] + c[ch]; return true; }
static bool fill_tb_ (ImageBuf &dst, const float *top, const float *bottom, 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_tb_<T>, OIIO::ref(dst), top, bottom, origroi, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case float h = std::max (1, origroi.height() - 1); for (ImageBuf::Iterator<T> p (dst, roi); !p.done(); ++p) { float v = (p.y() - origroi.ybegin) / h; for (int c = roi.chbegin; c < roi.chend; ++c) p[c] = lerp (top[c], bottom[c], v); } return true; }
static bool channel_sum_ (ImageBuf &dst, const ImageBuf &src, const float *weights, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( boost::bind(channel_sum_<D,S>, boost::ref(dst), boost::cref(src), weights, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } 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 sub_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(sub_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) r[c] = a[c] - b[c]; return true; }
static bool premult_ (ImageBuf &R, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( boost::bind(premult_<Rtype>, boost::ref(R), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } int alpha_channel = R.spec().alpha_channel; int z_channel = R.spec().z_channel; for (ImageBuf::Iterator<Rtype> r (R, roi); !r.done(); ++r) { float alpha = r[alpha_channel]; if (alpha == 1.0f) continue; for (int c = roi.chbegin; c < roi.chend; ++c) if (c != alpha_channel && c != z_channel) r[c] = r[c] * alpha; } return true; }
static bool mad_impl (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, const ImageBuf &C, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(mad_impl<Rtype,ABCtype>, OIIO::ref(R), OIIO::cref(A), OIIO::cref(B), OIIO::cref(C), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case ImageBuf::Iterator<Rtype> r (R, roi); ImageBuf::ConstIterator<ABCtype> a (A, roi); ImageBuf::ConstIterator<ABCtype> b (B, roi); ImageBuf::ConstIterator<ABCtype> c (C, roi); for ( ; !r.done(); ++r, ++a, ++b, ++c) for (int ch = roi.chbegin; ch < roi.chend; ++ch) r[ch] = a[ch] * b[ch] + c[ch]; return true; }
static bool flatten_ (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(flatten_<DSTTYPE>, boost::ref(dst), boost::cref(src), _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } const ImageSpec &srcspec (src.spec()); int nc = srcspec.nchannels; int alpha_channel, RA_channel, GA_channel, BA_channel; int R_channel, G_channel, B_channel; int Z_channel, Zback_channel; if (! find_deep_channels (srcspec, alpha_channel, RA_channel, GA_channel, BA_channel, R_channel, G_channel, B_channel, Z_channel, Zback_channel)) { dst.error ("No alpha channel could be identified"); return false; } ASSERT (alpha_channel >= 0 || (RA_channel >= 0 && GA_channel >= 0 && BA_channel >= 0)); float *val = ALLOCA (float, nc); float &RAval (RA_channel >= 0 ? val[RA_channel] : val[alpha_channel]); float &GAval (GA_channel >= 0 ? val[GA_channel] : val[alpha_channel]); float &BAval (BA_channel >= 0 ? val[BA_channel] : val[alpha_channel]); for (ImageBuf::Iterator<DSTTYPE> r (dst, roi); !r.done(); ++r) { int x = r.x(), y = r.y(), z = r.z(); int samps = src.deep_samples (x, y, z); // Clear accumulated values for this pixel (0 for colors, big for Z) memset (val, 0, nc*sizeof(float)); if (Z_channel >= 0 && samps == 0) val[Z_channel] = 1.0e30; if (Zback_channel >= 0 && samps == 0) val[Zback_channel] = 1.0e30; for (int s = 0; s < samps; ++s) { float RA = RAval, GA = GAval, BA = BAval; // make copies float alpha = (RA + GA + BA) / 3.0f; if (alpha >= 1.0f) break; for (int c = 0; c < nc; ++c) { float v = src.deep_value (x, y, z, c, s); if (c == Z_channel || c == Zback_channel) val[c] *= alpha; // because Z are not premultiplied float a; if (c == R_channel) a = RA; else if (c == G_channel) a = GA; else if (c == B_channel) a = BA; else a = alpha; val[c] += (1.0f - a) * v; } } for (int c = roi.chbegin; c < roi.chend; ++c) r[c] = val[c]; } return true; }
static bool colorconvert_impl (ImageBuf &R, const ImageBuf &A, const ColorProcessor* processor, bool unpremult, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Possible multiple thread case -- recurse via parallel_image ImageBufAlgo::parallel_image ( OIIO::bind(colorconvert_impl<Rtype,Atype>, OIIO::ref(R), OIIO::cref(A), processor, unpremult, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case int width = roi.width(); // Temporary space to hold one RGBA scanline std::vector<float> scanline(width*4, 0.0f); // Only process up to, and including, the first 4 channels. This // does let us process images with fewer than 4 channels, which is // the intent. // FIXME: Instead of loading the first 4 channels, obey // Rspec.alpha_channel index (but first validate that the // index is set properly for normal formats) int channelsToCopy = std::min (4, roi.nchannels()); // Walk through all data in our buffer. (i.e., crop or overscan) // FIXME: What about the display window? Should this actually promote // the datawindow to be union of data + display? This is useful if // the color of black moves. (In which case non-zero sections should // now be promoted). Consider the lin->log of a roto element, where // black now moves to non-black. float * dstPtr = NULL; const float fltmin = std::numeric_limits<float>::min(); // If the processor has crosstalk, and we'll be using it, we should // reset the channels to 0 before loading each scanline. bool clearScanline = (channelsToCopy<4 && (processor->hasChannelCrosstalk() || unpremult)); ImageBuf::ConstIterator<Atype> a (A, roi); ImageBuf::Iterator<Rtype> r (R, roi); for (int k = roi.zbegin; k < roi.zend; ++k) { for (int j = roi.ybegin; j < roi.yend; ++j) { // Clear the scanline if (clearScanline) memset (&scanline[0], 0, sizeof(float)*scanline.size()); // Load the scanline dstPtr = &scanline[0]; a.rerange (roi.xbegin, roi.xend, j, j+1, k, k+1); for ( ; !a.done(); ++a, dstPtr += 4) for (int c = 0; c < channelsToCopy; ++c) dstPtr[c] = a[c]; // Optionally unpremult if ((channelsToCopy >= 4) && unpremult) { for (int i = 0; i < width; ++i) { float alpha = scanline[4*i+3]; if (alpha > fltmin) { scanline[4*i+0] /= alpha; scanline[4*i+1] /= alpha; scanline[4*i+2] /= alpha; } } } // Apply the color transformation in place processor->apply (&scanline[0], width, 1, 4, sizeof(float), 4*sizeof(float), width*4*sizeof(float)); // Optionally premult if ((channelsToCopy >= 4) && unpremult) { for (int i = 0; i < width; ++i) { float alpha = scanline[4*i+3]; if (alpha > fltmin) { scanline[4*i+0] *= alpha; scanline[4*i+1] *= alpha; scanline[4*i+2] *= alpha; } } } // Store the scanline dstPtr = &scanline[0]; r.rerange (roi.xbegin, roi.xend, j, j+1, k, k+1); for ( ; !r.done(); ++r, dstPtr += 4) for (int c = 0; c < channelsToCopy; ++c) r[c] = dstPtr[c]; } } return true; }