一、引言

本人初次接触HDR方面的知识,有描述不正确的地方烦请见谅。

为方便文章描述,引用部分百度中的文章对HDR图像进行简单的描述。

高动态范围图像(High-Dynamic Range,简称HDR),相比普通的图像,可以提供更多的动态范围和图像细节,根据不同的曝光时间的LDR(Low-Dynamic Range)图像,利用每个曝光时间相对应最佳细节的LDR图像来合成最终HDR图像,能够更好的反映人真实环境中的视觉效果。

现实真正存在的亮度差,即最亮的物体亮度,和最小的物体亮度之比为108, 而人类的眼睛所能看到的范围是105左右,但是一般的显示器,照相机能表示的只有256种不同的亮度,计算一般的显示器,照相机能表示的只有256种不同的亮度机在表示图象的时候是用8bit(256)级或16bit(65536)级来区分图象的亮度的,但这区区几百或几万无法再现真实自然的光照情况。HDR文件是一种特殊图形文件格式,它的每一个像素除了普通的RGB信息,还有该点的实际亮度信息。普通的图形文件每个象素只有0 -255的灰度范围,这实际上是不够的。想象一下太阳的发光强度和一个纯黑的物体之间的灰度范围或者说亮度范围的差别,远远超过了256个级别。因此,一张普通的白天风景图片,看上去白云和太阳可能都呈现是同样的灰度/亮度,都是纯白色,但实际上白云和太阳之间实际的亮度不可能一样,他们之间的亮度差别是巨大的。因此,普通的图形文件格式是很不精确的,远远没有纪录到现实世界的实际状况。而HDR格式则记录了很广范围内的亮度信息。

但是最终,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较为理想(这里是指数据量化到了0-1之间的)。

  所以都取优化的参数,则上述过程可以自动进行。

作者提到上述log操作都是以10为底进行的,我觉得以e为底实际效果也没啥区别的。

  三、效果

按照这个思路编制程序后,确实能取得很不错的效果,比如上述两幅图像,按照前面讲的参数取值,解码后得到的图像如下:

 

可见,图像的细节较为完美的体现出来了。

当然,自动的参数不一定能调处最好的效果,比如还是这两幅图,手工选择一些参数,可以调出如下效果:

 

  特别是第一幅图,很有种蒙太奇的感觉。

在看看几张长出现在论文中的图像的结果:

 

 

 

 

          线性解码图                                              双边滤波解码图

  有些图线性解码啥都看不到,双边滤波解码后细节表现的就很清晰了。

