From 6df8ac03422784a94f5ccf78e4c68788ef055e7b Mon Sep 17 00:00:00 2001 From: zhijackchen <31141072+zhijackchen@users.noreply.github.com> Date: Fri, 15 Dec 2017 01:23:44 -0800 Subject: [PATCH] Merge pull request #10283 from zhijackchen:exr_export * Fix issue #10114 Convert table change From: CV_8U -> HALF CV_8S -> HALF CV_16U -> UINT CV_16S -> UINT CV_32S -> UINT CV_32F -> FLOAT To: CV_8U -> HALF CV_8S -> HALF CV_16U -> UINT CV_16S -> FLOAT CV_32S -> FLOAT loss precision CV_32F -> FLOAT Signed integer can't be presented well with UINT. Even adjust bias, CV16S and CV32S will be confused when load from exr file. Also fix CV_8S negative value incorrect bug * EXR import and export imread() from EXR returns CV_32F only imwrite() accepts CV_32 cv::Mat only and stores FLOAT images by default. Add imwrite() flag to store in HALF format. * fix compiling error * clean up * fix EXR import issues --- modules/imgcodecs/include/opencv2/imgcodecs.hpp | 7 + .../include/opencv2/imgcodecs/imgcodecs_c.h | 1 + modules/imgcodecs/src/grfmt_exr.cpp | 314 ++++++++------------- 3 files changed, 125 insertions(+), 197 deletions(-) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 46bb355..567344d 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -89,10 +89,17 @@ enum ImwriteFlags { IMWRITE_PNG_STRATEGY = 17, //!< One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_RLE. IMWRITE_PNG_BILEVEL = 18, //!< Binary level PNG, 0 or 1, default is 0. IMWRITE_PXM_BINARY = 32, //!< For PPM, PGM, or PBM, it can be a binary format flag, 0 or 1. Default value is 1. + IMWRITE_EXR_TYPE = (3 << 4) + 0, /* 48 */ //!< override EXR storage type (FLOAT (FP32) is default) IMWRITE_WEBP_QUALITY = 64, //!< For WEBP, it can be a quality from 1 to 100 (the higher is the better). By default (without any parameter) and for quality above 100 the lossless compression is used. IMWRITE_PAM_TUPLETYPE = 128,//!< For PAM, sets the TUPLETYPE field to the corresponding string value that is defined for the format }; +enum ImwriteEXRTypeFlags { + /*IMWRITE_EXR_TYPE_UNIT = 0, //!< not supported */ + IMWRITE_EXR_TYPE_HALF = 1, //!< store as HALF (FP16) + IMWRITE_EXR_TYPE_FLOAT = 2 //!< store as FP32 (default) + }; + //! Imwrite PNG specific flags used to tune the compression algorithm. /** These flags will be modify the way of PNG image compression and will be passed to the underlying zlib processing stage. diff --git a/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h b/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h index 3130710..c36dac3 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h +++ b/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h @@ -94,6 +94,7 @@ enum CV_IMWRITE_PNG_STRATEGY_RLE =3, CV_IMWRITE_PNG_STRATEGY_FIXED =4, CV_IMWRITE_PXM_BINARY =32, + CV_IMWRITE_EXR_TYPE = 48, CV_IMWRITE_WEBP_QUALITY =64, CV_IMWRITE_PAM_TUPLETYPE = 128, CV_IMWRITE_PAM_FORMAT_NULL = 0, diff --git a/modules/imgcodecs/src/grfmt_exr.cpp b/modules/imgcodecs/src/grfmt_exr.cpp index 78ffe6c..243023e 100644 --- a/modules/imgcodecs/src/grfmt_exr.cpp +++ b/modules/imgcodecs/src/grfmt_exr.cpp @@ -52,6 +52,10 @@ # pragma GCC diagnostic ignored "-Wshadow" #endif +/// C++ Standard Libraries +#include +#include + #include #include #include @@ -160,26 +164,8 @@ bool ExrDecoder::readHeader() if( result ) { - int uintcnt = 0; - int chcnt = 0; - if( m_red ) - { - chcnt++; - uintcnt += ( m_red->type == UINT ); - } - if( m_green ) - { - chcnt++; - uintcnt += ( m_green->type == UINT ); - } - if( m_blue ) - { - chcnt++; - uintcnt += ( m_blue->type == UINT ); - } - m_type = (chcnt == uintcnt) ? UINT : FLOAT; - - m_isfloat = (m_type == FLOAT); + m_type = FLOAT; + m_isfloat = ( m_type == FLOAT ); } if( !result ) @@ -193,12 +179,12 @@ bool ExrDecoder::readData( Mat& img ) { m_native_depth = CV_MAT_DEPTH(type()) == img.depth(); bool color = img.channels() > 1; - + int channels = 0; uchar* data = img.ptr(); size_t step = img.step; - bool justcopy = m_native_depth; - bool chromatorgb = false; - bool rgbtogray = false; + 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; int xsample[3] = {1, 1, 1}; @@ -210,7 +196,7 @@ bool ExrDecoder::readData( Mat& img ) AutoBuffer copy_buffer; - if( !m_native_depth || (!color && m_iscolor )) + if( !justcopy ) { copy_buffer.allocate(sizeof(float) * m_width * 3); buffer = copy_buffer; @@ -226,45 +212,44 @@ bool ExrDecoder::readData( Mat& img ) { if( color ) { - if( m_iscolor ) + if( m_blue ) { - if( m_blue ) - { - frame.insert( "BY", Slice( m_type, - buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep, - 12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 )); - xsample[0] = m_blue->ySampling; - } - if( m_green ) - { - frame.insert( "Y", Slice( m_type, - buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4, - 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); - xsample[1] = m_green->ySampling; - } - if( m_red ) - { - frame.insert( "RY", Slice( m_type, - buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8, - 12, ystep, m_red->xSampling, m_red->ySampling, 0.0 )); - xsample[2] = m_red->ySampling; - } - chromatorgb = true; + frame.insert( "BY", Slice( m_type, + buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep, + 12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 )); + xsample[0] = m_blue->ySampling; } else { + frame.insert( "BY", Slice( m_type, + buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep, + 12, ystep, 1, 1, 0.0 )); + } + if( m_green ) + { frame.insert( "Y", Slice( m_type, - buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep, - 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); - frame.insert( "Y", Slice( m_type, - buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4, - 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); - frame.insert( "Y", Slice( m_type, - buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8, - 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); - xsample[0] = m_green->ySampling; + buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4, + 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); xsample[1] = m_green->ySampling; - xsample[2] = m_green->ySampling; + } + else + { + frame.insert( "Y", Slice( m_type, + buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4, + 12, ystep, 1, 1, 0.0 )); + } + if( m_red ) + { + frame.insert( "RY", Slice( m_type, + buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8, + 12, ystep, m_red->xSampling, m_red->ySampling, 0.0 )); + xsample[2] = m_red->ySampling; + } + else + { + frame.insert( "RY", Slice( m_type, + buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8, + 12, ystep, 1, 1, 0.0 )); } } else @@ -284,6 +269,12 @@ bool ExrDecoder::readData( Mat& img ) 12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 )); xsample[0] = m_blue->ySampling; } + else + { + frame.insert( "B", Slice( m_type, + buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep, + 12, ystep, 1, 1, 0.0 )); + } if( m_green ) { frame.insert( "G", Slice( m_type, @@ -291,6 +282,12 @@ bool ExrDecoder::readData( Mat& img ) 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); xsample[1] = m_green->ySampling; } + else + { + frame.insert( "G", Slice( m_type, + buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4, + 12, ystep, 1, 1, 0.0 )); + } if( m_red ) { frame.insert( "R", Slice( m_type, @@ -298,13 +295,18 @@ bool ExrDecoder::readData( Mat& img ) 12, ystep, m_red->xSampling, m_red->ySampling, 0.0 )); xsample[2] = m_red->ySampling; } - if(color == 0) + else { - rgbtogray = true; - justcopy = false; + frame.insert( "R", Slice( m_type, + buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8, + 12, ystep, 1, 1, 0.0 )); } } + for (FrameBuffer::Iterator it = frame.begin(); it != frame.end(); it++) { + channels++; + } + m_file->setFrameBuffer( frame ); if( justcopy ) { @@ -321,6 +323,9 @@ bool ExrDecoder::readData( Mat& img ) } else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) UpSample( data, 1, step / xstep, xsample[0], m_green->ySampling ); + + if( chromatorgb ) + ChromaToBGR( (float *)data, m_height, step / xstep ); } else { @@ -330,41 +335,32 @@ bool ExrDecoder::readData( Mat& img ) { 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 ) { - if( xsample[0] != 1 ) - UpSampleX( (float *)buffer, 3, xsample[0] ); - if( xsample[1] != 1 ) - UpSampleX( (float *)buffer + 4, 3, xsample[1] ); - if( xsample[2] != 1 ) - UpSampleX( (float *)buffer + 8, 3, xsample[2] ); - RGBToGray( (float *)buffer, (float *)out ); } else { - if( xsample[0] != 1 ) - UpSampleX( (float *)buffer, 3, xsample[0] ); - if( xsample[1] != 1 ) - UpSampleX( (float *)(buffer + 4), 3, xsample[1] ); - if( xsample[2] != 1 ) - UpSampleX( (float *)(buffer + 8), 3, xsample[2] ); - if( chromatorgb ) ChromaToBGR( (float *)buffer, 1, step ); if( m_type == FLOAT ) { float *fi = (float *)buffer; - for( x = 0; x < m_width * 3; x++) + for( x = 0; x < m_width * img.channels(); x++) { - out[x] = cv::saturate_cast(fi[x]*5); + out[x] = cv::saturate_cast(fi[x]); } } else { unsigned *ui = (unsigned *)buffer; - for( x = 0; x < m_width * 3; x++) + for( x = 0; x < m_width * img.channels(); x++) { out[x] = cv::saturate_cast(ui[x]); } @@ -386,9 +382,6 @@ bool ExrDecoder::readData( Mat& img ) UpSampleY( data, 1, step / xstep, m_green->ySampling ); } - if( chromatorgb ) - ChromaToBGR( (float *)data, m_height, step / xstep ); - close(); return result; @@ -471,13 +464,7 @@ void ExrDecoder::ChromaToBGR( float *data, int numlines, int step ) for( int x = 0; x < m_width; x++ ) { double b, Y, r; - if( !m_native_depth ) - { - b = ((uchar *)data)[y * step + x * 3]; - Y = ((uchar *)data)[y * step + x * 3 + 1]; - r = ((uchar *)data)[y * step + x * 3 + 2]; - } - else if( m_type == FLOAT ) + if( m_type == FLOAT ) { b = data[y * step + x * 3]; Y = data[y * step + x * 3 + 1]; @@ -493,13 +480,7 @@ void ExrDecoder::ChromaToBGR( float *data, int numlines, int step ) b = (b + 1) * Y; Y = (Y - b * m_chroma.blue[1] - r * m_chroma.red[1]) / m_chroma.green[1]; - if( !m_native_depth ) - { - ((uchar *)data)[y * step + x * 3 + 0] = cv::saturate_cast(b); - ((uchar *)data)[y * step + x * 3 + 1] = cv::saturate_cast(Y); - ((uchar *)data)[y * step + x * 3 + 2] = cv::saturate_cast(r); - } - else if( m_type == FLOAT ) + if( m_type == FLOAT ) { data[y * step + x * 3] = (float)b; data[y * step + x * 3 + 1] = (float)Y; @@ -580,41 +561,49 @@ ExrEncoder::~ExrEncoder() bool ExrEncoder::isFormatSupported( int depth ) const { - return CV_MAT_DEPTH(depth) >= CV_8U && CV_MAT_DEPTH(depth) < CV_64F; + return ( CV_MAT_DEPTH(depth) == CV_32F ); } -// TODO scale appropriately -bool ExrEncoder::write( const Mat& img, const std::vector& ) +bool ExrEncoder::write( const Mat& img, const std::vector& params ) { int width = img.cols, height = img.rows; - int depth = img.depth(), channels = img.channels(); + int depth = img.depth(); + CV_Assert( depth == CV_32F ); + int channels = img.channels(); + CV_Assert( channels == 3 || channels == 1 ); bool result = false; - bool issigned = depth == CV_8S || depth == CV_16S || depth == CV_32S; - bool isfloat = depth == CV_32F || depth == CV_64F; - depth = CV_ELEM_SIZE1(depth)*8; - const size_t step = img.step; - Header header( width, height ); - Imf::PixelType type; + Imf::PixelType type = FLOAT; - if(depth == 8) - type = HALF; - else if(isfloat) - type = FLOAT; - else - type = UINT; + 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: + throw std::runtime_error( "IMWRITE_EXR_TYPE is invalid or not supported" ); + } + } + } if( channels == 3 ) { - header.channels().insert( "R", Channel( type )); - header.channels().insert( "G", Channel( type )); - header.channels().insert( "B", Channel( type )); + 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 )); + header.channels().insert( "Y", Channel( type ) ); //printf("gray\n"); } @@ -625,27 +614,21 @@ bool ExrEncoder::write( const Mat& img, const std::vector& ) char *buffer; size_t bufferstep; int size; - if( type == FLOAT && depth == 32 ) + Mat exrMat; + if( type == HALF ) { - buffer = (char *)const_cast(img.ptr()); - bufferstep = step; - size = 4; - } - else if( depth > 16 || type == UINT ) - { - buffer = (char *)new unsigned[width * channels]; - bufferstep = 0; - size = 4; + convertFp16(img, exrMat); + buffer = (char *)const_cast( exrMat.ptr() ); + bufferstep = exrMat.step; + size = 2; } else { - buffer = (char *)new half[width * channels]; - bufferstep = 0; - size = 2; + buffer = (char *)const_cast( img.ptr() ); + bufferstep = img.step; + size = 4; } - //printf("depth %d %s\n", depth, types[type]); - if( channels == 3 ) { frame.insert( "B", Slice( type, buffer, size * 3, bufferstep )); @@ -657,77 +640,14 @@ bool ExrEncoder::write( const Mat& img, const std::vector& ) file.setFrameBuffer( frame ); - int offset = issigned ? 1 << (depth - 1) : 0; - result = true; - if( type == FLOAT && depth == 32 ) + try { - try - { - file.writePixels( height ); - } - catch(...) - { - result = false; - } + file.writePixels( height ); } - else + catch(...) { - // int scale = 1 << (32 - depth); - // printf("scale %d\n", scale); - for(int line = 0; line < height; line++) - { - if(type == UINT) - { - unsigned *buf = (unsigned*)buffer; // FIXME 64-bit problems - - if( depth <= 8 ) - { - const uchar* sd = img.ptr(line); - for(int i = 0; i < width * channels; i++) - buf[i] = sd[i] + offset; - } - else if( depth <= 16 ) - { - const unsigned short *sd = img.ptr(line); - for(int i = 0; i < width * channels; i++) - buf[i] = sd[i] + offset; - } - else - { - const int *sd = img.ptr(line); // FIXME 64-bit problems - for(int i = 0; i < width * channels; i++) - buf[i] = (unsigned) sd[i] + offset; - } - } - else - { - half *buf = (half *)buffer; - - if( depth <= 8 ) - { - const uchar* sd = img.ptr(line); - for(int i = 0; i < width * channels; i++) - buf[i] = sd[i]; - } - else if( depth <= 16 ) - { - const unsigned short *sd = img.ptr(line); - for(int i = 0; i < width * channels; i++) - buf[i] = sd[i]; - } - } - try - { - file.writePixels( 1 ); - } - catch(...) - { - result = false; - break; - } - } - delete[] buffer; + result = false; } return result; -- 2.7.4