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
This commit is contained in:
zhijackchen 2017-12-15 01:23:44 -08:00 committed by Alexander Alekhin
parent 18ff806d5b
commit 6df8ac0342
3 changed files with 126 additions and 198 deletions

View File

@ -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.

View File

@ -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,

View File

@ -52,6 +52,10 @@
# pragma GCC diagnostic ignored "-Wshadow"
#endif
/// C++ Standard Libraries
#include <iostream>
#include <stdexcept>
#include <ImfHeader.h>
#include <ImfInputFile.h>
#include <ImfOutputFile.h>
@ -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<char> 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 + 4,
12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
xsample[1] = m_green->ySampling;
}
else
{
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;
xsample[1] = m_green->ySampling;
xsample[2] = m_green->ySampling;
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<uchar>(fi[x]*5);
out[x] = cv::saturate_cast<uchar>(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<uchar>(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<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 )
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<int>& )
bool ExrEncoder::write( const Mat& img, const std::vector<int>& 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<int>& )
char *buffer;
size_t bufferstep;
int size;
if( type == FLOAT && depth == 32 )
Mat exrMat;
if( type == HALF )
{
buffer = (char *)const_cast<uchar *>(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<uchar *>( exrMat.ptr() );
bufferstep = exrMat.step;
size = 2;
}
else
{
buffer = (char *)new half[width * channels];
bufferstep = 0;
size = 2;
buffer = (char *)const_cast<uchar *>( 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<int>& )
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<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;
result = false;
}
return result;