PageRenderTime 44ms CodeModel.GetById 2ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

/src/apps/ociobakelut/main.cpp

http://github.com/imageworks/OpenColorIO
C++ | 539 lines | 445 code | 72 blank | 22 comment | 91 complexity | f9f9a9b3e6f720b79d6c8358de03fd4a MD5 | raw file
  1// SPDX-License-Identifier: BSD-3-Clause
  2// Copyright Contributors to the OpenColorIO Project.
  3
  4#include <cmath>
  5#include <cstdlib>
  6#include <iostream>
  7#include <sstream>
  8#include <fstream>
  9#include <vector>
 10
 11#include <OpenColorIO/OpenColorIO.h>
 12namespace OCIO = OCIO_NAMESPACE;
 13
 14#include "apputils/argparse.h"
 15#include "ocioicc.h"
 16
 17static std::string outputfile;
 18
 19static int
 20parse_end_args(int argc, const char *argv[])
 21{
 22    if(argc>0)
 23    {
 24        outputfile = argv[0];
 25    }
 26
 27    return 0;
 28}
 29
 30OCIO::GroupTransformRcPtr
 31parse_luts(int argc, const char *argv[]);
 32
 33int main (int argc, const char* argv[])
 34{
 35
 36    bool help = false;
 37    int cubesize = -1;
 38    int shapersize = -1; // cubsize^2
 39    std::string format;
 40    std::string inputconfig;
 41    std::string inputspace;
 42    std::string shaperspace;
 43    std::string looks;
 44    std::string outputspace;
 45    bool usestdout = false;
 46    bool verbose = false;
 47
 48    int whitepointtemp = 6505;
 49    std::string displayicc;
 50    std::string description;
 51    std::string copyright = "No copyright. Use freely.";
 52
 53    // What are the allowed baker output formats?
 54    std::ostringstream formats;
 55    formats << "the LUT format to bake: ";
 56    for(int i=0; i<OCIO::Baker::getNumFormats(); ++i)
 57    {
 58        if(i!=0) formats << ", ";
 59        formats << OCIO::Baker::getFormatNameByIndex(i);
 60        formats << " (." << OCIO::Baker::getFormatExtensionByIndex(i) << ")";
 61    }
 62    formats << ", icc (.icc)";
 63
 64    std::string formatstr = formats.str();
 65
 66    std::string dummystr;
 67    float dummyf1, dummyf2, dummyf3;
 68
 69    ArgParse ap;
 70    ap.options("ociobakelut -- create a new LUT or ICC profile from an OCIO config or LUT file(s)\n\n"
 71               "usage:  ociobakelut [options] <OUTPUTFILE.LUT>\n\n"
 72               "example:  ociobakelut --inputspace lg10 --outputspace srgb8 --format flame lg_to_srgb.3dl\n"
 73               "example:  ociobakelut --lut filmlut.3dl --lut calibration.3dl --format flame display.3dl\n"
 74               "example:  ociobakelut --cccid 0 --lut cdlgrade.ccc --lut calibration.3dl --format flame graded_display.3dl\n"
 75               "example:  ociobakelut --lut look.3dl --offset 0.01 -0.02 0.03 --lut display.3dl --format flame display_with_look.3dl\n"
 76               "example:  ociobakelut --inputspace lg10 --outputspace srgb8 --format icc ~/Library/ColorSync/Profiles/test.icc\n"
 77               "example:  ociobakelut --lut filmlut.3dl --lut calibration.3dl --format icc ~/Library/ColorSync/Profiles/test.icc\n\n",
 78               "%*", parse_end_args, "",
 79               "<SEPARATOR>", "Using Existing OCIO Configurations",
 80               "--inputspace %s", &inputspace, "Input OCIO ColorSpace (or Role)",
 81               "--outputspace %s", &outputspace, "Output OCIO ColorSpace (or Role)",
 82               "--shaperspace %s", &shaperspace, "the OCIO ColorSpace or Role, for the shaper",
 83               "--looks %s", &looks, "the OCIO looks to apply",
 84               "--iconfig %s", &inputconfig, "Input .ocio configuration file (default: $OCIO)\n",
 85               "<SEPARATOR>", "Config-Free LUT Baking",
 86               "<SEPARATOR>", "    (all options can be specified multiple times, each is applied in order)",
 87               "--cccid %s", &dummystr, "Specify a CCCId for any following LUTs",
 88               "--lut %s", &dummystr, "Specify a LUT (forward direction)",
 89               "--invlut %s", &dummystr, "Specify a LUT (inverse direction)",
 90               "--slope %f %f %f", &dummyf1, &dummyf2, &dummyf3, "slope",
 91               "--offset %f %f %f", &dummyf1, &dummyf2, &dummyf3, "offset (float)",
 92               "--offset10 %f %f %f", &dummyf1, &dummyf2, &dummyf3, "offset (10-bit)",
 93               "--power %f %f %f", &dummyf1, &dummyf2, &dummyf3, "power",
 94               "--sat %f", &dummyf1, "saturation (ASC-CDL luma coefficients)\n",
 95               "<SEPARATOR>", "Baking Options",
 96               "--format %s", &format, formatstr.c_str(),
 97               "--shapersize %d", &shapersize, "size of the shaper (default: format specific)",
 98               "--cubesize %d", &cubesize, "size of the cube (default: format specific)",
 99               "--stdout", &usestdout, "Write to stdout (rather than file)",
100               "--v", &verbose, "Verbose",
101               "--help", &help, "Print help message\n",
102               "<SEPARATOR>", "ICC Options",
103               //"--cubesize %d", &cubesize, "size of the ICC CLUT cube (default: 32)",
104               "--whitepoint %d", &whitepointtemp, "whitepoint for the profile (default: 6505)",
105               "--displayicc %s", &displayicc , "an ICC profile which matches the OCIO profiles target display",
106               "--description %s", &description , "a meaningful description, this will show up in UI like photoshop (defaults to \"filename.icc\")",
107               "--copyright %s", &copyright , "a copyright field added in the file (default: \"No copyright. Use freely.\")\n",
108               // TODO: add --metadata option
109               NULL);
110
111    if (ap.parse(argc, argv) < 0)
112    {
113        std::cout << ap.geterror() << std::endl;
114        ap.usage();
115        std::cout << "\n";
116        return 1;
117    }
118
119    if (help || (argc == 1 ))
120    {
121        ap.usage();
122        std::cout << "\n";
123        return 1;
124    }
125
126    // If we're printing to stdout, disable verbose printouts
127    if(usestdout)
128    {
129        verbose = false;
130    }
131
132    // Create the OCIO processor for the specified transform.
133    OCIO::ConstConfigRcPtr config;
134
135    OCIO::GroupTransformRcPtr groupTransform;
136
137    try
138    {
139        groupTransform = parse_luts(argc, argv);
140    }
141    catch(const OCIO::Exception & e)
142    {
143        std::cerr << "\nERROR: " << e.what() << std::endl;
144        std::cerr << "See --help for more info." << std::endl;
145        return 1;
146    }
147    catch(...)
148    {
149        std::cerr << "\nERROR: An unknown error occurred in parse_luts" << std::endl;
150        std::cerr << "See --help for more info." << std::endl;
151        return 1;
152    }
153
154    if(!groupTransform)
155    {
156        std::cerr << "\nERROR: parse_luts returned null transform" << std::endl;
157        std::cerr << "See --help for more info." << std::endl;
158        return 1;
159    }
160
161    // If --luts have been specified, synthesize a new (temporary) configuration
162    // with the transformation embedded in a colorspace.
163    if(groupTransform->getNumTransforms() > 0)
164    {
165        if(!inputspace.empty())
166        {
167            std::cerr << "\nERROR: --inputspace is not allowed when using --lut\n\n";
168            std::cerr << "See --help for more info." << std::endl;
169            return 1;
170        }
171        if(!outputspace.empty())
172        {
173            std::cerr << "\nERROR: --outputspace is not allowed when using --lut\n\n";
174            std::cerr << "See --help for more info." << std::endl;
175            return 1;
176        }
177        if(!looks.empty())
178        {
179            std::cerr << "\nERROR: --looks is not allowed when using --lut\n\n";
180            std::cerr << "See --help for more info." << std::endl;
181            return 1;
182        }
183        if(!shaperspace.empty())
184        {
185            std::cerr << "\nERROR: --shaperspace is not allowed when using --lut\n\n";
186            std::cerr << "See --help for more info." << std::endl;
187            return 1;
188        }
189
190        OCIO::ConfigRcPtr editableConfig = OCIO::Config::Create();
191
192        OCIO::ColorSpaceRcPtr inputColorSpace = OCIO::ColorSpace::Create();
193        inputspace = "RawInput";
194        inputColorSpace->setName(inputspace.c_str());
195        editableConfig->addColorSpace(inputColorSpace);
196
197        OCIO::ColorSpaceRcPtr outputColorSpace = OCIO::ColorSpace::Create();
198        outputspace = "ProcessedOutput";
199        outputColorSpace->setName(outputspace.c_str());
200
201        outputColorSpace->setTransform(groupTransform,
202            OCIO::COLORSPACE_DIR_FROM_REFERENCE);
203
204        if(verbose)
205        {
206            std::cout << "[OpenColorIO DEBUG]: Specified Transform:";
207            std::cout << *groupTransform;
208            std::cout << "\n";
209        }
210
211        editableConfig->addColorSpace(outputColorSpace);
212        config = editableConfig;
213    }
214    else
215    {
216
217        if(inputspace.empty())
218        {
219            std::cerr << "\nERROR: You must specify the --inputspace.\n\n";
220            std::cerr << "See --help for more info." << std::endl;
221            return 1;
222        }
223
224        if(outputspace.empty())
225        {
226            std::cerr << "\nERROR: You must specify the --outputspace.\n\n";
227            std::cerr << "See --help for more info." << std::endl;
228            return 1;
229        }
230
231        if(format.empty())
232        {
233            std::cerr << "\nERROR: You must specify the LUT format using --format.\n\n";
234            std::cerr << "See --help for more info." << std::endl;
235            return 1;
236        }
237
238        if(!inputconfig.empty())
239        {
240            if(!usestdout && verbose)
241                std::cout << "[OpenColorIO INFO]: Loading " << inputconfig << std::endl;
242            config = OCIO::Config::CreateFromFile(inputconfig.c_str());
243        }
244        else if(OCIO::GetEnvVariable("OCIO"))
245        {
246            if(!usestdout && verbose)
247            {
248                std::cout << "[OpenColorIO INFO]: Loading $OCIO " 
249                          << OCIO::GetEnvVariable("OCIO") << std::endl;
250            }
251            config = OCIO::Config::CreateFromEnv();
252        }
253        else
254        {
255            std::cerr << "ERROR: You must specify an input OCIO configuration ";
256            std::cerr << "(either with --iconfig or $OCIO).\n\n";
257            ap.usage ();
258            return 1;
259        }
260    }
261
262    if(outputfile.empty() && !usestdout)
263    {
264        std::cerr << "\nERROR: You must specify the outputfile or --stdout.\n\n";
265        std::cerr << "See --help for more info." << std::endl;
266        return 1;
267    }
268
269    try
270    {
271        if(format == "icc")
272        {
273            if(description.empty())
274            {
275                description = outputfile;
276                if(verbose)
277                    std::cout << "[OpenColorIO INFO]: \"--description\" set to default value of filename.icc: " << outputfile << "" << std::endl;
278            }
279
280            if(usestdout)
281            {
282                std::cerr << "\nERROR: --stdout not supported when writing ICC profiles.\n\n";
283                std::cerr << "See --help for more info." << std::endl;
284                return 1;
285            }
286
287            if(outputfile.empty())
288            {
289                std::cerr << "ERROR: you need to specify a output ICC path\n";
290                std::cerr << "See --help for more info." << std::endl;
291                return 1;
292            }
293
294            if(cubesize<2) cubesize = 32; // default
295
296            OCIO::ConstCPUProcessorRcPtr processor;
297            if (!looks.empty())
298            {
299                OCIO::LookTransformRcPtr transform =
300                    OCIO::LookTransform::Create();
301                transform->setLooks(looks.c_str());
302                transform->setSrc(inputspace.c_str());
303                transform->setDst(outputspace.c_str());
304                processor = config->getProcessor(transform,
305                    OCIO::TRANSFORM_DIR_FORWARD)->getDefaultCPUProcessor();
306            }
307            else
308            {
309                processor = config->getProcessor(inputspace.c_str(),
310                    outputspace.c_str())->getDefaultCPUProcessor();
311            }
312
313            SaveICCProfileToFile(outputfile,
314                                 processor,
315                                 cubesize,
316                                 whitepointtemp,
317                                 displayicc,
318                                 description,
319                                 copyright,
320                                 verbose);
321        }
322        else
323        {
324
325            OCIO::BakerRcPtr baker = OCIO::Baker::Create();
326
327            // setup the baker for our LUT type
328            baker->setConfig(config);
329            baker->setFormat(format.c_str());
330            baker->setInputSpace(inputspace.c_str());
331            baker->setShaperSpace(shaperspace.c_str());
332            baker->setLooks(looks.c_str());
333            baker->setTargetSpace(outputspace.c_str());
334            if(shapersize!=-1) baker->setShaperSize(shapersize);
335            if(cubesize!=-1) baker->setCubeSize(cubesize);
336
337            // output LUT
338            std::ostringstream output;
339
340            if(!usestdout && verbose)
341                std::cout << "[OpenColorIO INFO]: Baking '" << format << "' LUT" << std::endl;
342
343            if(usestdout)
344            {
345                baker->bake(std::cout);
346            }
347            else
348            {
349                std::ofstream f(outputfile.c_str());
350                if(f.fail())
351                {
352                    std::cerr << "ERROR: Non-writable file path " << outputfile << " specified." << std::endl;
353                    return 1;
354                }
355                baker->bake(f);
356                if(verbose)
357                    std::cout << "[OpenColorIO INFO]: Wrote '" << outputfile << "'" << std::endl;
358            }
359        }
360    }
361    catch(OCIO::Exception & exception)
362    {
363        std::cerr << "OCIO Error: " << exception.what() << std::endl;
364        std::cerr << "See --help for more info." << std::endl;
365        return 1;
366    }
367    catch (std::exception& exception)
368    {
369        std::cerr << "Error: " << exception.what() << "\n";
370        std::cerr << "See --help for more info." << std::endl;
371        return 1;
372    }
373    catch(...)
374    {
375        std::cerr << "Unknown OCIO error encountered." << std::endl;
376        std::cerr << "See --help for more info." << std::endl;
377        return 1;
378    }
379
380    return 0;
381}
382
383
384// TODO: Replace this dirty argument parsing code with a clean version
385// that leverages the same codepath for the standard arguments.  If
386// the OIIO derived argparse does not suffice, options we may want to consider
387// include simpleopt, tclap, ultraopt
388
389// TODO: Use better input validation, instead of atof.
390// If too few arguments are provides for scale (let's say only two) and
391// the following argument is the start of another flag (let's say "--invlut")
392// then atof() will likely try to convert "--invlut" to its double equivalent,
393// resulting in an invalid (or at least undesired) scale value.
394
395OCIO::GroupTransformRcPtr
396parse_luts(int argc, const char *argv[])
397{
398    OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create();
399    const char *lastCCCId = NULL; // Ugly to use this but using GroupTransform::getTransform()
400                                  // returns a const object so we must set this
401                                  // prior to using --lut for now.
402
403    for(int i=0; i<argc; ++i)
404    {
405        std::string arg(argv[i]);
406
407        if(arg == "--lut" || arg == "-lut")
408        {
409            if(i+1>=argc)
410            {
411                throw OCIO::Exception("Error parsing --lut. Invalid num args");
412            }
413
414            OCIO::FileTransformRcPtr t = OCIO::FileTransform::Create();
415            t->setSrc(argv[i+1]);
416            t->setInterpolation(OCIO::INTERP_BEST);
417            if (lastCCCId)
418            {
419                t->setCCCId(lastCCCId);
420            }
421            groupTransform->appendTransform(t);
422
423            i += 1;
424        }
425        else if(arg == "--cccid" || arg == "-cccid")
426        {
427            if(i+1>=argc)
428            {
429                throw OCIO::Exception("Error parsing --cccid. Invalid num args");
430            }
431
432            lastCCCId = argv[i+1];
433
434            i += 1;
435        }
436        else if(arg == "--invlut" || arg == "-invlut")
437        {
438            if(i+1>=argc)
439            {
440                throw OCIO::Exception("Error parsing --invlut. Invalid num args");
441            }
442
443            OCIO::FileTransformRcPtr t = OCIO::FileTransform::Create();
444            t->setSrc(argv[i+1]);
445            t->setInterpolation(OCIO::INTERP_BEST);
446            t->setDirection(OCIO::TRANSFORM_DIR_INVERSE);
447            groupTransform->appendTransform(t);
448
449            i += 1;
450        }
451        else if(arg == "--slope" || arg == "-slope")
452        {
453            if(i+3>=argc)
454            {
455                throw OCIO::Exception("Error parsing --slope. Invalid num args");
456            }
457
458            OCIO::CDLTransformRcPtr t = OCIO::CDLTransform::Create();
459
460            double scale[3];
461            scale[0] = atof(argv[i+1]);
462            scale[1] = atof(argv[i+2]);
463            scale[2] = atof(argv[i+3]);
464            t->setSlope(scale);
465            groupTransform->appendTransform(t);
466
467            i += 3;
468        }
469        else if(arg == "--offset" || arg == "-offset")
470        {
471            if(i+3>=argc)
472            {
473                throw OCIO::Exception("Error parsing --offset. Invalid num args");
474            }
475
476            OCIO::CDLTransformRcPtr t = OCIO::CDLTransform::Create();
477
478            double offset[3];
479            offset[0] = atof(argv[i+1]);
480            offset[1] = atof(argv[i+2]);
481            offset[2] = atof(argv[i+3]);
482            t->setOffset(offset);
483            groupTransform->appendTransform(t);
484
485            i += 3;
486        }
487        else if(arg == "--offset10" || arg == "-offset10")
488        {
489            if(i+3>=argc)
490            {
491                throw OCIO::Exception("Error parsing --offset10. Invalid num args");
492            }
493
494            OCIO::CDLTransformRcPtr t = OCIO::CDLTransform::Create();
495
496            double offset[3];
497            offset[0] = atof(argv[i+1]) / 1023.0;
498            offset[1] = atof(argv[i+2]) / 1023.0;
499            offset[2] = atof(argv[i+3]) / 1023.0;
500            t->setOffset(offset);
501            groupTransform->appendTransform(t);
502            i += 3;
503        }
504        else if(arg == "--power" || arg == "-power")
505        {
506            if(i+3>=argc)
507            {
508                throw OCIO::Exception("Error parsing --power. Invalid num args");
509            }
510
511            OCIO::CDLTransformRcPtr t = OCIO::CDLTransform::Create();
512
513            double power[3];
514            power[0] = atof(argv[i+1]);
515            power[1] = atof(argv[i+2]);
516            power[2] = atof(argv[i+3]);
517            t->setPower(power);
518            groupTransform->appendTransform(t);
519
520            i += 3;
521        }
522        else if(arg == "--sat" || arg == "-sat")
523        {
524            if(i+1>=argc)
525            {
526                throw OCIO::Exception("Error parsing --sat. Invalid num args");
527            }
528
529            OCIO::CDLTransformRcPtr t = OCIO::CDLTransform::Create();
530            t->setSat(atof(argv[i+1]));
531            groupTransform->appendTransform(t);
532
533            i += 1;
534        }
535    }
536
537    return groupTransform;
538}
539