ImageBuf ImageBufAlgo::add(Image_or_Const A, Image_or_Const B, ROI roi, int nthreads) { ImageBuf result; bool ok = add(result, A, B, roi, nthreads); if (!ok && !result.has_error()) result.error("ImageBufAlgo::add() error"); return result; }
bool ImageBufAlgo::ociolook (ImageBuf &dst, const ImageBuf &src, string_view looks, string_view from, string_view to, bool inverse, bool unpremult, string_view key, string_view value, ColorConfig *colorconfig, ROI roi, int nthreads) { if (from.empty() || from == "current") { from = src.spec().get_string_attribute ("oiio:Colorspace", "Linear"); } if (to.empty() || to == "current") { to = src.spec().get_string_attribute ("oiio:Colorspace", "Linear"); } if (from.empty() || to.empty()) { dst.error ("Unknown color space name"); return false; } ColorProcessor *processor = NULL; { spin_lock lock (colorconfig_mutex); if (! colorconfig) colorconfig = default_colorconfig.get(); if (! colorconfig) default_colorconfig.reset (colorconfig = new ColorConfig); processor = colorconfig->createLookTransform (looks, from, to, inverse, key, value); if (! processor) { if (colorconfig->error()) dst.error ("%s", colorconfig->geterror()); else dst.error ("Could not construct the color transform"); return false; } } bool ok = colorconvert (dst, src, processor, unpremult, roi, nthreads); if (ok) dst.specmod().attribute ("oiio:ColorSpace", to); { spin_lock lock (colorconfig_mutex); colorconfig->deleteColorProcessor (processor); } return ok; }
bool ImageBufAlgo::reorient (ImageBuf &dst, const ImageBuf &src, int nthreads) { ImageBuf tmp; bool ok = false; switch (src.orientation()) { case 1: ok = dst.copy (src); break; case 2: ok = ImageBufAlgo::flop (dst, src); break; case 3: ok = ImageBufAlgo::rotate180 (dst, src); break; case 4: ok = ImageBufAlgo::flip (dst, src); break; case 5: ok = ImageBufAlgo::rotate270 (tmp, src); if (ok) ok = ImageBufAlgo::flop (dst, tmp); else dst.error ("%s", tmp.geterror()); break; case 6: ok = ImageBufAlgo::rotate90 (dst, src); break; case 7: ok = ImageBufAlgo::flip (tmp, src); if (ok) ok = ImageBufAlgo::rotate90 (dst, tmp); else dst.error ("%s", tmp.geterror()); break; case 8: ok = ImageBufAlgo::rotate270 (dst, src); break; } dst.set_orientation (1); return ok; }
bool ImageBufAlgo::resample (ImageBuf &dst, const ImageBuf &src, bool interpolate, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src)) return false; if (dst.nchannels() != src.nchannels()) { dst.error ("channel number mismatch: %d vs. %d", dst.spec().nchannels, src.spec().nchannels); return false; } if (dst.spec().depth > 1 || src.spec().depth > 1) { dst.error ("ImageBufAlgo::resample does not support volume images"); return false; } OIIO_DISPATCH_TYPES2 ("resample", resample_, dst.spec().format, src.spec().format, dst, src, interpolate, roi, nthreads); return false; }
bool ImageBufAlgo::add(ImageBuf& dst, Image_or_Const A_, Image_or_Const B_, ROI roi, int nthreads) { pvt::LoggedTimer logtime("IBA::add"); if (A_.is_img() && B_.is_img()) { const ImageBuf &A(A_.img()), &B(B_.img()); if (!IBAprep(roi, &dst, &A, &B)) return false; ROI origroi = roi; roi.chend = std::min(roi.chend, std::min(A.nchannels(), B.nchannels())); bool ok; OIIO_DISPATCH_COMMON_TYPES3(ok, "add", add_impl, dst.spec().format, A.spec().format, B.spec().format, dst, A, B, roi, nthreads); if (roi.chend < origroi.chend && A.nchannels() != B.nchannels()) { // Edge case: A and B differed in nchannels, we allocated dst to be // the bigger of them, but adjusted roi to be the lesser. Now handle // the channels that got left out because they were not common to // all the inputs. ASSERT(roi.chend <= dst.nchannels()); roi.chbegin = roi.chend; roi.chend = origroi.chend; if (A.nchannels() > B.nchannels()) { // A exists copy(dst, A, dst.spec().format, roi, nthreads); } else { // B exists copy(dst, B, dst.spec().format, roi, nthreads); } } return ok; } if (A_.is_val() && B_.is_img()) // canonicalize to A_img, B_val A_.swap(B_); if (A_.is_img() && B_.is_val()) { const ImageBuf& A(A_.img()); cspan<float> b = B_.val(); if (!IBAprep(roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP)) return false; IBA_FIX_PERCHAN_LEN_DEF(b, A.nchannels()); if (dst.deep()) { // While still serial, set up all the sample counts dst.deepdata()->set_all_samples(A.deepdata()->all_samples()); return add_impl_deep(dst, A, b, roi, nthreads); } bool ok; OIIO_DISPATCH_COMMON_TYPES2(ok, "add", add_impl, dst.spec().format, A.spec().format, dst, A, b, roi, nthreads); return ok; } // Remaining cases: error dst.error("ImageBufAlgo::add(): at least one argument must be an image"); return false; }
bool ImageBufAlgo::histogram (const ImageBuf &A, int channel, std::vector<imagesize_t> &histogram, int bins, float min, float max, imagesize_t *submin, imagesize_t *supermax, ROI roi) { if (A.spec().format != TypeDesc::TypeFloat) { A.error ("Unsupported pixel data format '%s'", A.spec().format); return false; } if (A.nchannels() == 0) { A.error ("Input image must have at least 1 channel"); return false; } if (channel < 0 || channel >= A.nchannels()) { A.error ("Invalid channel %d for input image with channels 0 to %d", channel, A.nchannels()-1); return false; } if (bins < 1) { A.error ("The number of bins must be at least 1"); return false; } if (max <= min) { A.error ("Invalid range, min must be strictly smaller than max"); return false; } // Specified ROI -> use it. Unspecified ROI -> initialize from A. if (! roi.defined()) roi = get_roi (A.spec()); histogram_impl<float> (A, channel, histogram, bins, min, max, submin, supermax, roi); return ! A.has_error(); }
bool ImageBufAlgo::capture_image (ImageBuf &dst, int cameranum, TypeDesc convert) { #ifdef USE_OPENCV IplImage *frame = NULL; { // This block is mutex-protected lock_guard lock (opencv_mutex); CvCapture *cvcam = cameras[cameranum]; if (! cvcam) { dst.error ("Could not create a capture camera (OpenCV error)"); return false; // failed somehow } frame = cvQueryFrame (cvcam); if (! frame) { dst.error ("Could not cvQueryFrame (OpenCV error)"); return false; // failed somehow } } time_t now; time (&now); struct tm tmtime; Sysutil::get_local_time (&now, &tmtime); std::string datetime = Strutil::format ("%4d:%02d:%02d %02d:%02d:%02d", tmtime.tm_year+1900, tmtime.tm_mon+1, tmtime.tm_mday, tmtime.tm_hour, tmtime.tm_min, tmtime.tm_sec); bool ok = ImageBufAlgo::from_IplImage (dst, frame, convert); // cvReleaseImage (&frame); // unnecessary? if (ok) dst.specmod().attribute ("DateTime", datetime); return ok; #else dst.error ("capture_image not supported -- no OpenCV support at compile time"); return false; #endif }
bool ImageBufAlgo::resize (ImageBuf &dst, const ImageBuf &src, Filter2D *filter, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src)) return false; if (dst.nchannels() != src.nchannels()) { dst.error ("channel number mismatch: %d vs. %d", dst.spec().nchannels, src.spec().nchannels); return false; } if (dst.spec().depth > 1 || src.spec().depth > 1) { dst.error ("ImageBufAlgo::resize does not support volume images"); return false; } // Set up a shared pointer with custom deleter to make sure any // filter we allocate here is properly destroyed. boost::shared_ptr<Filter2D> filterptr ((Filter2D*)NULL, Filter2D::destroy); bool allocfilter = (filter == NULL); if (allocfilter) { // If no filter was provided, punt and just linearly interpolate. const ImageSpec &srcspec (src.spec()); const ImageSpec &dstspec (dst.spec()); float wratio = float(dstspec.full_width) / float(srcspec.full_width); float hratio = float(dstspec.full_height) / float(srcspec.full_height); float w = 2.0f * std::max (1.0f, wratio); float h = 2.0f * std::max (1.0f, hratio); filter = Filter2D::create ("triangle", w, h); filterptr.reset (filter); } OIIO_DISPATCH_TYPES2 ("resize", resize_, dst.spec().format, src.spec().format, dst, src, filter, roi, nthreads); return false; }
bool ImageBufAlgo::flatten (ImageBuf &dst, const ImageBuf &src, ROI roi, int nthreads) { if (! src.deep()) { // For some reason, we were asked to flatten an already-flat image. // So just copy it. return dst.copy (src); } // Construct an ideal spec for dst, which is like src but not deep. ImageSpec force_spec = src.spec(); force_spec.deep = false; force_spec.channelformats.clear(); if (! IBAprep (roi, &dst, &src, NULL, &force_spec, IBAprep_SUPPORT_DEEP)) return false; if (dst.spec().deep) { dst.error ("Cannot flatten to a deep image"); return false; } const ImageSpec &srcspec (src.spec()); int alpha_channel, RA_channel, GA_channel, BA_channel; int R_channel, G_channel, B_channel, 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; } bool ok; OIIO_DISPATCH_TYPES (ok, "flatten", flatten_, dst.spec().format, dst, src, roi, nthreads); return ok; }
bool ImageBufAlgo::mad (ImageBuf &dst, const ImageBuf &A, const float *B, const float *C, ROI roi, int nthreads) { if (!A.initialized()) { dst.error ("Uninitialized input image"); return false; } if (! IBAprep (roi, &dst, &A)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "mad", mad_implf, dst.spec().format, A.spec().format, dst, A, B, C, roi, nthreads); return ok; }
// DEPRECATED 2-argument version bool ImageBufAlgo::fixNonFinite (ImageBuf &dst, const ImageBuf &src, NonFiniteFixMode mode, int *pixelsFixed) { ROI roi; IBAprep (roi, &dst, &src); if (dst.nchannels() != src.nchannels()) { dst.error ("channel number mismatch: %d vs. %d", dst.spec().nchannels, src.spec().nchannels); return false; } if ((const ImageBuf *)&dst != &src) if (! dst.copy (src)) return false; return fixNonFinite (dst, mode, pixelsFixed, roi); }
bool ImageBufAlgo::convolve (ImageBuf &dst, const ImageBuf &src, const ImageBuf &kernel, bool normalize, ROI roi, int nthreads) { if (! IBAprep (roi, &dst, &src)) return false; if (dst.nchannels() != src.nchannels()) { dst.error ("channel number mismatch: %d vs. %d", dst.spec().nchannels, src.spec().nchannels); return false; } OIIO_DISPATCH_TYPES2 ("convolve", convolve_, dst.spec().format, src.spec().format, dst, src, kernel, normalize, roi, nthreads); return false; }
bool ImageBufAlgo::div(ImageBuf& dst, Image_or_Const A_, Image_or_Const B_, ROI roi, int nthreads) { pvt::LoggedTimer logtime("IBA::div"); if (A_.is_img() && B_.is_img()) { const ImageBuf &A(A_.img()), &B(B_.img()); if (!IBAprep(roi, &dst, &A, &B, IBAprep_CLAMP_MUTUAL_NCHANNELS)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES3(ok, "div", div_impl, dst.spec().format, A.spec().format, B.spec().format, dst, A, B, roi, nthreads); return ok; } if (A_.is_val() && B_.is_img()) // canonicalize to A_img, B_val A_.swap(B_); if (A_.is_img() && B_.is_val()) { const ImageBuf& A(A_.img()); cspan<float> b = B_.val(); if (!IBAprep(roi, &dst, &A, IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP)) return false; IBA_FIX_PERCHAN_LEN_DEF(b, dst.nchannels()); int nc = dst.nchannels(); float* binv = OIIO_ALLOCA(float, nc); for (int c = 0; c < nc; ++c) binv[c] = (b[c] == 0.0f) ? 0.0f : 1.0f / b[c]; b = cspan<float>(binv, nc); // re-wrap if (dst.deep()) { // While still serial, set up all the sample counts dst.deepdata()->set_all_samples(A.deepdata()->all_samples()); return mul_impl_deep(dst, A, b, roi, nthreads); } bool ok; OIIO_DISPATCH_COMMON_TYPES2(ok, "div", mul_impl, dst.spec().format, A.spec().format, dst, A, b, roi, nthreads); return ok; } // Remaining cases: error dst.error("ImageBufAlgo::div(): at least one argument must be an image"); return false; }
bool ImageBufAlgo::mad (ImageBuf &dst, const ImageBuf &A, float b, float c, ROI roi, int nthreads) { if (!A.initialized()) { dst.error ("Uninitialized input image"); return false; } if (! IBAprep (roi, &dst, &A)) return false; std::vector<float> B (roi.chend, b); std::vector<float> C (roi.chend, c); bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "mad", mad_implf, dst.spec().format, A.spec().format, dst, A, &B[0], &C[0], roi, nthreads); return ok; }
bool ImageBufAlgo::computePixelStats (PixelStats &stats, const ImageBuf &src, ROI roi, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); else roi.chend = std::min (roi.chend, src.nchannels()); int nchannels = src.spec().nchannels; if (nchannels == 0) { src.error ("%d-channel images not supported", nchannels); return false; } OIIO_DISPATCH_TYPES ("computePixelStats", computePixelStats_, src.spec().format, src, stats, roi, nthreads); return false; }
static bool histogram_impl (const ImageBuf &A, int channel, std::vector<imagesize_t> &histogram, int bins, float min, float max, imagesize_t *submin, imagesize_t *supermax, ROI roi) { // Double check A's type. if (A.spec().format != BaseTypeFromC<Atype>::value) { A.error ("Unsupported pixel data format '%s'", A.spec().format); return false; } // Initialize. ImageBuf::ConstIterator<Atype, float> a (A, roi); float ratio = bins / (max-min); int bins_minus_1 = bins-1; bool submin_ok = submin != NULL; bool supermax_ok = supermax != NULL; if (submin_ok) *submin = 0; if (supermax_ok) *supermax = 0; histogram.assign(bins, 0); // Compute histogram. for ( ; ! a.done(); a++) { float c = a[channel]; if (c >= min && c < max) { // Map range min->max to 0->(bins-1). histogram[ (int) ((c-min) * ratio) ]++; } else if (c == max) { histogram[bins_minus_1]++; } else { if (submin_ok && c < min) (*submin)++; else if (supermax_ok) (*supermax)++; } } 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; }
bool ImageBufAlgo::noise (ImageBuf &dst, string_view noisetype, float A, float B, bool mono, int seed, ROI roi, int nthreads) { if (! IBAprep (roi, &dst)) return false; bool ok; if (noisetype == "gaussian" || noisetype == "normal") { OIIO_DISPATCH_TYPES (ok, "noise_gaussian", noise_gaussian_, dst.spec().format, dst, A, B, mono, seed, roi, nthreads); } else if (noisetype == "uniform") { OIIO_DISPATCH_TYPES (ok, "noise_uniform", noise_uniform_, dst.spec().format, dst, A, B, mono, seed, roi, nthreads); } else if (noisetype == "salt") { OIIO_DISPATCH_TYPES (ok, "noise_salt", noise_salt_, dst.spec().format, dst, A, B, mono, seed, roi, nthreads); } else { ok = false; dst.error ("noise", "unknown noise type \"%s\"", noisetype); } return ok; }
bool ImageBufAlgo::mad (ImageBuf &dst, const ImageBuf &A_, const ImageBuf &B_, const ImageBuf &C_, ROI roi, int nthreads) { const ImageBuf *A = &A_, *B = &B_, *C = &C_; if (!A->initialized() || !B->initialized() || !C->initialized()) { dst.error ("Uninitialized input image"); return false; } // To avoid the full cross-product of dst/A/B/C types, force A,B,C to // all be the same data type, copying if we have to. TypeDesc abc_type = type_merge (A->spec().format, B->spec().format, C->spec().format); ImageBuf Anew, Bnew, Cnew; if (A->spec().format != abc_type) { Anew.copy (*A, abc_type); A = &Anew; } if (B->spec().format != abc_type) { Bnew.copy (*B, abc_type); B = &Bnew; } if (C->spec().format != abc_type) { Cnew.copy (*C, abc_type); C = &Cnew; } ASSERT (A->spec().format == B->spec().format && A->spec().format == C->spec().format); if (! IBAprep (roi, &dst, A, B, C)) return false; bool ok; OIIO_DISPATCH_COMMON_TYPES2 (ok, "mad", mad_impl, dst.spec().format, abc_type, dst, *A, *B, *C, roi, nthreads); return ok; }
bool ImageBufAlgo::zover (ImageBuf &R, const ImageBuf &A, const ImageBuf &B, bool z_zeroisinf, ROI roi, int nthreads) { const ImageSpec &specR = R.spec(); const ImageSpec &specA = A.spec(); const ImageSpec &specB = B.spec(); int nchannels_R, nchannels_A, nchannels_B; int alpha_R, alpha_A, alpha_B; int z_R, z_A, z_B; int colors_R, colors_A, colors_B; bool initialized_R = decode_over_channels (R, nchannels_R, alpha_R, z_R, colors_R); bool initialized_A = decode_over_channels (A, nchannels_A, alpha_A, z_A, colors_A); bool initialized_B = decode_over_channels (B, nchannels_B, alpha_B, z_B, colors_B); if (! initialized_A || ! initialized_B) { R.error ("Can't 'zover' uninitialized images"); return false; } // Fail if the input images don't have a Z channel. if (z_A < 0 || z_B < 0 || (initialized_R && z_R < 0)) { R.error ("'zover' requires Z channels"); return false; } // Fail if the input images don't have an alpha channel. if (alpha_A < 0 || alpha_B < 0 || (initialized_R && alpha_R < 0)) { R.error ("'zover' requires alpha channels"); return false; } // Fail for mismatched channel counts if (colors_A != colors_B || colors_A < 1) { R.error ("Can't 'zover' images with mismatched color channel counts (%d vs %d)", colors_A, colors_B); return false; } // Fail for unaligned alpha or z channels if (alpha_A != alpha_B || z_A != z_B || (initialized_R && alpha_R != alpha_A) || (initialized_R && z_R != z_A)) { R.error ("Can't 'zover' images with mismatched channel order", colors_A, colors_B); return false; } // At present, this operation only supports ImageBuf's containing // float pixel data. if ((initialized_R && specR.format != TypeDesc::TypeFloat) || specA.format != TypeDesc::TypeFloat || specB.format != TypeDesc::TypeFloat) { R.error ("Unsupported pixel data format combination '%s = %s zover %s'", specR.format, specA.format, specB.format); return false; } // Uninitialized R -> size it to the union of A and B. if (! initialized_R) { ImageSpec newspec = specA; set_roi (newspec, roi_union (get_roi(specA), get_roi(specB))); R.reset ("zover", newspec); } // Specified ROI -> use it. Unspecified ROI -> initialize from R. if (! roi.defined()) roi = get_roi (R.spec()); parallel_image (boost::bind (over_impl<float,float,float>, boost::ref(R), boost::cref(A), boost::cref(B), _1, true, z_zeroisinf), roi, nthreads); return ! R.has_error(); }
// DEPRECATED version bool ImageBufAlgo::add (ImageBuf &dst, const ImageBuf &A, const ImageBuf &B, int options) { // Sanity checks // dst must be distinct from A and B if ((const void *)&A == (const void *)&dst || (const void *)&B == (const void *)&dst) { dst.error ("destination image must be distinct from source"); return false; } // all three images must have the same number of channels if (A.spec().nchannels != B.spec().nchannels) { dst.error ("channel number mismatch: %d vs. %d", A.spec().nchannels, B.spec().nchannels); return false; } // If dst has not already been allocated, set it to the right size, // make it unconditinally float if (! dst.pixels_valid()) { ImageSpec dstspec = A.spec(); dstspec.set_format (TypeDesc::TypeFloat); dst.alloc (dstspec); } // Clear dst pixels if instructed to do so if (options & ADD_CLEAR_DST) { zero (dst); } ASSERT (A.spec().format == TypeDesc::FLOAT && B.spec().format == TypeDesc::FLOAT && dst.spec().format == TypeDesc::FLOAT); ImageBuf::ConstIterator<float,float> a (A); ImageBuf::ConstIterator<float,float> b (B); ImageBuf::Iterator<float> d (dst); int nchannels = A.nchannels(); // Loop over all pixels in A for ( ; a.valid(); ++a) { // Point the iterators for B and dst to the corresponding pixel if (options & ADD_RETAIN_WINDOWS) { b.pos (a.x(), a.y()); } else { // ADD_ALIGN_WINDOWS: make B line up with A b.pos (a.x()-A.xbegin()+B.xbegin(), a.y()-A.ybegin()+B.ybegin()); } d.pos (a.x(), b.y()); if (! b.valid() || ! d.valid()) continue; // Skip pixels that don't align // Add the pixel for (int c = 0; c < nchannels; ++c) d[c] = a[c] + b[c]; } 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; }
OIIO_NAMESPACE_BEGIN bool ImageBufAlgo::from_IplImage (ImageBuf &dst, const IplImage *ipl, TypeDesc convert) { if (! ipl) { DASSERT (0 && "ImageBufAlgo::fromIplImage called with NULL ipl"); dst.error ("Passed NULL source IplImage"); return false; } #ifdef USE_OPENCV TypeDesc srcformat; switch (ipl->depth) { case int(IPL_DEPTH_8U) : srcformat = TypeDesc::UINT8; break; case int(IPL_DEPTH_8S) : srcformat = TypeDesc::INT8; break; case int(IPL_DEPTH_16U) : srcformat = TypeDesc::UINT16; break; case int(IPL_DEPTH_16S) : srcformat = TypeDesc::INT16; break; case int(IPL_DEPTH_32F) : srcformat = TypeDesc::FLOAT; break; case int(IPL_DEPTH_64F) : srcformat = TypeDesc::DOUBLE; break; default: DASSERT (0 && "unknown IplImage type"); dst.error ("Unsupported IplImage depth %d", (int)ipl->depth); return false; } TypeDesc dstformat = (convert != TypeDesc::UNKNOWN) ? convert : srcformat; ImageSpec spec (ipl->width, ipl->height, ipl->nChannels, dstformat); // N.B. The OpenCV headers say that ipl->alphaChannel, // ipl->colorModel, and ipl->channelSeq are ignored by OpenCV. if (ipl->dataOrder != IPL_DATA_ORDER_PIXEL) { // We don't handle separate color channels, and OpenCV doesn't either dst.error ("Unsupported IplImage data order %d", (int)ipl->dataOrder); return false; } dst.reset (dst.name(), spec); size_t pixelsize = srcformat.size()*spec.nchannels; // Account for the origin in the line step size, to end up with the // standard OIIO origin-at-upper-left: size_t linestep = ipl->origin ? -ipl->widthStep : ipl->widthStep; // Block copy and convert convert_image (spec.nchannels, spec.width, spec.height, 1, ipl->imageData, srcformat, pixelsize, linestep, 0, dst.pixeladdr(0,0), dstformat, spec.pixel_bytes(), spec.scanline_bytes(), 0); // FIXME - honor dataOrder. I'm not sure if it is ever used by // OpenCV. Fix when it becomes a problem. // OpenCV uses BGR ordering // FIXME: what do they do with alpha? if (spec.nchannels >= 3) { float pixel[4]; for (int y = 0; y < spec.height; ++y) { for (int x = 0; x < spec.width; ++x) { dst.getpixel (x, y, pixel, 4); float tmp = pixel[0]; pixel[0] = pixel[2]; pixel[2] = tmp; dst.setpixel (x, y, pixel, 4); } } } // FIXME -- the copy and channel swap should happen all as one loop, // probably templated by type. return true; #else dst.error ("fromIplImage not supported -- no OpenCV support at compile time"); return false; #endif }
bool ImageBufAlgo::render_text (ImageBuf &R, int x, int y, string_view text, int fontsize, string_view font_, const float *textcolor) { if (R.spec().depth > 1) { R.error ("ImageBufAlgo::render_text does not support volume images"); return false; } #ifdef USE_FREETYPE // If we know FT is broken, don't bother trying again if (ft_broken) return false; // Thread safety lock_guard ft_lock (ft_mutex); int error = 0; // If FT not yet initialized, do it now. if (! ft_library) { error = FT_Init_FreeType (&ft_library); if (error) { ft_broken = true; R.error ("Could not initialize FreeType for font rendering"); return false; } } // A set of likely directories for fonts to live, across several systems. std::vector<std::string> search_dirs; const char *home = getenv ("HOME"); if (home && *home) { std::string h (home); search_dirs.push_back (h + "/fonts"); search_dirs.push_back (h + "/Fonts"); search_dirs.push_back (h + "/Library/Fonts"); } const char *systemRoot = getenv ("SystemRoot"); if (systemRoot && *systemRoot) search_dirs.push_back (std::string(systemRoot) + "/Fonts"); search_dirs.push_back ("/usr/share/fonts"); search_dirs.push_back ("/Library/Fonts"); search_dirs.push_back ("C:/Windows/Fonts"); search_dirs.push_back ("/usr/local/share/fonts"); search_dirs.push_back ("/opt/local/share/fonts"); // Try $OPENIMAGEIOHOME/fonts const char *oiiohomedir = getenv ("OPENIMAGEIOHOME"); if (oiiohomedir && *oiiohomedir) search_dirs.push_back (std::string(oiiohomedir) + "/fonts"); // Try ../fonts relative to where this executing binary came from std::string this_program = OIIO::Sysutil::this_program_path (); if (this_program.size()) { std::string path = Filesystem::parent_path (this_program); path = Filesystem::parent_path (path); search_dirs.push_back (path+"/fonts"); } // Try to find the font. Experiment with several extensions std::string font = font_; if (font.empty()) { // nothing specified -- look for something to use as a default. for (int j = 0; default_font_name[j] && font.empty(); ++j) { static const char *extensions[] = { "", ".ttf", ".pfa", ".pfb", NULL }; for (int i = 0; font.empty() && extensions[i]; ++i) font = Filesystem::searchpath_find (std::string(default_font_name[j])+extensions[i], search_dirs, true, true); } if (font.empty()) { R.error ("Could not set default font face"); return false; } } else if (Filesystem::is_regular (font)) { // directly specified a filename -- use it } else { // A font name was specified but it's not a full path, look for it std::string f; static const char *extensions[] = { "", ".ttf", ".pfa", ".pfb", NULL }; for (int i = 0; f.empty() && extensions[i]; ++i) f = Filesystem::searchpath_find (font+extensions[i], search_dirs, true, true); if (f.empty()) { R.error ("Could not set font face to \"%s\"", font); return false; } font = f; } ASSERT (! font.empty()); if (! Filesystem::is_regular (font)) { R.error ("Could not find font \"%s\"", font); return false; } FT_Face face; // handle to face object error = FT_New_Face (ft_library, font.c_str(), 0 /* face index */, &face); if (error) { R.error ("Could not set font face to \"%s\"", font); return false; // couldn't open the face } error = FT_Set_Pixel_Sizes (face, // handle to face object 0, // pixel_width fontsize); // pixel_heigh if (error) { FT_Done_Face (face); R.error ("Could not set font size to %d", fontsize); return false; // couldn't set the character size } FT_GlyphSlot slot = face->glyph; // a small shortcut int nchannels = R.spec().nchannels; float *pixelcolor = ALLOCA (float, nchannels); if (! textcolor) { float *localtextcolor = ALLOCA (float, nchannels); for (int c = 0; c < nchannels; ++c) localtextcolor[c] = 1.0f; textcolor = localtextcolor; }
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; }