Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/imgcodecs/src/rgbe.cpp
16337 views
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
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15
// Third party copyrights are property of their respective owners.
16
//
17
// Redistribution and use in source and binary forms, with or without modification,
18
// are permitted provided that the following conditions are met:
19
//
20
// * Redistribution's of source code must retain the above copyright notice,
21
// this list of conditions and the following disclaimer.
22
//
23
// * Redistribution's in binary form must reproduce the above copyright notice,
24
// this list of conditions and the following disclaimer in the documentation
25
// and/or other materials provided with the distribution.
26
//
27
// * The name of the copyright holders may not be used to endorse or promote products
28
// derived from this software without specific prior written permission.
29
//
30
// This software is provided by the copyright holders and contributors "as is" and
31
// any express or implied warranties, including, but not limited to, the implied
32
// warranties of merchantability and fitness for a particular purpose are disclaimed.
33
// In no event shall the Intel Corporation or contributors be liable for any direct,
34
// indirect, incidental, special, exemplary, or consequential damages
35
// (including, but not limited to, procurement of substitute goods or services;
36
// loss of use, data, or profits; or business interruption) however caused
37
// and on any theory of liability, whether in contract, strict liability,
38
// or tort (including negligence or otherwise) arising in any way out of
39
// the use of this software, even if advised of the possibility of such damage.
40
//
41
//M*/
42
43
#include "precomp.hpp"
44
#include "rgbe.hpp"
45
#include <math.h>
46
#include <stdlib.h>
47
#include <string.h>
48
#include <ctype.h>
49
50
// This file contains code to read and write four byte rgbe file format
51
// developed by Greg Ward. It handles the conversions between rgbe and
52
// pixels consisting of floats. The data is assumed to be an array of floats.
53
// By default there are three floats per pixel in the order red, green, blue.
54
// (RGBE_DATA_??? values control this.) Only the mimimal header reading and
55
// writing is implemented. Each routine does error checking and will return
56
// a status value as defined below. This code is intended as a skeleton so
57
// feel free to modify it to suit your needs.
58
59
// Some opencv specific changes have been added:
60
// inline define specified, error handler uses CV_Error,
61
// defines changed to work in bgr color space.
62
//
63
// posted to http://www.graphics.cornell.edu/~bjw/
64
// written by Bruce Walter ([email protected]) 5/26/95
65
// based on code written by Greg Ward
66
67
#define INLINE inline
68
69
/* offsets to red, green, and blue components in a data (float) pixel */
70
#define RGBE_DATA_RED 2
71
#define RGBE_DATA_GREEN 1
72
#define RGBE_DATA_BLUE 0
73
/* number of floats per pixel */
74
#define RGBE_DATA_SIZE 3
75
76
enum rgbe_error_codes {
77
rgbe_read_error,
78
rgbe_write_error,
79
rgbe_format_error,
80
rgbe_memory_error
81
};
82
83
/* default error routine. change this to change error handling */
84
static int rgbe_error(int rgbe_error_code, const char *msg)
85
{
86
switch (rgbe_error_code) {
87
case rgbe_read_error:
88
CV_Error(cv::Error::StsError, "RGBE read error");
89
break;
90
case rgbe_write_error:
91
CV_Error(cv::Error::StsError, "RGBE write error");
92
break;
93
case rgbe_format_error:
94
CV_Error(cv::Error::StsError, cv::String("RGBE bad file format: ") +
95
cv::String(msg));
96
break;
97
default:
98
case rgbe_memory_error:
99
CV_Error(cv::Error::StsError, cv::String("RGBE error: \n") +
100
cv::String(msg));
101
}
102
}
103
104
/* standard conversion from float pixels to rgbe pixels */
105
/* note: you can remove the "inline"s if your compiler complains about it */
106
static INLINE void
107
float2rgbe(unsigned char rgbe[4], float red, float green, float blue)
108
{
109
float v;
110
int e;
111
112
v = red;
113
if (green > v) v = green;
114
if (blue > v) v = blue;
115
if (v < 1e-32) {
116
rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
117
}
118
else {
119
v = static_cast<float>(frexp(v,&e) * 256.0/v);
120
rgbe[0] = (unsigned char) (red * v);
121
rgbe[1] = (unsigned char) (green * v);
122
rgbe[2] = (unsigned char) (blue * v);
123
rgbe[3] = (unsigned char) (e + 128);
124
}
125
}
126
127
/* standard conversion from rgbe to float pixels */
128
/* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */
129
/* in the range [0,1] to map back into the range [0,1]. */
130
static INLINE void
131
rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4])
132
{
133
float f;
134
135
if (rgbe[3]) { /*nonzero pixel*/
136
f = static_cast<float>(ldexp(1.0,rgbe[3]-(int)(128+8)));
137
*red = rgbe[0] * f;
138
*green = rgbe[1] * f;
139
*blue = rgbe[2] * f;
140
}
141
else
142
*red = *green = *blue = 0.0;
143
}
144
145
/* default minimal header. modify if you want more information in header */
146
int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info)
147
{
148
const char *programtype = "RGBE";
149
150
if (info && (info->valid & RGBE_VALID_PROGRAMTYPE))
151
programtype = info->programtype;
152
if (fprintf(fp,"#?%s\n",programtype) < 0)
153
return rgbe_error(rgbe_write_error,NULL);
154
/* The #? is to identify file type, the programtype is optional. */
155
if (info && (info->valid & RGBE_VALID_GAMMA)) {
156
if (fprintf(fp,"GAMMA=%g\n",info->gamma) < 0)
157
return rgbe_error(rgbe_write_error,NULL);
158
}
159
if (info && (info->valid & RGBE_VALID_EXPOSURE)) {
160
if (fprintf(fp,"EXPOSURE=%g\n",info->exposure) < 0)
161
return rgbe_error(rgbe_write_error,NULL);
162
}
163
if (fprintf(fp,"FORMAT=32-bit_rle_rgbe\n\n") < 0)
164
return rgbe_error(rgbe_write_error,NULL);
165
if (fprintf(fp, "-Y %d +X %d\n", height, width) < 0)
166
return rgbe_error(rgbe_write_error,NULL);
167
return RGBE_RETURN_SUCCESS;
168
}
169
170
/* minimal header reading. modify if you want to parse more information */
171
int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info)
172
{
173
char buf[128];
174
float tempf;
175
int i;
176
177
if (info) {
178
info->valid = 0;
179
info->programtype[0] = 0;
180
info->gamma = info->exposure = 1.0;
181
}
182
183
// 1. read first line
184
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == NULL)
185
return rgbe_error(rgbe_read_error,NULL);
186
if ((buf[0] != '#')||(buf[1] != '?')) {
187
/* if you want to require the magic token then uncomment the next line */
188
/*return rgbe_error(rgbe_format_error,"bad initial token"); */
189
}
190
else if (info) {
191
info->valid |= RGBE_VALID_PROGRAMTYPE;
192
for(i=0;i<static_cast<int>(sizeof(info->programtype)-1);i++) {
193
if ((buf[i+2] == 0) || isspace(buf[i+2]))
194
break;
195
info->programtype[i] = buf[i+2];
196
}
197
info->programtype[i] = 0;
198
}
199
200
// 2. reading other header lines
201
bool hasFormat = false;
202
for(;;) {
203
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
204
return rgbe_error(rgbe_read_error,NULL);
205
if (buf[0] == '\n') // end of the header
206
break;
207
else if (buf[0] == '#') // comment
208
continue;
209
else if (strcmp(buf,"FORMAT=32-bit_rle_rgbe\n") == 0)
210
hasFormat = true;
211
else if (info && (sscanf(buf,"GAMMA=%g",&tempf) == 1)) {
212
info->gamma = tempf;
213
info->valid |= RGBE_VALID_GAMMA;
214
}
215
else if (info && (sscanf(buf,"EXPOSURE=%g",&tempf) == 1)) {
216
info->exposure = tempf;
217
info->valid |= RGBE_VALID_EXPOSURE;
218
}
219
}
220
if (strcmp(buf,"\n") != 0)
221
return rgbe_error(rgbe_format_error,
222
"missing blank line after FORMAT specifier");
223
if (!hasFormat)
224
return rgbe_error(rgbe_format_error, "missing FORMAT specifier");
225
226
// 3. reading resolution string
227
if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
228
return rgbe_error(rgbe_read_error,NULL);
229
if (sscanf(buf,"-Y %d +X %d",height,width) < 2)
230
return rgbe_error(rgbe_format_error,"missing image size specifier");
231
return RGBE_RETURN_SUCCESS;
232
}
233
234
/* simple write routine that does not use run length encoding */
235
/* These routines can be made faster by allocating a larger buffer and
236
fread-ing and fwrite-ing the data in larger chunks */
237
int RGBE_WritePixels(FILE *fp, float *data, int numpixels)
238
{
239
unsigned char rgbe[4];
240
241
while (numpixels-- > 0) {
242
float2rgbe(rgbe,data[RGBE_DATA_RED],
243
data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
244
data += RGBE_DATA_SIZE;
245
if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1)
246
return rgbe_error(rgbe_write_error,NULL);
247
}
248
return RGBE_RETURN_SUCCESS;
249
}
250
251
/* simple read routine. will not correctly handle run length encoding */
252
int RGBE_ReadPixels(FILE *fp, float *data, int numpixels)
253
{
254
unsigned char rgbe[4];
255
256
while(numpixels-- > 0) {
257
if (fread(rgbe, sizeof(rgbe), 1, fp) < 1)
258
return rgbe_error(rgbe_read_error,NULL);
259
rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
260
&data[RGBE_DATA_BLUE],rgbe);
261
data += RGBE_DATA_SIZE;
262
}
263
return RGBE_RETURN_SUCCESS;
264
}
265
266
/* The code below is only needed for the run-length encoded files. */
267
/* Run length encoding adds considerable complexity but does */
268
/* save some space. For each scanline, each channel (r,g,b,e) is */
269
/* encoded separately for better compression. */
270
271
static int RGBE_WriteBytes_RLE(FILE *fp, unsigned char *data, int numbytes)
272
{
273
#define MINRUNLENGTH 4
274
int cur, beg_run, run_count, old_run_count, nonrun_count;
275
unsigned char buf[2];
276
277
cur = 0;
278
while(cur < numbytes) {
279
beg_run = cur;
280
/* find next run of length at least 4 if one exists */
281
run_count = old_run_count = 0;
282
while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
283
beg_run += run_count;
284
old_run_count = run_count;
285
run_count = 1;
286
while( (beg_run + run_count < numbytes) && (run_count < 127)
287
&& (data[beg_run] == data[beg_run + run_count]))
288
run_count++;
289
}
290
/* if data before next big run is a short run then write it as such */
291
if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) {
292
buf[0] = static_cast<unsigned char>(128 + old_run_count); /*write short run*/
293
buf[1] = data[cur];
294
if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
295
return rgbe_error(rgbe_write_error,NULL);
296
cur = beg_run;
297
}
298
/* write out bytes until we reach the start of the next run */
299
while(cur < beg_run) {
300
nonrun_count = beg_run - cur;
301
if (nonrun_count > 128)
302
nonrun_count = 128;
303
buf[0] = static_cast<unsigned char>(nonrun_count);
304
if (fwrite(buf,sizeof(buf[0]),1,fp) < 1)
305
return rgbe_error(rgbe_write_error,NULL);
306
if (fwrite(&data[cur],sizeof(data[0])*nonrun_count,1,fp) < 1)
307
return rgbe_error(rgbe_write_error,NULL);
308
cur += nonrun_count;
309
}
310
/* write out next run if one was found */
311
if (run_count >= MINRUNLENGTH) {
312
buf[0] = static_cast<unsigned char>(128 + run_count);
313
buf[1] = data[beg_run];
314
if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
315
return rgbe_error(rgbe_write_error,NULL);
316
cur += run_count;
317
}
318
}
319
return RGBE_RETURN_SUCCESS;
320
#undef MINRUNLENGTH
321
}
322
323
int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width,
324
int num_scanlines)
325
{
326
unsigned char rgbe[4];
327
unsigned char *buffer;
328
int i, err;
329
330
if ((scanline_width < 8)||(scanline_width > 0x7fff))
331
/* run length encoding is not allowed so write flat*/
332
return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
333
buffer = (unsigned char *)malloc(sizeof(unsigned char)*4*scanline_width);
334
if (buffer == NULL)
335
/* no buffer space so write flat */
336
return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
337
while(num_scanlines-- > 0) {
338
rgbe[0] = 2;
339
rgbe[1] = 2;
340
rgbe[2] = static_cast<unsigned char>(scanline_width >> 8);
341
rgbe[3] = scanline_width & 0xFF;
342
if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) {
343
free(buffer);
344
return rgbe_error(rgbe_write_error,NULL);
345
}
346
for(i=0;i<scanline_width;i++) {
347
float2rgbe(rgbe,data[RGBE_DATA_RED],
348
data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
349
buffer[i] = rgbe[0];
350
buffer[i+scanline_width] = rgbe[1];
351
buffer[i+2*scanline_width] = rgbe[2];
352
buffer[i+3*scanline_width] = rgbe[3];
353
data += RGBE_DATA_SIZE;
354
}
355
/* write out each of the four channels separately run length encoded */
356
/* first red, then green, then blue, then exponent */
357
for(i=0;i<4;i++) {
358
if ((err = RGBE_WriteBytes_RLE(fp,&buffer[i*scanline_width],
359
scanline_width)) != RGBE_RETURN_SUCCESS) {
360
free(buffer);
361
return err;
362
}
363
}
364
}
365
free(buffer);
366
return RGBE_RETURN_SUCCESS;
367
}
368
369
int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width,
370
int num_scanlines)
371
{
372
unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
373
int i, count;
374
unsigned char buf[2];
375
376
if ((scanline_width < 8)||(scanline_width > 0x7fff))
377
/* run length encoding is not allowed so read flat*/
378
return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines);
379
scanline_buffer = NULL;
380
/* read in each successive scanline */
381
while(num_scanlines > 0) {
382
if (fread(rgbe,sizeof(rgbe),1,fp) < 1) {
383
free(scanline_buffer);
384
return rgbe_error(rgbe_read_error,NULL);
385
}
386
if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) {
387
/* this file is not run length encoded */
388
rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],&data[RGBE_DATA_BLUE],rgbe);
389
data += RGBE_DATA_SIZE;
390
free(scanline_buffer);
391
return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines-1);
392
}
393
if ((((int)rgbe[2])<<8 | rgbe[3]) != scanline_width) {
394
free(scanline_buffer);
395
return rgbe_error(rgbe_format_error,"wrong scanline width");
396
}
397
if (scanline_buffer == NULL)
398
scanline_buffer = (unsigned char *)
399
malloc(sizeof(unsigned char)*4*scanline_width);
400
if (scanline_buffer == NULL)
401
return rgbe_error(rgbe_memory_error,"unable to allocate buffer space");
402
403
ptr = &scanline_buffer[0];
404
/* read each of the four channels for the scanline into the buffer */
405
for(i=0;i<4;i++) {
406
ptr_end = &scanline_buffer[(i+1)*scanline_width];
407
while(ptr < ptr_end) {
408
if (fread(buf,sizeof(buf[0])*2,1,fp) < 1) {
409
free(scanline_buffer);
410
return rgbe_error(rgbe_read_error,NULL);
411
}
412
if (buf[0] > 128) {
413
/* a run of the same value */
414
count = buf[0]-128;
415
if ((count == 0)||(count > ptr_end - ptr)) {
416
free(scanline_buffer);
417
return rgbe_error(rgbe_format_error,"bad scanline data");
418
}
419
while(count-- > 0)
420
*ptr++ = buf[1];
421
}
422
else {
423
/* a non-run */
424
count = buf[0];
425
if ((count == 0)||(count > ptr_end - ptr)) {
426
free(scanline_buffer);
427
return rgbe_error(rgbe_format_error,"bad scanline data");
428
}
429
*ptr++ = buf[1];
430
if (--count > 0) {
431
if (fread(ptr,sizeof(*ptr)*count,1,fp) < 1) {
432
free(scanline_buffer);
433
return rgbe_error(rgbe_read_error,NULL);
434
}
435
ptr += count;
436
}
437
}
438
}
439
}
440
/* now convert data from buffer into floats */
441
for(i=0;i<scanline_width;i++) {
442
rgbe[0] = scanline_buffer[i];
443
rgbe[1] = scanline_buffer[i+scanline_width];
444
rgbe[2] = scanline_buffer[i+2*scanline_width];
445
rgbe[3] = scanline_buffer[i+3*scanline_width];
446
rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
447
&data[RGBE_DATA_BLUE],rgbe);
448
data += RGBE_DATA_SIZE;
449
}
450
num_scanlines--;
451
}
452
free(scanline_buffer);
453
return RGBE_RETURN_SUCCESS;
454
}
455
456