高动态范围图像(High-Dynamic Range,简称HDR),相比普通的图像,可以提供更多的动态范围和图像细节,根据不同的曝光时间的LDR(Low-Dynamic
Range)图像,利用每个曝光时间相对应最佳细节的LDR图像来合成最终HDR图像,能够更好的反映人真实环境中的视觉效果。
现实真正存在的亮度差,即最亮的物体亮度,和最小的物体亮度之比为108,
而人类的眼睛所能看到的范围是105左右,但是一般的显示器,照相机能表示的只有256种不同的亮度,计算一般的显示器,照相机能表示的只有256种不同的亮度机在表示图象的时候是用8bit(256)级或16bit(65536)级来区分图象的亮度的,但这区区几百或几万无法再现真实自然的光照情况。HDR文件是一种特殊图形文件格式,它的每一个像素除了普通的RGB信息,还有该点的实际亮度信息。普通的图形文件每个象素只有0
-255的灰度范围,这实际上是不够的。想象一下太阳的发光强度和一个纯黑的物体之间的灰度范围或者说亮度范围的差别,远远超过了256个级别。因此,一张普通的白天风景图片,看上去白云和太阳可能都呈现是同样的灰度/亮度,都是纯白色,但实际上白云和太阳之间实际的亮度不可能一样,他们之间的亮度差别是巨大的。因此,普通的图形文件格式是很不精确的,远远没有纪录到现实世界的实际状况。
以上均摘自百度相关网页,放在这里只是做个简单的介绍,我们实际关心的是如果已经有了原始的hdr数据,如何利用显示器完美的把文件中包含的图像信息表达出来。
简单的说,就是现在有一堆离散的数据,数据的分布范围可能很广,如何把这些离散的数据隐射到0到255之间。
最简单的当然是线性隐射,先算出离散数据的最大值和最小值,然后将数据线性的拉升至0到255之间,这种直接的操作往往无法得到满意的效果,会导致大量细节丢失,表现在视觉上就是一大块黑色或者一大块白色的,如下图所示:
上面两幅图要么是暗部太暗,要么是亮部太亮,整体对比度太强,导致细节信息大量丢失。
针对这一问题,很多人提出了不少相当不错的解决方案,比如基于全局操作符的,其中本文作者实现其中的基于快速双边滤波技术的HDR显示过程。
本文对应的参考论文地址: Fast Bilateral Filtering for the Display of High-Dynamic-Range Images
论文的细路很简单,首先他将原始的HDR数据分解成两个层:base layer 和 detail layer,然后降低base layer的对比度,不改变detail layer的数据,在将这两层合并。
其中:base layer的数据用 HDR原始数据进行双边滤波获取。
算法的简单流程入下所示:
1、input intensity= 1/61*(R*20+G*40+B)
2、r=R/(input intensity), g=G/input intensity, B=B/input intensity
3、log(base)=Bilateral(log(input intensity))
4、log(detail)=log(input intensity)-log(base)
5、log (output intensity)=log(base)*compressionfactor+log(detail) - log_absolute_scale
6、R output = r*10^(log(output intensity)), etc.
上述过程中的变量compressionfactor,log_absolute_scale原文作者的建议取值为:
compressionfactor = targetContrast/(max(log(base)) - min(log(base))) 对于很多图像,targetContrast使用log(5)能获得较为理想的值。
而log_absolute_scale= max(log(base))*compressionfactor;
在进行双边的时候,SigmaS一般取值为0.02*Max(Width,Height)比较合适,而SigmaR取值0.4较为理想。
所以都取优化的参数,则上述过程可以自动进行。
作者提到上述log操作都是以10为底进行的,我觉得以e为底实际效果也没啥区别的。
按照这个思路编制程序后,确实能取得很不错的效果,比如上述两幅图像,按照前面讲的参数取值,解码后得到的图像如下:
可见,图像的细节较为完美的体现出来了。
当然,自动的参数不一定能调处最好的效果,比如还是这两幅图,手工选择一些参数,可以调出如下效果:
特别是第一幅图,很有种蒙太奇的感觉。
在看看几张长出现在论文中的图像的结果:
线性解码图 双边滤波解码图
有些图线性解码啥都看不到,双边滤波解码后细节表现的就很清晰了。
HDR格式的原始数据的解码可以借助FreeImage来实现,一段简单的实现代码如下:
public Bitmap LoadHdrFormFreeImage(string FileName) { Bitmap Bmp = null; FREE_IMAGE_FORMAT fif = FREE_IMAGE_FORMAT.FIF_UNKNOWN; ; if (FreeImage.IsAvailable() == true) { fif = FreeImage.GetFileType(FileName, 0); if (fif != FREE_IMAGE_FORMAT.FIF_HDR) { MessageBox.Show("不是Hdr格式的图像."); return null; } fif = FreeImage.GetFIFFromFilename(FileName); FIBITMAP Dib = FreeImage.Load(fif, FileName, FREE_IMAGE_LOAD_FLAGS.DEFAULT); uint Bpp = FreeImage.GetBPP(Dib); if (Bpp != 96) { MessageBox.Show("无法支持的Hdr格式."); FreeImage.Unload(Dib); return null; } uint Width = FreeImage.GetWidth(Dib); // 图像宽度 uint Height = FreeImage.GetHeight(Dib); // 图像高度 uint Stride = FreeImage.GetPitch(Dib); // 图像扫描行的大小,必然是4的整数倍 IntPtr Bits = FreeImage.GetBits(Dib); float* Data = (float*)Bits; int Speed, Index; byte* Pixel; float Value; if (RawData != null) Marshal.FreeHGlobal((IntPtr)RawData); RawData = (float*)Marshal.AllocHGlobal((int)Width * (int)Height * 3 * sizeof(float)); CopyMemory(RawData, Data, (int)Width * (int)Height * 3 * sizeof(float)); Bmp = new Bitmap((int)Width, (int)Height, PixelFormat.Format24bppRgb); BitmapData BmpData = Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); Pixel = (byte*)BmpData.Scan0; for (int Y = 0; Y < Height; Y++) { Speed = Y * BmpData.Stride; Index = Y * (int)Width * 3; for (int X = 0; X < Width; X++) { Value = (Data[Index + 2] * 255); if (Value > 255) Value = 255; else if (Value < 0) Value = 0; Pixel[Speed] = (byte)Value; Value = (Data[Index + 1] * 255); if (Value > 255) Value = 255; else if (Value < 0) Value = 0; Pixel[Speed + 1] = (byte)Value; Value = (Data[Index + 0] * 255); if (Value > 255) Value = 255; else if (Value < 0) Value = 0; Pixel[Speed + 2] = (byte)Value; Index += 3; Speed += 3; } } FreeImage.Unload(Dib); Bmp.UnlockBits(BmpData); Bmp.RotateFlip(RotateFlipType.RotateNoneFlipY); return Bmp; } else return null; }
以上采用的是线性解码。
附一个解码的调用程序:http://files.cnblogs.com/Imageshop/ReadHdrTest.rar
更多的源码可参考:http://people.csail.mit.edu/sparis/code/src/tone_mapping
http://people.csail.mit.edu/fredo/PUBLI/Siggraph2002/
一些常见的用于测试的HDR图像可以从这里下载:http://www.pauldebevec.com/Research/HDR/
同样,这种 tone mapping算法也可以用在普通的RGB图像上,效果如下所示:
原图 增加base layer的对比度
原图 降低base layer的对比度