Skip to content

Commit cc22a73

Browse files
committed
EXR alpha support for 4 channel reading and writing. Issue opencv#16115.
1 parent 2d2d72a commit cc22a73

File tree

3 files changed

+268
-74
lines changed

3 files changed

+268
-74
lines changed

modules/imgcodecs/src/grfmt_exr.cpp

Lines changed: 109 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,13 @@ ExrDecoder::ExrDecoder()
8484
{
8585
m_signature = "\x76\x2f\x31\x01";
8686
m_file = 0;
87-
m_red = m_green = m_blue = 0;
87+
m_red = m_green = m_blue = m_alpha = 0;
8888
m_type = ((Imf::PixelType)0);
8989
m_iscolor = false;
9090
m_bit_depth = 0;
9191
m_isfloat = false;
9292
m_ischroma = false;
93+
m_hasalpha = false;
9394
m_native_depth = false;
9495

9596
}
@@ -113,7 +114,7 @@ void ExrDecoder::close()
113114

114115
int ExrDecoder::type() const
115116
{
116-
return CV_MAKETYPE((m_isfloat ? CV_32F : CV_32S), m_iscolor ? 3 : 1);
117+
return CV_MAKETYPE((m_isfloat ? CV_32F : CV_32S), ((m_iscolor && m_hasalpha) ? 4 : m_iscolor ? 3 : m_hasalpha ? 2 : 1));
117118
}
118119

119120

