Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/3rdparty/openexr/IlmImf/ImfAcesFile.cpp
16337 views
1
///////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright (c) 2007, Industrial Light & Magic, a division of Lucas
4
// Digital Ltd. LLC
5
//
6
// All rights reserved.
7
//
8
// Redistribution and use in source and binary forms, with or without
9
// modification, are permitted provided that the following conditions are
10
// met:
11
// * Redistributions of source code must retain the above copyright
12
// notice, this list of conditions and the following disclaimer.
13
// * Redistributions in binary form must reproduce the above
14
// copyright notice, this list of conditions and the following disclaimer
15
// in the documentation and/or other materials provided with the
16
// distribution.
17
// * Neither the name of Industrial Light & Magic nor the names of
18
// its contributors may be used to endorse or promote products derived
19
// from this software without specific prior written permission.
20
//
21
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
//
33
///////////////////////////////////////////////////////////////////////////
34
35
//-----------------------------------------------------------------------------
36
//
37
// ACES image file I/O.
38
//
39
//-----------------------------------------------------------------------------
40
41
#include <ImfAcesFile.h>
42
#include <ImfRgbaFile.h>
43
#include <ImfStandardAttributes.h>
44
#include <Iex.h>
45
#include <algorithm> // for std::max()
46
47
using namespace std;
48
using namespace Imath;
49
using namespace Iex;
50
51
namespace Imf {
52
53
54
const Chromaticities &
55
acesChromaticities ()
56
{
57
static const Chromaticities acesChr
58
(V2f (0.73470, 0.26530), // red
59
V2f (0.00000, 1.00000), // green
60
V2f (0.00010, -0.07700), // blue
61
V2f (0.32168, 0.33767)); // white
62
63
return acesChr;
64
}
65
66
67
class AcesOutputFile::Data
68
{
69
public:
70
71
Data();
72
~Data();
73
74
RgbaOutputFile * rgbaFile;
75
};
76
77
78
AcesOutputFile::Data::Data ():
79
rgbaFile (0)
80
{
81
// empty
82
}
83
84
85
AcesOutputFile::Data::~Data ()
86
{
87
delete rgbaFile;
88
}
89
90
91
namespace {
92
93
void
94
checkCompression (Compression compression)
95
{
96
//
97
// Not all compression methods are allowed in ACES files.
98
//
99
100
switch (compression)
101
{
102
case NO_COMPRESSION:
103
case PIZ_COMPRESSION:
104
case B44A_COMPRESSION:
105
break;
106
107
default:
108
throw ArgExc ("Invalid compression type for ACES file.");
109
}
110
}
111
112
} // namespace
113
114
115
AcesOutputFile::AcesOutputFile
116
(const std::string &name,
117
const Header &header,
118
RgbaChannels rgbaChannels,
119
int numThreads)
120
:
121
_data (new Data)
122
{
123
checkCompression (header.compression());
124
125
Header newHeader = header;
126
addChromaticities (newHeader, acesChromaticities());
127
addAdoptedNeutral (newHeader, acesChromaticities().white);
128
129
_data->rgbaFile = new RgbaOutputFile (name.c_str(),
130
newHeader,
131
rgbaChannels,
132
numThreads);
133
134
_data->rgbaFile->setYCRounding (7, 6);
135
}
136
137
138
AcesOutputFile::AcesOutputFile
139
(OStream &os,
140
const Header &header,
141
RgbaChannels rgbaChannels,
142
int numThreads)
143
:
144
_data (new Data)
145
{
146
checkCompression (header.compression());
147
148
Header newHeader = header;
149
addChromaticities (newHeader, acesChromaticities());
150
addAdoptedNeutral (newHeader, acesChromaticities().white);
151
152
_data->rgbaFile = new RgbaOutputFile (os,
153
header,
154
rgbaChannels,
155
numThreads);
156
157
_data->rgbaFile->setYCRounding (7, 6);
158
}
159
160
161
AcesOutputFile::AcesOutputFile
162
(const std::string &name,
163
const Imath::Box2i &displayWindow,
164
const Imath::Box2i &dataWindow,
165
RgbaChannels rgbaChannels,
166
float pixelAspectRatio,
167
const Imath::V2f screenWindowCenter,
168
float screenWindowWidth,
169
LineOrder lineOrder,
170
Compression compression,
171
int numThreads)
172
:
173
_data (new Data)
174
{
175
checkCompression (compression);
176
177
Header newHeader (displayWindow,
178
dataWindow.isEmpty()? displayWindow: dataWindow,
179
pixelAspectRatio,
180
screenWindowCenter,
181
screenWindowWidth,
182
lineOrder,
183
compression);
184
185
addChromaticities (newHeader, acesChromaticities());
186
addAdoptedNeutral (newHeader, acesChromaticities().white);
187
188
_data->rgbaFile = new RgbaOutputFile (name.c_str(),
189
newHeader,
190
rgbaChannels,
191
numThreads);
192
193
_data->rgbaFile->setYCRounding (7, 6);
194
}
195
196
197
AcesOutputFile::AcesOutputFile
198
(const std::string &name,
199
int width,
200
int height,
201
RgbaChannels rgbaChannels,
202
float pixelAspectRatio,
203
const Imath::V2f screenWindowCenter,
204
float screenWindowWidth,
205
LineOrder lineOrder,
206
Compression compression,
207
int numThreads)
208
:
209
_data (new Data)
210
{
211
checkCompression (compression);
212
213
Header newHeader (width,
214
height,
215
pixelAspectRatio,
216
screenWindowCenter,
217
screenWindowWidth,
218
lineOrder,
219
compression);
220
221
addChromaticities (newHeader, acesChromaticities());
222
addAdoptedNeutral (newHeader, acesChromaticities().white);
223
224
_data->rgbaFile = new RgbaOutputFile (name.c_str(),
225
newHeader,
226
rgbaChannels,
227
numThreads);
228
229
_data->rgbaFile->setYCRounding (7, 6);
230
}
231
232
233
AcesOutputFile::~AcesOutputFile ()
234
{
235
delete _data;
236
}
237
238
239
void
240
AcesOutputFile::setFrameBuffer
241
(const Rgba *base,
242
size_t xStride,
243
size_t yStride)
244
{
245
_data->rgbaFile->setFrameBuffer (base, xStride, yStride);
246
}
247
248
249
void
250
AcesOutputFile::writePixels (int numScanLines)
251
{
252
_data->rgbaFile->writePixels (numScanLines);
253
}
254
255
256
int
257
AcesOutputFile::currentScanLine () const
258
{
259
return _data->rgbaFile->currentScanLine();
260
}
261
262
263
const Header &
264
AcesOutputFile::header () const
265
{
266
return _data->rgbaFile->header();
267
}
268
269
270
const Imath::Box2i &
271
AcesOutputFile::displayWindow () const
272
{
273
return _data->rgbaFile->displayWindow();
274
}
275
276
277
const Imath::Box2i &
278
AcesOutputFile::dataWindow () const
279
{
280
return _data->rgbaFile->dataWindow();
281
}
282
283
284
float
285
AcesOutputFile::pixelAspectRatio () const
286
{
287
return _data->rgbaFile->pixelAspectRatio();
288
}
289
290
291
const Imath::V2f
292
AcesOutputFile::screenWindowCenter () const
293
{
294
return _data->rgbaFile->screenWindowCenter();
295
}
296
297
298
float
299
AcesOutputFile::screenWindowWidth () const
300
{
301
return _data->rgbaFile->screenWindowWidth();
302
}
303
304
305
LineOrder
306
AcesOutputFile::lineOrder () const
307
{
308
return _data->rgbaFile->lineOrder();
309
}
310
311
312
Compression
313
AcesOutputFile::compression () const
314
{
315
return _data->rgbaFile->compression();
316
}
317
318
319
RgbaChannels
320
AcesOutputFile::channels () const
321
{
322
return _data->rgbaFile->channels();
323
}
324
325
326
void
327
AcesOutputFile::updatePreviewImage (const PreviewRgba pixels[])
328
{
329
_data->rgbaFile->updatePreviewImage (pixels);
330
}
331
332
333
class AcesInputFile::Data
334
{
335
public:
336
337
Data();
338
~Data();
339
340
void initColorConversion ();
341
342
RgbaInputFile * rgbaFile;
343
344
Rgba * fbBase;
345
size_t fbXStride;
346
size_t fbYStride;
347
int minX;
348
int maxX;
349
350
bool mustConvertColor;
351
M44f fileToAces;
352
};
353
354
355
AcesInputFile::Data::Data ():
356
rgbaFile (0),
357
fbBase (0),
358
fbXStride (0),
359
fbYStride (0),
360
minX (0),
361
maxX (0),
362
mustConvertColor (false)
363
{
364
// empty
365
}
366
367
368
AcesInputFile::Data::~Data ()
369
{
370
delete rgbaFile;
371
}
372
373
374
void
375
AcesInputFile::Data::initColorConversion ()
376
{
377
const Header &header = rgbaFile->header();
378
379
Chromaticities fileChr;
380
381
if (hasChromaticities (header))
382
fileChr = chromaticities (header);
383
384
V2f fileNeutral = fileChr.white;
385
386
if (hasAdoptedNeutral (header))
387
fileNeutral = adoptedNeutral (header);
388
389
const Chromaticities acesChr = acesChromaticities();
390
391
V2f acesNeutral = acesChr.white;
392
393
if (fileChr.red == acesChr.red &&
394
fileChr.green == acesChr.green &&
395
fileChr.blue == acesChr.blue &&
396
fileChr.white == acesChr.white &&
397
fileNeutral == acesNeutral)
398
{
399
//
400
// The file already contains ACES data,
401
// color conversion is not necessary.
402
403
return;
404
}
405
406
mustConvertColor = true;
407
minX = header.dataWindow().min.x;
408
maxX = header.dataWindow().max.x;
409
410
//
411
// Create a matrix that transforms colors from the
412
// RGB space of the input file into the ACES space
413
// using a color adaptation transform to move the
414
// white point.
415
//
416
417
//
418
// We'll need the Bradford cone primary matrix and its inverse
419
//
420
421
static const M44f bradfordCPM
422
(0.895100, -0.750200, 0.038900, 0.000000,
423
0.266400, 1.713500, -0.068500, 0.000000,
424
-0.161400, 0.036700, 1.029600, 0.000000,
425
0.000000, 0.000000, 0.000000, 1.000000);
426
427
const static M44f inverseBradfordCPM
428
(0.986993, 0.432305, -0.008529, 0.000000,
429
-0.147054, 0.518360, 0.040043, 0.000000,
430
0.159963, 0.049291, 0.968487, 0.000000,
431
0.000000, 0.000000, 0.000000, 1.000000);
432
433
//
434
// Convert the white points of the two RGB spaces to XYZ
435
//
436
437
float fx = fileNeutral.x;
438
float fy = fileNeutral.y;
439
V3f fileNeutralXYZ (fx / fy, 1, (1 - fx - fy) / fy);
440
441
float ax = acesNeutral.x;
442
float ay = acesNeutral.y;
443
V3f acesNeutralXYZ (ax / ay, 1, (1 - ax - ay) / ay);
444
445
//
446
// Compute the Bradford transformation matrix
447
//
448
449
V3f ratio ((acesNeutralXYZ * bradfordCPM) /
450
(fileNeutralXYZ * bradfordCPM));
451
452
M44f ratioMat (ratio[0], 0, 0, 0,
453
0, ratio[1], 0, 0,
454
0, 0, ratio[2], 0,
455
0, 0, 0, 1);
456
457
M44f bradfordTrans = bradfordCPM *
458
ratioMat *
459
inverseBradfordCPM;
460
461
//
462
// Build a combined file-RGB-to-ACES-RGB conversion matrix
463
//
464
465
fileToAces = RGBtoXYZ (fileChr, 1) * bradfordTrans * XYZtoRGB (acesChr, 1);
466
}
467
468
469
AcesInputFile::AcesInputFile (const std::string &name, int numThreads):
470
_data (new Data)
471
{
472
_data->rgbaFile = new RgbaInputFile (name.c_str(), numThreads);
473
_data->initColorConversion();
474
}
475
476
477
AcesInputFile::AcesInputFile (IStream &is, int numThreads):
478
_data (new Data)
479
{
480
_data->rgbaFile = new RgbaInputFile (is, numThreads);
481
_data->initColorConversion();
482
}
483
484
485
AcesInputFile::~AcesInputFile ()
486
{
487
delete _data;
488
}
489
490
491
void
492
AcesInputFile::setFrameBuffer (Rgba *base, size_t xStride, size_t yStride)
493
{
494
_data->rgbaFile->setFrameBuffer (base, xStride, yStride);
495
_data->fbBase = base;
496
_data->fbXStride = xStride;
497
_data->fbYStride = yStride;
498
}
499
500
501
void
502
AcesInputFile::readPixels (int scanLine1, int scanLine2)
503
{
504
//
505
// Copy the pixels from the RgbaInputFile into the frame buffer.
506
//
507
508
_data->rgbaFile->readPixels (scanLine1, scanLine2);
509
510
//
511
// If the RGB space of the input file is not the same as the ACES
512
// RGB space, then the pixels in the frame buffer must be transformed
513
// into the ACES RGB space.
514
//
515
516
if (!_data->mustConvertColor)
517
return;
518
519
int minY = min (scanLine1, scanLine2);
520
int maxY = max (scanLine1, scanLine2);
521
522
for (int y = minY; y <= maxY; ++y)
523
{
524
Rgba *base = _data->fbBase +
525
_data->fbXStride * _data->minX +
526
_data->fbYStride * y;
527
528
for (int x = _data->minX; x <= _data->maxX; ++x)
529
{
530
V3f aces = V3f (base->r, base->g, base->b) * _data->fileToAces;
531
532
base->r = aces[0];
533
base->g = aces[1];
534
base->b = aces[2];
535
536
base += _data->fbXStride;
537
}
538
}
539
}
540
541
542
void
543
AcesInputFile::readPixels (int scanLine)
544
{
545
readPixels (scanLine, scanLine);
546
}
547
548
549
const Header &
550
AcesInputFile::header () const
551
{
552
return _data->rgbaFile->header();
553
}
554
555
556
const Imath::Box2i &
557
AcesInputFile::displayWindow () const
558
{
559
return _data->rgbaFile->displayWindow();
560
}
561
562
563
const Imath::Box2i &
564
AcesInputFile::dataWindow () const
565
{
566
return _data->rgbaFile->dataWindow();
567
}
568
569
570
float
571
AcesInputFile::pixelAspectRatio () const
572
{
573
return _data->rgbaFile->pixelAspectRatio();
574
}
575
576
577
const Imath::V2f
578
AcesInputFile::screenWindowCenter () const
579
{
580
return _data->rgbaFile->screenWindowCenter();
581
}
582
583
584
float
585
AcesInputFile::screenWindowWidth () const
586
{
587
return _data->rgbaFile->screenWindowWidth();
588
}
589
590
591
LineOrder
592
AcesInputFile::lineOrder () const
593
{
594
return _data->rgbaFile->lineOrder();
595
}
596
597
598
Compression
599
AcesInputFile::compression () const
600
{
601
return _data->rgbaFile->compression();
602
}
603
604
605
RgbaChannels
606
AcesInputFile::channels () const
607
{
608
return _data->rgbaFile->channels();
609
}
610
611
612
const char *
613
AcesInputFile::fileName () const
614
{
615
return _data->rgbaFile->fileName();
616
}
617
618
619
bool
620
AcesInputFile::isComplete () const
621
{
622
return _data->rgbaFile->isComplete();
623
}
624
625
626
int
627
AcesInputFile::version () const
628
{
629
return _data->rgbaFile->version();
630
}
631
632
} // namespace Imf
633
634