@@ -79,22 +79,27 @@ int main(int argc, const char **argv)
7979 bool help = false ;
8080 bool useLut = false ;
8181 bool useDisplayView = false ;
82+ bool useInvertView = false ;
8283
8384 ap.options (" ocioconvert -- apply colorspace transform to an image \n\n "
8485 " usage: ocioconvert [options] inputimage inputcolorspace outputimage outputcolorspace\n "
8586 " or: ocioconvert [options] --lut lutfile inputimage outputimage\n "
86- " or: ocioconvert [options] --view inputimage inputcolorspace outputimage displayname viewname\n\n " ,
87+ " or: ocioconvert [options] --view inputimage inputcolorspace outputimage displayname viewname\n "
88+ " or: ocioconvert [options] --invertview inputimage displayname viewname outputimage outputcolorspace\n\n " ,
8789 " %*" , parse_end_args, " " ,
8890 " <SEPARATOR>" , " Options:" ,
89- " --lut" , &useLut, " Convert using a LUT rather than a config file" ,
90- " --view" , &useDisplayView, " Convert to a (display,view) pair rather than to "
91- " an output color space" ,
92- " --gpu" , &usegpu, " Use GPU color processing instead of CPU (CPU is the default)" ,
93- " --gpulegacy" , &usegpuLegacy, " Use the legacy (i.e. baked) GPU color processing "
94- " instead of the CPU one (--gpu is ignored)" ,
95- " --gpuinfo" , &outputgpuInfo, " Output the OCIO shader program" ,
96- " --help" , &help, " Print help message" ,
97- " -v" , &verbose, " Display general information" ,
91+ " --lut" , &useLut, " Convert using a LUT rather than a config file" ,
92+ " --view" , &useDisplayView, " Convert to a (display,view) pair rather than to "
93+ " an output color space" ,
94+ " --invertview" , &useInvertView, " Convert from a (display,view) pair rather than "
95+ " from a color space" ,
96+ " --gpu" , &usegpu, " Use GPU color processing instead of CPU (CPU is the default)" ,
97+ " --gpulegacy" , &usegpuLegacy, " Use the legacy (i.e. baked) GPU color processing "
98+ " instead of the CPU one (--gpu is ignored)" ,
99+ " --gpuinfo" , &outputgpuInfo, " Output the OCIO shader program" ,
100+ " --h" , &help, " Display the help and exit" ,
101+ " --help" , &help, " Display the help and exit" ,
102+ " -v" , &verbose, " Display general information" ,
98103 " <SEPARATOR>" , " \n OpenImageIO options:" ,
99104 " --float-attribute %L" , &floatAttrs, " \" name=float\" pair defining OIIO float attribute "
100105 " for outputimage" ,
@@ -138,7 +143,7 @@ int main(int argc, const char **argv)
138143 const char * display = nullptr ;
139144 const char * view = nullptr ;
140145
141- if (!useLut && !useDisplayView)
146+ if (!useLut && !useDisplayView && !useInvertView )
142147 {
143148 if (args.size () != 4 )
144149 {
@@ -186,6 +191,27 @@ int main(int argc, const char **argv)
186191 display = args[3 ].c_str ();
187192 view = args[4 ].c_str ();
188193 }
194+ else if (useDisplayView && useInvertView)
195+ {
196+ std::cerr << " ERROR: Options view & invertview can't be used at the same time." << std::endl;
197+ ap.usage ();
198+ exit (1 );
199+ }
200+ else if (useInvertView)
201+ {
202+ if (args.size () != 5 )
203+ {
204+ std::cerr << " ERROR: Expecting 5 arguments for --invertview option, found "
205+ << args.size () << " ." << std::endl;
206+ ap.usage ();
207+ exit (1 );
208+ }
209+ inputimage = args[0 ].c_str ();
210+ display = args[1 ].c_str ();
211+ view = args[2 ].c_str ();
212+ outputimage = args[3 ].c_str ();
213+ outputcolorspace = args[4 ].c_str ();
214+ }
189215
190216 if (verbose)
191217 {
@@ -229,8 +255,12 @@ int main(int argc, const char **argv)
229255 std::cout << " Using GPU color processing." << std::endl;
230256 }
231257
232- OIIO::ImageSpec spec;
233- OCIO::ImgBuffer img;
258+ OIIO::ImageSpec specInput;
259+ OIIO::ImageSpec specOutput;
260+ // Create an input image buffer.
261+ OCIO::ImgBuffer imgInput;
262+ // Create an output image buffer.
263+ OCIO::ImgBuffer imgOutput;
234264 int imgwidth = 0 ;
235265 int imgheight = 0 ;
236266 int components = 0 ;
@@ -251,7 +281,7 @@ int main(int argc, const char **argv)
251281 exit (1 );
252282 }
253283
254- f->open (inputimage, spec );
284+ f->open (inputimage, specInput );
255285
256286 std::string error = f->geterror ();
257287 if (!error.empty ())
@@ -260,18 +290,18 @@ int main(int argc, const char **argv)
260290 exit (1 );
261291 }
262292
263- OCIO::PrintImageSpec (spec , verbose);
293+ OCIO::PrintImageSpec (specInput , verbose);
264294
265- imgwidth = spec .width ;
266- imgheight = spec .height ;
267- components = spec .nchannels ;
295+ imgwidth = specInput .width ;
296+ imgheight = specInput .height ;
297+ components = specInput .nchannels ;
268298
269299 if (usegpu || usegpuLegacy)
270300 {
271- spec .format = OIIO::TypeDesc::FLOAT;
272- img .allocate (spec );
301+ specInput .format = OIIO::TypeDesc::FLOAT;
302+ imgInput .allocate (specInput );
273303
274- const bool ok = f->read_image (spec .format , img .getBuffer ());
304+ const bool ok = f->read_image (specInput .format , imgInput .getBuffer ());
275305 if (!ok)
276306 {
277307 std::cerr << " ERROR: Reading \" " << inputimage << " \" failed with: "
@@ -287,9 +317,9 @@ int main(int argc, const char **argv)
287317 }
288318 else
289319 {
290- img .allocate (spec );
320+ imgInput .allocate (specInput );
291321
292- const bool ok = f->read_image (spec .format , img .getBuffer ());
322+ const bool ok = f->read_image (specInput .format , imgInput .getBuffer ());
293323 if (!ok)
294324 {
295325 std::cerr << " ERROR: Reading \" " << inputimage << " \" failed with: "
@@ -323,17 +353,17 @@ int main(int argc, const char **argv)
323353
324354 if (croptofull)
325355 {
326- imgwidth = spec .full_width ;
327- imgheight = spec .full_height ;
356+ imgwidth = specInput .full_width ;
357+ imgheight = specInput .full_height ;
328358
329359 std::cout << " cropping to " << imgwidth
330360 << " x" << imgheight << std::endl;
331361 }
332362
333- if (croptofull || (int )kchannels.size () < spec .nchannels )
363+ if (croptofull || (int )kchannels.size () < specInput .nchannels )
334364 {
335365 // Redefine the spec so it matches the new bounding box.
336- OIIO::ImageSpec croppedSpec = spec ;
366+ OIIO::ImageSpec croppedSpec = specInput ;
337367
338368 croppedSpec.x = 0 ;
339369 croppedSpec.y = 0 ;
@@ -344,53 +374,53 @@ int main(int argc, const char **argv)
344374 OCIO::ImgBuffer croppedImg (croppedSpec);
345375
346376 void * croppedBuf = croppedImg.getBuffer ();
347- void * imgBuf = img .getBuffer ();
377+ void * imgBuf = imgInput .getBuffer ();
348378
349379 // crop down bounding box and ditch all but n channels.
350380 // img is a flattened 3 dimensional matrix heightxwidthxchannels.
351381 // fill croppedimg with only the needed pixels.
352- for (int y=0 ; y < spec .height ; y++)
382+ for (int y=0 ; y < specInput .height ; y++)
353383 {
354- for (int x=0 ; x < spec .width ; x++)
384+ for (int x=0 ; x < specInput .width ; x++)
355385 {
356386 for (int k=0 ; k < (int )kchannels.size (); k++)
357387 {
358388 int channel = kchannels[k];
359- int current_pixel_y = y + spec .y ;
360- int current_pixel_x = x + spec .x ;
389+ int current_pixel_y = y + specInput .y ;
390+ int current_pixel_x = x + specInput .x ;
361391
362392 if (current_pixel_y >= 0 &&
363393 current_pixel_x >= 0 &&
364394 current_pixel_y < imgheight &&
365395 current_pixel_x < imgwidth)
366396 {
367- const size_t imgIdx = (y * spec .width * components)
397+ const size_t imgIdx = (y * specInput .width * components)
368398 + (x * components) + channel;
369399
370400 const size_t cropIdx = (current_pixel_y * imgwidth * kchannels.size ())
371401 + (current_pixel_x * kchannels.size ())
372402 + channel;
373403
374- if (spec .format ==OIIO::TypeDesc::FLOAT)
404+ if (specInput .format ==OIIO::TypeDesc::FLOAT)
375405 {
376406 ((float *)croppedBuf)[cropIdx] = ((float *)imgBuf)[imgIdx];
377407 }
378- else if (spec .format ==OIIO::TypeDesc::HALF)
408+ else if (specInput .format ==OIIO::TypeDesc::HALF)
379409 {
380410 ((half*)croppedBuf)[cropIdx] = ((half*)imgBuf)[imgIdx];
381411 }
382- else if (spec .format ==OIIO::TypeDesc::UINT16)
412+ else if (specInput .format ==OIIO::TypeDesc::UINT16)
383413 {
384414 ((uint16_t *)croppedBuf)[cropIdx] = ((uint16_t *)imgBuf)[imgIdx];
385415 }
386- else if (spec .format ==OIIO::TypeDesc::UINT8)
416+ else if (specInput .format ==OIIO::TypeDesc::UINT8)
387417 {
388418 ((uint8_t *)croppedBuf)[cropIdx] = ((uint8_t *)imgBuf)[imgIdx];
389419 }
390420 else
391421 {
392422 std::cerr << " ERROR: Unsupported image type: "
393- << spec .format << " ." << std::endl;
423+ << specInput .format << " ." << std::endl;
394424 exit (1 );
395425 }
396426 }
@@ -400,7 +430,7 @@ int main(int argc, const char **argv)
400430
401431 components = (int )(kchannels.size ());
402432
403- img = std::move (croppedImg);
433+ imgInput = std::move (croppedImg);
404434 }
405435 }
406436 catch (...)
@@ -447,7 +477,7 @@ int main(int argc, const char **argv)
447477
448478 oglApp->setPrintShader (outputgpuInfo);
449479
450- oglApp->initImage (imgwidth, imgheight, comp, (float *)img .getBuffer ());
480+ oglApp->initImage (imgwidth, imgheight, comp, (float *)imgInput .getBuffer ());
451481
452482 oglApp->createGLBuffers ();
453483 }
@@ -482,6 +512,14 @@ int main(int argc, const char **argv)
482512 t->setView (view);
483513 processor = config->getProcessor (t);
484514 }
515+ else if (useInvertView)
516+ {
517+ OCIO::DisplayViewTransformRcPtr t = OCIO::DisplayViewTransform::Create ();
518+ t->setSrc (outputcolorspace);
519+ t->setDisplay (display);
520+ t->setView (view);
521+ processor = config->getProcessor (t, OCIO::TRANSFORM_DIR_INVERSE);
522+ }
485523 else
486524 {
487525 processor = config->getProcessor (inputcolorspace, outputcolorspace);
@@ -498,6 +536,37 @@ int main(int argc, const char **argv)
498536 exit (1 );
499537 }
500538
539+ // Copy specInput into specOutput.
540+ specOutput = specInput;
541+
542+ /*
543+ Set the bit-depth of the output buffer to be used by OIIO.
544+
545+ OIIO may change the bit-depth when writing to a file (e.g. if the output image ends in "jpg" it will be converted to 8-bit integer),
546+ and OCIO is not trying to analyze the filename to emulate OIIO's decision making process.
547+
548+ Additionally, the color space conversion may require more bits than the source image.
549+ For example, converting a log image to linear requires at least a half-float output format.
550+
551+ For most cases, half-float strikes a good balance between precision and storage space.
552+ But if the input depth would lose precision when converted to half-float, use full float for the output depth instead.
553+ */
554+ if (specInput.format == OIIO::TypeDesc::UINT16 || specInput.format == OIIO::TypeDesc::FLOAT)
555+ {
556+ specOutput.set_format (OIIO::TypeDesc::FLOAT);
557+ }
558+ else if (specInput.format == OIIO::TypeDesc::UINT8 || specInput.format == OIIO::TypeDesc::HALF)
559+ {
560+ specOutput.set_format (OIIO::TypeDesc::HALF);
561+ }
562+ else
563+ {
564+ throw OCIO::Exception (" The OIIO:TypeDesc of the input image pixels must be UINT8, UINT16, HALF or FLOAT." );
565+ }
566+
567+ // Allocate imgOutput buffer using specOutput.
568+ imgOutput.allocate (specOutput);
569+
501570#ifdef OCIO_GPU_ENABLED
502571 if (usegpu || usegpuLegacy)
503572 {
@@ -513,22 +582,22 @@ int main(int argc, const char **argv)
513582 oglApp->setShader (shaderDesc);
514583 oglApp->reshape (imgwidth, imgheight);
515584 oglApp->redisplay ();
516- oglApp->readImage ((float *)img .getBuffer ());
585+ oglApp->readImage ((float *)imgOutput .getBuffer ());
517586 }
518587 else
519588#endif // OCIO_GPU_ENABLED
520589 {
521- const OCIO::BitDepth bitDepth = OCIO::GetBitDepth (spec);
522-
523590 OCIO::ConstCPUProcessorRcPtr cpuProcessor
524- = processor->getOptimizedCPUProcessor (bitDepth, bitDepth,
591+ = processor->getOptimizedCPUProcessor (OCIO::GetBitDepth (specInput),
592+ OCIO::GetBitDepth (specOutput),
525593 OCIO::OPTIMIZATION_DEFAULT);
526594
527595 const std::chrono::high_resolution_clock::time_point start
528596 = std::chrono::high_resolution_clock::now ();
529597
530- OCIO::ImageDescRcPtr imgDesc = OCIO::CreateImageDesc (spec, img);
531- cpuProcessor->apply (*imgDesc);
598+ OCIO::ImageDescRcPtr srcImgDesc = OCIO::CreateImageDesc (specInput, imgInput);
599+ OCIO::ImageDescRcPtr dstImgDesc = OCIO::CreateImageDesc (specOutput, imgOutput);
600+ cpuProcessor->apply (*srcImgDesc, *dstImgDesc);
532601
533602 if (verbose)
534603 {
@@ -573,7 +642,7 @@ int main(int argc, const char **argv)
573642 continue ;
574643 }
575644
576- spec .attribute (name, fval);
645+ specOutput .attribute (name, fval);
577646 }
578647
579648 for (unsigned int i=0 ; i<intAttrs.size (); ++i)
@@ -589,7 +658,7 @@ int main(int argc, const char **argv)
589658 continue ;
590659 }
591660
592- spec .attribute (name, ival);
661+ specOutput .attribute (name, ival);
593662 }
594663
595664 for (unsigned int i=0 ; i<stringAttrs.size (); ++i)
@@ -603,7 +672,7 @@ int main(int argc, const char **argv)
603672 continue ;
604673 }
605674
606- spec .attribute (name, value);
675+ specOutput .attribute (name, value);
607676 }
608677
609678 if (parseerror)
@@ -633,12 +702,14 @@ int main(int argc, const char **argv)
633702
634703 if (outputcolorspace)
635704 {
636- spec .attribute (" oiio:ColorSpace" , outputcolorspace);
705+ specOutput .attribute (" oiio:ColorSpace" , outputcolorspace);
637706 }
638707
639- f->open (outputimage, spec);
708+ OCIO::PrintImageSpec (specOutput, verbose);
709+
710+ f->open (outputimage, specOutput);
640711
641- if (!f->write_image (spec .format , img .getBuffer ()))
712+ if (!f->write_image (specOutput .format , imgOutput .getBuffer ()))
642713 {
643714 std::cerr << " ERROR: Writing \" " << outputimage << " \" failed with: "
644715 << f->geterror () << " ." << std::endl;
0 commit comments