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
pull/9767/merge
zhijackchen 7 years ago committed by Alexander Alekhin
parent 18ff806d5b
commit 6df8ac0342
  1. 7
      modules/imgcodecs/include/opencv2/imgcodecs.hpp
  2. 1
      modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h
  3. 314
      modules/imgcodecs/src/grfmt_exr.cpp

@ -89,10 +89,17 @@ enum ImwriteFlags {
IMWRITE_PNG_STRATEGY = 17, //!< One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_RLE. 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_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_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_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 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. //! 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. /** These flags will be modify the way of PNG image compression and will be passed to the underlying zlib processing stage.

@ -94,6 +94,7 @@ enum
CV_IMWRITE_PNG_STRATEGY_RLE =3, CV_IMWRITE_PNG_STRATEGY_RLE =3,
CV_IMWRITE_PNG_STRATEGY_FIXED =4, CV_IMWRITE_PNG_STRATEGY_FIXED =4,
CV_IMWRITE_PXM_BINARY =32, CV_IMWRITE_PXM_BINARY =32,
CV_IMWRITE_EXR_TYPE = 48,
CV_IMWRITE_WEBP_QUALITY =64, CV_IMWRITE_WEBP_QUALITY =64,
CV_IMWRITE_PAM_TUPLETYPE = 128, CV_IMWRITE_PAM_TUPLETYPE = 128,
CV_IMWRITE_PAM_FORMAT_NULL = 0, CV_IMWRITE_PAM_FORMAT_NULL = 0,

@ -52,6 +52,10 @@
# pragma GCC diagnostic ignored "-Wshadow" # pragma GCC diagnostic ignored "-Wshadow"
#endif #endif
/// C++ Standard Libraries
#include <iostream>
#include <stdexcept>
#include <ImfHeader.h> #include <ImfHeader.h>
#include <ImfInputFile.h> #include <ImfInputFile.h>
#include <ImfOutputFile.h> #include <ImfOutputFile.h>
@ -160,26 +164,8 @@ bool ExrDecoder::readHeader()
if( result ) if( result )
{ {
int uintcnt = 0; m_type = FLOAT;
int chcnt = 0; m_isfloat = ( m_type == FLOAT );
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);
} }
if( !result ) if( !result )
@ -193,12 +179,12 @@ bool ExrDecoder::readData( Mat& img )
{ {
m_native_depth = CV_MAT_DEPTH(type()) == img.depth(); m_native_depth = CV_MAT_DEPTH(type()) == img.depth();
bool color = img.channels() > 1; bool color = img.channels() > 1;
int channels = 0;
uchar* data = img.ptr(); uchar* data = img.ptr();
size_t step = img.step; size_t step = img.step;
bool justcopy = m_native_depth; bool justcopy = ( m_native_depth && (color == m_iscolor) );
bool chromatorgb = false; bool chromatorgb = ( m_ischroma && color );
bool rgbtogray = false; bool rgbtogray = ( !m_ischroma && m_iscolor && !color );
bool result = true; bool result = true;
FrameBuffer frame; FrameBuffer frame;
int xsample[3] = {1, 1, 1}; int xsample[3] = {1, 1, 1};
@ -210,7 +196,7 @@ bool ExrDecoder::readData( Mat& img )
AutoBuffer<char> copy_buffer; AutoBuffer<char> copy_buffer;
if( !m_native_depth || (!color && m_iscolor )) if( !justcopy )
{ {
copy_buffer.allocate(sizeof(float) * m_width * 3); copy_buffer.allocate(sizeof(float) * m_width * 3);
buffer = copy_buffer; buffer = copy_buffer;
@ -226,45 +212,44 @@ bool ExrDecoder::readData( Mat& img )
{ {
if( color ) 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,
frame.insert( "BY", Slice( m_type, 12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 ));
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep, xsample[0] = m_blue->ySampling;
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;
} }
else 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, frame.insert( "Y", Slice( m_type,
buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep, buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
12, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); 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;
xsample[1] = m_green->ySampling; 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 else
@ -284,6 +269,12 @@ bool ExrDecoder::readData( Mat& img )
12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 )); 12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 ));
xsample[0] = m_blue->ySampling; 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 ) if( m_green )
{ {
frame.insert( "G", Slice( m_type, 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 )); 12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
xsample[1] = m_green->ySampling; 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 ) if( m_red )
{ {
frame.insert( "R", Slice( m_type, 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 )); 12, ystep, m_red->xSampling, m_red->ySampling, 0.0 ));
xsample[2] = m_red->ySampling; xsample[2] = m_red->ySampling;
} }
if(color == 0) else
{ {
rgbtogray = true; frame.insert( "R", Slice( m_type,
justcopy = false; 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 ); m_file->setFrameBuffer( frame );
if( justcopy ) if( justcopy )
{ {
@ -321,6 +323,9 @@ bool ExrDecoder::readData( Mat& img )
} }
else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
UpSample( data, 1, step / xstep, xsample[0], m_green->ySampling ); UpSample( data, 1, step / xstep, xsample[0], m_green->ySampling );
if( chromatorgb )
ChromaToBGR( (float *)data, m_height, step / xstep );
} }
else else
{ {
@ -330,41 +335,32 @@ bool ExrDecoder::readData( Mat& img )
{ {
m_file->readPixels( 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 ) 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 ); RGBToGray( (float *)buffer, (float *)out );
} }
else 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 ) if( chromatorgb )
ChromaToBGR( (float *)buffer, 1, step ); ChromaToBGR( (float *)buffer, 1, step );
if( m_type == FLOAT ) if( m_type == FLOAT )
{ {
float *fi = (float *)buffer; 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<uchar>(fi[x]*5); out[x] = cv::saturate_cast<uchar>(fi[x]);
} }
} }
else else
{ {
unsigned *ui = (unsigned *)buffer; 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<uchar>(ui[x]); out[x] = cv::saturate_cast<uchar>(ui[x]);
} }
@ -386,9 +382,6 @@ bool ExrDecoder::readData( Mat& img )
UpSampleY( data, 1, step / xstep, m_green->ySampling ); UpSampleY( data, 1, step / xstep, m_green->ySampling );
} }
if( chromatorgb )
ChromaToBGR( (float *)data, m_height, step / xstep );
close(); close();
return result; return result;
@ -471,13 +464,7 @@ void ExrDecoder::ChromaToBGR( float *data, int numlines, int step )
for( int x = 0; x < m_width; x++ ) for( int x = 0; x < m_width; x++ )
{ {
double b, Y, r; double b, Y, r;
if( !m_native_depth ) if( m_type == FLOAT )
{
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 )
{ {
b = data[y * step + x * 3]; b = data[y * step + x * 3];
Y = data[y * step + x * 3 + 1]; Y = data[y * step + x * 3 + 1];
@ -493,13 +480,7 @@ void ExrDecoder::ChromaToBGR( float *data, int numlines, int step )
b = (b + 1) * Y; b = (b + 1) * Y;
Y = (Y - b * m_chroma.blue[1] - r * m_chroma.red[1]) / m_chroma.green[1]; Y = (Y - b * m_chroma.blue[1] - r * m_chroma.red[1]) / m_chroma.green[1];
if( !m_native_depth ) if( m_type == FLOAT )
{
((uchar *)data)[y * step + x * 3 + 0] = cv::saturate_cast<uchar>(b);
((uchar *)data)[y * step + x * 3 + 1] = cv::saturate_cast<uchar>(Y);
((uchar *)data)[y * step + x * 3 + 2] = cv::saturate_cast<uchar>(r);
}
else if( m_type == FLOAT )
{ {
data[y * step + x * 3] = (float)b; data[y * step + x * 3] = (float)b;
data[y * step + x * 3 + 1] = (float)Y; data[y * step + x * 3 + 1] = (float)Y;
@ -580,41 +561,49 @@ ExrEncoder::~ExrEncoder()
bool ExrEncoder::isFormatSupported( int depth ) const 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<int>& params )
bool ExrEncoder::write( const Mat& img, const std::vector<int>& )
{ {
int width = img.cols, height = img.rows; 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 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 ); Header header( width, height );
Imf::PixelType type; Imf::PixelType type = FLOAT;
if(depth == 8) for( size_t i = 0; i < params.size(); i += 2 )
type = HALF; {
else if(isfloat) if( params[i] == CV_IMWRITE_EXR_TYPE )
type = FLOAT; {
else switch( params[i+1] )
type = UINT; {
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 ) if( channels == 3 )
{ {
header.channels().insert( "R", Channel( type )); header.channels().insert( "R", Channel( type ) );
header.channels().insert( "G", Channel( type )); header.channels().insert( "G", Channel( type ) );
header.channels().insert( "B", Channel( type )); header.channels().insert( "B", Channel( type ) );
//printf("bunt\n"); //printf("bunt\n");
} }
else else
{ {
header.channels().insert( "Y", Channel( type )); header.channels().insert( "Y", Channel( type ) );
//printf("gray\n"); //printf("gray\n");
} }
@ -625,27 +614,21 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& )
char *buffer; char *buffer;
size_t bufferstep; size_t bufferstep;
int size; int size;
if( type == FLOAT && depth == 32 ) Mat exrMat;
if( type == HALF )
{ {
buffer = (char *)const_cast<uchar *>(img.ptr()); convertFp16(img, exrMat);
bufferstep = step; buffer = (char *)const_cast<uchar *>( exrMat.ptr() );
size = 4; bufferstep = exrMat.step;
} size = 2;
else if( depth > 16 || type == UINT )
{
buffer = (char *)new unsigned[width * channels];
bufferstep = 0;
size = 4;
} }
else else
{ {
buffer = (char *)new half[width * channels]; buffer = (char *)const_cast<uchar *>( img.ptr() );
bufferstep = 0; bufferstep = img.step;
size = 2; size = 4;
} }
//printf("depth %d %s\n", depth, types[type]);
if( channels == 3 ) if( channels == 3 )
{ {
frame.insert( "B", Slice( type, buffer, size * 3, bufferstep )); frame.insert( "B", Slice( type, buffer, size * 3, bufferstep ));
@ -657,77 +640,14 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& )
file.setFrameBuffer( frame ); file.setFrameBuffer( frame );
int offset = issigned ? 1 << (depth - 1) : 0;
result = true; result = true;
if( type == FLOAT && depth == 32 ) try
{ {
try file.writePixels( height );
{
file.writePixels( height );
}
catch(...)
{
result = false;
}
} }
else catch(...)
{ {
// int scale = 1 << (32 - depth); result = false;
// 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<unsigned short>(line);
for(int i = 0; i < width * channels; i++)
buf[i] = sd[i] + offset;
}
else
{
const int *sd = img.ptr<int>(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<unsigned short>(line);
for(int i = 0; i < width * channels; i++)
buf[i] = sd[i];
}
}
try
{
file.writePixels( 1 );
}
catch(...)
{
result = false;
break;
}
}
delete[] buffer;
} }
return result; return result;

Loading…
Cancel
Save