std::string ImageBufAlgo::computePixelHashSHA1 (const ImageBuf &src, const std::string & extrainfo, ROI roi, int blocksize, int nthreads) { if (! roi.defined()) roi = get_roi (src.spec()); // Fall back to whole-image hash for only one block if (blocksize <= 0 || blocksize >= roi.height()) return simplePixelHashSHA1 (src, extrainfo, roi); // Request for 0 threads means "use the OIIO global thread count" if (nthreads <= 0) OIIO::getattribute ("threads", nthreads); int nblocks = (roi.height()+blocksize-1) / blocksize; std::vector<std::string> results (nblocks); if (nthreads <= 1) { sha1_hasher (&src, roi, blocksize, &results[0], 0); } else { // parallel case boost::thread_group threads; int blocks_per_thread = (nblocks+nthreads-1) / nthreads; ROI broi = roi; for (int b = 0, t = 0; b < nblocks; b += blocks_per_thread, ++t) { int y = roi.ybegin + b*blocksize; if (y >= roi.yend) break; broi.ybegin = y; broi.yend = std::min (y+blocksize*blocks_per_thread, roi.yend); threads.add_thread (new boost::thread (sha1_hasher, &src, broi, blocksize, &results[0], b)); } threads.join_all (); } #ifdef USE_OPENSSL // If OpenSSL was available at build time, use its SHA-1 // implementation, which is about 20% faster than CSHA1. SHA_CTX sha; SHA1_Init (&sha); for (int b = 0; b < nblocks; ++b) SHA1_Update (&sha, results[b].c_str(), results[b].size()); if (extrainfo.size()) SHA1_Update (&sha, extrainfo.c_str(), extrainfo.size()); unsigned char md[SHA_DIGEST_LENGTH]; char hash_digest[2*SHA_DIGEST_LENGTH+1]; SHA1_Final (md, &sha); for (int i = 0; i < SHA_DIGEST_LENGTH; ++i) sprintf (hash_digest+2*i, "%02X", (int)md[i]); hash_digest[2*SHA_DIGEST_LENGTH] = 0; return std::string (hash_digest); #else // Fall back on CSHA1 if OpenSSL was not available or if CSHA1 sha; sha.Reset (); for (int b = 0; b < nblocks; ++b) sha.Update ((const unsigned char *)results[b].c_str(), results[b].size()); if (extrainfo.size()) sha.Update ((const unsigned char *)extrainfo.c_str(), extrainfo.size()); sha.Final (); std::string hash_digest; sha.ReportHashStl (hash_digest, CSHA1::REPORT_HEX_SHORT); return hash_digest; #endif }
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(); }
static void print_stats (const std::string &filename, const ImageSpec &originalspec, int subimage=0, int miplevel=0, bool indentmip=false) { const char *indent = indentmip ? " " : " "; ImageBuf input; if (! read_input (filename, input, subimage, miplevel)) { std::cerr << "Stats: read error: " << input.geterror() << "\n"; return; } PixelStats stats; if (! computePixelStats (stats, input)) { printf ("%sStats: (unable to compute)\n", indent); return; } // The original spec is used, otherwise the bit depth will // be reported incorrectly (as FLOAT) unsigned int maxval = get_intsample_maxval (originalspec); printf ("%sStats Min: ", indent); for (unsigned int i=0; i<stats.min.size(); ++i) { print_stats_num (stats.min[i], maxval, true); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats Max: ", indent); for (unsigned int i=0; i<stats.max.size(); ++i) { print_stats_num (stats.max[i], maxval, true); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats Avg: ", indent); for (unsigned int i=0; i<stats.avg.size(); ++i) { print_stats_num (stats.avg[i], maxval, false); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats StdDev: ", indent); for (unsigned int i=0; i<stats.stddev.size(); ++i) { print_stats_num (stats.stddev[i], maxval, false); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats NanCount: ", indent); for (unsigned int i=0; i<stats.nancount.size(); ++i) { printf ("%llu ", (unsigned long long)stats.nancount[i]); } printf ("\n"); printf ("%sStats InfCount: ", indent); for (unsigned int i=0; i<stats.infcount.size(); ++i) { printf ("%llu ", (unsigned long long)stats.infcount[i]); } printf ("\n"); printf ("%sStats FiniteCount: ", indent); for (unsigned int i=0; i<stats.finitecount.size(); ++i) { printf ("%llu ", (unsigned long long)stats.finitecount[i]); } printf ("\n"); std::vector<float> constantValues(input.spec().nchannels); if(isConstantColor(input, &constantValues[0])) { printf ("%sConstant: Yes\n", indent); printf ("%sConstant Color: ", indent); for (unsigned int i=0; i<constantValues.size(); ++i) { print_stats_num (constantValues[i], maxval, false); printf (" "); } print_stats_footer (maxval); printf ("\n"); } else { printf ("%sConstant: No\n", indent); } if(isMonochrome(input)) { printf ("%sMonochrome: Yes\n", indent); } else { printf ("%sMonochrome: No\n", indent); } }
// Test ImageBuf::zero and ImageBuf::fill void test_zero_fill () { const int WIDTH = 8; const int HEIGHT = 6; const int CHANNELS = 4; ImageSpec spec (WIDTH, HEIGHT, CHANNELS, TypeDesc::FLOAT); spec.alpha_channel = 3; // Create a buffer -- pixels should be undefined ImageBuf A ("A", spec); // Set a pixel to an odd value, make sure it takes const float arbitrary1[CHANNELS] = { 0.2, 0.3, 0.4, 0.5 }; A.setpixel (1, 1, arbitrary1); float pixel[CHANNELS]; // test pixel A.getpixel (1, 1, pixel); for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], arbitrary1[c]); // Zero out and test that it worked ImageBufAlgo::zero (A); for (int j = 0; j < HEIGHT; ++j) { for (int i = 0; i < WIDTH; ++i) { float pixel[CHANNELS]; A.getpixel (i, j, pixel); for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], 0.0f); } } // Test fill of whole image const float arbitrary2[CHANNELS] = { 0.6, 0.7, 0.3, 0.9 }; ImageBufAlgo::fill (A, arbitrary2); for (int j = 0; j < HEIGHT; ++j) { for (int i = 0; i < WIDTH; ++i) { float pixel[CHANNELS]; A.getpixel (i, j, pixel); for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], arbitrary2[c]); } } // Test fill of partial image const float arbitrary3[CHANNELS] = { 0.42, 0.43, 0.44, 0.45 }; { const int xbegin = 3, xend = 5, ybegin = 0, yend = 4; ImageBufAlgo::fill (A, arbitrary3, ROI(xbegin, xend, ybegin, yend)); for (int j = 0; j < HEIGHT; ++j) { for (int i = 0; i < WIDTH; ++i) { float pixel[CHANNELS]; A.getpixel (i, j, pixel); if (j >= ybegin && j < yend && i >= xbegin && i < xend) { for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], arbitrary3[c]); } else { for (int c = 0; c < CHANNELS; ++c) OIIO_CHECK_EQUAL (pixel[c], arbitrary2[c]); } } } } }
bool ImageBufAlgo::colorconvert (ImageBuf &dst, const ImageBuf &src, const ColorProcessor* processor, bool unpremult) { // If the processor is NULL, return false (error) if (!processor) return false; ImageSpec dstspec = dst.spec(); std::vector<float> scanline(dstspec.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 dstspec.alpha_channel index // (but first validate that the index is set properly for normal formats) int channelsToCopy = std::min (4, dstspec.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 // // FIXME: Use the ImageBuf::ConstIterator<T,T> s (src); s.isValid() // idiom for traversal instead, to allow for more efficient tile access // iteration order float * dstPtr = NULL; const float fltmin = std::numeric_limits<float>::min(); for (int k = dstspec.z; k < dstspec.z+dstspec.depth; k++) { for (int j = dstspec.y; j < dstspec.y+dstspec.height; j++) { // Load the scanline dstPtr = &scanline[0]; for (int i = dstspec.x; i < dstspec.x+dstspec.width ; i++) { src.getpixel (i, j, dstPtr, channelsToCopy); dstPtr += 4; } // Optionally unpremult if ((channelsToCopy>=4) && unpremult) { float alpha = 0.0; for (int i=0; i<dstspec.width; ++i) { 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 // This is always an rgba float image, due to the conversion above. for(int i=0; i<dstspec.width; ++i) { scanline[4*i+0] = (*processor->t2)((*processor->t1)(scanline[4*i+0])); scanline[4*i+1] = (*processor->t2)((*processor->t1)(scanline[4*i+1])); scanline[4*i+2] = (*processor->t2)((*processor->t1)(scanline[4*i+2])); } // Optionally premult if ((channelsToCopy>=4) && unpremult) { float alpha = 0.0; for (int i=0; i<dstspec.width; ++i) { 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]; for (int i = dstspec.x; i < dstspec.x+dstspec.width ; i++) { dst.setpixel (i, j, dstPtr, channelsToCopy); dstPtr += 4; } } } return true; }
static void print_stats (const std::string &filename, const ImageSpec &originalspec, int subimage=0, int miplevel=0, bool indentmip=false) { PixelStats stats; const char *indent = indentmip ? " " : " "; ImageBuf input; if (! read_input (filename, input, subimage, miplevel)) { std::cerr << "Stats: read error: " << input.geterror() << "\n"; return; } if (! computePixelStats (stats, input)) { printf ("%sStats: (unable to compute)\n", indent); if (input.has_error()) std::cerr << "Error: " << input.geterror() << "\n"; return; } // The original spec is used, otherwise the bit depth will // be reported incorrectly (as FLOAT) unsigned int maxval = (unsigned int)get_intsample_maxval (originalspec); printf ("%sStats Min: ", indent); for (unsigned int i=0; i<stats.min.size(); ++i) { print_stats_num (stats.min[i], maxval, true); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats Max: ", indent); for (unsigned int i=0; i<stats.max.size(); ++i) { print_stats_num (stats.max[i], maxval, true); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats Avg: ", indent); for (unsigned int i=0; i<stats.avg.size(); ++i) { print_stats_num (stats.avg[i], maxval, false); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats StdDev: ", indent); for (unsigned int i=0; i<stats.stddev.size(); ++i) { print_stats_num (stats.stddev[i], maxval, false); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats NanCount: ", indent); for (unsigned int i=0; i<stats.nancount.size(); ++i) { printf ("%llu ", (unsigned long long)stats.nancount[i]); } printf ("\n"); printf ("%sStats InfCount: ", indent); for (unsigned int i=0; i<stats.infcount.size(); ++i) { printf ("%llu ", (unsigned long long)stats.infcount[i]); } printf ("\n"); printf ("%sStats FiniteCount: ", indent); for (unsigned int i=0; i<stats.finitecount.size(); ++i) { printf ("%llu ", (unsigned long long)stats.finitecount[i]); } printf ("\n"); if (input.deep()) { const DeepData *dd (input.deepdata()); size_t npixels = dd->pixels(); size_t totalsamples = 0, emptypixels = 0; size_t maxsamples = 0, minsamples = std::numeric_limits<size_t>::max(); for (size_t p = 0; p < npixels; ++p) { size_t c = dd->samples(p); totalsamples += c; if (c > maxsamples) maxsamples = c; if (c < minsamples) minsamples = c; if (c == 0) ++emptypixels; } printf ("%sMin deep samples in any pixel : %llu\n", indent, (unsigned long long)minsamples); printf ("%sMax deep samples in any pixel : %llu\n", indent, (unsigned long long)maxsamples); printf ("%sAverage deep samples per pixel: %.2f\n", indent, double(totalsamples)/double(npixels)); printf ("%sTotal deep samples in all pixels: %llu\n", indent, (unsigned long long)totalsamples); printf ("%sPixels with deep samples : %llu\n", indent, (unsigned long long)(npixels-emptypixels)); printf ("%sPixels with no deep samples: %llu\n", indent, (unsigned long long)emptypixels); } else { std::vector<float> constantValues(input.spec().nchannels); if (isConstantColor(input, &constantValues[0])) { printf ("%sConstant: Yes\n", indent); printf ("%sConstant Color: ", indent); for (unsigned int i=0; i<constantValues.size(); ++i) { print_stats_num (constantValues[i], maxval, false); printf (" "); } print_stats_footer (maxval); printf ("\n"); } else { printf ("%sConstant: No\n", indent); } if (isMonochrome(input)) { printf ("%sMonochrome: Yes\n", indent); } else { printf ("%sMonochrome: No\n", indent); } } }
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; }
#include "image.h" using namespace std; using namespace bandit; using namespace Splash; go_bandit([]() { /*********/ describe("Image class", [&]() { Image image; before_each([&]() { image = Image(); }); it("should get the same image specs", [&]() { ImageBuf srcImg = image.get(); auto obj = image.serialize(); image.deserialize(obj); ImageBuf dstImg = image.get(); ImageSpec src = srcImg.spec(); ImageSpec dst = dstImg.spec(); bool isIdentical = true; if (src.width != dst.width) isIdentical = false; if (src.height != dst.height) isIdentical = false; if (src.channels != dst.channels) isIdentical = false; if (src.format != dst.format) isIdentical = false;
static void print_stats (const std::string &filename, const ImageSpec &originalspec, int subimage=0, int miplevel=0, bool indentmip=false) { const char *indent = indentmip ? " " : " "; ImageBuf input; if (! read_input (filename, input, subimage, miplevel)) { std::cerr << "Stats: read error: " << input.geterror() << "\n"; return; } PixelStats stats; if (! computePixelStats (stats, input)) { printf ("%sStats: (unable to compute)\n", indent); return; } // The original spec is used, otherwise the bit depth will // be reported incorrectly (as FLOAT) unsigned int maxval = (unsigned int)get_intsample_maxval (originalspec); printf ("%sStats Min: ", indent); for (unsigned int i=0; i<stats.min.size(); ++i) { print_stats_num (stats.min[i], maxval, true); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats Max: ", indent); for (unsigned int i=0; i<stats.max.size(); ++i) { print_stats_num (stats.max[i], maxval, true); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats Avg: ", indent); for (unsigned int i=0; i<stats.avg.size(); ++i) { print_stats_num (stats.avg[i], maxval, false); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats StdDev: ", indent); for (unsigned int i=0; i<stats.stddev.size(); ++i) { print_stats_num (stats.stddev[i], maxval, false); printf (" "); } print_stats_footer (maxval); printf ("\n"); printf ("%sStats NanCount: ", indent); for (unsigned int i=0; i<stats.nancount.size(); ++i) { printf ("%llu ", (unsigned long long)stats.nancount[i]); } printf ("\n"); printf ("%sStats InfCount: ", indent); for (unsigned int i=0; i<stats.infcount.size(); ++i) { printf ("%llu ", (unsigned long long)stats.infcount[i]); } printf ("\n"); printf ("%sStats FiniteCount: ", indent); for (unsigned int i=0; i<stats.finitecount.size(); ++i) { printf ("%llu ", (unsigned long long)stats.finitecount[i]); } printf ("\n"); if (input.deep()) { const DeepData *dd (input.deepdata()); size_t npixels = dd->nsamples.size(); size_t totalsamples = 0, emptypixels = 0; size_t maxsamples = 0, minsamples = std::numeric_limits<size_t>::max(); size_t maxsamples_npixels = 0; float mindepth = std::numeric_limits<float>::max(); float maxdepth = -std::numeric_limits<float>::max(); Imath::V3i maxsamples_pixel(-1,-1,-1), minsamples_pixel(-1,-1,-1); Imath::V3i mindepth_pixel(-1,-1,-1), maxdepth_pixel(-1,-1,-1); size_t sampoffset = 0; int depthchannel = -1; for (int c = 0; c < input.nchannels(); ++c) if (Strutil::iequals (originalspec.channelnames[c], "Z")) depthchannel = c; int xend = originalspec.x + originalspec.width; int yend = originalspec.y + originalspec.height; int zend = originalspec.z + originalspec.depth; size_t p = 0; for (int z = originalspec.z; z < zend; ++z) { for (int y = originalspec.y; y < yend; ++y) { for (int x = originalspec.x; x < xend; ++x, ++p) { size_t c = input.deep_samples (x, y, z); totalsamples += c; if (c == maxsamples) ++maxsamples_npixels; if (c > maxsamples) { maxsamples = c; maxsamples_pixel.setValue (x, y, z); maxsamples_npixels = 1; } if (c < minsamples) minsamples = c; if (c == 0) ++emptypixels; if (depthchannel >= 0) { for (unsigned int s = 0; s < c; ++s) { float d = input.deep_value (x, y, z, depthchannel, s); if (d < mindepth) { mindepth = d; mindepth_pixel.setValue (x, y, z); } if (d > maxdepth) { maxdepth = d; maxdepth_pixel.setValue (x, y, z); } } } sampoffset += c; } } } printf ("%sMin deep samples in any pixel : %llu\n", indent, (unsigned long long)minsamples); printf ("%sMax deep samples in any pixel : %llu\n", indent, (unsigned long long)maxsamples); printf ("%s%llu pixel%s had the max of %llu samples, including (x=%d, y=%d)\n", indent, (unsigned long long)maxsamples_npixels, maxsamples_npixels > 1 ? "s" : "", (unsigned long long)maxsamples, maxsamples_pixel.x, maxsamples_pixel.y); printf ("%sAverage deep samples per pixel: %.2f\n", indent, double(totalsamples)/double(npixels)); printf ("%sTotal deep samples in all pixels: %llu\n", indent, (unsigned long long)totalsamples); printf ("%sPixels with deep samples : %llu\n", indent, (unsigned long long)(npixels-emptypixels)); printf ("%sPixels with no deep samples: %llu\n", indent, (unsigned long long)emptypixels); printf ("%sMinimum depth was %g at (%d, %d)\n", indent, mindepth, mindepth_pixel.x, mindepth_pixel.y); printf ("%sMaximum depth was %g at (%d, %d)\n", indent, maxdepth, maxdepth_pixel.x, maxdepth_pixel.y); } else { std::vector<float> constantValues(input.spec().nchannels); if (isConstantColor(input, &constantValues[0])) { printf ("%sConstant: Yes\n", indent); printf ("%sConstant Color: ", indent); for (unsigned int i=0; i<constantValues.size(); ++i) { print_stats_num (constantValues[i], maxval, false); printf (" "); } print_stats_footer (maxval); printf ("\n"); } else { printf ("%sConstant: No\n", indent); } if( isMonochrome(input)) { printf ("%sMonochrome: Yes\n", indent); } else { printf ("%sMonochrome: No\n", indent); } } }
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; }
int main (int argc, char *argv[]) { getargs (argc, argv); std::cout << "Comparing \"" << filenames[0] << "\" and \"" << filenames[1] << "\"\n"; // Create a private ImageCache so we can customize its cache size // and instruct it store everything internally as floats. ImageCache *imagecache = ImageCache::create (true); imagecache->attribute ("forcefloat", 1); if (sizeof(void *) == 4) // 32 bit or 64? imagecache->attribute ("max_memory_MB", 512.0); else imagecache->attribute ("max_memory_MB", 2048.0); imagecache->attribute ("autotile", 256); #ifdef DEBUG imagecache->attribute ("statistics:level", 2); #endif // force a full diff, even for files tagged with the same // fingerprint, just in case some mistake has been made. imagecache->attribute ("deduplicate", 0); ImageBuf img0, img1; if (! read_input (filenames[0], img0, imagecache) || ! read_input (filenames[1], img1, imagecache)) return ErrFile; // ImageSpec spec0 = img0.spec(); // stash it int ret = ErrOK; for (int subimage = 0; subimage < img0.nsubimages(); ++subimage) { if (subimage > 0 && !compareall) break; if (subimage >= img1.nsubimages()) break; if (! read_input (filenames[0], img0, imagecache, subimage) || ! read_input (filenames[1], img1, imagecache, subimage)) { std::cout << "Failed to read subimage " << subimage << "\n"; return ErrFile; } if (img0.nmiplevels() != img1.nmiplevels()) { std::cout << "Files do not match in their number of MIPmap levels\n"; } for (int m = 0; m < img0.nmiplevels(); ++m) { if (m > 0 && !compareall) break; if (m > 0 && img0.nmiplevels() != img1.nmiplevels()) { std::cout << "Files do not match in their number of MIPmap levels\n"; ret = ErrDifferentSize; break; } if (! read_input (filenames[0], img0, imagecache, subimage, m) || ! read_input (filenames[1], img1, imagecache, subimage, m)) return ErrFile; // Compare the dimensions of the images. Fail if they // aren't the same resolution and number of channels. No // problem, though, if they aren't the same data type. if (! same_size (img0, img1)) { print_subimage (img0, subimage, m); std::cout << "Images do not match in size: "; std::cout << "(" << img0.spec().width << "x" << img0.spec().height; if (img0.spec().depth > 1) std::cout << "x" << img0.spec().depth; std::cout << "x" << img0.spec().nchannels << ")"; std::cout << " versus "; std::cout << "(" << img1.spec().width << "x" << img1.spec().height; if (img1.spec().depth > 1) std::cout << "x" << img1.spec().depth; std::cout << "x" << img1.spec().nchannels << ")\n"; ret = ErrDifferentSize; break; } if (img0.deep() != img1.deep()) { std::cout << "One image contains deep data, the other does not\n"; ret = ErrDifferentSize; break; } int npels = img0.spec().width * img0.spec().height * img0.spec().depth; if (npels == 0) npels = 1; // Avoid divide by zero for 0x0 images ASSERT (img0.spec().format == TypeDesc::FLOAT); // Compare the two images. // ImageBufAlgo::CompareResults cr; ImageBufAlgo::compare (img0, img1, failthresh, warnthresh, cr); int yee_failures = 0; if (perceptual && ! img0.deep()) yee_failures = ImageBufAlgo::compare_Yee (img0, img1); if (cr.nfail > (failpercent/100.0 * npels) || cr.maxerror > hardfail || yee_failures > (failpercent/100.0 * npels)) { ret = ErrFail; } else if (cr.nwarn > (warnpercent/100.0 * npels) || cr.maxerror > hardwarn) { if (ret != ErrFail) ret = ErrWarn; } // Print the report // if (verbose || ret != ErrOK) { if (compareall) print_subimage (img0, subimage, m); std::cout << " Mean error = "; safe_double_print (cr.meanerror); std::cout << " RMS error = "; safe_double_print (cr.rms_error); std::cout << " Peak SNR = "; safe_double_print (cr.PSNR); std::cout << " Max error = " << cr.maxerror; if (cr.maxerror != 0) { std::cout << " @ (" << cr.maxx << ", " << cr.maxy; if (img0.spec().depth > 1) std::cout << ", " << cr.maxz; std::cout << ", " << img0.spec().channelnames[cr.maxc] << ')'; } std::cout << "\n"; // when Visual Studio is used float values in scientific foramt are // printed with three digit exponent. We change this behaviour to fit // Linux way #ifdef _MSC_VER _set_output_format(_TWO_DIGIT_EXPONENT); #endif std::streamsize precis = std::cout.precision(); std::cout << " " << cr.nwarn << " pixels (" << std::setprecision(3) << (100.0*cr.nwarn / npels) << std::setprecision(precis) << "%) over " << warnthresh << "\n"; std::cout << " " << cr.nfail << " pixels (" << std::setprecision(3) << (100.0*cr.nfail / npels) << std::setprecision(precis) << "%) over " << failthresh << "\n"; if (perceptual) std::cout << " " << yee_failures << " pixels (" << std::setprecision(3) << (100.0*yee_failures / npels) << std::setprecision(precis) << "%) failed the perceptual test\n"; } // If the user requested that a difference image be output, // do that. N.B. we only do this for the first subimage // right now, because ImageBuf doesn't really know how to // write subimages. if (diffimage.size() && (cr.maxerror != 0 || !outdiffonly)) { ImageBuf diff (diffimage, img0.spec()); ImageBuf::ConstIterator<float,float> pix0 (img0); ImageBuf::ConstIterator<float,float> pix1 (img1); ImageBuf::Iterator<float,float> pixdiff (diff); // Subtract the second image from the first. At which // time we no longer need the second image, so free it. if (diffabs) { for ( ; pix0.valid(); ++pix0) { pix1.pos (pix0.x(), pix0.y()); // ensure alignment pixdiff.pos (pix0.x(), pix0.y()); for (int c = 0; c < img0.nchannels(); ++c) pixdiff[c] = diffscale * fabsf (pix0[c] - pix1[c]); } } else { for ( ; pix0.valid(); ++pix0) { pix1.pos (pix0.x(), pix0.y()); // ensure alignment pixdiff.pos (pix0.x(), pix0.y()); for (int c = 0; c < img0.spec().nchannels; ++c) pixdiff[c] = diffscale * (pix0[c] - pix1[c]); } } diff.save (diffimage); // Clear diff image name so we only save the first // non-matching subimage. diffimage = ""; } } } if (compareall && img0.nsubimages() != img1.nsubimages()) { std::cout << "Images had differing numbers of subimages (" << img0.nsubimages() << " vs " << img1.nsubimages() << ")\n"; ret = ErrFail; } if (!compareall && (img0.nsubimages() > 1 || img1.nsubimages() > 1)) { std::cout << "Only compared the first subimage (of " << img0.nsubimages() << " and " << img1.nsubimages() << ", respectively)\n"; } if (ret == ErrOK) std::cout << "PASS\n"; else if (ret == ErrWarn) std::cout << "WARNING\n"; else std::cout << "FAILURE\n"; ImageCache::destroy (imagecache); return ret; }
static void test_texture3d (ustring filename) { std::cerr << "Testing 3d texture " << filename << ", output = " << output_filename << "\n"; const int nchannels = 4; ImageSpec outspec (output_xres, output_yres, nchannels, TypeDesc::HALF); ImageBuf image (output_filename, outspec); ImageBufAlgo::zero (image); Imath::M33f scale; scale.scale (Imath::V2f (0.5, 0.5)); Imath::M33f rot; rot.rotate (radians(30.0f)); Imath::M33f trans; trans.translate (Imath::V2f (0.35f, 0.15f)); Imath::M33f xform = scale * rot * trans; xform.invert(); TextureOptions opt; opt.sblur = blur; opt.tblur = blur; opt.rblur = blur; opt.swidth = width; opt.twidth = width; opt.rwidth = width; opt.nchannels = nchannels; float fill = 0; opt.fill = fill; if (missing[0] >= 0) opt.missingcolor.init ((float *)&missing, 0); opt.swrap = opt.twrap = opt.rwrap = TextureOptions::WrapPeriodic; int shadepoints = blocksize*blocksize; Imath::V3f *P = ALLOCA (Imath::V3f, shadepoints); Runflag *runflags = ALLOCA (Runflag, shadepoints); Imath::V3f *dPdx = ALLOCA (Imath::V3f, shadepoints); Imath::V3f *dPdy = ALLOCA (Imath::V3f, shadepoints); Imath::V3f *dPdz = ALLOCA (Imath::V3f, shadepoints); float *result = ALLOCA (float, shadepoints*nchannels); for (int iter = 0; iter < iters; ++iter) { // Iterate over blocks // Trick: switch to second texture, if given, for second iteration if (iter && filenames.size() > 1) filename = ustring (filenames[1]); for (int by = 0; by < output_yres; by+=blocksize) { for (int bx = 0; bx < output_xres; bx+=blocksize) { // Process pixels within a block. First save the texture warp // (s,t) and derivatives into SIMD vectors. int idx = 0; for (int y = by; y < by+blocksize; ++y) { for (int x = bx; x < bx+blocksize; ++x) { if (x < output_xres && y < output_yres) { if (nowarp) { P[idx][0] = (float)x/output_xres * scalest; P[idx][1] = (float)y/output_yres * scalest; P[idx][2] = 0.5f * scalest; P[idx] += offset; dPdx[idx][0] = 1.0f/output_xres * scalest; dPdx[idx][1] = 0; dPdx[idx][2] = 0; dPdy[idx][0] = 0; dPdy[idx][1] = 1.0f/output_yres * scalest; dPdy[idx][2] = 0; dPdz[idx].setValue (0,0,0); } else { Imath::V3f coord = warp ((float)x/output_xres, (float)y/output_yres, 0.5, xform); coord *= scalest; coord += offset; Imath::V3f coordx = warp ((float)(x+1)/output_xres, (float)y/output_yres, 0.5, xform); coordx *= scalest; coordx += offset; Imath::V3f coordy = warp ((float)x/output_xres, (float)(y+1)/output_yres, 0.5, xform); coordy *= scalest; coordy += offset; P[idx] = coord; dPdx[idx] = coordx - coord; dPdy[idx] = coordy - coord; dPdz[idx].setValue (0,0,0); } runflags[idx] = RunFlagOn; } else { runflags[idx] = RunFlagOff; } ++idx; } } // Call the texture system to do the filtering. bool ok = texsys->texture3d (filename, opt, runflags, 0, shadepoints, Varying(P), Varying(dPdx), Varying(dPdy), Varying(dPdz), result); if (! ok) { std::string e = texsys->geterror (); if (! e.empty()) std::cerr << "ERROR: " << e << "\n"; } for (int i = 0; i < shadepoints*nchannels; ++i) result[i] *= scalefactor; // Save filtered pixels back to the image. idx = 0; for (int y = by; y < by+blocksize; ++y) { for (int x = bx; x < bx+blocksize; ++x) { if (runflags[idx]) { image.setpixel (x, y, result + idx*nchannels); } ++idx; } } } } } if (! image.save ()) std::cerr << "Error writing " << output_filename << " : " << image.geterror() << "\n"; }
static void test_plain_texture () { std::cerr << "Testing 2d texture " << filenames[0] << ", output = " << output_filename << "\n"; const int nchannels = 4; ImageSpec outspec (output_xres, output_yres, nchannels, TypeDesc::HALF); ImageBuf image (output_filename, outspec); ImageBufAlgo::zero (image); Imath::M33f scale; scale.scale (Imath::V2f (0.5, 0.5)); Imath::M33f rot; rot.rotate (radians(30.0f)); Imath::M33f trans; trans.translate (Imath::V2f (0.35f, 0.15f)); Imath::M33f xform = scale * rot * trans; xform.invert(); TextureOptions opt; opt.sblur = blur; opt.tblur = blur; opt.swidth = width; opt.twidth = width; opt.nchannels = nchannels; float fill = 1; opt.fill = fill; if (missing[0] >= 0) opt.missingcolor.init ((float *)&missing, 0); // opt.interpmode = TextureOptions::InterpSmartBicubic; // opt.mipmode = TextureOptions::MipModeAniso; opt.swrap = opt.twrap = TextureOptions::WrapPeriodic; // opt.twrap = TextureOptions::WrapBlack; #if 1 TextureOpt opt1; opt1.sblur = blur; opt1.tblur = blur; opt1.swidth = width; opt1.twidth = width; opt1.nchannels = nchannels; opt1.fill = fill; if (missing[0] >= 0) opt1.missingcolor = (float *)&missing; opt1.swrap = opt1.twrap = TextureOpt::WrapPeriodic; #endif int shadepoints = blocksize*blocksize; float *s = ALLOCA (float, shadepoints); float *t = ALLOCA (float, shadepoints); Runflag *runflags = ALLOCA (Runflag, shadepoints); float *dsdx = ALLOCA (float, shadepoints); float *dtdx = ALLOCA (float, shadepoints); float *dsdy = ALLOCA (float, shadepoints); float *dtdy = ALLOCA (float, shadepoints); float *result = ALLOCA (float, shadepoints*nchannels); ustring filename = ustring (filenames[0]); TextureSystem::Perthread *perthread_info = texsys->get_perthread_info (); TextureSystem::TextureHandle *texture_handle = texsys->get_texture_handle (filename); for (int iter = 0; iter < iters; ++iter) { if (iters > 1 && filenames.size() > 1) { // Use a different filename for each iteration int texid = std::min (iter, (int)filenames.size()-1); filename = ustring (filenames[texid]); std::cerr << "iter " << iter << " file " << filename << "\n"; } // Iterate over blocks for (int by = 0, b = 0; by < output_yres; by+=blocksize) { for (int bx = 0; bx < output_xres; bx+=blocksize, ++b) { // Trick: switch to other textures on later iterations, if any if (iters == 1 && filenames.size() > 1) { // Use a different filename from block to block filename = ustring (filenames[b % (int)filenames.size()]); } // Process pixels within a block. First save the texture warp // (s,t) and derivatives into SIMD vectors. int idx = 0; for (int y = by; y < by+blocksize; ++y) { for (int x = bx; x < bx+blocksize; ++x) { if (x < output_xres && y < output_yres) { if (nowarp) { s[idx] = (float)x/output_xres * scalest + offset[0]; t[idx] = (float)y/output_yres + offset[1]; dsdx[idx] = 1.0f/output_xres * scalest * scalest; dtdx[idx] = 0; dsdy[idx] = 0; dtdy[idx] = 1.0f/output_yres * scalest; } else { Imath::V3f coord = warp ((float)x/output_xres, (float)y/output_yres, xform); coord *= scalest; coord += offset; Imath::V3f coordx = warp ((float)(x+1)/output_xres, (float)y/output_yres, xform); coordx *= scalest; coordx += offset; Imath::V3f coordy = warp ((float)x/output_xres, (float)(y+1)/output_yres, xform); coordy *= scalest; coordy += offset; s[idx] = coord[0]; t[idx] = coord[1]; dsdx[idx] = coordx[0] - coord[0]; dtdx[idx] = coordx[1] - coord[1]; dsdy[idx] = coordy[0] - coord[0]; dtdy[idx] = coordy[1] - coord[1]; } runflags[idx] = RunFlagOn; } else { runflags[idx] = RunFlagOff; } ++idx; } } // Call the texture system to do the filtering. bool ok; if (blocksize == 1) { if (use_handle) ok = texsys->texture (texture_handle, perthread_info, opt1, s[0], t[0], dsdx[0], dtdx[0], dsdy[0], dtdy[0], result); else ok = texsys->texture (filename, opt1, s[0], t[0], dsdx[0], dtdx[0], dsdy[0], dtdy[0], result); } else { ok = texsys->texture (filename, opt, runflags, 0, shadepoints, Varying(s), Varying(t), Varying(dsdx), Varying(dtdx), Varying(dsdy), Varying(dtdy), result); } if (! ok) { std::string e = texsys->geterror (); if (! e.empty()) std::cerr << "ERROR: " << e << "\n"; } for (int i = 0; i < shadepoints*nchannels; ++i) result[i] *= scalefactor; // Save filtered pixels back to the image. idx = 0; for (int y = by; y < by+blocksize; ++y) { for (int x = bx; x < bx+blocksize; ++x) { if (runflags[idx]) { image.setpixel (x, y, result + idx*nchannels); } ++idx; } } } } } if (! image.save ()) std::cerr << "Error writing " << output_filename << " : " << image.geterror() << "\n"; }
static bool computePixelStats_ (const ImageBuf &src, ImageBufAlgo::PixelStats &stats, 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; // Use local storage for smaller batches, then merge the batches // into the final results. This preserves precision for large // images, where the running total may be too big to incorporate the // contributions of individual pixel values without losing // precision. // // This approach works best when the batch size is the sqrt of // numpixels, which makes the num batches roughly equal to the // number of pixels / batch. ImageBufAlgo::PixelStats tmp; reset (tmp, nchannels); reset (stats, nchannels); int PIXELS_PER_BATCH = std::max (1024, static_cast<int>(sqrt((double)src.spec().image_pixels()))); if (src.deep()) { // Loop over all pixels ... for (ImageBuf::ConstIterator<T> s(src, roi); ! s.done(); ++s) { int samples = s.deep_samples(); if (! samples) continue; for (int c = roi.chbegin; c < roi.chend; ++c) { for (int i = 0; i < samples; ++i) { float value = s.deep_value (c, i); val (tmp, c, value); if ((tmp.finitecount[c] % PIXELS_PER_BATCH) == 0) { merge (stats, tmp); reset (tmp, nchannels); } } } } } else { // Non-deep case // Loop over all pixels ... for (ImageBuf::ConstIterator<T> s(src, roi); ! s.done(); ++s) { for (int c = roi.chbegin; c < roi.chend; ++c) { float value = s[c]; val (tmp, c, value); if ((tmp.finitecount[c] % PIXELS_PER_BATCH) == 0) { merge (stats, tmp); reset (tmp, nchannels); } } } } // Merge anything left over merge (stats, tmp); // Compute final results finalize (stats); return ! src.has_error(); };
bool ImageBuf::copy_pixels (const ImageBuf &src) { // compute overlap int xbegin = std::max (this->xbegin(), src.xbegin()); int xend = std::min (this->xend(), src.xend()); int ybegin = std::max (this->ybegin(), src.ybegin()); int yend = std::min (this->yend(), src.yend()); int zbegin = std::max (this->zbegin(), src.zbegin()); int zend = std::min (this->zend(), src.zend()); int nchannels = std::min (this->nchannels(), src.nchannels()); // If we aren't copying over all our pixels, zero out the pixels if (xbegin != this->xbegin() || xend != this->xend() || ybegin != this->ybegin() || yend != this->yend() || zbegin != this->zbegin() || zend != this->zend() || nchannels != this->nchannels()) ImageBufAlgo::zero (*this); // Call template copy_pixels_ based on src data type switch (src.spec().format.basetype) { case TypeDesc::FLOAT : copy_pixels_<float> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::UINT8 : copy_pixels_<unsigned char> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::INT8 : copy_pixels_<char> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::UINT16: copy_pixels_<unsigned short> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::INT16 : copy_pixels_<short> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::UINT : copy_pixels_<unsigned int> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::INT : copy_pixels_<int> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::HALF : copy_pixels_<half> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::DOUBLE: copy_pixels_<double> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::UINT64: copy_pixels_<unsigned long long> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; case TypeDesc::INT64 : copy_pixels_<long long> (*this, src, xbegin, xend, ybegin, yend, zbegin, zend, nchannels); break; default: ASSERT (0); } return true; }
bool ImageRec::read () { if (elaborated()) return true; static ustring u_subimages("subimages"), u_miplevels("miplevels"); static boost::regex regex_sha ("SHA-1=[[:xdigit:]]*[ ]*"); int subimages = 0; ustring uname (name()); if (! m_imagecache->get_image_info (uname, 0, 0, u_subimages, TypeDesc::TypeInt, &subimages)) { error ("file not found: \"%s\"", name()); return false; // Image not found } m_subimages.resize (subimages); bool allok = true; for (int s = 0; s < subimages; ++s) { int miplevels = 0; m_imagecache->get_image_info (uname, s, 0, u_miplevels, TypeDesc::TypeInt, &miplevels); m_subimages[s].m_miplevels.resize (miplevels); m_subimages[s].m_specs.resize (miplevels); for (int m = 0; m < miplevels; ++m) { // Force a read now for reasonable-sized first images in the // file. This can greatly speed up the multithread case for // tiled images by not having multiple threads working on the // same image lock against each other on the file handle. // We guess that "reasonable size" is 50 MB, that's enough to // hold a 2048x1536 RGBA float image. Larger things will // simply fall back on ImageCache. bool forceread = (s == 0 && m == 0 && m_imagecache->imagespec(uname,s,m)->image_bytes() < 50*1024*1024); ImageBuf *ib = new ImageBuf (name(), m_imagecache); bool ok = ib->read (s, m, forceread, TypeDesc::FLOAT /* force float */); if (!ok) error ("%s", ib->geterror()); allok &= ok; // Remove any existing SHA-1 hash from the spec. ib->specmod().erase_attribute ("oiio:SHA-1"); std::string desc = ib->spec().get_string_attribute ("ImageDescription"); if (desc.size()) ib->specmod().attribute ("ImageDescription", boost::regex_replace (desc, regex_sha, "")); m_subimages[s].m_miplevels[m].reset (ib); m_subimages[s].m_specs[m] = ib->spec(); // For ImageRec purposes, we need to restore a few of the // native settings. const ImageSpec &nativespec (ib->nativespec()); // m_subimages[s].m_specs[m].format = nativespec.format; m_subimages[s].m_specs[m].tile_width = nativespec.tile_width; m_subimages[s].m_specs[m].tile_height = nativespec.tile_height; m_subimages[s].m_specs[m].tile_depth = nativespec.tile_depth; } } m_time = Filesystem::last_write_time (name()); m_elaborated = true; return allok; }
void test_compute () { double time; ROI roi (0, xres, 0, yres, 0, 1, 0, channels); std::cout << "Test straightforward as 1D array of float: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); // imgR.write ("ref.exr"); std::cout << "Test array iterated like an image: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays_like_image, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array iterated like an image, multithreaded: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays_like_image_multithread_wrapper, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array as 1D, using SIMD: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays_simd4, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array iterated like an image, using SIMD: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays_like_image_simd, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test array iterated like an image, using SIMD, multithreaded: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_arrays_like_image_simd_multithread_wrapper, roi), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test ImageBufAlgo::mad 1 thread: "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_IBA, roi, 1), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); std::cout << "Test ImageBufAlgo::mad multi-thread " << numthreads << ": "; ImageBufAlgo::zero (imgR); time = time_trial (OIIO::bind (test_IBA, roi, numthreads), ntrials, iterations) / iterations; std::cout << Strutil::format ("%.1f Mvals/sec", (size/1.0e6)/time) << std::endl; OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,0), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,1), 0.25, 0.001); OIIO_CHECK_EQUAL_THRESH (imgR.getchannel(xres/2,yres/2,0,2), 0.50, 0.001); }