Skip to content

Commit b80361c

Browse files
Adsk contrib - Implement invertview for ocioconvert, ocioperf and ociowrite (#1648)
* Implement invertview for ocioconvert, ocioperf and ociowrite Signed-off-by: Cedrik Fuoco <[email protected]> * Fix issue with operator precedence warning on Linux Signed-off-by: Cedrik Fuoco <[email protected]> * Added comment on why ocioconvert is changing the output buffer bit-depth and changed bitwise AND operator into logicla AND operator (same result but makes more sense here) Signed-off-by: Cedrik Fuoco <[email protected]> * Fix issue with --gpu path Signed-off-by: Cedrik Fuoco <[email protected]> Co-authored-by: Doug Walker <[email protected]>
1 parent 68b510b commit b80361c

File tree

3 files changed

+301
-112
lines changed

3 files changed

+301
-112
lines changed

src/apps/ocioconvert/main.cpp

Lines changed: 123 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -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>", "\nOpenImageIO 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

Comments
 (0)