1/*M///////////////////////////////////////////////////////////////////////////////////////
2//
3//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4//
5//  By downloading, copying, installing or using the software you agree to this license.
6//  If you do not agree to this license, do not download, install,
7//  copy or use the software.
8//
9//
10//                        Intel License Agreement
11//                For Open Source Computer Vision Library
12//
13// Copyright (C) 2000, Intel Corporation, all rights reserved.
14// Third party copyrights are property of their respective owners.
15//
16// Redistribution and use in source and binary forms, with or without modification,
17// are permitted provided that the following conditions are met:
18//
19//   * Redistribution's of source code must retain the above copyright notice,
20//     this list of conditions and the following disclaimer.
21//
22//   * Redistribution's in binary form must reproduce the above copyright notice,
23//     this list of conditions and the following disclaimer in the documentation
24//     and/or other materials provided with the distribution.
25//
26//   * The name of Intel Corporation may not be used to endorse or promote products
27//     derived from this software without specific prior written permission.
28//
29// This software is provided by the copyright holders and contributors "as is" and
30// any express or implied warranties, including, but not limited to, the implied
31// warranties of merchantability and fitness for a particular purpose are disclaimed.
32// In no event shall the Intel Corporation or contributors be liable for any direct,
33// indirect, incidental, special, exemplary, or consequential damages
34// (including, but not limited to, procurement of substitute goods or services;
35// loss of use, data, or profits; or business interruption) however caused
36// and on any theory of liability, whether in contract, strict liability,
37// or tort (including negligence or otherwise) arising in any way out of
38// the use of this software, even if advised of the possibility of such damage.
39//
40//M*/
41
42#include "_highgui.h"
43
44#ifdef HAVE_ILMIMF
45
46#include <OpenEXR/ImfHeader.h>
47#include <OpenEXR/ImfInputFile.h>
48#include <OpenEXR/ImfOutputFile.h>
49#include <OpenEXR/ImfChannelList.h>
50#include <OpenEXR/ImfStandardAttributes.h>
51#include <OpenEXR/half.h>
52#include "grfmt_exr.h"
53
54#if defined _MSC_VER && _MSC_VER >= 1200
55#pragma comment(lib, "Half.lib")
56#pragma comment(lib, "Iex.lib")
57#pragma comment(lib, "IlmImf.lib")
58#pragma comment(lib, "IlmThread.lib")
59#pragma comment(lib, "Imath.lib")
60
61#undef UINT
62#define UINT ((Imf::PixelType)0)
63#undef HALF
64#define HALF ((Imf::PixelType)1)
65#undef FLOAT
66#define FLOAT ((Imf::PixelType)2)
67#undef uint
68#define uint unsigned
69
70#endif
71
72// Exr Filter Factory
73GrFmtExr::GrFmtExr()
74{
75    m_sign_len = 4;
76    m_signature = "\x76\x2f\x31\x01";
77    m_description = "OpenEXR Image files (*.exr)";
78}
79
80
81GrFmtExr::~GrFmtExr()
82{
83}
84
85
86GrFmtReader* GrFmtExr::NewReader( const char* filename )
87{
88    return new GrFmtExrReader( filename );
89}
90
91
92GrFmtWriter* GrFmtExr::NewWriter( const char* filename )
93{
94    return new GrFmtExrWriter( filename );
95}
96
97
98/////////////////////// GrFmtExrReader ///////////////////
99
100GrFmtExrReader::GrFmtExrReader( const char* filename ) : GrFmtReader( filename )
101{
102    m_file = new InputFile( filename );
103    m_red = m_green = m_blue = 0;
104}
105
106
107GrFmtExrReader::~GrFmtExrReader()
108{
109    Close();
110}
111
112
113void  GrFmtExrReader::Close()
114{
115    if( m_file )
116    {
117        delete m_file;
118        m_file = 0;
119    }
120
121    GrFmtReader::Close();
122}
123
124bool  GrFmtExrReader::ReadHeader()
125{
126    bool result = false;
127
128    if( !m_file ) // probably paranoid
129        return false;
130
131    m_datawindow = m_file->header().dataWindow();
132    m_width = m_datawindow.max.x - m_datawindow.min.x + 1;
133    m_height = m_datawindow.max.y - m_datawindow.min.y + 1;
134
135    // the type HALF is converted to 32 bit float
136    // and the other types supported by OpenEXR are 32 bit anyway
137    m_bit_depth = 32;
138
139    if( hasChromaticities( m_file->header() ))
140        m_chroma = chromaticities( m_file->header() );
141
142    const ChannelList &channels = m_file->header().channels();
143    m_red = channels.findChannel( "R" );
144    m_green = channels.findChannel( "G" );
145    m_blue = channels.findChannel( "B" );
146    if( m_red || m_green || m_blue )
147    {
148        m_iscolor = true;
149        m_ischroma = false;
150        result = true;
151    }
152    else
153    {
154        m_green = channels.findChannel( "Y" );
155        if( m_green )
156        {
157            m_ischroma = true;
158            m_red = channels.findChannel( "RY" );
159            m_blue = channels.findChannel( "BY" );
160            m_iscolor = (m_blue || m_red);
161            result = true;
162        }
163        else
164            result = false;
165    }
166
167    if( result )
168    {
169        int uintcnt = 0;
170        int chcnt = 0;
171        if( m_red )
172        {
173            chcnt++;
174            uintcnt += ( m_red->type == UINT );
175        }
176        if( m_green )
177        {
178            chcnt++;
179            uintcnt += ( m_green->type == UINT );
180        }
181        if( m_blue )
182        {
183            chcnt++;
184            uintcnt += ( m_blue->type == UINT );
185        }
186        m_type = (chcnt == uintcnt) ? UINT : FLOAT;
187
188        m_isfloat = (m_type == FLOAT);
189    }
190
191    if( !result )
192        Close();
193
194    return result;
195}
196
197
198bool  GrFmtExrReader::ReadData( uchar* data, int step, int color )
199{
200    bool justcopy = m_native_depth;
201    bool chromatorgb = false;
202    bool rgbtogray = false;
203    bool result = true;
204    FrameBuffer frame;
205    int xsample[3] = {1, 1, 1};
206    char *buffer;
207    int xstep;
208    int ystep;
209
210    xstep = m_native_depth ? 4 : 1;
211
212    if( !m_native_depth || (!color && m_iscolor ))
213    {
214        buffer = (char *)new float[ m_width * 3 ];
215        ystep = 0;
216    }
217    else
218    {
219        buffer = (char *)data;
220        ystep = step;
221    }
222
223    if( m_ischroma )
224    {
225        if( color )
226        {
227            if( m_iscolor )
228            {
229                if( m_blue )
230                {
231                    frame.insert( "BY", Slice( m_type,
232                                    buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep,
233                                    12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 ));
234                    xsample[0] = m_blue->ySampling;
235                }
236                if( m_green )
237                {
238                    frame.insert( "Y", Slice( m_type,
239                                    buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
240                                    12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
241                    xsample[1] = m_green->ySampling;
242                }
243                if( m_red )
244                {
245                    frame.insert( "RY", Slice( m_type,
246                                    buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8,
247                                    12, ystep, m_red->xSampling, m_red->ySampling, 0.0 ));
248                    xsample[2] = m_red->ySampling;
249                }
250                chromatorgb = true;
251            }
252            else
253            {
254                frame.insert( "Y", Slice( m_type,
255                              buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep,
256                              12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
257                frame.insert( "Y", Slice( m_type,
258                              buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
259                              12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
260                frame.insert( "Y", Slice( m_type,
261                              buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8,
262                              12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
263                xsample[0] = m_green->ySampling;
264                xsample[1] = m_green->ySampling;
265                xsample[2] = m_green->ySampling;
266            }
267        }
268        else
269        {
270            frame.insert( "Y", Slice( m_type,
271                            buffer - m_datawindow.min.x * 4 - m_datawindow.min.y * ystep,
272                            4, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
273            xsample[0] = m_green->ySampling;
274        }
275    }
276    else
277    {
278        if( m_blue )
279        {
280            frame.insert( "B", Slice( m_type,
281                            buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep,
282                            12, ystep, m_blue->xSampling, m_blue->ySampling, 0.0 ));
283            xsample[0] = m_blue->ySampling;
284        }
285        if( m_green )
286        {
287            frame.insert( "G", Slice( m_type,
288                            buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 4,
289                            12, ystep, m_green->xSampling, m_green->ySampling, 0.0 ));
290            xsample[1] = m_green->ySampling;
291        }
292        if( m_red )
293        {
294            frame.insert( "R", Slice( m_type,
295                            buffer - m_datawindow.min.x * 12 - m_datawindow.min.y * ystep + 8,
296                            12, ystep, m_red->xSampling, m_red->ySampling, 0.0 ));
297            xsample[2] = m_red->ySampling;
298        }
299        if(color == 0)
300        {
301            rgbtogray = true;
302            justcopy = false;
303        }
304    }
305
306    m_file->setFrameBuffer( frame );
307    if( justcopy )
308    {
309        m_file->readPixels( m_datawindow.min.y, m_datawindow.max.y );
310
311        if( color )
312        {
313            if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
314                UpSample( data, 3, step / xstep, xsample[0], m_blue->ySampling );
315            if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
316                UpSample( data + xstep, 3, step / xstep, xsample[1], m_green->ySampling );
317            if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
318                UpSample( data + 2 * xstep, 3, step / xstep, xsample[2], m_red->ySampling );
319        }
320        else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
321            UpSample( data, 1, step / xstep, xsample[0], m_green->ySampling );
322    }
323    else
324    {
325        uchar *out = data;
326        int x, y;
327        for( y = m_datawindow.min.y; y <= m_datawindow.max.y; y++ )
328        {
329            m_file->readPixels( y, y );
330
331            if( rgbtogray )
332            {
333                if( xsample[0] != 1 )
334                    UpSampleX( (float *)buffer, 3, xsample[0] );
335                if( xsample[1] != 1 )
336                    UpSampleX( (float *)buffer + 4, 3, xsample[1] );
337                if( xsample[2] != 1 )
338                    UpSampleX( (float *)buffer + 8, 3, xsample[2] );
339
340                RGBToGray( (float *)buffer, (float *)out );
341            }
342            else
343            {
344                if( xsample[0] != 1 )
345                    UpSampleX( (float *)buffer, 3, xsample[0] );
346                if( xsample[1] != 1 )
347                    UpSampleX( (float *)(buffer + 4), 3, xsample[1] );
348                if( xsample[2] != 1 )
349                    UpSampleX( (float *)(buffer + 8), 3, xsample[2] );
350
351                if( chromatorgb )
352                    ChromaToBGR( (float *)buffer, 1, step );
353
354                if( m_type == FLOAT )
355                {
356                    float *fi = (float *)buffer;
357                    for( x = 0; x < m_width * 3; x++)
358                    {
359                        int t = cvRound(fi[x]*5);
360                        out[x] = CV_CAST_8U(t);
361                    }
362                }
363                else
364                {
365                    uint *ui = (uint *)buffer;
366                    for( x = 0; x < m_width * 3; x++)
367                    {
368                        uint t = ui[x];
369                        out[x] = CV_CAST_8U(t);
370                    }
371                }
372            }
373
374            out += step;
375        }
376        if( color )
377        {
378            if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
379                UpSampleY( data, 3, step / xstep, m_blue->ySampling );
380            if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
381                UpSampleY( data + xstep, 3, step / xstep, m_green->ySampling );
382            if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
383                UpSampleY( data + 2 * xstep, 3, step / xstep, m_red->ySampling );
384        }
385        else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
386            UpSampleY( data, 1, step / xstep, m_green->ySampling );
387    }
388
389    if( chromatorgb )
390        ChromaToBGR( (float *)data, m_height, step / xstep );
391
392    Close();
393
394    return result;
395}
396
397/**
398// on entry pixel values are stored packed in the upper left corner of the image
399// this functions expands them by duplication to cover the whole image
400 */
401void  GrFmtExrReader::UpSample( uchar *data, int xstep, int ystep, int xsample, int ysample )
402{
403    for( int y = (m_height - 1) / ysample, yre = m_height - ysample; y >= 0; y--, yre -= ysample )
404    {
405        for( int x = (m_width - 1) / xsample, xre = m_width - xsample; x >= 0; x--, xre -= xsample )
406        {
407            for( int i = 0; i < ysample; i++ )
408            {
409                for( int n = 0; n < xsample; n++ )
410                {
411                    if( !m_native_depth )
412                        data[(yre + i) * ystep + (xre + n) * xstep] = data[y * ystep + x * xstep];
413                    else if( m_type == FLOAT )
414                        ((float *)data)[(yre + i) * ystep + (xre + n) * xstep] = ((float *)data)[y * ystep + x * xstep];
415                    else
416                        ((uint *)data)[(yre + i) * ystep + (xre + n) * xstep] = ((uint *)data)[y * ystep + x * xstep];
417                }
418            }
419        }
420    }
421}
422
423/**
424// on entry pixel values are stored packed in the upper left corner of the image
425// this functions expands them by duplication to cover the whole image
426 */
427void  GrFmtExrReader::UpSampleX( float *data, int xstep, int xsample )
428{
429    for( int x = (m_width - 1) / xsample, xre = m_width - xsample; x >= 0; x--, xre -= xsample )
430    {
431        for( int n = 0; n < xsample; n++ )
432        {
433            if( m_type == FLOAT )
434                ((float *)data)[(xre + n) * xstep] = ((float *)data)[x * xstep];
435            else
436                ((uint *)data)[(xre + n) * xstep] = ((uint *)data)[x * xstep];
437        }
438    }
439}
440
441/**
442// on entry pixel values are stored packed in the upper left corner of the image
443// this functions expands them by duplication to cover the whole image
444 */
445void  GrFmtExrReader::UpSampleY( uchar *data, int xstep, int ystep, int ysample )
446{
447    for( int y = m_height - ysample, yre = m_height - ysample; y >= 0; y -= ysample, yre -= ysample )
448    {
449        for( int x = 0; x < m_width; x++ )
450        {
451            for( int i = 1; i < ysample; i++ )
452            {
453                if( !m_native_depth )
454                    data[(yre + i) * ystep + x * xstep] = data[y * ystep + x * xstep];
455                else if( m_type == FLOAT )
456                    ((float *)data)[(yre + i) * ystep + x * xstep] = ((float *)data)[y * ystep + x * xstep];
457                else
458                    ((uint *)data)[(yre + i) * ystep + x * xstep] = ((uint *)data)[y * ystep + x * xstep];
459            }
460        }
461    }
462}
463
464/**
465// algorithm from ImfRgbaYca.cpp
466 */
467void  GrFmtExrReader::ChromaToBGR( float *data, int numlines, int step )
468{
469    int x, y, t;
470
471    for( y = 0; y < numlines; y++ )
472    {
473        for( x = 0; x < m_width; x++ )
474        {
475            double b, Y, r;
476            if( !m_native_depth )
477            {
478                b = ((uchar *)data)[y * step + x * 3];
479                Y = ((uchar *)data)[y * step + x * 3 + 1];
480                r = ((uchar *)data)[y * step + x * 3 + 2];
481            }
482            else if( m_type == FLOAT )
483            {
484                b = data[y * step + x * 3];
485                Y = data[y * step + x * 3 + 1];
486                r = data[y * step + x * 3 + 2];
487            }
488            else
489            {
490                b = ((uint *)data)[y * step + x * 3];
491                Y = ((uint *)data)[y * step + x * 3 + 1];
492                r = ((uint *)data)[y * step + x * 3 + 2];
493            }
494            r = (r + 1) * Y;
495            b = (b + 1) * Y;
496            Y = (Y - b * m_chroma.blue[1] - r * m_chroma.red[1]) / m_chroma.green[1];
497
498            if( !m_native_depth )
499            {
500                int t = cvRound(b);
501                ((uchar *)data)[y * step + x * 3] = CV_CAST_8U(t);
502                t = cvRound(Y);
503                ((uchar *)data)[y * step + x * 3 + 1] = CV_CAST_8U(t);
504                t = cvRound(r);
505                ((uchar *)data)[y * step + x * 3 + 2] = CV_CAST_8U(t);
506            }
507            else if( m_type == FLOAT )
508            {
509                data[y * step + x * 3] = (float)b;
510                data[y * step + x * 3 + 1] = (float)Y;
511                data[y * step + x * 3 + 2] = (float)r;
512            }
513            else
514            {
515                int t = cvRound(b);
516                ((uint *)data)[y * step + x * 3] = (uint)MAX(t,0);
517                t = cvRound(Y);
518                ((uint *)data)[y * step + x * 3 + 1] = (uint)MAX(t,0);
519                t = cvRound(r);
520                ((uint *)data)[y * step + x * 3 + 2] = (uint)MAX(t,0);
521            }
522        }
523    }
524}
525
526
527/**
528// convert one row to gray
529*/
530void  GrFmtExrReader::RGBToGray( float *in, float *out )
531{
532    if( m_type == FLOAT )
533    {
534        if( m_native_depth )
535        {
536            for( int i = 0, n = 0; i < m_width; i++, n += 3 )
537                out[i] = in[n] * m_chroma.blue[0] + in[n + 1] * m_chroma.green[0] + in[n + 2] * m_chroma.red[0];
538        }
539        else
540        {
541            uchar *o = (uchar *)out;
542            for( int i = 0, n = 0; i < m_width; i++, n += 3 )
543                o[i] = (uchar) (in[n] * m_chroma.blue[0] + in[n + 1] * m_chroma.green[0] + in[n + 2] * m_chroma.red[0]);
544        }
545    }
546    else // UINT
547    {
548        if( m_native_depth )
549        {
550            uint *ui = (uint *)in;
551            for( int i = 0; i < m_width * 3; i++ )
552                ui[i] -= 0x80000000;
553            int *si = (int *)in;
554            for( int i = 0, n = 0; i < m_width; i++, n += 3 )
555                ((int *)out)[i] = int(si[n] * m_chroma.blue[0] + si[n + 1] * m_chroma.green[0] + si[n + 2] * m_chroma.red[0]);
556        }
557        else // how to best convert float to uchar?
558        {
559            uint *ui = (uint *)in;
560            for( int i = 0, n = 0; i < m_width; i++, n += 3 )
561                ((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));
562        }
563    }
564}
565
566/////////////////////// GrFmtExrWriter ///////////////////
567
568
569GrFmtExrWriter::GrFmtExrWriter( const char* filename ) : GrFmtWriter( filename )
570{
571}
572
573
574GrFmtExrWriter::~GrFmtExrWriter()
575{
576}
577
578
579bool  GrFmtExrWriter::IsFormatSupported( int depth )
580{
581    return depth == IPL_DEPTH_8U || depth == IPL_DEPTH_8S ||
582           depth == IPL_DEPTH_16U || depth == IPL_DEPTH_16S ||
583           depth == IPL_DEPTH_32S || depth == IPL_DEPTH_32F;
584           // TODO: do (or should) we support 64f?
585}
586
587
588// TODO scale appropriately
589bool  GrFmtExrWriter::WriteImage( const uchar* data, int step,
590                                  int width, int height, int depth, int channels )
591{
592    bool result = false;
593
594    Header header( width, height );
595    PixelType type;
596    bool issigned = depth < 0;
597    bool isfloat = depth == IPL_DEPTH_32F || depth == IPL_DEPTH_64F;
598
599    if(depth == IPL_DEPTH_8U || depth == IPL_DEPTH_8S)
600        type = HALF;
601    else if(isfloat)
602        type = FLOAT;
603    else
604        type = UINT;
605
606    depth &= 255;
607
608    if( channels == 3 )
609    {
610        header.channels().insert( "R", Channel( type ));
611        header.channels().insert( "G", Channel( type ));
612        header.channels().insert( "B", Channel( type ));
613        //printf("bunt\n");
614    }
615    else
616    {
617        header.channels().insert( "Y", Channel( type ));
618        //printf("gray\n");
619    }
620
621    OutputFile file( m_filename, header );
622
623    FrameBuffer frame;
624
625    char *buffer;
626    int bufferstep;
627    int size;
628    if( type == FLOAT && depth == 32 )
629    {
630        buffer = (char *)const_cast<uchar *>(data);
631        bufferstep = step;
632        size = 4;
633    }
634    else if( depth > 16 || type == UINT )
635    {
636        buffer = (char *)new uint[width * channels];
637        bufferstep = 0;
638        size = 4;
639    }
640    else
641    {
642        buffer = (char *)new half[width * channels];
643        bufferstep = 0;
644        size = 2;
645    }
646
647    //printf("depth %d %s\n", depth, types[type]);
648
649    if( channels == 3 )
650    {
651        frame.insert( "B", Slice( type, buffer, size * 3, bufferstep ));
652        frame.insert( "G", Slice( type, buffer + size, size * 3, bufferstep ));
653        frame.insert( "R", Slice( type, buffer + size * 2, size * 3, bufferstep ));
654    }
655    else
656        frame.insert( "Y", Slice( type, buffer, size, bufferstep ));
657
658    file.setFrameBuffer( frame );
659
660    int offset = issigned ? 1 << (depth - 1) : 0;
661
662    result = true;
663    if( type == FLOAT && depth == 32 )
664    {
665        try
666        {
667            file.writePixels( height );
668        }
669        catch(...)
670        {
671            result = false;
672        }
673    }
674    else
675    {
676    //    int scale = 1 << (32 - depth);
677    //    printf("scale %d\n", scale);
678        for(int line = 0; line < height; line++)
679        {
680            if(type == UINT)
681            {
682                uint *buf = (uint *)buffer; // FIXME 64-bit problems
683
684                if( depth <= 8 )
685                {
686                    for(int i = 0; i < width * channels; i++)
687                        buf[i] = data[i] + offset;
688                }
689                else if( depth <= 16 )
690                {
691                    unsigned short *sd = (unsigned short *)data;
692                    for(int i = 0; i < width * channels; i++)
693                        buf[i] = sd[i] + offset;
694                }
695                else
696                {
697                    int *sd = (int *)data; // FIXME 64-bit problems
698                    for(int i = 0; i < width * channels; i++)
699                        buf[i] = (uint) sd[i] + offset;
700                }
701            }
702            else
703            {
704                half *buf = (half *)buffer;
705
706                if( depth <= 8 )
707                {
708                    for(int i = 0; i < width * channels; i++)
709                        buf[i] = data[i];
710                }
711                else if( depth <= 16 )
712                {
713                    unsigned short *sd = (unsigned short *)data;
714                    for(int i = 0; i < width * channels; i++)
715                        buf[i] = sd[i];
716                }
717            }
718            try
719            {
720                file.writePixels( 1 );
721            }
722            catch(...)
723            {
724                result = false;
725                break;
726            }
727            data += step;
728        }
729        delete buffer;
730    }
731
732    return result;
733}
734
735#endif
736
737/* End of file. */
738