HDR格式的原始数据的解码可以借助FreeImage来实现,FreeImage似乎已经讲这些数据量化到了0和1之间(不一定正确)。一段简单的实现代码如下:

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, );
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 != )
{
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 * * sizeof(float));
CopyMemory(RawData, Data, (int)Width * (int)Height * * sizeof(float)); Bmp = new Bitmap((int)Width, (int)Height, PixelFormat.Format24bppRgb);
BitmapData BmpData = Bmp.LockBits(new Rectangle(, , Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
Pixel = (byte*)BmpData.Scan0; for (int Y = ; Y < Height; Y++)
{
Speed = Y * BmpData.Stride;
Index = Y * (int)Width * ;
for (int X = ; X < Width; X++)
{
Value = (Data[Index + ] * );
if (Value > )
Value = ;
else if (Value < )
Value = ;
Pixel[Speed] = (byte)Value;
Value = (Data[Index + ] * );
if (Value > )
Value = ;
else if (Value < )
Value = ;
Pixel[Speed + ] = (byte)Value;
Value = (Data[Index + ] * );
if (Value > )
Value = ;
else if (Value < )
Value = ;
Pixel[Speed + ] = (byte)Value;
Index += ;
Speed += ;
}
}
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的对比度

对于普通的整体偏暗的图像,该方式也能取得相当理想的效果,一些测试结果如下:

  

  

  

很多其他的算法也能起到类似上述的效果,不过他们一般很容易产生halo现象。

相信对于偏亮的普通照片,也可以有同样的处理能力(未找到合适的测试图片)。

*********************************作者: laviewpbt   时间: 2013.11.18   联系QQ:  33184777  转载请保留本行信息************************

Tone Mapping算法系列一:基于Fast Bilateral Filtering 算法的 High-Dynamic Range(HDR) 图像显示技术。的更多相关文章

  1. 基于Fast Bilateral Filtering 算法的 High-Dynamic Range(HDR) 图像显示技术。

    一.引言 本人初次接触HDR方面的知识,有描述不正确的地方烦请见谅. 为方便文章描述,引用部分百度中的文章对HDR图像进行简单的描述. 高动态范围图像(High-Dynamic Range,简称HDR ...

  2. [算法系列之二十七]Kruskal最小生成树算法

    简单介绍 求最小生成树一共同拥有两种算法,一个是就是本文所说的Kruskal算法,还有一个就是Prime算法. 在具体解说Kruskal最小生成树算法之前,让我们先回想一下什么是最小生成树. 我们有一 ...

  3. 数据结构与算法系列研究七——图、prim算法、dijkstra算法

    图.prim算法.dijkstra算法 1. 图的定义 图(Graph)可以简单表示为G=<V, E>,其中V称为顶点(vertex)集合,E称为边(edge)集合.图论中的图(graph ...

  4. 阅读Real-Time O(1) Bilateral Filtering 一文的相关感受。

    研究双边滤波有很长一段时间了,最近看了一篇Real-Time O(1) Bilateral Filtering的论文,标题很吸引人,就研读了一番,经过几天的攻读,基本已理解其思想,现将这一过程做一简单 ...

  5. Tone Mapping算法系列二:一种自适应对数映射的高对比度图像显示技术及其速度优化。

    办公室今天停电,幸好本本还有电,同事们好多都去打麻将去了,话说麻将这东西玩起来也还是有味的,不过我感觉我是输了不舒服,赢了替输的人不舒服,所以干脆拜别麻坛四五年了,在办公室一个人整理下好久前的一片论文 ...

  6. 基于物理的渲染—HDR Tone Mapping

    在游戏引擎渲染管线中,我们对于R.G.B通道颜色信息的数值范围通常设置在[0,1]之间(或者是[0,255]).其中,0代表没有光亮度,1代表显示器能够显示的最大光亮度.这个表示方式虽然直接易懂,但它 ...

  7. Computer Vision_33_SIFT:Fast Adaptive Bilateral Filtering——2018

    此部分是计算机视觉部分,主要侧重在底层特征提取,视频分析,跟踪,目标检测和识别方面等方面.对于自己不太熟悉的领域比如摄像机标定和立体视觉,仅仅列出上google上引用次数比较多的文献.有一些刚刚出版的 ...

  8. 关于Cewu Lu等的《Combining Sketch and Tone for Pencil Drawing Production》一文铅笔画算法的理解和笔录。

     相关论文的链接:Combining Sketch and Tone for Pencil Drawing Production 第一次看<Combining Sketch and Tone f ...

  9. 算法系列:FFT 001

    转载自http://blog.csdn.net/orbit/article/details/17210461 2012年9月的时候,一个南京的大学生从电视台播放的一段记者采访360总裁周鸿祎的视频中破 ...

随机推荐

  1. Revit Family API 添加参数与尺寸标注

    使用FamilyManager其他的与普通添加参数与标注没区别. [TransactionAttribute(Autodesk.Revit.Attributes.TransactionMode.Man ...

  2. .Net Discovery 系列之七--深入理解.Net垃圾收集机制(拾贝篇)

    关于.Net垃圾收集器(Garbage Collection),Aicken已经在“.Net Discovery 系列”文章中有2篇的涉及,这一篇文章是对上2篇文章的补充,关于“.Net Discov ...

  3. c编程:输入一个数字n,则n代表n行,每行输入2个数字a,b计算每行的a+b问题。

    输入 第一行输入要计算的数据组数 n 接下来的 n 行,每行包括两个数a和b 输出 每行输出一组数据中a+b的值 例子输入 2 1 2 4 0 例子输出 3 4 代码: #include<std ...

  4. 在Visual Studio中开发一个C语言程序

    →新建一个项目→选择"其他语言","Visual C++",并选择"win32控制台应用程序",并给控制台应用程序起名.→点击"下 ...

  5. ASP.NET MVC异步验证是如何工作的02,异步验证表单元素的创建

    在上一篇"ASP.NET MVC异步验证是如何工作的01,jQuery的验证方式.错误信息提示.validate方法的背后"中,了解了jQuery如何验证,如何显示错误信息,本篇要 ...

  6. javascript:currentStyle和getComputedStyle的兼容写法

    currentStyle:获取计算后的样式,也叫当前样式.最终样式. 优点:可以获取元素的最终样式,包括浏览器的默认值,而不像style只能获取行间样式,所以更常用到. 注意:不能获取复合样式如bac ...

  7. Lombok使用详解(转)

    本文转自https://blog.csdn.net/u010695794/article/details/70441432 2017年04月22日 15:17:00 阅读数:10394 Lombok使 ...

  8. 冰川时代5:星际碰撞Ice Age: Collision Course迅雷下载

    影片讲述松鼠奎特为了追松果,偶然引发了宇宙事件,改变并威胁着冰川时代的世界.为了拯救自己,话唠树懒希德.猛犸象曼尼.剑齿虎迪亚哥,以及别的动物群族必须离开家园,踏上了他们充满喜剧色彩的冒险旅程,他们来 ...

  9. 犯罪现场调查第一季/全集CSI迅雷下载

    英文译名 CSI (第1季) (2000首播)CBS. 本季看点:<犯罪现场调查>赌城拉斯维加斯吸引着做发财梦的人,也吸引着形形色色的罪犯,该市警察局的犯罪现场调查局在全美国名列第二.该剧 ...

  10. Toast的用法(可以设置显示时间,自定义布局的,线程中的Toast)

           自定义的Toast类 布局文件 <?xml version="1.0" encoding="utf-8"?> <LinearLa ...