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 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; }
inline void compare_value (ImageBuf::ConstIterator<BUFT,float> &a, int chan, float aval, float bval, ImageBufAlgo::CompareResults &result, float &maxval, double &batcherror, double &batch_sqrerror, bool &failed, bool &warned, float failthresh, float warnthresh) { maxval = std::max (maxval, std::max (aval, bval)); double f = fabs (aval - bval); batcherror += f; batch_sqrerror += f*f; if (f > result.maxerror) { result.maxerror = f; result.maxx = a.x(); result.maxy = a.y(); result.maxz = a.z(); result.maxc = chan; } if (! warned && f > warnthresh) { ++result.nwarn; warned = true; } if (! failed && f > failthresh) { ++result.nfail; failed = true; } }
inline void compare_value (ImageBuf::ConstIterator<BUFT,float> &a, int chan, float aval, float bval, ImageBufAlgo::CompareResults &result, float &maxval, double &batcherror, double &batch_sqrerror, bool &failed, bool &warned, float failthresh, float warnthresh) { if (!isfinite(aval) || !isfinite(bval)) { if (isnan(aval) == isnan(bval) && isinf(aval) == isinf(bval)) return; // NaN may match NaN, Inf may match Inf if (isfinite(result.maxerror)) { // non-finite errors trump finite ones result.maxerror = std::numeric_limits<float>::infinity(); result.maxx = a.x(); result.maxy = a.y(); result.maxz = a.z(); result.maxc = chan; return; } } maxval = std::max (maxval, std::max (aval, bval)); double f = fabs (aval - bval); batcherror += f; batch_sqrerror += f*f; // We use the awkward '!(a<=threshold)' construct so that we have // failures when f is a NaN (since all comparisons involving NaN will // return false). if (!(f <= result.maxerror)) { result.maxerror = f; result.maxx = a.x(); result.maxy = a.y(); result.maxz = a.z(); result.maxc = chan; } if (! warned && !(f <= warnthresh)) { ++result.nwarn; warned = true; } if (! failed && !(f <= failthresh)) { ++result.nfail; failed = 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 inline void get_pixel_channels_ (const ImageBuf &buf, int xbegin, int xend, int ybegin, int yend, int zbegin, int zend, int chbegin, int chend, D *r, stride_t xstride, stride_t ystride, stride_t zstride) { int w = (xend-xbegin), h = (yend-ybegin); int nchans = chend - chbegin; ImageSpec::auto_stride (xstride, ystride, zstride, sizeof(D), nchans, w, h); for (ImageBuf::ConstIterator<S,D> p (buf, xbegin, xend, ybegin, yend, zbegin, zend); !p.done(); ++p) { imagesize_t offset = (p.z()-zbegin)*zstride + (p.y()-ybegin)*ystride + (p.x()-xbegin)*xstride; D *rc = (D *)((char *)r + offset); for (int c = 0; c < nchans; ++c) rc[c] = p[c+chbegin]; } }
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; }