/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #include "precomp.hpp" #ifdef HAVE_OPENEXR #if defined _MSC_VER && _MSC_VER >= 1200 # pragma warning( disable: 4100 4244 4267 ) #endif #if defined __GNUC__ && defined __APPLE__ # pragma GCC diagnostic ignored "-Wshadow" #endif /// C++ Standard Libraries #include #include #include #include #include #include #include #include #include #include "grfmt_exr.hpp" #include "OpenEXRConfig.h" #if defined _WIN32 #undef UINT #define UINT ((Imf::PixelType)0) #undef HALF #define HALF ((Imf::PixelType)1) #undef FLOAT #define FLOAT ((Imf::PixelType)2) #endif namespace cv { /////////////////////// ExrDecoder /////////////////// ExrDecoder::ExrDecoder() { m_signature = "\x76\x2f\x31\x01"; m_file = 0; m_red = m_green = m_blue = m_alpha = 0; m_type = ((Imf::PixelType)0); m_iscolor = false; m_bit_depth = 0; m_isfloat = false; m_ischroma = false; m_hasalpha = false; m_native_depth = false; } ExrDecoder::~ExrDecoder() { close(); } void ExrDecoder::close() { if( m_file ) { delete m_file; m_file = 0; } } int ExrDecoder::type() const { return CV_MAKETYPE((m_isfloat ? CV_32F : CV_32S), ((m_iscolor && m_hasalpha) ? 4 : m_iscolor ? 3 : m_hasalpha ? 2 : 1)); } bool ExrDecoder::readHeader() { bool result = false; m_file = new InputFile( m_filename.c_str() ); if( !m_file ) // probably paranoid return false; m_datawindow = m_file->header().dataWindow(); m_width = m_datawindow.max.x - m_datawindow.min.x + 1; m_height = m_datawindow.max.y - m_datawindow.min.y + 1; // the type HALF is converted to 32 bit float // and the other types supported by OpenEXR are 32 bit anyway m_bit_depth = 32; if( hasChromaticities( m_file->header() )) m_chroma = chromaticities( m_file->header() ); const ChannelList &channels = m_file->header().channels(); m_red = channels.findChannel( "R" ); m_green = channels.findChannel( "G" ); m_blue = channels.findChannel( "B" ); m_alpha = channels.findChannel( "A" ); if( m_alpha ) // alpha channel supported in RGB, Y, and YC scenarios m_hasalpha = true; if( m_red || m_green || m_blue ) { m_iscolor = true; m_ischroma = false; result = true; } else { m_green = channels.findChannel( "Y" ); if( !m_green ) { m_green = channels.findChannel( "Z" ); // Distance of the front of a sample from the viewer } if( m_green ) { m_ischroma = true; m_red = channels.findChannel( "RY" ); m_blue = channels.findChannel( "BY" ); m_iscolor = (m_blue || m_red); result = true; } else result = false; } if( result ) { m_type = FLOAT; m_isfloat = ( m_type == FLOAT ); } if( !result ) close(); return result; } bool ExrDecoder::readData( Mat& img ) { m_native_depth = CV_MAT_DEPTH(type()) == img.depth(); bool color = img.channels() > 2; // output mat has 3+ channels; Y or YA are the 1 and 2 channel scenario bool alphasupported = ( img.channels() % 2 == 0 ); // even number of channels indicates alpha int channels = 0; uchar* data = img.ptr(); size_t step = img.step; bool justcopy = ( m_native_depth && (color == m_iscolor) ); bool chromatorgb = ( m_ischroma && color ); bool rgbtogray = ( !m_ischroma && m_iscolor && !color ); bool result = true; FrameBuffer frame; const int defaultchannels = 3; int xsample[defaultchannels] = {1, 1, 1}; char *buffer; CV_Assert(m_type == FLOAT); const size_t floatsize = sizeof(float); size_t xstep = m_native_depth ? floatsize : 1; // 4 bytes if native depth (FLOAT), otherwise converting to 1 byte U8 depth size_t ystep = 0; const int channelstoread = ( (m_iscolor && alphasupported) ? 4 : ( (m_iscolor && !m_ischroma) || color) ? 3 : alphasupported ? 2 : 1 ); // number of channels to read may exceed channels in output img size_t xStride = floatsize * channelstoread; AutoBuffer copy_buffer; if( !justcopy ) { copy_buffer.allocate(floatsize * m_width * defaultchannels); buffer = copy_buffer.data(); ystep = 0; } else { buffer = (char *)data; ystep = step; } if( m_ischroma ) { if( color ) { if( m_blue ) { frame.insert( "BY", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep, xStride, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 )); xsample[0] = m_blue->xSampling; } else { frame.insert( "BY", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep, xStride, ystep, 1, 1, 0.0 )); } if( m_green ) { frame.insert( "Y", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + floatsize, xStride, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); xsample[1] = m_green->xSampling; } else { frame.insert( "Y", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + floatsize, xStride, ystep, 1, 1, 0.0 )); } if( m_red ) { frame.insert( "RY", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + (floatsize * 2), xStride, ystep, m_red->xSampling, m_red->ySampling, 0.0 )); xsample[2] = m_red->xSampling; } else { frame.insert( "RY", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + (floatsize * 2), xStride, ystep, 1, 1, 0.0 )); } } else { frame.insert( "Y", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep, xStride, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); xsample[0] = m_green->xSampling; } } else { if( m_blue ) { frame.insert( "B", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep, xStride, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 )); xsample[0] = m_blue->xSampling; } else { frame.insert( "B", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep, xStride, ystep, 1, 1, 0.0 )); } if( m_green ) { frame.insert( "G", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + floatsize, xStride, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); xsample[1] = m_green->xSampling; } else { frame.insert( "G", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + floatsize, xStride, ystep, 1, 1, 0.0 )); } if( m_red ) { frame.insert( "R", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + (floatsize * 2), xStride, ystep, m_red->xSampling, m_red->ySampling, 0.0 )); xsample[2] = m_red->xSampling; } else { frame.insert( "R", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + (floatsize * 2), xStride, ystep, 1, 1, 0.0 )); } } if( justcopy && m_hasalpha && alphasupported ) { // alpha preserved only in justcopy scenario where alpha is desired (alphasupported) // and present in original file (m_hasalpha) CV_Assert(channelstoread == img.channels()); int offset = (channelstoread - 1) * floatsize; frame.insert( "A", Slice( m_type, buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + offset, xStride, ystep, m_alpha->xSampling, m_alpha->ySampling, 0.0 )); } for (FrameBuffer::Iterator it = frame.begin(); it != frame.end(); it++) { channels++; } CV_Assert(channels == channelstoread); if( (channels != channelstoread) || (!justcopy && channels > defaultchannels) ) { // safety checking what ought to be true here close(); return false; } m_file->setFrameBuffer( frame ); if( justcopy ) { m_file->readPixels( m_datawindow.min.y, m_datawindow.max.y ); if( m_iscolor ) { if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) UpSample( data, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling ); if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) UpSample( data + xstep, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling ); if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) UpSample( data + 2 * xstep, channelstoread, step / xstep, m_red->xSampling, m_red->ySampling ); } else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) UpSample( data, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling ); if( chromatorgb ) ChromaToBGR( (float *)data, m_height, channelstoread, step / xstep ); } else { uchar *out = data; int x, y; for( y = m_datawindow.min.y; y <= m_datawindow.max.y; y++ ) { m_file->readPixels( y, y ); for( int i = 0; i < channels; i++ ) { if( xsample[i] != 1 ) UpSampleX( (float *)buffer + i, channels, xsample[i] ); } if( rgbtogray ) { RGBToGray( (float *)buffer, (float *)out ); } else { if( chromatorgb ) ChromaToBGR( (float *)buffer, 1, defaultchannels, step ); if( m_type == FLOAT ) { float *fi = (float *)buffer; for( x = 0; x < m_width * img.channels(); x++) { out[x] = cv::saturate_cast(fi[x]); } } else { unsigned *ui = (unsigned *)buffer; for( x = 0; x < m_width * img.channels(); x++) { out[x] = cv::saturate_cast(ui[x]); } } } out += step; } if( color ) { if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) UpSampleY( data, defaultchannels, step / xstep, m_blue->ySampling ); if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) UpSampleY( data + xstep, defaultchannels, step / xstep, m_green->ySampling ); if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) UpSampleY( data + 2 * xstep, defaultchannels, step / xstep, m_red->ySampling ); } else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) UpSampleY( data, 1, step / xstep, m_green->ySampling ); } close(); return result; } /** // on entry pixel values are stored packed in the upper left corner of the image // this functions expands them by duplication to cover the whole image */ void ExrDecoder::UpSample( uchar *data, int xstep, int ystep, int xsample, int ysample ) { for( int y = (m_height - 1) / ysample, yre = m_height - ysample; y >= 0; y--, yre -= ysample ) { for( int x = (m_width - 1) / xsample, xre = m_width - xsample; x >= 0; x--, xre -= xsample ) { for( int i = 0; i < ysample; i++ ) { for( int n = 0; n < xsample; n++ ) { if( !m_native_depth ) data[(yre + i) * ystep + (xre + n) * xstep] = data[y * ystep + x * xstep]; else if( m_type == FLOAT ) ((float *)data)[(yre + i) * ystep + (xre + n) * xstep] = ((float *)data)[y * ystep + x * xstep]; else ((unsigned *)data)[(yre + i) * ystep + (xre + n) * xstep] = ((unsigned *)data)[y * ystep + x * xstep]; } } } } } /** // on entry pixel values are stored packed in the upper left corner of the image // this functions expands them by duplication to cover the whole image */ void ExrDecoder::UpSampleX( float *data, int xstep, int xsample ) { for( int x = (m_width - 1) / xsample, xre = m_width - xsample; x >= 0; x--, xre -= xsample ) { for( int n = 0; n < xsample; n++ ) { if( m_type == FLOAT ) ((float *)data)[(xre + n) * xstep] = ((float *)data)[x * xstep]; else ((unsigned *)data)[(xre + n) * xstep] = ((unsigned *)data)[x * xstep]; } } } /** // on entry pixel values are stored packed in the upper left corner of the image // this functions expands them by duplication to cover the whole image */ void ExrDecoder::UpSampleY( uchar *data, int xstep, int ystep, int ysample ) { for( int y = m_height - ysample, yre = m_height - ysample; y >= 0; y -= ysample, yre -= ysample ) { for( int x = 0; x < m_width; x++ ) { for( int i = 1; i < ysample; i++ ) { if( !m_native_depth ) data[(yre + i) * ystep + x * xstep] = data[y * ystep + x * xstep]; else if( m_type == FLOAT ) ((float *)data)[(yre + i) * ystep + x * xstep] = ((float *)data)[y * ystep + x * xstep]; else ((unsigned *)data)[(yre + i) * ystep + x * xstep] = ((unsigned *)data)[y * ystep + x * xstep]; } } } } /** // algorithm from ImfRgbaYca.cpp */ void ExrDecoder::ChromaToBGR( float *data, int numlines, int xstep, int ystep ) { for( int y = 0; y < numlines; y++ ) { for( int x = 0; x < m_width; x++ ) { double b, Y, r; if( m_type == FLOAT ) { b = data[y * ystep + x * xstep]; Y = data[y * ystep + x * xstep + 1]; r = data[y * ystep + x * xstep + 2]; } else { b = ((unsigned *)data)[y * ystep + x * xstep]; Y = ((unsigned *)data)[y * ystep + x * xstep + 1]; r = ((unsigned *)data)[y * ystep + x * xstep + 2]; } r = (r + 1) * Y; b = (b + 1) * Y; Y = (Y - b * m_chroma.blue[1] - r * m_chroma.red[1]) / m_chroma.green[1]; if( m_type == FLOAT ) { data[y * ystep + x * xstep] = (float)b; data[y * ystep + x * xstep + 1] = (float)Y; data[y * ystep + x * xstep + 2] = (float)r; } else { int t = cvRound(b); ((unsigned *)data)[y * ystep + x * xstep + 0] = (unsigned)MAX(t, 0); t = cvRound(Y); ((unsigned *)data)[y * ystep + x * xstep + 1] = (unsigned)MAX(t, 0); t = cvRound(r); ((unsigned *)data)[y * ystep + x * xstep + 2] = (unsigned)MAX(t, 0); } } } } /** // convert one row to gray */ void ExrDecoder::RGBToGray( float *in, float *out ) { if( m_type == FLOAT ) { if( m_native_depth ) { for( int i = 0, n = 0; i < m_width; i++, n += 3 ) out[i] = in[n] * m_chroma.blue[0] + in[n + 1] * m_chroma.green[0] + in[n + 2] * m_chroma.red[0]; } else { uchar *o = (uchar *)out; for( int i = 0, n = 0; i < m_width; i++, n += 3 ) o[i] = (uchar) (in[n] * m_chroma.blue[0] + in[n + 1] * m_chroma.green[0] + in[n + 2] * m_chroma.red[0]); } } else // UINT { if( m_native_depth ) { unsigned *ui = (unsigned *)in; for( int i = 0; i < m_width * 3; i++ ) ui[i] -= 0x80000000; int *si = (int *)in; for( int i = 0, n = 0; i < m_width; i++, n += 3 ) ((int *)out)[i] = int(si[n] * m_chroma.blue[0] + si[n + 1] * m_chroma.green[0] + si[n + 2] * m_chroma.red[0]); } else // how to best convert float to uchar? { unsigned *ui = (unsigned *)in; for( int i = 0, n = 0; i < m_width; i++, n += 3 ) ((uchar *)out)[i] = uchar((ui[n] * m_chroma.blue[0] + ui[n + 1] * m_chroma.green[0] + ui[n + 2] * m_chroma.red[0]) * (256.0 / 4294967296.0)); } } } ImageDecoder ExrDecoder::newDecoder() const { return makePtr(); } /////////////////////// ExrEncoder /////////////////// ExrEncoder::ExrEncoder() { m_description = "OpenEXR Image files (*.exr)"; } ExrEncoder::~ExrEncoder() { } bool ExrEncoder::isFormatSupported( int depth ) const { return ( CV_MAT_DEPTH(depth) == CV_32F ); } bool ExrEncoder::write( const Mat& img, const std::vector& params ) { int width = img.cols, height = img.rows; int depth = img.depth(); CV_Assert( depth == CV_32F ); int channels = img.channels(); bool result = false; Header header( width, height ); Imf::PixelType type = FLOAT; for( size_t i = 0; i < params.size(); i += 2 ) { if( params[i] == CV_IMWRITE_EXR_TYPE ) { switch( params[i+1] ) { case IMWRITE_EXR_TYPE_HALF: type = HALF; break; case IMWRITE_EXR_TYPE_FLOAT: type = FLOAT; break; default: CV_Error(Error::StsBadArg, "IMWRITE_EXR_TYPE is invalid or not supported"); } } if ( params[i] == IMWRITE_EXR_COMPRESSION ) { switch ( params[i + 1] ) { case IMWRITE_EXR_COMPRESSION_NO: header.compression() = NO_COMPRESSION; break; case IMWRITE_EXR_COMPRESSION_RLE: header.compression() = RLE_COMPRESSION; break; case IMWRITE_EXR_COMPRESSION_ZIPS: header.compression() = ZIPS_COMPRESSION; break; case IMWRITE_EXR_COMPRESSION_ZIP: header.compression() = ZIP_COMPRESSION; break; case IMWRITE_EXR_COMPRESSION_PIZ: header.compression() = PIZ_COMPRESSION; break; case IMWRITE_EXR_COMPRESSION_PXR24: header.compression() = PXR24_COMPRESSION; break; case IMWRITE_EXR_COMPRESSION_B44: header.compression() = B44_COMPRESSION; break; case IMWRITE_EXR_COMPRESSION_B44A: header.compression() = B44A_COMPRESSION; break; #if ((OPENEXR_VERSION_MAJOR * 1000 + OPENEXR_VERSION_MINOR) >= (2 * 1000 + 2)) // available since version 2.2.0 case IMWRITE_EXR_COMPRESSION_DWAA: header.compression() = DWAA_COMPRESSION; break; case IMWRITE_EXR_COMPRESSION_DWAB: header.compression() = DWAB_COMPRESSION; break; #endif default: CV_Error(Error::StsBadArg, "IMWRITE_EXR_COMPRESSION is invalid or not supported"); } } } if( channels == 3 || channels == 4 ) { header.channels().insert( "R", Channel( type ) ); header.channels().insert( "G", Channel( type ) ); header.channels().insert( "B", Channel( type ) ); //printf("bunt\n"); } else { header.channels().insert( "Y", Channel( type ) ); //printf("gray\n"); } if( channels % 2 == 0 ) { // even number of channels indicates Alpha header.channels().insert( "A", Channel( type ) ); } OutputFile file( m_filename.c_str(), header ); FrameBuffer frame; char *buffer; size_t bufferstep; int size; Mat exrMat; if( type == HALF ) { convertFp16(img, exrMat); buffer = (char *)const_cast( exrMat.ptr() ); bufferstep = exrMat.step; size = 2; } else { buffer = (char *)const_cast( img.ptr() ); bufferstep = img.step; size = 4; } if( channels == 3 || channels == 4 ) { frame.insert( "B", Slice( type, buffer, size * channels, bufferstep )); frame.insert( "G", Slice( type, buffer + size, size * channels, bufferstep )); frame.insert( "R", Slice( type, buffer + size * 2, size * channels, bufferstep )); } else frame.insert( "Y", Slice( type, buffer, size * channels, bufferstep )); if( channels % 2 == 0 ) { // even channel count indicates Alpha channel frame.insert( "A", Slice( type, buffer + size * (channels - 1), size * channels, bufferstep )); } file.setFrameBuffer( frame ); result = true; try { file.writePixels( height ); } catch(...) { result = false; } return result; } ImageEncoder ExrEncoder::newEncoder() const { return makePtr(); } } #endif /* End of file. */