Exemplo n.º 1
0
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;
    }
}
Exemplo n.º 2
0
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";
}
Exemplo n.º 3
0
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;
        }
    }
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
0
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;
}
Exemplo n.º 6
0
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;
}
Exemplo n.º 7
0
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;
}