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; }
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 {
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 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 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 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 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 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 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 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; }
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 bool resize_ (ImageBuf &dst, const ImageBuf &src, Filter2D *filter, ROI roi, int nthreads) { if (nthreads != 1 && roi.npixels() >= 1000) { // Lots of pixels and request for multi threads? Parallelize. ImageBufAlgo::parallel_image ( boost::bind(resize_<DSTTYPE,SRCTYPE>, boost::ref(dst), boost::cref(src), filter, _1 /*roi*/, 1 /*nthreads*/), roi, nthreads); return true; } // Serial case const ImageSpec &srcspec (src.spec()); const ImageSpec &dstspec (dst.spec()); int nchannels = dstspec.nchannels; // Local copies of the source image window, converted to float float srcfx = srcspec.full_x; float srcfy = srcspec.full_y; float srcfw = srcspec.full_width; float srcfh = srcspec.full_height; // Ratios of dst/src size. Values larger than 1 indicate that we // are maximizing (enlarging the image), and thus want to smoothly // interpolate. Values less than 1 indicate that we are minimizing // (shrinking the image), and thus want to properly filter out the // high frequencies. float xratio = float(dstspec.full_width) / srcfw; // 2 upsize, 0.5 downsize float yratio = float(dstspec.full_height) / srcfh; 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; float *pel = ALLOCA (float, nchannels); float filterrad = filter->width() / 2.0f; // radi,radj is the filter radius, as an integer, in source pixels. We // will filter the source over [x-radi, x+radi] X [y-radj,y+radj]. int radi = (int) ceilf (filterrad/xratio); int radj = (int) ceilf (filterrad/yratio); int xtaps = 2*radi + 1; int ytaps = 2*radj + 1; bool separable = filter->separable(); float *xfiltval = NULL, *yfiltval = NULL; if (separable) { // Allocate temp space to cache the filter weights xfiltval = ALLOCA (float, xtaps); yfiltval = ALLOCA (float, ytaps); } #if 0 std::cerr << "Resizing " << srcspec.full_width << "x" << srcspec.full_height << " to " << dstspec.full_width << "x" << dstspec.full_height << "\n"; std::cerr << "ratios = " << xratio << ", " << yratio << "\n"; std::cerr << "examining src filter support radius of " << radi << " x " << radj << " pixels\n"; std::cerr << "dst range " << roi << "\n"; std::cerr << "separable filter\n"; #endif // We're going to loop over all output pixels we're interested in. // // (s,t) = NDC space coordinates of the output sample we are computing. // This is the "sample point". // (src_xf, src_xf) = source pixel space float coordinates of the // sample we're computing. We want to compute the weighted sum // of all the source image pixels that fall under the filter when // centered at that location. // (src_x, src_y) = image space integer coordinates of the floor, // i.e., the closest pixel in the source image. // src_xf_frac and src_yf_frac are the position within that pixel // of our sample. ImageBuf::Iterator<DSTTYPE> out (dst, roi); for (int y = roi.ybegin; y < roi.yend; ++y) { float t = (y-dstfy+0.5f)*dstpixelheight; float src_yf = srcfy + t * srcfh; int src_y; float src_yf_frac = floorfrac (src_yf, &src_y); // If using separable filters, our vertical set of filter tap // weights will be the same for the whole scanline we're on. Just // compute and normalize them once. float totalweight_y = 0.0f; if (separable) { for (int j = 0; j < ytaps; ++j) { float w = filter->yfilt (yratio * (j-radj-(src_yf_frac-0.5f))); yfiltval[j] = w; totalweight_y += w; } for (int i = 0; i <= ytaps; ++i) yfiltval[i] /= totalweight_y; } for (int x = roi.xbegin; x < roi.xend; ++x) { float s = (x-dstfx+0.5f)*dstpixelwidth; float src_xf = srcfx + s * srcfw; int src_x; float src_xf_frac = floorfrac (src_xf, &src_x); for (int c = 0; c < nchannels; ++c) pel[c] = 0.0f; if (separable) { // Cache and normalize the horizontal filter tap weights // just once for this (x,y) position, reuse for all vertical // taps. float totalweight_x = 0.0f; for (int i = 0; i < xtaps; ++i) { float w = filter->xfilt (xratio * (i-radi-(src_xf_frac-0.5f))); xfiltval[i] = w; totalweight_x += w; } if (totalweight_x != 0.0f) { for (int i = 0; i < xtaps; ++i) // normalize x filter xfiltval[i] /= totalweight_x; // weights ImageBuf::ConstIterator<SRCTYPE> srcpel (src, src_x-radi, src_x+radi+1, src_y-radj, src_y+radj+1, 0, 1, ImageBuf::WrapClamp); for (int j = -radj; j <= radj; ++j) { float wy = yfiltval[j+radj]; if (wy == 0.0f) { // 0 weight for this y tap -- move to next line srcpel.pos (srcpel.x(), srcpel.y()+1, srcpel.z()); continue; } for (int i = 0; i < xtaps; ++i, ++srcpel) { float w = wy * xfiltval[i]; for (int c = 0; c < nchannels; ++c) pel[c] += w * srcpel[c]; } } } // Copy the pixel value (already normalized) to the output. DASSERT (out.x() == x && out.y() == y); if (totalweight_y == 0.0f) { // zero it out for (int c = 0; c < nchannels; ++c) out[c] = 0.0f; } else { for (int c = 0; c < nchannels; ++c) out[c] = pel[c]; } } else { // Non-separable float totalweight = 0.0f; ImageBuf::ConstIterator<SRCTYPE> srcpel (src, src_x-radi, src_x+radi+1, src_y-radi, src_y+radi+1, 0, 1, ImageBuf::WrapClamp); for (int j = -radj; j <= radj; ++j) { for (int i = -radi; i <= radi; ++i, ++srcpel) { float w = (*filter)(xratio * (i-(src_xf_frac-0.5f)), yratio * (j-(src_yf_frac-0.5f))); totalweight += w; if (w == 0.0f) continue; DASSERT (! srcpel.done()); for (int c = 0; c < nchannels; ++c) pel[c] += w * srcpel[c]; } } DASSERT (srcpel.done()); // Rescale pel to normalize the filter and write it to the // output image. DASSERT (out.x() == x && out.y() == y); if (totalweight == 0.0f) { // zero it out for (int c = 0; c < nchannels; ++c) out[c] = 0.0f; } else { for (int c = 0; c < nchannels; ++c) out[c] = pel[c] / totalweight; } } ++out; } } 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; }