/*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. // // // Intel License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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 "_highgui.h" #ifdef HAVE_ILMIMF #include <OpenEXR/ImfHeader.h> #include <OpenEXR/ImfInputFile.h> #include <OpenEXR/ImfOutputFile.h> #include <OpenEXR/ImfChannelList.h> #include <OpenEXR/ImfStandardAttributes.h> #include <OpenEXR/half.h> #include "grfmt_exr.h" #if defined _MSC_VER && _MSC_VER >= 1200 #pragma comment(lib, "Half.lib") #pragma comment(lib, "Iex.lib") #pragma comment(lib, "IlmImf.lib") #pragma comment(lib, "IlmThread.lib") #pragma comment(lib, "Imath.lib") #undef UINT #define UINT ((Imf::PixelType)0) #undef HALF #define HALF ((Imf::PixelType)1) #undef FLOAT #define FLOAT ((Imf::PixelType)2) #undef uint #define uint unsigned #endif // Exr Filter Factory GrFmtExr::GrFmtExr() { m_sign_len = 4; m_signature = "\x76\x2f\x31\x01"; m_description = "OpenEXR Image files (*.exr)"; } GrFmtExr::~GrFmtExr() { } GrFmtReader* GrFmtExr::NewReader( const char* filename ) { return new GrFmtExrReader( filename ); } GrFmtWriter* GrFmtExr::NewWriter( const char* filename ) { return new GrFmtExrWriter( filename ); } /////////////////////// GrFmtExrReader /////////////////// GrFmtExrReader::GrFmtExrReader( const char* filename ) : GrFmtReader( filename ) { m_file = new InputFile( filename ); m_red = m_green = m_blue = 0; } GrFmtExrReader::~GrFmtExrReader() { Close(); } void GrFmtExrReader::Close() { if( m_file ) { delete m_file; m_file = 0; } GrFmtReader::Close(); } bool GrFmtExrReader::ReadHeader() { bool result = false; 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" ); 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_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 ) { 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); } if( !result ) Close(); return result; } bool GrFmtExrReader::ReadData( uchar* data, int step, int color ) { bool justcopy = m_native_depth; bool chromatorgb = false; bool rgbtogray = false; bool result = true; FrameBuffer frame; int xsample[3] = {1, 1, 1}; char *buffer; int xstep; int ystep; xstep = m_native_depth ? 4 : 1; if( !m_native_depth || (!color && m_iscolor )) { buffer = (char *)new float[ m_width * 3 ]; ystep = 0; } else { buffer = (char *)data; ystep = step; } if( m_ischroma ) { if( color ) { if( m_iscolor ) { 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; } 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; } } else { frame.insert( "Y", Slice( m_type, buffer - m_datawindow.min.x * 4 - m_datawindow.min.y * ystep, 4, ystep, m_green->xSampling, m_green->ySampling, 0.0 )); xsample[0] = m_green->ySampling; } } else { if( m_blue ) { frame.insert( "B", 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( "G", 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( "R", 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; } if(color == 0) { rgbtogray = true; justcopy = false; } } m_file->setFrameBuffer( frame ); if( justcopy ) { m_file->readPixels( m_datawindow.min.y, m_datawindow.max.y ); if( color ) { if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) UpSample( data, 3, step / xstep, xsample[0], m_blue->ySampling ); if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) UpSample( data + xstep, 3, step / xstep, xsample[1], m_green->ySampling ); if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) UpSample( data + 2 * xstep, 3, step / xstep, xsample[2], m_red->ySampling ); } else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) UpSample( data, 1, step / xstep, xsample[0], m_green->ySampling ); } else { uchar *out = data; int x, y; for( y = m_datawindow.min.y; y <= m_datawindow.max.y; y++ ) { m_file->readPixels( y, y ); 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++) { int t = cvRound(fi[x]*5); out[x] = CV_CAST_8U(t); } } else { uint *ui = (uint *)buffer; for( x = 0; x < m_width * 3; x++) { uint t = ui[x]; out[x] = CV_CAST_8U(t); } } } out += step; } if( color ) { if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) ) UpSampleY( data, 3, step / xstep, m_blue->ySampling ); if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) ) UpSampleY( data + xstep, 3, step / xstep, m_green->ySampling ); if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) ) UpSampleY( data + 2 * xstep, 3, 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 ); } if( chromatorgb ) ChromaToBGR( (float *)data, m_height, step / xstep ); 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 GrFmtExrReader::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 ((uint *)data)[(yre + i) * ystep + (xre + n) * xstep] = ((uint *)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 GrFmtExrReader::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 ((uint *)data)[(xre + n) * xstep] = ((uint *)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 GrFmtExrReader::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 ((uint *)data)[(yre + i) * ystep + x * xstep] = ((uint *)data)[y * ystep + x * xstep]; } } } } /** // algorithm from ImfRgbaYca.cpp */ void GrFmtExrReader::ChromaToBGR( float *data, int numlines, int step ) { int x, y, t; for( y = 0; y < numlines; y++ ) { for( 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 ) { b = data[y * step + x * 3]; Y = data[y * step + x * 3 + 1]; r = data[y * step + x * 3 + 2]; } else { b = ((uint *)data)[y * step + x * 3]; Y = ((uint *)data)[y * step + x * 3 + 1]; r = ((uint *)data)[y * step + x * 3 + 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_native_depth ) { int t = cvRound(b); ((uchar *)data)[y * step + x * 3] = CV_CAST_8U(t); t = cvRound(Y); ((uchar *)data)[y * step + x * 3 + 1] = CV_CAST_8U(t); t = cvRound(r); ((uchar *)data)[y * step + x * 3 + 2] = CV_CAST_8U(t); } else if( m_type == FLOAT ) { data[y * step + x * 3] = (float)b; data[y * step + x * 3 + 1] = (float)Y; data[y * step + x * 3 + 2] = (float)r; } else { int t = cvRound(b); ((uint *)data)[y * step + x * 3] = (uint)MAX(t,0); t = cvRound(Y); ((uint *)data)[y * step + x * 3 + 1] = (uint)MAX(t,0); t = cvRound(r); ((uint *)data)[y * step + x * 3 + 2] = (uint)MAX(t,0); } } } } /** // convert one row to gray */ void GrFmtExrReader::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 ) { uint *ui = (uint *)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? { uint *ui = (uint *)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)); } } } /////////////////////// GrFmtExrWriter /////////////////// GrFmtExrWriter::GrFmtExrWriter( const char* filename ) : GrFmtWriter( filename ) { } GrFmtExrWriter::~GrFmtExrWriter() { } bool GrFmtExrWriter::IsFormatSupported( int depth ) { return depth == IPL_DEPTH_8U || depth == IPL_DEPTH_8S || depth == IPL_DEPTH_16U || depth == IPL_DEPTH_16S || depth == IPL_DEPTH_32S || depth == IPL_DEPTH_32F; // TODO: do (or should) we support 64f? } // TODO scale appropriately bool GrFmtExrWriter::WriteImage( const uchar* data, int step, int width, int height, int depth, int channels ) { bool result = false; Header header( width, height ); PixelType type; bool issigned = depth < 0; bool isfloat = depth == IPL_DEPTH_32F || depth == IPL_DEPTH_64F; if(depth == IPL_DEPTH_8U || depth == IPL_DEPTH_8S) type = HALF; else if(isfloat) type = FLOAT; else type = UINT; depth &= 255; if( channels == 3 ) { 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"); } OutputFile file( m_filename, header ); FrameBuffer frame; char *buffer; int bufferstep; int size; if( type == FLOAT && depth == 32 ) { buffer = (char *)const_cast<uchar *>(data); bufferstep = step; size = 4; } else if( depth > 16 || type == UINT ) { buffer = (char *)new uint[width * channels]; bufferstep = 0; size = 4; } else { buffer = (char *)new half[width * channels]; bufferstep = 0; size = 2; } //printf("depth %d %s\n", depth, types[type]); if( channels == 3 ) { frame.insert( "B", Slice( type, buffer, size * 3, bufferstep )); frame.insert( "G", Slice( type, buffer + size, size * 3, bufferstep )); frame.insert( "R", Slice( type, buffer + size * 2, size * 3, bufferstep )); } else frame.insert( "Y", Slice( type, buffer, size, bufferstep )); file.setFrameBuffer( frame ); int offset = issigned ? 1 << (depth - 1) : 0; result = true; if( type == FLOAT && depth == 32 ) { try { file.writePixels( height ); } catch(...) { result = false; } } else { // int scale = 1 << (32 - depth); // printf("scale %d\n", scale); for(int line = 0; line < height; line++) { if(type == UINT) { uint *buf = (uint *)buffer; // FIXME 64-bit problems if( depth <= 8 ) { for(int i = 0; i < width * channels; i++) buf[i] = data[i] + offset; } else if( depth <= 16 ) { unsigned short *sd = (unsigned short *)data; for(int i = 0; i < width * channels; i++) buf[i] = sd[i] + offset; } else { int *sd = (int *)data; // FIXME 64-bit problems for(int i = 0; i < width * channels; i++) buf[i] = (uint) sd[i] + offset; } } else { half *buf = (half *)buffer; if( depth <= 8 ) { for(int i = 0; i < width * channels; i++) buf[i] = data[i]; } else if( depth <= 16 ) { unsigned short *sd = (unsigned short *)data; for(int i = 0; i < width * channels; i++) buf[i] = sd[i]; } } try { file.writePixels( 1 ); } catch(...) { result = false; break; } data += step; } delete buffer; } return result; } #endif /* End of file. */