Esempio n. 1
0
int
main (int argc, char *argv[])
{
    Timer alltimer;

    ImageSpec configspec;
    getargs (argc, argv, configspec);

    OIIO::attribute ("threads", nthreads);

    // N.B. This will apply to the default IC that any ImageBuf's get.
    ImageCache *ic = ImageCache::create ();  // get the shared one
    ic->attribute ("forcefloat", 1);   // Force float upon read
    ic->attribute ("max_memory_MB", 1024.0);  // 1 GB cache

    ImageBufAlgo::MakeTextureMode mode = ImageBufAlgo::MakeTxTexture;
    if (shadowmode)
        mode = ImageBufAlgo::MakeTxShadow;
    if (envlatlmode)
        mode = ImageBufAlgo::MakeTxEnvLatl;
    if (lightprobemode)
        mode = ImageBufAlgo::MakeTxEnvLatlFromLightProbe;
    bool ok = ImageBufAlgo::make_texture (mode, filenames[0],
                                          outputfilename, configspec,
                                          &std::cout);
    if (stats)
        std::cout << "\n" << ic->getstats();

    return ok ? 0 : EXIT_FAILURE;
}
Esempio n. 2
0
int
main (int argc, char *argv[])
{
    Timer alltimer;
    getargs (argc, argv);

    if (stats) {
        ImageCache *ic = ImageCache::create ();  // get the shared one
        ic->attribute ("forcefloat", 1);   // Force float upon read
        ic->attribute ("max_memory_MB", 1024.0);  // 1 GB cache
        // N.B. This will apply to the default IC that any ImageBuf's get.
    }

    if (mipmapmode) {
        make_texturemap ("texture map");
    } else if (shadowmode) {
        make_texturemap ("shadow map");
    } else if (shadowcubemode) {
        std::cerr << "Shadow cubes currently unsupported\n";
    } else if (volshadowmode) {
        std::cerr << "Volume shadows currently unsupported\n";
    } else if (envlatlmode) {
        make_texturemap ("latlong environment map");
    } else if (envcubemode) {
        std::cerr << "Environment cubes currently unsupported\n";
    } else if (lightprobemode) {
        std::cerr << "Light probes currently unsupported\n";
    } else if (vertcrossmode) {
        std::cerr << "Vertcross currently unsupported\n";
    } else if (latl2envcubemode) {
        std::cerr << "Latlong->cube conversion currently unsupported\n";
    }

    if (verbose || stats) {
        std::cout << "maketx Runtime statistics (seconds):\n";
        double alltime = alltimer();
        std::cout << Strutil::format ("  total runtime:   %5.2f\n", alltime);
        std::cout << Strutil::format ("  file read:       %5.2f\n", stat_readtime);
        std::cout << Strutil::format ("  file write:      %5.2f\n", stat_writetime);
        std::cout << Strutil::format ("  initial resize:  %5.2f\n", stat_resizetime);
        std::cout << Strutil::format ("  mip computation: %5.2f\n", stat_miptime);
        std::cout << Strutil::format ("  unaccounted:     %5.2f\n",
                                      alltime-stat_readtime-stat_writetime-stat_resizetime-stat_miptime);
        size_t kb = Sysutil::memory_used(true) / 1024;
        std::cout << Strutil::format ("maketx memory used: %5.1f MB\n",
                                      (double)kb/1024.0);
    }

    Filter2D::destroy (filter);

    if (stats) {
        ImageCache *ic = ImageCache::create ();  // get the shared one
        std::cout << "\n" << ic->getstats();
    }

    return 0;
}
Esempio n. 3
0
int
main (int argc, char *argv[])
{
    getargs (argc, argv);

    if (! foreground_mode)
        Sysutil::put_in_background (argc, argv);

    // LG
//    Q_INIT_RESOURCE(iv);
    QApplication app(argc, argv);
    ImageViewer *mainWin = new ImageViewer;
    mainWin->show();

    // Set up the imagecache with parameters that make sense for iv
    ImageCache *imagecache = ImageCache::create (true);
    imagecache->attribute ("autotile", 256);

    // Make sure we are the top window with the focus.
    mainWin->raise ();
    mainWin->activateWindow ();

    // Add the images
    BOOST_FOREACH (const std::string &s, filenames) {
        mainWin->add_image (s);
    }
Esempio n. 4
0
int
main (int argc, char *argv[])
{
    Timer alltimer;

    // Globally force classic "C" locale, and turn off all formatting
    // internationalization, for the entire maketx application.
    std::locale::global (std::locale::classic());

    ImageSpec configspec;
    Filesystem::convert_native_arguments (argc, (const char **)argv);
    getargs (argc, argv, configspec);

    OIIO::attribute ("threads", nthreads);

    // N.B. This will apply to the default IC that any ImageBuf's get.
    ImageCache *ic = ImageCache::create ();  // get the shared one
    ic->attribute ("forcefloat", 1);   // Force float upon read
    ic->attribute ("max_memory_MB", 1024.0);  // 1 GB cache

    ImageBufAlgo::MakeTextureMode mode = ImageBufAlgo::MakeTxTexture;
    if (shadowmode)
        mode = ImageBufAlgo::MakeTxShadow;
    if (envlatlmode)
        mode = ImageBufAlgo::MakeTxEnvLatl;
    if (lightprobemode)
        mode = ImageBufAlgo::MakeTxEnvLatlFromLightProbe;
    if (bumpslopesmode)
        mode = ImageBufAlgo::MakeTxBumpWithSlopes;
    
    bool ok = ImageBufAlgo::make_texture (mode, filenames[0],
                                          outputfilename, configspec,
                                          &std::cout);
    if (runstats)
        std::cout << "\n" << ic->getstats();

    return ok ? 0 : EXIT_FAILURE;
}
Esempio n. 5
0
int
main (int argc, char *argv[])
{
    Filesystem::convert_native_arguments (argc, (const char **)argv);
    getargs (argc, argv);

    if (! quiet)
        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);
    // 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::cerr << "Failed to read subimage " << subimage << "\n";
            return ErrFile;
        }

        if (img0.nmiplevels() != img1.nmiplevels()) {
            if (! quiet)
                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::cerr << "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;

            if (img0.deep() != img1.deep()) {
                std::cerr << "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()) {
                ImageBufAlgo::CompareResults cr;
                yee_failures = ImageBufAlgo::compare_Yee (img0, img1, cr);
            }

            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 && !quiet)) {
                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;
                    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";
// 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;
                if (diffabs)
                    ImageBufAlgo::absdiff (diff, img0, img1);
                else
                    ImageBufAlgo::sub (diff, img0, img1);
                if (diffscale != 1.0f)
                    ImageBufAlgo::mul (diff, diff, diffscale);
                diff.write (diffimage);

                // Clear diff image name so we only save the first
                // non-matching subimage.
                diffimage = "";
            }
        }
    }

    if (compareall && img0.nsubimages() != img1.nsubimages()) {
        if (! quiet)
            std::cerr << "Images had differing numbers of subimages ("
                      << img0.nsubimages() << " vs " << img1.nsubimages() << ")\n";
        ret = ErrFail;
    }
    if (!compareall && (img0.nsubimages() > 1 || img1.nsubimages() > 1)) {
        if (! quiet)
            std::cout << "Only compared the first subimage (of "
                      << img0.nsubimages() << " and " << img1.nsubimages()
                      << ", respectively)\n";
    }

    if (ret == ErrOK) {
        if (! quiet)
            std::cout << "PASS\n";
    }
    else if (ret == ErrWarn) {
        if (! quiet)
            std::cout << "WARNING\n";
    }
    else if (ret) {
        if (quiet)
            std::cerr << "FAILURE\n";
        else
            std::cout << "FAILURE\n";
    }

    imagecache->invalidate_all (true);
    ImageCache::destroy (imagecache);
    return ret;
}
Esempio n. 6
0
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

    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 (compareall) {
            std::cout << "Subimage " << subimage << ": ";
            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";
        }

        if (! read_input (filenames[0], img0, imagecache, subimage) ||
            ! read_input (filenames[1], img1, imagecache, subimage))
            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;

            if (compareall && img0.nmiplevels() > 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 = ErrDifferentSize;
                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, failthresh, warnthresh, cr);

            int yee_failures = 0;
            if (perceptual)
                yee_failures = ImageBufAlgo::compare_Yee (img0, img1);

            // 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 " << 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 (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;
            }

            // 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;
}
Esempio n. 7
0
static void
getargs (int argc, char *argv[], ImageSpec &configspec)
{
    bool help = false;
    // Basic runtime options
    std::string dataformatname = "";
    std::string fileformatname = "";
    std::vector<std::string> mipimages;
    int tile[3] = { 64, 64, 1 };  // FIXME if we ever support volume MIPmaps
    std::string compression = "zip";
    bool updatemode = false;
    bool checknan = false;
    std::string fixnan; // none, black, box3
    bool set_full_to_pixels = false;
    bool do_highlight_compensation = false;
    std::string filtername;
    // Options controlling file metadata or mipmap creation
    float fovcot = 0.0f;
    std::string wrap = "black";
    std::string swrap;
    std::string twrap;
    bool doresize = false;
    Imath::M44f Mcam(0.0f), Mscr(0.0f);  // Initialize to 0
    bool separate = false;
    bool nomipmap = false;
    bool prman_metadata = false;
    bool constant_color_detect = false;
    bool monochrome_detect = false;
    bool opaque_detect = false;
    bool compute_average = true;
    int nchannels = -1;
    bool prman = false;
    bool oiio = false;
    bool ignore_unassoc = false;  // ignore unassociated alpha tags
    bool unpremult = false;
    bool sansattrib = false;
    float sharpen = 0.0f;
    std::string incolorspace;
    std::string outcolorspace;
    std::string colorconfigname;
    std::string channelnames;
    std::vector<std::string> string_attrib_names, string_attrib_values;
    std::vector<std::string> any_attrib_names, any_attrib_values;
    filenames.clear();

    ArgParse ap;
    ap.options ("maketx -- convert images to tiled, MIP-mapped textures\n"
                OIIO_INTRO_STRING "\n"
                "Usage:  maketx [options] file...",
                  "%*", parse_files, "",
                  "--help", &help, "Print help message",
                  "-v", &verbose, "Verbose status messages",
                  "-o %s", &outputfilename, "Output filename",
                  "--threads %d", &nthreads, "Number of threads (default: #cores)",
                  "-u", &updatemode, "Update mode",
                  "--format %s", &fileformatname, "Specify output file format (default: guess from extension)",
                  "--nchannels %d", &nchannels, "Specify the number of output image channels.",
                  "--chnames %s", &channelnames, "Rename channels (comma-separated)",
                  "-d %s", &dataformatname, "Set the output data format to one of: "
                          "uint8, sint8, uint16, sint16, half, float",
                  "--tile %d %d", &tile[0], &tile[1], "Specify tile size",
                  "--separate", &separate, "Use planarconfig separate (default: contiguous)",
                  "--compression %s", &compression, "Set the compression method (default = zip, if possible)",
                  "--fovcot %f", &fovcot, "Override the frame aspect ratio. Default is width/height.",
                  "--wrap %s", &wrap, "Specify wrap mode (black, clamp, periodic, mirror)",
                  "--swrap %s", &swrap, "Specific s wrap mode separately",
                  "--twrap %s", &twrap, "Specific t wrap mode separately",
                  "--resize", &doresize, "Resize textures to power of 2 (default: no)",
                  "--noresize %!", &doresize, "Do not resize textures to power of 2 (deprecated)",
                  "--filter %s", &filtername, filter_help_string().c_str(),
                  "--hicomp", &do_highlight_compensation,
                          "Compress HDR range before resize, expand after.",
                  "--sharpen %f", &sharpen, "Sharpen MIP levels (default = 0.0 = no)",
                  "--nomipmap", &nomipmap, "Do not make multiple MIP-map levels",
                  "--checknan", &checknan, "Check for NaN/Inf values (abort if found)",
                  "--fixnan %s", &fixnan, "Attempt to fix NaN/Inf values in the image (options: none, black, box3)",
                  "--fullpixels", &set_full_to_pixels, "Set the 'full' image range to be the pixel data window",
                  "--Mcamera %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
                          &Mcam[0][0], &Mcam[0][1], &Mcam[0][2], &Mcam[0][3], 
                          &Mcam[1][0], &Mcam[1][1], &Mcam[1][2], &Mcam[1][3], 
                          &Mcam[2][0], &Mcam[2][1], &Mcam[2][2], &Mcam[2][3], 
                          &Mcam[3][0], &Mcam[3][1], &Mcam[3][2], &Mcam[3][3], 
                          "Set the camera matrix",
                  "--Mscreen %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f",
                          &Mscr[0][0], &Mscr[0][1], &Mscr[0][2], &Mscr[0][3], 
                          &Mscr[1][0], &Mscr[1][1], &Mscr[1][2], &Mscr[1][3], 
                          &Mscr[2][0], &Mscr[2][1], &Mscr[2][2], &Mscr[2][3], 
                          &Mscr[3][0], &Mscr[3][1], &Mscr[3][2], &Mscr[3][3], 
                          "Set the screen matrix",
                  "--prman-metadata", &prman_metadata, "Add prman specific metadata",
                  "--attrib %L %L", &any_attrib_names, &any_attrib_values, "Sets metadata attribute (name, value)",
                  "--sattrib %L %L", &string_attrib_names, &string_attrib_values, "Sets string metadata attribute (name, value)",
                  "--sansattrib", &sansattrib, "Write command line into Software & ImageHistory but remove --sattrib and --attrib options",
                  "--constant-color-detect", &constant_color_detect, "Create 1-tile textures from constant color inputs",
                  "--monochrome-detect", &monochrome_detect, "Create 1-channel textures from monochrome inputs",
                  "--opaque-detect", &opaque_detect, "Drop alpha channel that is always 1.0",
                  "--no-compute-average %!", &compute_average, "Don't compute and store average color",
                  "--ignore-unassoc", &ignore_unassoc, "Ignore unassociated alpha tags in input (don't autoconvert)",
                  "--runstats", &runstats, "Print runtime statistics",
                  "--stats", &runstats, "", // DEPRECATED 1.6
                  "--mipimage %L", &mipimages, "Specify an individual MIP level",
                  "<SEPARATOR>", "Basic modes (default is plain texture):",
                  "--shadow", &shadowmode, "Create shadow map",
                  "--envlatl", &envlatlmode, "Create lat/long environment map",
                  "--lightprobe", &lightprobemode, "Create lat/long environment map from a light probe",
                  "--bumpslopes", &bumpslopesmode, "Create a 6 channels bump-map with height, derivatives and square derivatives from an height or a normal map",
            
//                  "--envcube", &envcubemode, "Create cubic env map (file order: px, nx, py, ny, pz, nz) (UNIMP)",
                  "<SEPARATOR>", colortitle_help_string().c_str(),
                  "--colorconfig %s", &colorconfigname, "Explicitly specify an OCIO configuration file",
                  "--colorconvert %s %s", &incolorspace, &outcolorspace,
                          colorconvert_help_string().c_str(),
                  "--unpremult", &unpremult, "Unpremultiply before color conversion, then premultiply "
                          "after the color conversion.  You'll probably want to use this flag "
                          "if your image contains an alpha channel.",
                  "<SEPARATOR>", "Configuration Presets",
                  "--prman", &prman, "Use PRMan-safe settings for tile size, planarconfig, and metadata.",
                  "--oiio", &oiio, "Use OIIO-optimized settings for tile size, planarconfig, metadata.",
                  NULL);
    if (ap.parse (argc, (const char**)argv) < 0) {
        std::cerr << ap.geterror() << std::endl;
        ap.usage ();
        exit (EXIT_FAILURE);
    }
    if (help) {
        ap.usage ();
        exit (EXIT_FAILURE);
    }
    if (filenames.empty()) {
        ap.briefusage ();
        std::cout << "\nFor detailed help: maketx --help\n";
        exit (EXIT_SUCCESS);
    }

    int optionsum = ((int)shadowmode + (int)envlatlmode + (int)envcubemode +
                     (int)lightprobemode) + (int)bumpslopesmode;
    if (optionsum > 1) {
        std::cerr << "maketx ERROR: At most one of the following options may be set:\n"
                  << "\t--shadow --envlatl --envcube --lightprobe\n";
        exit (EXIT_FAILURE);
    }
    if (optionsum == 0)
        mipmapmode = true;
    
    if (prman && oiio) {
        std::cerr << "maketx ERROR: '--prman' compatibility, and '--oiio' optimizations are mutually exclusive.\n";
        std::cerr << "\tIf you'd like both prman and oiio compatibility, you should choose --prman\n";
        std::cerr << "\t(at the expense of oiio-specific optimizations)\n";
        exit (EXIT_FAILURE);
    }

    if (filenames.size() != 1) {
        std::cerr << "maketx ERROR: requires exactly one input filename\n";
        exit (EXIT_FAILURE);
    }


//    std::cout << "Converting " << filenames[0] << " to " << outputfilename << "\n";

    // Figure out which data format we want for output
    if (! dataformatname.empty()) {
        if (dataformatname == "uint8")
            configspec.format = TypeDesc::UINT8;
        else if (dataformatname == "int8" || dataformatname == "sint8")
            configspec.format = TypeDesc::INT8;
        else if (dataformatname == "uint16")
            configspec.format = TypeDesc::UINT16;
        else if (dataformatname == "int16" || dataformatname == "sint16")
            configspec.format = TypeDesc::INT16;
        else if (dataformatname == "half")
            configspec.format = TypeDesc::HALF;
        else if (dataformatname == "float")
            configspec.format = TypeDesc::FLOAT;
        else if (dataformatname == "double")
            configspec.format = TypeDesc::DOUBLE;
        else {
            std::cerr << "maketx ERROR: unknown data format \"" << dataformatname << "\"\n";
            exit (EXIT_FAILURE);
        }
    }

    configspec.tile_width  = tile[0];
    configspec.tile_height = tile[1];
    configspec.tile_depth  = tile[2];
    configspec.attribute ("compression", compression);
    if (fovcot != 0.0f)
        configspec.attribute ("fovcot", fovcot);
    configspec.attribute ("planarconfig", separate ? "separate" : "contig");
    if (Mcam != Imath::M44f(0.0f))
        configspec.attribute ("worldtocamera", TypeMatrix, &Mcam);
    if (Mscr != Imath::M44f(0.0f))
        configspec.attribute ("worldtoscreen", TypeMatrix, &Mscr);
    std::string wrapmodes = (swrap.size() ? swrap : wrap) + ',' + 
                            (twrap.size() ? twrap : wrap);
    configspec.attribute ("wrapmodes", wrapmodes);

    configspec.attribute ("maketx:verbose", verbose);
    configspec.attribute ("maketx:runstats", runstats);
    configspec.attribute ("maketx:resize", doresize);
    configspec.attribute ("maketx:nomipmap", nomipmap);
    configspec.attribute ("maketx:updatemode", updatemode);
    configspec.attribute ("maketx:constant_color_detect", constant_color_detect);
    configspec.attribute ("maketx:monochrome_detect", monochrome_detect);
    configspec.attribute ("maketx:opaque_detect", opaque_detect);
    configspec.attribute ("maketx:compute_average", compute_average);
    configspec.attribute ("maketx:unpremult", unpremult);
    configspec.attribute ("maketx:incolorspace", incolorspace);
    configspec.attribute ("maketx:outcolorspace", outcolorspace);
    configspec.attribute ("maketx:colorconfig", colorconfigname);
    configspec.attribute ("maketx:checknan", checknan);
    configspec.attribute ("maketx:fixnan", fixnan);
    configspec.attribute ("maketx:set_full_to_pixels", set_full_to_pixels);
    configspec.attribute ("maketx:highlightcomp", (int)do_highlight_compensation);
    configspec.attribute ("maketx:sharpen", sharpen);
    if (filtername.size())
        configspec.attribute ("maketx:filtername", filtername);
    configspec.attribute ("maketx:nchannels", nchannels);
    configspec.attribute ("maketx:channelnames", channelnames);
    if (fileformatname.size())
        configspec.attribute ("maketx:fileformatname", fileformatname);
    configspec.attribute ("maketx:prman_metadata", prman_metadata);
    configspec.attribute ("maketx:oiio_options", oiio);
    configspec.attribute ("maketx:prman_options", prman);
    if (mipimages.size())
        configspec.attribute ("maketx:mipimages", Strutil::join(mipimages,";"));

    std::string cmdline = Strutil::format ("OpenImageIO %s : %s",
                                     OIIO_VERSION_STRING,
                                     command_line_string (argc, argv, sansattrib));
    configspec.attribute ("Software", cmdline);
    configspec.attribute ("maketx:full_command_line", cmdline);

    // Add user-specified string attributes
    for (size_t i = 0; i < string_attrib_names.size(); ++i) {
        configspec.attribute (string_attrib_names[i], string_attrib_values[i]);
    }

    // Add user-specified "any" attributes -- try to deduce the type
    for (size_t i = 0; i < any_attrib_names.size(); ++i) {
        string_view s = any_attrib_values[i];
        // Does it parse as an int (and nothing more?)
        int ival;
        if (Strutil::parse_int(s,ival)) {
            Strutil::skip_whitespace(s);
            if (! s.size()) {
                configspec.attribute (any_attrib_names[i], ival);
                continue;
            }
        }
        s = any_attrib_values[i];
        // Does it parse as a float (and nothing more?)
        float fval;
        if (Strutil::parse_float(s,fval)) {
            Strutil::skip_whitespace(s);
            if (! s.size()) {
                configspec.attribute (any_attrib_names[i], fval);
                continue;
            }
        }
        // OK, treat it like a string
        configspec.attribute (any_attrib_names[i], any_attrib_values[i]);
    }

    if (ignore_unassoc) {
        configspec.attribute ("maketx:ignore_unassoc", (int)ignore_unassoc);
        ImageCache *ic = ImageCache::create ();  // get the shared one
        ic->attribute ("unassociatedalpha", (int)ignore_unassoc);
    }
}