@@ -141,6 +142,11 @@ bool ExrDecoder::readHeader()
141142
m_red = channels.findChannel( "R" );
142143
m_green = channels.findChannel( "G" );
143144
m_blue = channels.findChannel( "B" );
145+
m_alpha = channels.findChannel( "A" );
146+
147+
if( m_alpha ) // alpha channel supported in RGB, Y, and YC scenarios
148+
m_hasalpha = true;
149+
144150
if( m_red || m_green || m_blue )
145151
{
146152
m_iscolor = true;
@@ -178,7 +184,8 @@ bool ExrDecoder::readHeader()
178184
bool ExrDecoder::readData( Mat& img )
179185
{
180186
m_native_depth = CV_MAT_DEPTH(type()) == img.depth();
181-
bool color = img.channels() > 1;
187+
bool color = img.channels() > 2; // output mat has 3+ channels; Y or YA are the 1 and 2 channel scenario
188+
bool alphasupported = ( img.channels() % 2 == 0 ); // even number of channels indicates alpha
182189
int channels = 0;
183190
uchar* data = img.ptr();
184191
size_t step = img.step;
@@ -187,18 +194,22 @@ bool ExrDecoder::readData( Mat& img )
187194
bool rgbtogray = ( !m_ischroma && m_iscolor && !color );
188195
bool result = true;
189196
FrameBuffer frame;
190-
int xsample[3] = {1, 1, 1};
197+
const int defaultchannels = 3;
198+
int xsample[defaultchannels] = {1, 1, 1};
191199
char *buffer;
192-
size_t xstep = 0;
200+
CV_Assert(m_type == FLOAT);
201+
const size_t floatsize = sizeof(float);
202+
size_t xstep = m_native_depth ? floatsize : 1; // 4 bytes if native depth (FLOAT), otherwise converting to 1 byte U8 depth
193203
size_t ystep = 0;
194-
195-
xstep = m_native_depth ? 4 : 1;
204+
const int channelstoread = ( (m_iscolor && alphasupported) ? 4 :
205+
( (m_iscolor && !m_ischroma) || color) ? 3 : alphasupported ? 2 : 1 ); // number of channels to read may exceed channels in output img
206+
size_t xStride = floatsize * channelstoread;
196207

197208
AutoBuffer<char> copy_buffer;
198209

199210
if( !justcopy )
200211
{
201-
copy_buffer.allocate(sizeof(float) * m_width * 3);
212+
copy_buffer.allocate(floatsize * m_width * defaultchannels);
202213
buffer = copy_buffer.data();
203214
ystep = 0;
204215
}
@@ -215,117 +226,135 @@ bool ExrDecoder::readData( Mat& img )
215226
if( m_blue )
216227
{
217228
frame.insert( "BY", Slice( m_type,
218-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep,
219-
12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 ));
220-
xsample[0] = m_blue->ySampling;
229+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep,
230+
xStride, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 ));
231+
xsample[0] = m_blue->xSampling;
221232
}
222233
else
223234
{
224235
frame.insert( "BY", Slice( m_type,
225-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep,
226-
12, ystep, 1, 1, 0.0 ));
236+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep,
237+
xStride, ystep, 1, 1, 0.0 ));
227238
}
228239
if( m_green )
229240
{
230241
frame.insert( "Y", Slice( m_type,
231-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
232-
12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
233-
xsample[1] = m_green->ySampling;
242+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + floatsize,
243+
xStride, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
244+
xsample[1] = m_green->xSampling;
234245
}
235246
else
236247
{
237248
frame.insert( "Y", Slice( m_type,
238-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
239-
12, ystep, 1, 1, 0.0 ));
249+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + floatsize,
250+
xStride, ystep, 1, 1, 0.0 ));
240251
}
241252
if( m_red )
242253
{
243254
frame.insert( "RY", Slice( m_type,
244-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8,
245-
12, ystep, m_red->xSampling, m_red->ySampling, 0.0 ));
246-
xsample[2] = m_red->ySampling;
255+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + (floatsize * 2),
256+
xStride, ystep, m_red->xSampling, m_red->ySampling, 0.0 ));
257+
xsample[2] = m_red->xSampling;
247258
}
248259
else
249260
{
250261
frame.insert( "RY", Slice( m_type,
251-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8,
252-
12, ystep, 1, 1, 0.0 ));
262+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + (floatsize * 2),
263+
xStride, ystep, 1, 1, 0.0 ));
253264
}
254265
}
255266
else
256267
{
257268
frame.insert( "Y", Slice( m_type,
258-
buffer - m_datawindow.min.x * 4 - m_datawindow.min.y * ystep,
259-
4, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
260-
xsample[0] = m_green->ySampling;
269+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep,
270+
xStride, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
271+
xsample[0] = m_green->xSampling;
261272
}
262273
}
263274
else
264275
{
265276
if( m_blue )
266277
{
267278
frame.insert( "B", Slice( m_type,
268-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep,
269-
12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 ));
270-
xsample[0] = m_blue->ySampling;
279+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep,
280+
xStride, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 ));
281+
xsample[0] = m_blue->xSampling;
271282
}
272283
else
273284
{
274285
frame.insert( "B", Slice( m_type,
275-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep,
276-
12, ystep, 1, 1, 0.0 ));
286+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep,
287+
xStride, ystep, 1, 1, 0.0 ));
277288
}
278289
if( m_green )
279290
{
280291
frame.insert( "G", Slice( m_type,
281-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
282-
12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
283-
xsample[1] = m_green->ySampling;
292+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + floatsize,
293+
xStride, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
294+
xsample[1] = m_green->xSampling;
284295
}
285296
else
286297
{
287298
frame.insert( "G", Slice( m_type,
288-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
289-
12, ystep, 1, 1, 0.0 ));
299+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + floatsize,
300+
xStride, ystep, 1, 1, 0.0 ));
290301
}
291302
if( m_red )
292303
{
293304
frame.insert( "R", Slice( m_type,
294-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8,
295-
12, ystep, m_red->xSampling, m_red->ySampling, 0.0 ));
296-
xsample[2] = m_red->ySampling;
305+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + (floatsize * 2),
306+
xStride, ystep, m_red->xSampling, m_red->ySampling, 0.0 ));
307+
xsample[2] = m_red->xSampling;
297308
}
298309
else
299310
{
300311
frame.insert( "R", Slice( m_type,
301-
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8,
302-
12, ystep, 1, 1, 0.0 ));
312+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + (floatsize * 2),
313+
xStride, ystep, 1, 1, 0.0 ));
303314
}
304315
}
305316

