ImageRec::ImageRec (ImageRec &A, ImageRec &B, int subimage_to_copy, WinMerge pixwin, WinMerge fullwin, TypeDesc pixeltype) : m_name(A.name()), m_elaborated(true), m_metadata_modified(false), m_pixels_modified(false), m_was_output(false), m_imagecache(A.m_imagecache) { A.read (); B.read (); int subimages = (subimage_to_copy < 0) ? std::min(A.subimages(), B.subimages()) : 1; int first_subimage = clamp (subimage_to_copy, 0, subimages-1); m_subimages.resize (subimages); for (int s = 0; s < subimages; ++s) { int srcsub = s + first_subimage; m_subimages[s].m_miplevels.resize (1); m_subimages[s].m_specs.resize (1); const ImageBuf &Aib (A(srcsub)); const ImageBuf &Bib (B(srcsub)); const ImageSpec &Aspec (Aib.spec()); const ImageSpec &Bspec (Bib.spec()); ImageSpec spec = Aspec; ROI Aroi = get_roi (Aspec), Aroi_full = get_roi_full (Aspec); ROI Broi = get_roi (Bspec), Broi_full = get_roi_full (Bspec); switch (pixwin) { case WinMergeUnion : set_roi (spec, roi_union (Aroi, Broi)); break; case WinMergeIntersection : set_roi (spec, roi_intersection (Aroi, Broi)); break; case WinMergeA : set_roi (spec, Aroi); break; case WinMergeB : set_roi (spec, Broi); break; } switch (fullwin) { case WinMergeUnion : set_roi_full (spec, roi_union (Aroi_full, Broi_full)); break; case WinMergeIntersection : set_roi_full (spec, roi_intersection (Aroi_full, Broi_full)); break; case WinMergeA : set_roi_full (spec, Aroi_full); break; case WinMergeB : set_roi_full (spec, Broi_full); break; } if (pixeltype != TypeDesc::UNKNOWN) spec.set_format (pixeltype); spec.nchannels = std::min (Aspec.nchannels, Bspec.nchannels); spec.channelnames.resize (spec.nchannels); spec.channelformats.clear (); ImageBuf *ib = new ImageBuf (spec); m_subimages[s].m_miplevels[0].reset (ib); m_subimages[s].m_specs[0] = spec; } }
inline void print_subimage (ImageRec &img0, int subimage, int miplevel) { if (img0.subimages() > 1) std::cout << "Subimage " << subimage << ' '; if (img0.miplevels(subimage) > 1) std::cout << " MIP level " << miplevel << ' '; if (img0.subimages() > 1 || img0.miplevels(subimage) > 1) std::cout << ": "; const ImageSpec &spec (*img0.spec(subimage)); std::cout << spec.width << " x " << spec.height; if (spec.depth > 1) std::cout << " x " << spec.depth; std::cout << ", " << spec.nchannels << " channel\n"; }
ImageRec::ImageRec (ImageRec &img, int subimage_to_copy, int miplevel_to_copy, bool writable, bool copy_pixels) : m_name(img.name()), m_elaborated(true), m_metadata_modified(false), m_pixels_modified(false), m_was_output(false), m_imagecache(img.m_imagecache) { img.read (); int first_subimage = std::max (0, subimage_to_copy); int subimages = (subimage_to_copy < 0) ? img.subimages() : 1; m_subimages.resize (subimages); for (int s = 0; s < subimages; ++s) { int srcsub = s + first_subimage; int first_miplevel = std::max (0, miplevel_to_copy); int miplevels = (miplevel_to_copy < 0) ? img.miplevels(srcsub) : 1; m_subimages[s].m_miplevels.resize (miplevels); m_subimages[s].m_specs.resize (miplevels); for (int m = 0; m < miplevels; ++m) { int srcmip = m + first_miplevel; const ImageBuf &srcib (img(srcsub,srcmip)); const ImageSpec &srcspec (*img.spec(srcsub,srcmip)); ImageBuf *ib = NULL; if (writable || img.pixels_modified() || !copy_pixels) { // Make our own copy of the pixels ib = new ImageBuf (srcspec); if (copy_pixels) ib->copy_pixels (srcib); } else { // The other image is not modified, and we don't need to be // writable, either. ib = new ImageBuf (img.name(), srcib.imagecache()); bool ok = ib->read (srcsub, srcmip, false /*force*/, img.m_input_dataformat /*convert*/); ASSERT (ok); } m_subimages[s].m_miplevels[m].reset (ib); m_subimages[s].m_specs[m] = srcspec; } } }
int OiioTool::do_action_diff (ImageRec &ir0, ImageRec &ir1, Oiiotool &ot, int perceptual) { std::cout << "Computing " << (perceptual ? "perceptual " : "") << "diff of \"" << ir0.name() << "\" vs \"" << ir1.name() << "\"\n"; ir0.read (); ir1.read (); int ret = DiffErrOK; for (int subimage = 0; subimage < ir0.subimages(); ++subimage) { if (subimage > 0 && !ot.allsubimages) break; if (subimage >= ir1.subimages()) break; for (int m = 0; m < ir0.miplevels(subimage); ++m) { if (m > 0 && !ot.allsubimages) break; if (m > 0 && ir0.miplevels(subimage) != ir1.miplevels(subimage)) { std::cout << "Files do not match in their number of MIPmap levels\n"; ret = DiffErrDifferentSize; break; } ImageBuf &img0 (ir0(subimage,m)); ImageBuf &img1 (ir1(subimage,m)); 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; int yee_failures = 0; switch (perceptual) { case 1 : yee_failures = ImageBufAlgo::compare_Yee (img0, img1, cr); break; default: ImageBufAlgo::compare (img0, img1, ot.diff_failthresh, ot.diff_warnthresh, cr); break; } if (cr.nfail > (ot.diff_failpercent/100.0 * npels) || cr.maxerror > ot.diff_hardfail || yee_failures > (ot.diff_failpercent/100.0 * npels)) { ret = DiffErrFail; } else if (cr.nwarn > (ot.diff_warnpercent/100.0 * npels) || cr.maxerror > ot.diff_hardwarn) { if (ret != DiffErrFail) ret = DiffErrWarn; } // Print the report // if (ot.verbose || ot.debug || ret != DiffErrOK) { if (ot.allsubimages) print_subimage (ir0, 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; if (cr.maxc < (int)img0.spec().channelnames.size()) std::cout << ", " << img0.spec().channelnames[cr.maxc] << ')'; else if (cr.maxc < (int)img1.spec().channelnames.size()) std::cout << ", " << img1.spec().channelnames[cr.maxc] << ')'; else std::cout << ", channel " << cr.maxc << ')'; } std::cout << "\n"; std::streamsize precis = std::cout.precision(); std::cout << " " << cr.nwarn << " pixels (" << std::setprecision(3) << (100.0*cr.nwarn / npels) << std::setprecision(precis) << "%) over " << ot.diff_warnthresh << "\n"; std::cout << " " << cr.nfail << " pixels (" << std::setprecision(3) << (100.0*cr.nfail / npels) << std::setprecision(precis) << "%) over " << ot.diff_failthresh << "\n"; if (perceptual == 1) std::cout << " " << yee_failures << " pixels (" << std::setprecision(3) << (100.0*yee_failures / npels) << std::setprecision(precis) << "%) failed the perceptual test\n"; } } } if (ot.allsubimages && ir0.subimages() != ir1.subimages()) { std::cout << "Images had differing numbers of subimages (" << ir0.subimages() << " vs " << ir1.subimages() << ")\n"; ret = DiffErrFail; } if (!ot.allsubimages && (ir0.subimages() > 1 || ir1.subimages() > 1)) { std::cout << "Only compared the first subimage (of " << ir0.subimages() << " and " << ir1.subimages() << ", respectively)\n"; } if (ret == DiffErrOK) std::cout << "PASS\n"; else if (ret == DiffErrWarn) std::cout << "WARNING\n"; else { std::cout << "FAILURE\n"; ot.return_value = ret; } return ret; }
GDALDataset* SGIDataset::Open(GDALOpenInfo* poOpenInfo) { /* -------------------------------------------------------------------- */ /* First we check to see if the file has the expected header */ /* bytes. */ /* -------------------------------------------------------------------- */ if(poOpenInfo->nHeaderBytes < 12) return NULL; ImageRec tmpImage; memcpy(&tmpImage, poOpenInfo->pabyHeader, 12); tmpImage.Swap(); if(tmpImage.imagic != 474) return NULL; if (tmpImage.type != 0 && tmpImage.type != 1) return NULL; if (tmpImage.bpc != 1 && tmpImage.bpc != 2) return NULL; if (tmpImage.dim != 1 && tmpImage.dim != 2 && tmpImage.dim != 3) return NULL; if(tmpImage.bpc != 1) { CPLError(CE_Failure, CPLE_NotSupported, "The SGI driver only supports 1 byte channel values.\n"); return NULL; } /* -------------------------------------------------------------------- */ /* Create a corresponding GDALDataset. */ /* -------------------------------------------------------------------- */ SGIDataset* poDS; poDS = new SGIDataset(); poDS->eAccess = poOpenInfo->eAccess; /* -------------------------------------------------------------------- */ /* Open the file using the large file api. */ /* -------------------------------------------------------------------- */ if( poDS->eAccess == GA_ReadOnly ) poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb"); else poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb+"); if(poDS->fpImage == NULL) { CPLError(CE_Failure, CPLE_OpenFailed, "VSIFOpenL(%s) failed unexpectedly in sgidataset.cpp\n%s", poOpenInfo->pszFilename, VSIStrerror( errno ) ); delete poDS; return NULL; } /* -------------------------------------------------------------------- */ /* Read pre-image data after ensuring the file is rewound. */ /* -------------------------------------------------------------------- */ VSIFSeekL(poDS->fpImage, 0, SEEK_SET); if(VSIFReadL((void*)(&(poDS->image)), 1, 12, poDS->fpImage) != 12) { CPLError(CE_Failure, CPLE_OpenFailed, "file read error while reading header in sgidataset.cpp"); delete poDS; return NULL; } poDS->image.Swap(); poDS->image.file = poDS->fpImage; poDS->image.fileName = poOpenInfo->pszFilename; /* -------------------------------------------------------------------- */ /* Capture some information from the file that is of interest. */ /* -------------------------------------------------------------------- */ poDS->nRasterXSize = poDS->image.xsize; poDS->nRasterYSize = poDS->image.ysize; if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0) { CPLError(CE_Failure, CPLE_OpenFailed, "Invalid image dimensions : %d x %d", poDS->nRasterXSize, poDS->nRasterYSize); delete poDS; return NULL; } poDS->nBands = MAX(1,poDS->image.zsize); if (poDS->nBands > 256) { CPLError(CE_Failure, CPLE_OpenFailed, "Too many bands : %d", poDS->nBands); delete poDS; return NULL; } int numItems = (int(poDS->image.bpc) == 1) ? 256 : 65536; poDS->image.tmp = (unsigned char*)VSICalloc(poDS->image.xsize,numItems); if (poDS->image.tmp == NULL) { CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); delete poDS; return NULL; } /* -------------------------------------------------------------------- */ /* Read RLE Pointer tables. */ /* -------------------------------------------------------------------- */ if(int(poDS->image.type) == 1) // RLE compressed { int x = poDS->image.ysize * poDS->nBands * sizeof(GUInt32); poDS->image.rowStart = (GUInt32*)VSIMalloc2(poDS->image.ysize, poDS->nBands * sizeof(GUInt32)); poDS->image.rowSize = (GInt32*)VSIMalloc2(poDS->image.ysize, poDS->nBands * sizeof(GUInt32)); if (poDS->image.rowStart == NULL || poDS->image.rowSize == NULL) { CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory"); delete poDS; return NULL; } memset(poDS->image.rowStart, 0, x); memset(poDS->image.rowSize, 0, x); poDS->image.rleEnd = 512 + (2 * x); VSIFSeekL(poDS->fpImage, 512, SEEK_SET); if((int) VSIFReadL(poDS->image.rowStart, 1, x, poDS->image.file) != x) { delete poDS; CPLError(CE_Failure, CPLE_OpenFailed, "file read error while reading start positions in sgidataset.cpp"); return NULL; } if((int) VSIFReadL(poDS->image.rowSize, 1, x, poDS->image.file) != x) { delete poDS; CPLError(CE_Failure, CPLE_OpenFailed, "file read error while reading row lengths in sgidataset.cpp"); return NULL; } ConvertLong(poDS->image.rowStart, x/(int)sizeof(GUInt32)); ConvertLong((GUInt32*)poDS->image.rowSize, x/(int)sizeof(GInt32)); } else // uncompressed. { poDS->image.rowStart = NULL; poDS->image.rowSize = NULL; } /* -------------------------------------------------------------------- */ /* Create band information objects. */ /* -------------------------------------------------------------------- */ for(int iBand = 0; iBand < poDS->nBands; iBand++) poDS->SetBand(iBand+1, new SGIRasterBand(poDS, iBand+1)); /* -------------------------------------------------------------------- */ /* Check for world file. */ /* -------------------------------------------------------------------- */ poDS->bGeoTransformValid = GDALReadWorldFile(poOpenInfo->pszFilename, ".wld", poDS->adfGeoTransform); /* -------------------------------------------------------------------- */ /* Initialize any PAM information. */ /* -------------------------------------------------------------------- */ poDS->SetDescription(poOpenInfo->pszFilename); poDS->TryLoadXML(); /* -------------------------------------------------------------------- */ /* Check for overviews. */ /* -------------------------------------------------------------------- */ poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); return poDS; }
GDALDataset* SGIDataset::Open(GDALOpenInfo* poOpenInfo) { /* -------------------------------------------------------------------- */ /* First we check to see if the file has the expected header */ /* bytes. */ /* -------------------------------------------------------------------- */ if(poOpenInfo->nHeaderBytes < 12 || poOpenInfo->fpL == nullptr ) return nullptr; ImageRec tmpImage; memcpy(&tmpImage.imagic, poOpenInfo->pabyHeader + 0, 2); memcpy(&tmpImage.type, poOpenInfo->pabyHeader + 2, 1); memcpy(&tmpImage.bpc, poOpenInfo->pabyHeader + 3, 1); memcpy(&tmpImage.dim, poOpenInfo->pabyHeader + 4, 2); memcpy(&tmpImage.xsize, poOpenInfo->pabyHeader + 6, 2); memcpy(&tmpImage.ysize, poOpenInfo->pabyHeader + 8, 2); memcpy(&tmpImage.zsize, poOpenInfo->pabyHeader + 10, 2); tmpImage.Swap(); if(tmpImage.imagic != 474) return nullptr; if (tmpImage.type != 0 && tmpImage.type != 1) return nullptr; if (tmpImage.bpc != 1 && tmpImage.bpc != 2) return nullptr; if (tmpImage.dim != 1 && tmpImage.dim != 2 && tmpImage.dim != 3) return nullptr; if(tmpImage.bpc != 1) { CPLError(CE_Failure, CPLE_NotSupported, "The SGI driver only supports 1 byte channel values.\n"); return nullptr; } /* -------------------------------------------------------------------- */ /* Create a corresponding GDALDataset. */ /* -------------------------------------------------------------------- */ SGIDataset* poDS = new SGIDataset(); poDS->eAccess = poOpenInfo->eAccess; poDS->fpImage = poOpenInfo->fpL; poOpenInfo->fpL = nullptr; /* -------------------------------------------------------------------- */ /* Read pre-image data after ensuring the file is rewound. */ /* -------------------------------------------------------------------- */ VSIFSeekL(poDS->fpImage, 0, SEEK_SET); if(VSIFReadL(reinterpret_cast<void*>( &(poDS->image) ), 1, 12, poDS->fpImage) != 12) { CPLError(CE_Failure, CPLE_OpenFailed, "file read error while reading header in sgidataset.cpp"); delete poDS; return nullptr; } poDS->image.Swap(); poDS->image.file = poDS->fpImage; poDS->image.fileName = poOpenInfo->pszFilename; /* -------------------------------------------------------------------- */ /* Capture some information from the file that is of interest. */ /* -------------------------------------------------------------------- */ poDS->nRasterXSize = poDS->image.xsize; poDS->nRasterYSize = poDS->image.ysize; if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0) { CPLError(CE_Failure, CPLE_OpenFailed, "Invalid image dimensions : %d x %d", poDS->nRasterXSize, poDS->nRasterYSize); delete poDS; return nullptr; } poDS->nBands = std::max(static_cast<GUInt16>(1), poDS->image.zsize); if (poDS->nBands > 256) { CPLError(CE_Failure, CPLE_OpenFailed, "Too many bands : %d", poDS->nBands); delete poDS; return nullptr; } const int numItems = (static_cast<int>( poDS->image.bpc ) == 1) ? 256 : 65536; if( poDS->image.xsize > INT_MAX / numItems ) { delete poDS; return nullptr; } poDS->image.tmpSize = poDS->image.xsize * numItems; poDS->image.tmp = (unsigned char*)VSI_CALLOC_VERBOSE(poDS->image.xsize,numItems); if (poDS->image.tmp == nullptr) { delete poDS; return nullptr; } /* -------------------------------------------------------------------- */ /* Read RLE Pointer tables. */ /* -------------------------------------------------------------------- */ if( static_cast<int>( poDS->image.type ) == 1 ) // RLE compressed { const size_t x = static_cast<size_t>(poDS->image.ysize) * poDS->nBands * sizeof(GUInt32); poDS->image.rowStart = reinterpret_cast<GUInt32*>( VSI_MALLOC2_VERBOSE(poDS->image.ysize, poDS->nBands * sizeof(GUInt32) ) ); poDS->image.rowSize = reinterpret_cast<GInt32 *>( VSI_MALLOC2_VERBOSE(poDS->image.ysize, poDS->nBands * sizeof(GUInt32) ) ); if (poDS->image.rowStart == nullptr || poDS->image.rowSize == nullptr) { delete poDS; return nullptr; } memset(poDS->image.rowStart, 0, x); memset(poDS->image.rowSize, 0, x); poDS->image.rleEnd = static_cast<GUInt32>(512 + (2 * x)); VSIFSeekL(poDS->fpImage, 512, SEEK_SET); if( VSIFReadL(poDS->image.rowStart, 1, x, poDS->image.file ) != x ) { delete poDS; CPLError(CE_Failure, CPLE_OpenFailed, "file read error while reading start positions in sgidataset.cpp"); return nullptr; } if( VSIFReadL(poDS->image.rowSize, 1, x, poDS->image.file) != x) { delete poDS; CPLError(CE_Failure, CPLE_OpenFailed, "file read error while reading row lengths in sgidataset.cpp"); return nullptr; } ConvertLong(poDS->image.rowStart, static_cast<int>(x / static_cast<int>( sizeof(GUInt32))) ); ConvertLong(reinterpret_cast<GUInt32 *>( poDS->image.rowSize ), static_cast<int>(x / static_cast<int>( sizeof(GInt32) )) ); } else // uncompressed. { poDS->image.rowStart = nullptr; poDS->image.rowSize = nullptr; } /* -------------------------------------------------------------------- */ /* Create band information objects. */ /* -------------------------------------------------------------------- */ for(int iBand = 0; iBand < poDS->nBands; iBand++) poDS->SetBand(iBand+1, new SGIRasterBand(poDS, iBand+1)); /* -------------------------------------------------------------------- */ /* Check for world file. */ /* -------------------------------------------------------------------- */ poDS->bGeoTransformValid = GDALReadWorldFile(poOpenInfo->pszFilename, ".wld", poDS->adfGeoTransform); /* -------------------------------------------------------------------- */ /* Initialize any PAM information. */ /* -------------------------------------------------------------------- */ poDS->SetDescription(poOpenInfo->pszFilename); poDS->TryLoadXML(); /* -------------------------------------------------------------------- */ /* Check for overviews. */ /* -------------------------------------------------------------------- */ poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); return poDS; }
int OiioTool::do_action_diff (ImageRec &ir0, ImageRec &ir1, Oiiotool &ot) { std::cout << "Computing diff of \"" << ir0.name() << "\" vs \"" << ir1.name() << "\"\n"; ir0.read (); ir1.read (); int ret = DiffErrOK; for (int subimage = 0; subimage < ir0.subimages(); ++subimage) { if (subimage > 0 && !ot.allsubimages) break; if (subimage >= ir1.subimages()) break; if (ot.allsubimages) { std::cout << "Subimage " << subimage << ": "; const ImageSpec &spec (*ir0.spec(subimage)); std::cout << spec.width << " x " << spec.height; if (spec.depth > 1) std::cout << " x " << spec.depth; std::cout << ", " << spec.nchannels << " channel\n"; } if (ir0.miplevels(subimage) != ir1.miplevels(subimage)) { std::cout << "Files do not match in their number of MIPmap levels\n"; } for (int m = 0; m < ir0.miplevels(subimage); ++m) { if (m > 0 && !ot.allsubimages) break; if (m > 0 && ir0.miplevels(subimage) != ir1.miplevels(subimage)) { std::cout << "Files do not match in their number of MIPmap levels\n"; ret = DiffErrDifferentSize; break; } ImageBuf &img0 (ir0(subimage,m)); ImageBuf &img1 (ir1(subimage,m)); if (ot.allsubimages && ir0.miplevels(subimage) > 1) { std::cout << " MIP level " << m << ": "; std::cout << img0.spec().width << " x " << img0.spec().height; if (img0.spec().depth > 1) std::cout << " x " << img0.spec().depth; std::cout << ", " << img0.spec().nchannels << " channel\n"; } // 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)) { 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 = DiffErrDifferentSize; break; } int npels = img0.spec().width * img0.spec().height * img0.spec().depth; ASSERT (img0.spec().format == TypeDesc::FLOAT); // Compare the two images. // ImageBufAlgo::CompareResults cr; ImageBufAlgo::compare (img0, img1, ot.diff_failthresh, ot.diff_warnthresh, cr); int yee_failures = 0; #if 0 if (perceptual) yee_failures = ImageBufAlgo::compare_Yee (img0, img1); #endif // Print the report // 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 int precis = std::cout.precision(); std::cout << " " << cr.nwarn << " pixels (" << std::setprecision(3) << (100.0*cr.nwarn / npels) << std::setprecision(precis) << "%) over " << ot.diff_warnthresh << "\n"; std::cout << " " << cr.nfail << " pixels (" << std::setprecision(3) << (100.0*cr.nfail / npels) << std::setprecision(precis) << "%) over " << ot.diff_failthresh << "\n"; #if 0 if (perceptual) std::cout << " " << yee_failures << " pixels (" << std::setprecision(3) << (100.0*yee_failures / npels) << std::setprecision(precis) << "%) failed the perceptual test\n"; #endif if (cr.nfail > (ot.diff_failpercent/100.0 * npels) || cr.maxerror > ot.diff_hardfail || yee_failures > (ot.diff_failpercent/100.0 * npels)) { ret = DiffErrFail; } else if (cr.nwarn > (ot.diff_warnpercent/100.0 * npels) || cr.maxerror > ot.diff_hardwarn) { if (ret != DiffErrFail) ret = DiffErrWarn; } #if 0 // 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 = ""; } #endif } } if (ot.allsubimages && ir0.subimages() != ir1.subimages()) { std::cout << "Images had differing numbers of subimages (" << ir0.subimages() << " vs " << ir1.subimages() << ")\n"; ret = DiffErrFail; } if (!ot.allsubimages && (ir0.subimages() > 1 || ir1.subimages() > 1)) { std::cout << "Only compared the first subimage (of " << ir0.subimages() << " and " << ir1.subimages() << ", respectively)\n"; } if (ret == DiffErrOK) std::cout << "PASS\n"; else if (ret == DiffErrWarn) std::cout << "WARNING\n"; else { std::cout << "FAILURE\n"; } return ret; }