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