HDR文件格式简介及其读写函数
转自:http://blog.csdn.net/lqhbupt/article/details/7828827
1、HDR简介
HDR的全称是High-DynamicRange(高动态范围)。在此,我们先解释一下什么是DynamicRange(动态范围),动态范围是指图像中所包含的从“最亮”至“最暗”的比值,也就是图像从“最亮”到“最暗”之间灰度划分的等级数;动态范围越大,所能表示的层次越丰富,所包含的色彩空间也越广。那高动态范围(HDR)顾名思义就是从“最亮”到“最暗”可以达到非常高的比值。
在日常生活中我们经常遇到这样的情况:突然从黑暗的房间中走到阳光下,眼睛会无法睁开;清晨阳光会穿透窗帘像光柱般照射入房间;反光度较高的物体在强光下会在周围产生光晕。以上这些生活中随处可见的现象在有HDR以前无法在3D世界中呈现!最大的原因就在于我们使用8~16bit的整数数据,使用8~16bit的整数数据是整个图象处理失真的关键点,所以我们对以往的运算方法做了以下二方面的重大改进:
1、使用16bit、32bit的数据来提高像素数据的精度。既然继续使用8bit的数据来记录像素的特征不能满足HDR数据所需要的高精度运算的要求,在这种情况下,我们考虑使用16bit、32bit的数据记录来提高像素数据的精度都是可以的。使用了更多的数据来保存像素特征之后,无论是像素的对比度还是像素可以体现的色彩数目都有了巨大的提高。
2、图象数据采用浮点数据。HDR真正的巨大变革来自于浮点数据的引入。我们可以采用浮点方式来处理和存放亮度数据,抛弃不准确的整数数据;同时计算机在引入浮点数据来存储象素的各个参数并且在运算的全过程都使用浮点数据,这样就可以有效的提高据的精确度。
那么采用HDR后动态范围最大可以有多大呢?我们看如下的公式,就可以知道我们到底使用了HDR后动态值可以有多大,而动态值的大小直接表现了动态范围的大小:DynamicRange=log10(Max Intensity / Min Intensity)。公式中intensity是指强度,我们对最大亮度除以最低亮度的结果取对数,得到的结果就是动态范围的相对数值。根据公式计算,当我们在亮度通道使用8bit的的情况下,最高亮度255,最低亮度1。那么计算得出的动态范围就是数值约为2.4,加上单位就是24dB。同理可以计算得出16bit的亮度通道的动态范围是数值约是4.8,是使用8bit亮度通道的一倍。理论上在HDR模式下,动态范围的数值最高可以到达76.8。在NVIDIA所使用的OpenEXR中表现出来的HDR动态范围的数值最大值约有12.0,远远高出单纯使用16bit亮度通道的所带来的亮度体验,这是采用了优秀算法的结果。OpenEXR所能实现的最大动态范围已经超过了人眼的9,带来了更加真实的视觉体验。
2、HDRI文件格式介绍(OpenEXR、RadianceRGBE、FloatTIFF)
HDRI(High-DynamicRange Image)就是记录采用了HDR技术的图象数据文件。常用的HDRI文件有OpenEXR、RadianceRGBE、FloatTIFF三种格式。
2.1 OpenEXR文件格式
OpenEXR是由工业光魔(IndustrialLight & Magic)开发的一种HDR标准。OpenEXR文件的扩展名为.exr,常见的OpenEXR文件是FP16(16bitFloat Point,也被称为halfFloat Point)数据图像文件,每个通道的数据类型是FP16,一共四个通道64bpp,每个通道1个bit位用来标志“指数”,5个bit用来存放指数的值,10个bit存放色度坐标(u,v)的尾数,其动态范围从6.14× 10 ^ -5到6.41× 10 ^ 4。
在OpenEXR的算法里面共使用16bit来表示光照数据。虽然看起来和使用16bit亮度通道运算位数相同,但是OpenEXR巧妙的采用了1个bit位用来标志“指数”,5个bit用来存放指数的值,10个bit存放色度坐标的尾数。这样就轻易的解决了浮点数值由于位数少而精度不高的问题。大大的拓宽的在FP16下的动态范围。根据实际的计算结果:在正规化的情况下OpenEXR可以提供和人眼基本相同的动态范围,最暗到最亮是0.00006103515625(6.14 ×10 ^ -5)到65504(6.41 ×10 ^ 4),动态范围是9.03;非正规化条件下,OpenEXR可以提供从最暗到最亮的数值从0.000000059604644775390625(5.96 ×10 ^ -8 )到65504(6.41 ×10 ^ 4),化为动态范围表示就是12。
下面是Still写的OpenEXR读写代码,保存的.exr文件采用Zips压缩编码。
- bool COpenExr::Load(const char fileName[], int& width, int& height, float** pixels)
- {
- std::vector<float> vecpixels;
- if(!Load(fileName, width, height, vecpixels))
- return false;
- int num = width * height * 3;
- *pixels = new float[num];
- if(NULL == *pixels)
- return false;
- std::vector<float>::pointer ptr = &vecpixels[0];
- memcpy(*pixels, ptr, num * 4);
- return true;
- }
- bool COpenExr::Load(const char fileName[], int& width, int& height, std::vector<float> &pixels)
- {
- Imf::Array<Imf::Rgba> pixelsdata;
- bool bLoad = loadImage(fileName, width, height, pixelsdata);
- if(!bLoad) return false;
- for(int y = 0; y < height; y++)
- {
- int i = y * width;
- for(int x = 0; x < width; x++)
- {
- int j = i + x;
- const Imf::Rgba &rp = pixelsdata[j];
- pixels.push_back( float(rp.r));
- pixels.push_back( float(rp.g));
- pixels.push_back( float(rp.b));
- }
- }
- return true;
- }
- bool COpenExr::loadImage (const char fileName[], int& width, int& height, Imf::Array<Imf::Rgba>& pixels)
- {
- Imf::RgbaInputFile in (fileName);
- Imath::Box2i dataWindow = in.dataWindow();
- int dw, dh, dx, dy;
- width = dw = dataWindow.max.x - dataWindow.min.x + 1;
- height = dh = dataWindow.max.y - dataWindow.min.y + 1;
- dx = dataWindow.min.x;
- dy = dataWindow.min.y;
- pixels.resizeErase (dw * dh);
- in.setFrameBuffer (pixels - dx - dy * dw, 1, dw);
- try
- {
- in.readPixels (dataWindow.min.y, dataWindow.max.y);
- }catch (const exception &e)
- {
- std::cerr << e.what() << std::endl;
- return false;
- }
- return true;
- }
- bool COpenExr::Save(const char fileName[], int width, int height, const float* pixels)
- {
- std::vector<float> vecpixels(pixels, pixels + width * height * 3);
- return Save(fileName, width, height, vecpixels);
- }
- bool COpenExr::Save(const char fileName[], int width, int height, const std::vector<float> pixels)
- {
- Imf::Array<Imf::Rgba> pixelsdata;
- pixelsdata.resizeErase(width * height);
- for(int y = 0; y < height; y++)
- {
- int i = y * width;
- for(int x = 0; x < width; x++)
- {
- int j = i + x;
- half r = pixels[j * 3 ];
- half g = pixels[j * 3 + 1];
- half b = pixels[j * 3 + 2];
- pixelsdata[j] = Imf::Rgba(r, g, b);
- }
- }
- return SaveImage(fileName, width, height, pixelsdata);
- }
- bool COpenExr::SaveImage(const char fileName[], int width, int height, const Imf::Array<Imf::Rgba> &pixels)
- {
- Imf::RgbaOutputFile file (fileName, width, height);
- file.setFrameBuffer(pixels, 1, width);
- try
- {
- file.writePixels(height);
- }catch(const exception &e)
- {
- std::cerr<< e.what() <<std::endl;
- return false;
- }
- return true;
- }
官方库链接地址:http://www.openexr.com/
2.2 Radiance RGBE文件格式
RGBE文件的扩展名为.hdr,RGBE正式名称为RadianceRGBE格式。这个本来是BR、FR等作为radiance材质的一种格式,也叫做radiancemap,后来成为流行的一种HDR格式。所谓E,就是指数。RadianceRGBE文件每个通道为8bitBYTE数据类型,4个通道一共是32bit。RGBE可以使用RLE压缩编码压缩,也可以不压缩。由文件头、RGBE数据组成。
文件头如下:
类型输出格式
char programtype[16]; //#?Radiance/n#Generated by still/n
float gamma; //1.0
float exposure; //1.0
字符串常量//FORMAT=32-bit_rle_rgbe/n/n
int nWidth, int nHeight //-Y nHeight +X nWidth/n
RGBE数据与HDRFP32(RGB)相互转换公式如下:
1、rgbe->FP32(RGB)
如果e为0, R = G= B = 0.0,否则:
R = r * 2^(e – 128 - 8);
G = g * 2^(e – 128 - 8);
B = b * 2^(e – 128 - 8);
2、FP32(RGB)-> rgbe
v = max(R, G, B);
如果v< 1e-32, r = g = b = e = 0, 否则:
将v用科学计算法表示成v = m * 2 ^ n ( 0 < m < 1):
r = R * m * 256.0/v;
g = G * m * 256.0/v;
b = B * m * 256.0/v;
e = n + 128;
Still注:
1、我们一般说HDR采用FP32,指的是HDR图象运算时候的内存数据类型,而RadianceRGBE文件采用8bitBYTE类型存储HDR数据。也就是说打开RadianceRGBE文件,要使用上面的公式1将RadianceRGBE文件的8bitBYTE文件数据转换为FP32的HDR内存数据进行运算;保存为RadianceRGBE文件时,要使用上面的公式2将HDR的FP32内存数据转换为RadianceRGBE的8bitBYTE文件数据进行保存。同理,OpenEXR文件的读写也存在将其FP16的文件数据到HDR的 FP32图象数据的转换;而下面将要讲的FloatTiff是不需要进行数据转换,直接将HDR的FP 32图象数据保存到TIFF文件中即可。
2、Radiance有多种文件格式,其官方库包含内容比较复杂,所以,实际的读写没有使用其官方库,而是使用了网络上一个简单的C语言读写类,Still并对其进行了部分修改(在文件头写入“Generatedby Still”)。
读写类链接地址:http://www.graphics.cornell.edu/~bjw/rgbe.html
官方库链接地址:http://radsite.lbl.gov/radiance/
****************************************************************************************
rgbe.txt - description of interface
rgbe.h - header file
rgbe.c - C code
Where can I find more information?
See "Real Pixels" by Greg Ward in Graphics Gems II .
*****************************************************************************************
Here's the minimal code for using these files. sampleminimal writing code:
f = fopen(image_filename,"wb");
RGBE_WriteHeader(f,image_width,image_height,NULL);
RGBE_WritePixels(f,image,image_width*image_height);
fclose(f);
For run length encoding instead of RGBE_WritePixels, useRGBE_WritePixels_RLE(f,image,image_width,image_height). sample minimal readingcode:
f = fopen(image_filename,"rb");
RGBE_ReadHeader(f,&image_width,&image_height,NULL);
image = (float *)malloc(sizeof(float)*3*image_width*image_height);
RGBE_ReadPixels_RLE(f,image,image_width,image_height);
fclose(f);
******************************************************************************************
- //rgbe.h--header file
- #ifndef _H_RGBE
- #define _H_RGBE
- /* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE.
- * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY,
- * IT IS STRICTLY USE AT YOUR OWN RISK. */
- /* utility for reading and writing Ward's rgbe image format.
- See rgbe.txt file for more details.
- */
- #include <stdio.h>
- typedef struct {
- int valid; /* indicate which fields are valid */
- char programtype[16]; /* listed at beginning of file to identify it
- * after "#?". defaults to "RGBE" */
- float gamma; /* image has already been gamma corrected with
- * given gamma. defaults to 1.0 (no correction) */
- float exposure; /* a value of 1.0 in an image corresponds to
- * <exposure> watts/steradian/m^2.
- * defaults to 1.0 */
- } rgbe_header_info;
- /* flags indicating which fields in an rgbe_header_info are valid */
- #define RGBE_VALID_PROGRAMTYPE 0x01
- #define RGBE_VALID_GAMMA 0x02
- #define RGBE_VALID_EXPOSURE 0x04
- /* return codes for rgbe routines */
- #define RGBE_RETURN_SUCCESS 0
- #define RGBE_RETURN_FAILURE -1
- /* read or write headers */
- /* you may set rgbe_header_info to null if you want to */
- int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info);
- int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info);
- /* read or write pixels */
- /* can read or write pixels in chunks of any size including single pixels*/
- int RGBE_WritePixels(FILE *fp, float *data, int numpixels);
- int RGBE_ReadPixels(FILE *fp, float *data, int numpixels);
- /* read or write run length encoded files */
- /* must be called to read or write whole scanlines */
- int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width,
- int num_scanlines);
- int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width,
- int num_scanlines);
- #endif /* _H_RGBE */
- //rgbe.c -- C code
- /* THIS CODE CARRIES NO GUARANTEE OF USABILITY OR FITNESS FOR ANY PURPOSE.
- * WHILE THE AUTHORS HAVE TRIED TO ENSURE THE PROGRAM WORKS CORRECTLY,
- * IT IS STRICTLY USE AT YOUR OWN RISK. */
- #include "rgbe.h"
- #include <math.h>
- #include <malloc.h>
- #include <string.h>
- #include <ctype.h>
- /* This file contains code to read and write four byte rgbe file format
- developed by Greg Ward. It handles the conversions between rgbe and
- pixels consisting of floats. The data is assumed to be an array of floats.
- By default there are three floats per pixel in the order red, green, blue.
- (RGBE_DATA_??? values control this.) Only the mimimal header reading and
- writing is implemented. Each routine does error checking and will return
- a status value as defined below. This code is intended as a skeleton so
- feel free to modify it to suit your needs.
- (Place notice here if you modified the code.)
- posted to http://www.graphics.cornell.edu/~bjw/
- written by Bruce Walter (bjw@graphics.cornell.edu) 5/26/95
- based on code written by Greg Ward
- */
- #ifdef _CPLUSPLUS
- /* define if your compiler understands inline commands */
- #define INLINE inline
- #else
- #define INLINE
- #endif
- /* offsets to red, green, and blue components in a data (float) pixel */
- #define RGBE_DATA_RED 0
- #define RGBE_DATA_GREEN 1
- #define RGBE_DATA_BLUE 2
- /* number of floats per pixel */
- #define RGBE_DATA_SIZE 3
- enum rgbe_error_codes {
- rgbe_read_error,
- rgbe_write_error,
- rgbe_format_error,
- rgbe_memory_error,
- };
- /* default error routine. change this to change error handling */
- static int rgbe_error(int rgbe_error_code, char *msg)
- {
- switch (rgbe_error_code) {
- case rgbe_read_error:
- perror("RGBE read error");
- break;
- case rgbe_write_error:
- perror("RGBE write error");
- break;
- case rgbe_format_error:
- fprintf(stderr,"RGBE bad file format: %s/n",msg);
- break;
- default:
- case rgbe_memory_error:
- fprintf(stderr,"RGBE error: %s/n",msg);
- }
- return RGBE_RETURN_FAILURE;
- }
- /* standard conversion from float pixels to rgbe pixels */
- /* note: you can remove the "inline"s if your compiler complains about it */
- static INLINE void
- float2rgbe(unsigned char rgbe[4], float red, float green, float blue)
- {
- float v;
- int e;
- v = red;
- if (green > v) v = green;
- if (blue > v) v = blue;
- if (v < 1e-32) {
- rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
- }
- else {
- v = frexp(v,&e) * 256.0/v;
- rgbe[0] = (unsigned char) (red * v);
- rgbe[1] = (unsigned char) (green * v);
- rgbe[2] = (unsigned char) (blue * v);
- rgbe[3] = (unsigned char) (e + 128);
- }
- }
- /* standard conversion from rgbe to float pixels */
- /* note: Ward uses ldexp(col+0.5,exp-(128+8)). However we wanted pixels */
- /* in the range [0,1] to map back into the range [0,1]. */
- static INLINE void
- rgbe2float(float *red, float *green, float *blue, unsigned char rgbe[4])
- {
- float f;
- if (rgbe[3]) { /*nonzero pixel*/
- f = ldexp(1.0,rgbe[3]-(int)(128+8));
- *red = rgbe[0] * f;
- *green = rgbe[1] * f;
- *blue = rgbe[2] * f;
- }
- else
- *red = *green = *blue = 0.0;
- }
- /* default minimal header. modify if you want more information in header */
- int RGBE_WriteHeader(FILE *fp, int width, int height, rgbe_header_info *info)
- {
- char *programtype = "RGBE";
- if (info && (info->valid & RGBE_VALID_PROGRAMTYPE))
- programtype = info->programtype;
- if (fprintf(fp,"#?%s/n",programtype) < 0)
- return rgbe_error(rgbe_write_error,NULL);
- /* The #? is to identify file type, the programtype is optional. */
- if (info && (info->valid & RGBE_VALID_GAMMA)) {
- if (fprintf(fp,"GAMMA=%g/n",info->gamma) < 0)
- return rgbe_error(rgbe_write_error,NULL);
- }
- if (info && (info->valid & RGBE_VALID_EXPOSURE)) {
- if (fprintf(fp,"EXPOSURE=%g/n",info->exposure) < 0)
- return rgbe_error(rgbe_write_error,NULL);
- }
- if (fprintf(fp,"FORMAT=32-bit_rle_rgbe/n/n") < 0)
- return rgbe_error(rgbe_write_error,NULL);
- if (fprintf(fp, "-Y %d +X %d/n", height, width) < 0)
- return rgbe_error(rgbe_write_error,NULL);
- return RGBE_RETURN_SUCCESS;
- }
- /* minimal header reading. modify if you want to parse more information */
- int RGBE_ReadHeader(FILE *fp, int *width, int *height, rgbe_header_info *info)
- {
- char buf[128];
- int found_format;
- float tempf;
- int i;
- found_format = 0;
- if (info) {
- info->valid = 0;
- info->programtype[0] = 0;
- info->gamma = info->exposure = 1.0;
- }
- if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == NULL)
- return rgbe_error(rgbe_read_error,NULL);
- if ((buf[0] != '#')||(buf[1] != '?')) {
- /* if you want to require the magic token then uncomment the next line */
- /*return rgbe_error(rgbe_format_error,"bad initial token"); */
- }
- else if (info) {
- info->valid |= RGBE_VALID_PROGRAMTYPE;
- for(i=0;i<sizeof(info->programtype)-1;i++) {
- if ((buf[i+2] == 0) || isspace(buf[i+2]))
- break;
- info->programtype[i] = buf[i+2];
- }
- info->programtype[i] = 0;
- if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
- return rgbe_error(rgbe_read_error,NULL);
- }
- for(;;) {
- if ((buf[0] == 0)||(buf[0] == '/n'))
- return rgbe_error(rgbe_format_error,"no FORMAT specifier found");
- else if (strcmp(buf,"FORMAT=32-bit_rle_rgbe/n") == 0)
- break; /* format found so break out of loop */
- else if (info && (sscanf(buf,"GAMMA=%g",&tempf) == 1)) {
- info->gamma = tempf;
- info->valid |= RGBE_VALID_GAMMA;
- }
- else if (info && (sscanf(buf,"EXPOSURE=%g",&tempf) == 1)) {
- info->exposure = tempf;
- info->valid |= RGBE_VALID_EXPOSURE;
- }
- if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
- return rgbe_error(rgbe_read_error,NULL);
- }
- if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
- return rgbe_error(rgbe_read_error,NULL);
- if (strcmp(buf,"/n") != 0)
- return rgbe_error(rgbe_format_error,
- "missing blank line after FORMAT specifier");
- if (fgets(buf,sizeof(buf)/sizeof(buf[0]),fp) == 0)
- return rgbe_error(rgbe_read_error,NULL);
- if (sscanf(buf,"-Y %d +X %d",height,width) < 2)
- return rgbe_error(rgbe_format_error,"missing image size specifier");
- return RGBE_RETURN_SUCCESS;
- }
- /* simple write routine that does not use run length encoding */
- /* These routines can be made faster by allocating a larger buffer and
- fread-ing and fwrite-ing the data in larger chunks */
- int RGBE_WritePixels(FILE *fp, float *data, int numpixels)
- {
- unsigned char rgbe[4];
- while (numpixels-- > 0) {
- float2rgbe(rgbe,data[RGBE_DATA_RED],
- data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
- data += RGBE_DATA_SIZE;
- if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1)
- return rgbe_error(rgbe_write_error,NULL);
- }
- return RGBE_RETURN_SUCCESS;
- }
- /* simple read routine. will not correctly handle run length encoding */
- int RGBE_ReadPixels(FILE *fp, float *data, int numpixels)
- {
- unsigned char rgbe[4];
- while(numpixels-- > 0) {
- if (fread(rgbe, sizeof(rgbe), 1, fp) < 1)
- return rgbe_error(rgbe_read_error,NULL);
- rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
- &data[RGBE_DATA_BLUE],rgbe);
- data += RGBE_DATA_SIZE;
- }
- return RGBE_RETURN_SUCCESS;
- }
- /* The code below is only needed for the run-length encoded files. */
- /* Run length encoding adds considerable complexity but does */
- /* save some space. For each scanline, each channel (r,g,b,e) is */
- /* encoded separately for better compression. */
- static int RGBE_WriteBytes_RLE(FILE *fp, unsigned char *data, int numbytes)
- {
- #define MINRUNLENGTH 4
- int cur, beg_run, run_count, old_run_count, nonrun_count;
- unsigned char buf[2];
- cur = 0;
- while(cur < numbytes) {
- beg_run = cur;
- /* find next run of length at least 4 if one exists */
- run_count = old_run_count = 0;
- while((run_count < MINRUNLENGTH) && (beg_run < numbytes)) {
- beg_run += run_count;
- old_run_count = run_count;
- run_count = 1;
- while( (beg_run + run_count < numbytes) && (run_count < 127)
- && (data[beg_run] == data[beg_run + run_count]))
- run_count++;
- }
- /* if data before next big run is a short run then write it as such */
- if ((old_run_count > 1)&&(old_run_count == beg_run - cur)) {
- buf[0] = 128 + old_run_count; /*write short run*/
- buf[1] = data[cur];
- if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
- return rgbe_error(rgbe_write_error,NULL);
- cur = beg_run;
- }
- /* write out bytes until we reach the start of the next run */
- while(cur < beg_run) {
- nonrun_count = beg_run - cur;
- if (nonrun_count > 128)
- nonrun_count = 128;
- buf[0] = nonrun_count;
- if (fwrite(buf,sizeof(buf[0]),1,fp) < 1)
- return rgbe_error(rgbe_write_error,NULL);
- if (fwrite(&data[cur],sizeof(data[0])*nonrun_count,1,fp) < 1)
- return rgbe_error(rgbe_write_error,NULL);
- cur += nonrun_count;
- }
- /* write out next run if one was found */
- if (run_count >= MINRUNLENGTH) {
- buf[0] = 128 + run_count;
- buf[1] = data[beg_run];
- if (fwrite(buf,sizeof(buf[0])*2,1,fp) < 1)
- return rgbe_error(rgbe_write_error,NULL);
- cur += run_count;
- }
- }
- return RGBE_RETURN_SUCCESS;
- #undef MINRUNLENGTH
- }
- int RGBE_WritePixels_RLE(FILE *fp, float *data, int scanline_width,
- int num_scanlines)
- {
- unsigned char rgbe[4];
- unsigned char *buffer;
- int i, err;
- if ((scanline_width < 8)||(scanline_width > 0x7fff))
- /* run length encoding is not allowed so write flat*/
- return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
- buffer = (unsigned char *)malloc(sizeof(unsigned char)*4*scanline_width);
- if (buffer == NULL)
- /* no buffer space so write flat */
- return RGBE_WritePixels(fp,data,scanline_width*num_scanlines);
- while(num_scanlines-- > 0) {
- rgbe[0] = 2;
- rgbe[1] = 2;
- rgbe[2] = scanline_width >> 8;
- rgbe[3] = scanline_width & 0xFF;
- if (fwrite(rgbe, sizeof(rgbe), 1, fp) < 1) {
- free(buffer);
- return rgbe_error(rgbe_write_error,NULL);
- }
- for(i=0;i<scanline_width;i++) {
- float2rgbe(rgbe,data[RGBE_DATA_RED],
- data[RGBE_DATA_GREEN],data[RGBE_DATA_BLUE]);
- buffer[i] = rgbe[0];
- buffer[i+scanline_width] = rgbe[1];
- buffer[i+2*scanline_width] = rgbe[2];
- buffer[i+3*scanline_width] = rgbe[3];
- data += RGBE_DATA_SIZE;
- }
- /* write out each of the four channels separately run length encoded */
- /* first red, then green, then blue, then exponent */
- for(i=0;i<4;i++) {
- if ((err = RGBE_WriteBytes_RLE(fp,&buffer[i*scanline_width],
- scanline_width)) != RGBE_RETURN_SUCCESS) {
- free(buffer);
- return err;
- }
- }
- }
- free(buffer);
- return RGBE_RETURN_SUCCESS;
- }
- int RGBE_ReadPixels_RLE(FILE *fp, float *data, int scanline_width,
- int num_scanlines)
- {
- unsigned char rgbe[4], *scanline_buffer, *ptr, *ptr_end;
- int i, count;
- unsigned char buf[2];
- if ((scanline_width < 8)||(scanline_width > 0x7fff))
- /* run length encoding is not allowed so read flat*/
- return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines);
- scanline_buffer = NULL;
- /* read in each successive scanline */
- while(num_scanlines > 0) {
- if (fread(rgbe,sizeof(rgbe),1,fp) < 1) {
- free(scanline_buffer);
- return rgbe_error(rgbe_read_error,NULL);
- }
- if ((rgbe[0] != 2)||(rgbe[1] != 2)||(rgbe[2] & 0x80)) {
- /* this file is not run length encoded */
- rgbe2float(&data[0],&data[1],&data[2],rgbe);
- data += RGBE_DATA_SIZE;
- free(scanline_buffer);
- return RGBE_ReadPixels(fp,data,scanline_width*num_scanlines-1);
- }
- if ((((int)rgbe[2])<<8 | rgbe[3]) != scanline_width) {
- free(scanline_buffer);
- return rgbe_error(rgbe_format_error,"wrong scanline width");
- }
- if (scanline_buffer == NULL)
- scanline_buffer = (unsigned char *)
- malloc(sizeof(unsigned char)*4*scanline_width);
- if (scanline_buffer == NULL)
- return rgbe_error(rgbe_memory_error,"unable to allocate buffer space");
- ptr = &scanline_buffer[0];
- /* read each of the four channels for the scanline into the buffer */
- for(i=0;i<4;i++) {
- ptr_end = &scanline_buffer[(i+1)*scanline_width];
- while(ptr < ptr_end) {
- if (fread(buf,sizeof(buf[0])*2,1,fp) < 1) {
- free(scanline_buffer);
- return rgbe_error(rgbe_read_error,NULL);
- }
- if (buf[0] > 128) {
- /* a run of the same value */
- count = buf[0]-128;
- if ((count == 0)||(count > ptr_end - ptr)) {
- free(scanline_buffer);
- return rgbe_error(rgbe_format_error,"bad scanline data");
- }
- while(count-- > 0)
- *ptr++ = buf[1];
- }
- else {
- /* a non-run */
- count = buf[0];
- if ((count == 0)||(count > ptr_end - ptr)) {
- free(scanline_buffer);
- return rgbe_error(rgbe_format_error,"bad scanline data");
- }
- *ptr++ = buf[1];
- if (--count > 0) {
- if (fread(ptr,sizeof(*ptr)*count,1,fp) < 1) {
- free(scanline_buffer);
- return rgbe_error(rgbe_read_error,NULL);
- }
- ptr += count;
- }
- }
- }
- }
- /* now convert data from buffer into floats */
- for(i=0;i<scanline_width;i++) {
- rgbe[0] = scanline_buffer[i];
- rgbe[1] = scanline_buffer[i+scanline_width];
- rgbe[2] = scanline_buffer[i+2*scanline_width];
- rgbe[3] = scanline_buffer[i+3*scanline_width];
- rgbe2float(&data[RGBE_DATA_RED],&data[RGBE_DATA_GREEN],
- &data[RGBE_DATA_BLUE],rgbe);
- data += RGBE_DATA_SIZE;
- }
- num_scanlines--;
- }
- free(scanline_buffer);
- return RGBE_RETURN_SUCCESS;
- }
2.3 FloatTiff文件格式
Tiff文件的扩展名为.tif(.tiff),FloatTiff每个通道为FP32(32bit Float Point)类型,一共3个通道96bpp。用Tiff文件存储HDR数据,直接将HDR的FP32保存到TIFF文件中,有官方库可以利用。下面是Still写的代码样例,HDR数据我采用的是LZW压缩编码:
- bool CFloatTiff::Load(const char fileName[], int& width, int& height, float** pixels)
- {
- TIFF* fp = NULL;
- if((fp = TIFFOpen(fileName, "r")) == NULL)
- return false;
- //获取信息
- uint16 bps, spp, datatype, photometric, compression, planarconfig, fillorder;
- //每个通道占据的数据位数
- if( (TIFFGetField(fp, TIFFTAG_BITSPERSAMPLE, &bps) == 0) || (bps != 32))
- return false;
- //每个象素的通道数目
- if((TIFFGetField(fp, TIFFTAG_SAMPLESPERPIXEL, &spp) == 0) || (spp != 3))
- return false;
- //每个通道的数据类型
- if((TIFFGetField(fp, TIFFTAG_SAMPLEFORMAT, &datatype) == 0) || (datatype != AMPLEFORMAT_IEEEFP))
- return false;
- //图像的数据采用的颜色模型
- if((TIFFGetField(fp, TIFFTAG_PHOTOMETRIC, &photometric) == 0) || (photometric != PHOTOMETRIC_RGB))
- return false;
- TIFFGetField(fp, TIFFTAG_IMAGEWIDTH, &width);
- TIFFGetField(fp, TIFFTAG_IMAGELENGTH, &height);
- int num = width * height * 3;
- *pixels = new float[num];
- if(NULL == *pixels)
- return false;
- if( TIFFReadEncodedStrip(fp, 0, *pixels, width * height * 3 * 4) == -1)
- return false;
- TIFFClose(fp);
- return true;
- }
- bool CFloatTiff::Save(const char fileName[], int width, int height, const float* pixels)
- {
- if(NULL == pixels)
- return false;
- TIFF *fp = NULL;
- if((fp = TIFFOpen(fileName, "w")) == NULL)
- return false;
- TIFFSetField(fp, TIFFTAG_IMAGEWIDTH, width);
- TIFFSetField(fp, TIFFTAG_IMAGELENGTH, height);
- TIFFSetField(fp, TIFFTAG_COMPRESSION, COMPRESSION_LZW);//COMPRESSION_DEFLATE;
- TIFFSetField(fp, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
- TIFFSetField(fp, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
- TIFFSetField(fp, TIFFTAG_BITSPERSAMPLE, 32);
- TIFFSetField(fp, TIFFTAG_SAMPLESPERPIXEL, 3);
- TIFFSetField(fp, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
- if(TIFFWriteEncodedStrip(fp, 0, const_cast<float*>(pixels), width * height * 3 * 4) == -1)
- return false;
- TIFFClose(fp);
- return true;
- }
官方库链接地址:http://www.remotesensing.org/libtiff/
Still注:
1、这篇文章的基础知识大部分来自:《光与影的魔术棒——HDR技术解析》http://www.cqumzh.cn/topic_show.php?tid=200271。
2、这段时间工作比较忙,关于OpenEXR文件格式的详细介绍需要翻译相关文档,而且这部分内容是05年接触的,重新总结需要一些时间;还有HDR合成、ToneMapping方面的技术下次再奉上。
HDR文件格式简介及其读写函数的更多相关文章
- 「C语言」文件的概念与简单数据流的读写函数
写完「C语言」单链表/双向链表的建立/遍历/插入/删除 后,如何将内存中的链表信息及时的保存到文件中,又能够及时的从文件中读取出来进行处理,便需要用到”文件“的相关知识点进行文件的输入.输出. 其实, ...
- (转载)C++文件读写函数之——fopen、fread和fwrite、fgetc和fputc、fgets和fputs、ftellf和fseek、rewind
http://blog.sina.com.cn/s/blog_61437b3b0102v0bt.html http://blog.csdn.net/chenwk891/article/details/ ...
- 字符编码知识简介和iconv函数的简单使用
字符编码知识简介和iconv函数的简单使用 字符编码知识简介 我们知道,在计算机的世界其实只有0和1.期初计算机主要用于科学计算,而我们知道一个数,除了用我们常用对10进制表示,也可以用2进制表示,所 ...
- Linux驱动总结3- unlocked_ioctl和堵塞(waitqueue)读写函数的实现 【转】
转自:http://blog.chinaunix.net/uid-20937170-id-3033633.html 学习了驱动程序的设计,感觉在学习驱动的同时学习linux内核,也是很不错的过程哦,做 ...
- Hive 中的复合数据结构简介以及一些函数的用法说明
参见下面这篇博客: Hive 中的复合数据结构简介以及一些函数的用法说明
- 1. python 字符串简介与常用函数
1. python中的字符串简介与常用函数 在python中,字符串变成了一个强大的处理工具集,他是不可变的,也就是说字符串包含字符与字符的顺序,他不可以原处修改 字符串是我们后面需要学习的稍大一点的 ...
- C++文件读写函数之——fopen、fread和fwrite、fgetc和fputc、fgets和fputs、ftellf和fseek、rewind
由于最近经常使用到c语言中的读写文件,所以在此总结以下,方便以后查找. 在c中,文件操作都是由库函数来实现的,主要是分为读和写两种操作,以下详细讲解以下所有有关文件操作的邯郸乎的用法: //C++写入 ...
- Python:pandas(一)——常用、读写函数read_csv、read_excel、to_csv、to_excel
学习自:pandas1.2.1documentation 0.常用 1)读写 ①从不同文本文件中读取数据的函数,都是read_xxx的形式:写函数则是to_xxx: ②对前n行感兴趣,或者用于检查读进 ...
- Python-Day3 Python基础进阶之集和/文件读写/函数
一.集和 集合是一个无序的,不重复的数据组合,它的主要作用如下: 去重,把一个列表变成集合,就自动去重了 关系测试,测试两组数据之前的交集.差集.并集等关系 1.创建集合 >>> s ...
随机推荐
- php 获取客户端IP地址经纬度所在城市
1. [代码]获取客户端IP地址经纬度所在城市 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 <?php $getIp=$_SERVER["REMOTE_ADDR ...
- JavaScript Date的原型方法扩展
在JavaScript开发中,经常需要对Date类型的对象进行各种验证或格式化,但是js并没有提供那么多的那么细的函数,所以只好自己去用 prototype 扩充了,下面是我自己实现的Date类型常用 ...
- switch语法的盲点
switch语法在项目使用的频率很低,今天看到一个相关的例子引发一些思考,,同时自己也写了一些简单的例子如下: 实例1: int dayOfWeek = 5; switch (dayOfWeek){ ...
- AIM Tech Round (Div. 2) B
B. Making a String time limit per test 1 second memory limit per test 256 megabytes input standard i ...
- [转载][mysql]mysql字符集干货
源地址:http://www.blogjava.net/zyskm/archive/2013/04/09/361888.html 字符集的概念大家都清楚,校对规则很多人不了解,一般数据库开发中也用不到 ...
- [ssh]ssh系列之一
1.使用ssh建立sock代理 ssh -D 7070 -f -N user@host -D -f 后台执行 -N 不登陆shell执行
- bzoj 4723 [POI2017]Flappy Bird 模拟
[POI2017]Flappy Bird Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 482 Solved: 196[Submit][Status ...
- 中频IF
Intermediate 频率是指中频. 一般RF射频有三个频率,基带频率,中频,射频. 射频是发射到空中的频率,也就是我们一般说的2.4G, Sub-1G.但这个频率太高,不利于放大器做增益控制.所 ...
- JQuery源码实现
技术提高篇--- 推荐--- 动脑学院--- http://www.toutiao.com/a6368703139592569089/
- CSS知识之 background-size 用法详细介绍
background-size是CSS3新增的比较实用的属性,使用它可以随心所欲地控制背景图的显示大小,而在css2中背景图的大小是不可控制的. 基本语法: 用于设置背景图片的大小,有2个可选值,第1 ...