2、HDRI文件格式介绍(OpenEXR、Radiance RGBE、Float TIFF)
HDRI(High-Dynamic Range Image)就是记录采用了HDR技术的图象数据文件。常用的HDRI文件有OpenEXR、Radiance RGBE、FloatTIFF三种格式。
2.1 OpenEXR文件格式
OpenEXR是由工业光魔(Industrial Light & Magic)开发的一种HDR标准。OpenEXR文件的扩展名为.exr,常见的OpenEXR文件是FP16(16bit Float Point,也被称为half Float 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正式名称为Radiance RGBE格式。这个本来是BR、FR等作为radiance材质的一种格式,也叫做radiance map,后来成为流行的一种HDR格式。所谓E,就是指数。Radiance RGBE文件每个通道为8bit BYTE数据类型,4个通道一共是32 bit。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数据与HDR FP32(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图象运算时候的内存数据类型,而Radiance RGBE文件采用8bit BYTE类型存储HDR数据。也就是说打开Radiance RGBE文件,要使用上面的公式1将Radiance RGBE文件的8bit BYTE文件数据转换为FP 32的HDR内存数据进行运算;保存为Radiance RGBE文件时,要使用上面的公式2将HDR的FP32内存数据转换为Radiance RGBE的8bit BYTE文件数据进行保存。同理,OpenEXR文件的读写也存在将其FP 16的文件数据到HDR的 FP32图象数据的转换;而下面将要讲的Float Tiff是不需要进行数据转换,直接将HDR的FP 32图象数据保存到TIFF文件中即可。
2、Radiance有多种文件格式,其官方库包含内容比较复杂,所以,实际的读写没有使用其官方库,而是使用了网络上一个简单的C语言读写类,Still并对其进行了部分修改(在文件头写入“Generated by Still”)。
读写类链接地址:http://www.graphics.cornell.edu/~bjw/rgbe.html
官方库链接地址:http://radsite.lbl.gov/radiance/
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方面的技术下次再奉上。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=609464