317+
if( justcopy && m_hasalpha && alphasupported )
318+
{ // alpha preserved only in justcopy scenario where alpha is desired (alphasupported)
319+
// and present in original file (m_hasalpha)
320+
CV_Assert(channelstoread == img.channels());
321+
int offset = (channelstoread - 1) * floatsize;
322+
frame.insert( "A", Slice( m_type,
323+
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + offset,
324+
xStride, ystep, m_alpha->xSampling, m_alpha->ySampling, 0.0 ));
325+
}
326+
306327
for (FrameBuffer::Iterator it = frame.begin(); it != frame.end(); it++) {
307328
channels++;
308329
}
309330

331+
CV_Assert(channels == channelstoread);
332+
333+
if( (channels != channelstoread) || (!justcopy && channels > defaultchannels) )
334+
{ // safety checking what ought to be true here
335+
close();
336+
return false;
337+
}
338+
310339
m_file->setFrameBuffer( frame );
311340
if( justcopy )
312341
{
313342
m_file->readPixels( m_datawindow.min.y, m_datawindow.max.y );
314343

315-
if( color )
344+
if( m_iscolor )
316345
{
317346
if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
318-
UpSample( data, 3, step / xstep, xsample[0], m_blue->ySampling );
347+
UpSample( data, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling );
319348
if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
320-
UpSample( data + xstep, 3, step / xstep, xsample[1], m_green->ySampling );
349+
UpSample( data + xstep, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling );
321350
if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
322-
UpSample( data + 2 * xstep, 3, step / xstep, xsample[2], m_red->ySampling );
351+
UpSample( data + 2 * xstep, channelstoread, step / xstep, m_red->xSampling, m_red->ySampling );
323352
}
324353
else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
325-
UpSample( data, 1, step / xstep, xsample[0], m_green->ySampling );
354+
UpSample( data, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling );
326355

327356
if( chromatorgb )
328-
ChromaToBGR( (float *)data, m_height, step / xstep );
357+
ChromaToBGR( (float *)data, m_height, channelstoread, step / xstep );
329358
}
330359
else
331360
{
@@ -347,7 +376,7 @@ bool ExrDecoder::readData( Mat& img )
347376
else
348377
{
349378
if( chromatorgb )
350-
ChromaToBGR( (float *)buffer, 1, step );
379+
ChromaToBGR( (float *)buffer, 1, defaultchannels, step );
351380

352381
if( m_type == FLOAT )
353382
{
@@ -372,11 +401,11 @@ bool ExrDecoder::readData( Mat& img )
372401
if( color )
373402
{
374403
if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
375-
UpSampleY( data, 3, step / xstep, m_blue->ySampling );
404+
UpSampleY( data, defaultchannels, step / xstep, m_blue->ySampling );
376405
if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
377-
UpSampleY( data + xstep, 3, step / xstep, m_green->ySampling );
406+
UpSampleY( data + xstep, defaultchannels, step / xstep, m_green->ySampling );
378407
if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
379-
UpSampleY( data + 2 * xstep, 3, step / xstep, m_red->ySampling );
408+
UpSampleY( data + 2 * xstep, defaultchannels, step / xstep, m_red->ySampling );
380409
}
381410
else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
382411
UpSampleY( data, 1, step / xstep, m_green->ySampling );
@@ -457,7 +486,7 @@ void ExrDecoder::UpSampleY( uchar *data, int xstep, int ystep, int ysample )
457486
/**
458487
// algorithm from ImfRgbaYca.cpp
459488
*/
460-
void ExrDecoder::ChromaToBGR( float *data, int numlines, int step )
489+
void ExrDecoder::ChromaToBGR( float *data, int numlines, int xstep, int ystep )
461490
{
462491
for( int y = 0; y < numlines; y++ )
463492
{
@@ -466,34 +495,34 @@ void ExrDecoder::ChromaToBGR( float *data, int numlines, int step )
466495
double b, Y, r;
467496
if( m_type == FLOAT )
468497
{
469-
b = data[y * step + x * 3];
470-
Y = data[y * step + x * 3 + 1];
471-
r = data[y * step + x * 3 + 2];
498+
b = data[y * ystep + x * xstep];
499+
Y = data[y * ystep + x * xstep + 1];
500+
r = data[y * ystep + x * xstep + 2];
472501
}
473502
else
474503
{
475-
b = ((unsigned *)data)[y * step + x * 3];
476-
Y = ((unsigned *)data)[y * step + x * 3 + 1];
477-
r = ((unsigned *)data)[y * step + x * 3 + 2];
504+
b = ((unsigned *)data)[y * ystep + x * xstep];
505+
Y = ((unsigned *)data)[y * ystep + x * xstep + 1];
506+
r = ((unsigned *)data)[y * ystep + x * xstep + 2];
478507
}
479508
r = (r + 1) * Y;
480509
b = (b + 1) * Y;
481510
Y = (Y - b * m_chroma.blue[1] - r * m_chroma.red[1]) / m_chroma.green[1];
482511

483512
if( m_type == FLOAT )
484513
{
485-
data[y * step + x * 3] = (float)b;
486-
data[y * step + x * 3 + 1] = (float)Y;
487-
data[y * step + x * 3 + 2] = (float)r;
514+
data[y * ystep + x * xstep] = (float)b;
515+
data[y * ystep + x * xstep + 1] = (float)Y;
516+
data[y * ystep + x * xstep + 2] = (float)r;
488517
}
489518
else
490519
{
491520
int t = cvRound(b);
492-
((unsigned *)data)[y * step + x * 3 + 0] = (unsigned)MAX(t, 0);
521+
((unsigned *)data)[y * ystep + x * xstep + 0] = (unsigned)MAX(t, 0);
493522
t = cvRound(Y);
494-
((unsigned *)data)[y * step + x * 3 + 1] = (unsigned)MAX(t, 0);
523+
((unsigned *)data)[y * ystep + x * xstep + 1] = (unsigned)MAX(t, 0);
495524
t = cvRound(r);
496-
((unsigned *)data)[y * step + x * 3 + 2] = (unsigned)MAX(t, 0);
525+
((unsigned *)data)[y * ystep + x * xstep + 2] = (unsigned)MAX(t, 0);
497526
}
498527
}
499528
}
@@ -571,7 +600,6 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
571600
int depth = img.depth();
572601
CV_Assert( depth == CV_32F );
573602
int channels = img.channels();
574-
CV_Assert( channels == 3 || channels == 1 );
575603
bool result = false;
576604
Header header( width, height );
577605
Imf::PixelType type = FLOAT;
@@ -594,7 +622,7 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
594622
}
595623
}
596624

597-
if( channels == 3 )
625+
if( channels == 3 || channels == 4 )
598626
{
599627
header.channels().insert( "R", Channel( type ) );
600628
header.channels().insert( "G", Channel( type ) );
@@ -607,6 +635,11 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
607635
//printf("gray\n");
608636
}
609637

638+
if( channels % 2 == 0 )
639+
{ // even number of channels indicates Alpha
640+
header.channels().insert( "A", Channel( type ) );
641+
}
642+
610643
OutputFile file( m_filename.c_str(), header );
611644

612645
FrameBuffer frame;
@@ -629,14 +662,19 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
629662
size = 4;
630663
}
631664

632-
if( channels == 3 )
665+
if( channels == 3 || channels == 4 )
633666
{
634-
frame.insert( "B", Slice( type, buffer, size * 3, bufferstep ));
635-
frame.insert( "G", Slice( type, buffer + size, size * 3, bufferstep ));
636-
frame.insert( "R", Slice( type, buffer + size * 2, size * 3, bufferstep ));
667+
frame.insert( "B", Slice( type, buffer, size * channels, bufferstep ));
668+
frame.insert( "G", Slice( type, buffer + size, size * channels, bufferstep ));
669+
frame.insert( "R", Slice( type, buffer + size * 2, size * channels, bufferstep ));
637670
}
638671
else
639-
frame.insert( "Y", Slice( type, buffer, size, bufferstep ));
672+
frame.insert( "Y", Slice( type, buffer, size * channels, bufferstep ));
673+
674+
if( channels % 2 == 0 )
675+
{ // even channel count indicates Alpha channel
676+
frame.insert( "A", Slice( type, buffer + size * (channels - 1), size * channels, bufferstep ));
677+
}
640678

641679
file.setFrameBuffer( frame );
642680

0 commit comments

Comments
 (0)