精华内容
下载资源
问答
  • HDR, tone mapping代码

    2017-03-21 13:16:41
    HDR, tone mapping代码
  • tone mapping曲线的本质是一个亮度映射函数,即横坐标(X 轴)是输入亮度,纵坐 标(Y 轴)是输出亮度。其作用是将输入图像的亮度映射到一个更窄的亮度范围,从而实现动态范围的压缩。 Tone mapping中的对数函数...

        tone mapping曲线的本质是一个亮度映射函数,即横坐标(X 轴)是输入亮度,纵坐 标(Y 轴)是输出亮度。其作用是将输入图像的亮度映射到一个更窄的亮度范围,从而实现动态范围的压缩。

         Tone mapping中的对数函数曲线性质,主要取决于输入数据范围大小。如果输入数据相对于[0,65535]的输入数据范围比较小,比如8bit的输入数据,其对数函数的曲线将没有那么陡峭,输出较小的数值Yg,对高亮部分的压缩也比较小; 相反对于16bit输入数据其暗区曲线较陡峭,高亮部分压缩较大。

        如上图亮度映射曲线所示。Drc 根据tone mapping算法原理,硬件使用查找表的形式开放出相应的可调接口给到用户进行不同类型的tone mapping 亮度映射函数的设置,实现不同的DRC效果。DRC算法中还需要需要考虑到颜色还原、halo atrifact、noise suppression.的处理,以及tone mapping 处理之后的对比度增强处理,才能实现比较好的DRC效果。

        线性压缩和tone mapping压缩图像对比:

     

         左侧是Tone mapping效果图,右侧是线性压缩之后的效果图。可以看出,经过线性压缩之后的效果,要么高亮的部分完全过曝,较暗的部分完全看不清图像信息。

        选取要硬件实现的tone mapping算法时,需要兼顾色调映射的效果和算法复杂度。虽然专用硬件运算速度很快,但硬件运算的灵活性远不及软件运算。全局色调映射算法本质上是一条单增的映射曲线,硬件实现相对简单。但由于不同相邻照度值的像素点会映射至同一灰度值,局部对比度很低,画质下降十分明显,且单一的曲线很难适应各种场景。局部色调映射效果好,但算法复杂度较高,有些算法算法硬件化成本很高,有些算法甚至无法实现硬件化。所以有些DRC算法先是进行全局tone mapping,之后再进行局部对比度增强。

       tone mapping的曲线为非线性函数,一般来说,非线性函数的硬件实现方法有:用以下方法拟合tone mapping的压缩函数 比如log函数

    1. 进行泰勒级数展开,提取级数的前几个乘加项进行 Verilog 描述;
    2. 查找表法,权衡查找精度和硬件资源遍历变量的范围;
    3. 折线拟合法,通过将非线性函数进行分段表示,然后用一次函数对每段折线进行描述;
    4. 多次函数拟合法,这种方法是级数展开法和折线拟合法的折衷,即用二次或者三次函数去拟合分段的非线性函数;
    5. 坐标旋转数值计算方法(CORDIC, Coordinate Rotation Digital Computer),通过基本的移位与加法运算代替乘法运算,通过迭代的方式计算三角函数、开方、反三角、指数等运算。

    实际实现的时候,大多数采用分段折线拟合的方式构建tone mapping算法所需要的曲线。

    展开全文
  • Tone Mapping(色调映射) 认识

    千次阅读 2021-02-21 10:06:26
    Tone Mapping(色调映射) 认识 目录 【背景 - 高动态图像】 【起源】 【定义】 【举个例子】 【研究分类】 详细 1. 【背景 - 高动态图像】 图像的动态范围: 图像的 Max(亮度值) / Min(亮度值) 高动态图像 ...

    转自:https://www.cnblogs.com/cjhd/p/7530440.html

    Tone Mapping(色调映射) 认识

    • 目录

           

    1. 【背景 - 高动态图像】
    2. 【起源】
    3. 【定义】
    4. 【举个例子】
    5. 【研究分类】
    • 详细

    1. 【背景 - 高动态图像】

    图像的动态范围: 图像的 Max(亮度值) /  Min(亮度值)

    高动态图像       : 动态范围大的图像

    高动态 VS 普通 : 高动态图像(位数 > 8位)    普通灰度图像(位数 = 8位 一般情况)   显示器的灰度Only 8位

    怎么办 -处理     : 将高动态图像的颜色 -> 变换 才能显示出来;

    另外高动态图像的灰度值分布的很不均匀,只有少数的像素点较亮,所以如果直接对图像进行线性的归一化(把灰度最大值映射为255,最小值映射为0)再显示,则图像会一片黑(参考左下图)。色调映射(tone mapping)就是为了解决这个问题而生。

                  --- 延伸:自然图像亮度特点。 亮度单位是坎德拉/平方米(cd/m2), 自然界中的亮度从星光(10^-2 cd/m2数量级),到日光(10^4 cd/m2数量级),动态范围为10^6,非常大。

    2. 【起源】

    eg1 : 在18世纪时,画家在作画的时候面临着这样一个问题:自然界中光线亮度的范围非常大,而颜料颜色的范围却非常有限。

                因此,找出一套行之有效的颜色转换方法,用范围有限的颜料,画出颜色范围很宽的自然光,变得尤为重要。现如今,这种颜色的转换的方法,就成为色调映射。

    eg2 : 在摄影学中,底片的对比度往往很低,直接冲洗的话,很难表现图片中的细节。

                所以也需要对底片进行处理(Dodging and Burning),这也引入了色调映射的一类方法,详细内容会在后面叙述。

    eg3 : 在20世纪70年代,电子相机的出现,人们开始使用模拟电路,来处理图像。

                之后随着数字相机的出现与发展,发展出了现在的各种色调映射(Tone mapping)算法

    3. 【定义】

     色调映射算法 : 指对图像颜色进行映射变换的算法。

                    目的, 调整图像的灰度,使得处理后的图像人眼看起来更加舒适,能更好的表达原图里的信息与特征。

                   理解 : 通常被理解为将颜色值从高动态范围(HDR)映射到低动态范围(LDR) 的过程。

                       在Unity(虚拟现实引擎软件)中,这意味着对于大多数平台上任意16 位的浮点型颜色值将被映射为 [0,1] 范围内的传统8位颜色值。

    4. 【举个例子】

     Gamma校正

     

    5. 【研究分类】

     色调映射研究方法大致分为两类 : 全局 / 局部

    1> 全局算法

    特点 :

    1. 任意相同颜色的像素点,在映射后,还是相同的颜色;

    2.全局算法一般较简单,速度快;

    3.全局算法的性能一般劣于局部方法;

    存在算法 : 直方图均衡化、Gamma、对数校正、直方图规定化、分段灰度变换

    2>局部算法

    特点 :

     

    1.映射前颜色相同的像素点,映射后颜色可能不同

    2.局部算法一般较全局方法更复杂,速度相对较慢;

    3.局部算法的性能一般优于全局方法;

    4.会出现光晕等现象

    存在算法 : 分块中值直方图等

     

    特别感谢新浪博客作者 陈法圣 资料链接: http://blog.sina.com.cn/s/blog_6a5781350102w707.html 

    相关公式参考链接 :http://www.cnblogs.com/bigbigtree/p/3458797.html

    相关优秀算法示例 :https://wenku.baidu.com/view/d5996bb8be23482fb5da4c26.html

    以上内容只是简单搭起框架 , 日后在做项目或研究过程中会持续更新。。。

    展开全文
  • Tone mapping

    千次阅读 2018-03-12 16:22:39
    转自KlayGE游戏引擎Reinhard早期的普遍做法是一篇叫做Photographic Tone Reproduction for Digital Images的论文,大家就用作者的名字称它为Reinhard tone mapping。这是个经验公式,把HDR到LDR的变换简单的描述了...

    转自KlayGE游戏引擎

    Reinhard

    早期的普遍做法是一篇叫做 Photographic Tone Reproduction for Digital Images 的论文,大家就用作者的名字称它为Reinhard tone mapping。这是个经验公式,把HDR到LDR的变换简单的描述了出来。
    1
    2
    3
    4
    5
    6

    float3 ReinhardToneMapping(float3 color, float adapted_lum)
    {
        const float MIDDLE_GREY = 1;
        color *= MIDDLE_GREY / adapted_lum;
        return color / (1.0f + color);
    }

    其中color是线性的HDR颜色,adapted_lum是根据整个画面统计出来的亮度。MIDDLE_GREY表示把什么值定义成灰。这个值就是纯粹的magic number了,根据需要调整。Reinhard的曲线是这样的,可以看出总体形状是个S型。


    从结果上看,经过Reinhard tone mapping是这样的。


    这种tone mapping的方法更多地来自于经验,没什么原理在后面。它的优点是简单直接,把亮的变暗,暗的变量。这样暗处和亮处细节就都出来了。但缺点也很明显,就是灰暗。个个颜色都朝着灰色的方向被压缩了,画面像蒙了一层纱。


    CE
    后来在2007年学习了CryEngine 2的tone mapping处理法之后,就改成用这种方法。这个方法更简单,只要一行,而且没有magic number。用一个exp来模拟S曲线。

    1
    2
    3
    4

    float3 CEToneMapping(float3 color, float adapted_lum) 
    {
        return 1 - exp(-adapted_lum * color);
    }

    CE的曲线中间的区域更偏向于小的方向,这部分曲线也更陡。


    这个方法得到的结果比Reinhard有更大的对比度,颜色更鲜艳一些,虽然还是有点灰。



    Filmic
    CE的tone mapping就那么用了好几年,直到2010年,看到了一个叫Filmic tone mapping的方法,来自于Uncharted 2。当年我也写过一篇文章讲这件事情。这个方法的本质是把原图和让艺术家用专业照相软件模拟胶片的感觉,人肉tone mapping后的结果去做曲线拟合,得到一个高次曲线的表达式。这样的表达式应用到渲染结果后,就能在很大程度上自动接近人工调整的结果。最后出来的曲线是这样的。总的来说也是S型,但增长的区域很长。


    从结果看,对比度更大,而且完全消除了灰蒙的感觉。


    而代码就有点复杂了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    float3 F(float3 x)
    {
        const float A = 0.22f;
        const float B = 0.30f;
        const float C = 0.10f;
        const float D = 0.20f;
        const float E = 0.01f;
        const float F = 0.30f;
        return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
    }

    float3 Uncharted2ToneMapping(float3 color, float adapted_lum)
    {
        const float WHITE = 11.2f;
        return F(1.6f * adapted_lum * color) / F(WHITE);
    }

    那些ABCDEF都是多项式的系数,而WHITE是个magic number,表示白色的位置。这个方法开启了tone mapping的新路径,让人们知道了曲线拟合的好处。并且,其他颜色空间的变换,比如gamma矫正,也可以一起合并到这个曲线里来,一次搞定,不会增加额外开销。缺点就是运算量有点大,两个多项式的计算,并且相除。

    因为Filmic tone mapping的优异表现,大部分游戏都切换到了这个方法。包括CE自己,也在某个时候完成了切换。


    ACES
    到了2016年,美国电影艺术与科学学会,就是颁布奥斯卡奖的那个机构,在推广一个叫Academy Color Encoding System(ACES)的东西。这是一套颜色编码系统,或者说是一个新的颜色空间。它是一个通用的数据交换格式,一方面可以不同的输入设备转成ACES,另一方面可以把ACES在不同的显示设备上正确显示。不管你是LDR,还是HDR,都可以在ACES里表达出来。这就直接解决了在不同设备间互通数据的颜色转换问题。然而对于实时渲染来说,没必要用全套ACES。因为第一,没有什么“输入设备”。渲染出来的HDR图像就是个线性的数据,所以直接就在ACES空间中。而输出的时候需要一次tone mapping,转到LDR或另一个HDR。也就是说,我们只要ACES里的非常小的一条路径,而不是纷繁复杂的整套体系。

    那么这条路径有多小呢?只要几行,系数来自于Krzysztof Narkowicz的博客文章

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    float3 ACESToneMapping(float3 color, float adapted_lum)
    {
        const float A = 2.51f;
        const float B = 0.03f;
        const float C = 2.43f;
        const float D = 0.59f;
        const float E = 0.14f;
        color *= adapted_lum;
        return (color * (A * color + B)) / (color * (C * color + D) + E);
    }

    看着很像Uncharted 2的做法吧,都是多项式拟合。但是式子比Uncharted的简单,并不需要算两个多项式并相除,只要算一个,一次搞定。它的曲线是这样的。


    S感很浓,并且比Uncharted的更向小的方向移,即便很小的数值和接近1的数值也有梯度。这样能很好地保留暗处和亮处的细节。至于视觉效果如何呢?看看这个。

    可以看出来,比之前的任何一个都要鲜艳,并且没有因此丢掉细节!当之无愧成为目前最好的tone mapping算法。

    更好的地方是,按照前面说的,ACES为的是解决所有设备之间的颜色空间转换问题。所以这个tone mapper不但可以用于HDR到LDR的转换,还可以用于从一个HDR转到另一个HDR。也就是从根本上解决了VDR的问题。这个函数的输出是线性空间的,所以要接到LDR的设备,只要做一次sRGB校正。要接到HDR10的设备,只要做一次Rec 2020颜色矩阵乘法。Tone mapping部分是通用的,这也是比之前几个算法都好的地方。

    目前一些新的游戏,比如Rise of the Tomb Raider、UE 4.8,也都切换到ACES的tone mapping曲线。
    展开全文
  • 是一本讲述色调映射以及图像优化的书籍,我从国外网站上下载的,很难找的一本书。对学习ISP及图像处理很有帮助。
  • tone mapping

    2015-11-09 20:04:19
    mablab 实现图像处理,将图像的高光和低光部分都映射到一张图片上,便于打印和显示器的输出。
  • Adaptive Local Tone Mapping Based on Retinex ,描述了一种快速简单而又有效的低照度图像恢复算法
  • 利用l0,l1范数对图像进行分层,分为细节层和基本层D1和B1,然后再对基本层进行分层,得到更加细小的细节层和基本层D2和B2,最后再对B2基本层进行gamma压缩,对D1进行拉伸,再将处理过后的D1
  • Tone Mapping(简洁直观的Tonemapping介绍)

    千次阅读 2019-11-25 14:36:54
    转自:...What is tone mapping? Most monitors are capable of displaying RGB values in the range of[0, 255][0,255]. However, in real life, there is no limit on the amount ...

    转自:https://64.github.io/tonemapping/

    What is tone mapping?

    Most monitors are capable of displaying RGB values in the range of [0, 255][0,255]. However, in real life, there is no limit on the amount of light 'energy' incident on a point. Most renderers output linear radiance values in [0, \infty)[0,∞), which needs to be mapped into a viewable range. Those radiance values are described as High Dynamic Range (HDR), because they are unlimited, and the viewable target range is described as Low Dynamic Range (LDR), because there is a fixed limit of 255. Put simply, tone mapping is the process of mapping HDR values in [0, \infty)[0,∞) into LDR values (e.g values in [0, 255][0,255] or [0.0, 1.0][0.0,1.0]).

    tone mapping operator (TMO) is essentially just a function which maps an input color (e.g an RGB triple) to an output color:

    C_{\mathrm{out}} = \mathrm{TMO}(C_{\mathrm{in}})Cout​=TMO(Cin​)

    At this point you might be a little confused as to why you've never had to think about tone mapping inside your program. If you're using an API like OpenGL, your radiance values are probably just being clamped by the implementation to [0.0, 1.0][0.0,1.0] in the final framebuffer. This is essentially a trivial tone mapping operator:

    \mathrm{TMO_{clamp}}(C) = \mathrm{clamp}(C, 0.0, 1.0)TMOclamp​(C)=clamp(C,0.0,1.0)

    This TMO, as you might guess, is quite flawed. Here's what it looks like when we apply this TMO to the memorial scene (the HDR scene file can be downloaded here):

     

    Tone mapping with clamp

    High radiance values are be completely lost beyond a certain point. This results in the white areas (like the middle stained glass window) looking completely 'blown out'. For this particular scene it's not too bad because most of the radiance values are in [0.0, 1.0][0.0,1.0] already, but for a brighter outdoors scene it would look a lot worse. You could alleviate the issue somewhat by dividing the input color by some value before clamping:

    \mathrm{TMO_{clamp2}}(C) = \mathrm{clamp}\left(\frac{C}{k}, 0.0, 1.0\right)TMOclamp2​(C)=clamp(kC​,0.0,1.0)

    However, as it turns out, we don't really want a linear relationship between input and output color. Human color perception is very non-linear - the difference between 1 light source and 2 light sources illuminating a point appears much greater than the difference between 100 light sources and 101 light sources. Non-linear TMOs are potentially able to reproduce images which can convey more detail to our eyes. Floats actually work great for storing HDR values for this exact reason - they have higher precision nearer zero, and lower precision as their value increases towards infinity.

    The rest of this guide will explore a few simple tone mapping operators, as well as some underlying theory which should help you research the topic further.

    Reinhard

    This is one of the simplest and most common TMOs, described by Reinhard et al. in this paper. Simply put, it is:

    \mathrm{TMO_{reinhard}}(C) = \frac{C}{1 + C}TMOreinhard​(C)=1+CC​

    (Note that this isn't exactly what Reinhard is proposing in his paper, but more on that further down.)

    This is mathematically guaranteed to produce a value in [0.0, 1.0][0.0,1.0]. By differentiating, you can see that C_{\mathrm{out}}Cout​ has an inverse-square falloff (e.g a radiance value of 4 results in something which is only 1.2 times as bright as a radiance value of 2).

    In C++:

    vec3 reinhard(vec3 v)
    {
        return v / (1.0f + v);
    }
    

    Compare this image to the previous 'clamp' TMO. Reinhard looks much more grey-ish, and the whites are less blown out (more detail is noticeable in the middle stained glass window).

     

    Reinhard tone mapping

    Extended Reinhard

    The problem with the 'simple' Reinhard TMO is that it doesn't necessarily make good use of the full Low Dynamic Range. If our max scene radiance happened to be (1.0, 1.0, 1.0)(1.0,1.0,1.0) then the resulting maximum brightness would only be (0.5, 0.5, 0.5)(0.5,0.5,0.5) - only half of the available range. Fortunately, the paper by Reinhard presents a way to scale and make use of the full range:

    \mathrm{TMO_{reinhardext}}(C) = \frac{C\left(1 + \frac{C}{C_{\mathrm{white}}^2}\right)}{1 + C}TMOreinhardext​(C)=1+CC(1+Cwhite2​C​)​

    where C_{\mathrm{white}}Cwhite​ is the biggest radiance value in the scene. Now, our biggest radiance value will get mapped to (1.0, 1.0, 1.0)(1.0,1.0,1.0), using the full LDR.

    (Note that you can also just set C_\mathrm{white}Cwhite​ to a value lower than the maximum radiance, which will ensure that anything higher gets mapped to (1.0, 1.0, 1.0)(1.0,1.0,1.0) - for this reason it is sometimes referred to as the 'white point'.)

    In C++:

    vec3 reinhard_extended(vec3 v, float max_white)
    {
        vec3 numerator = v * (1.0f + (v / vec3(max_white * max_white)));
        return numerator / (1.0f + v);
    }
    

    If we just set our max scene radiance as the white point, this TMO appears almost exactly the same as the simple reinhard operator. That's because the max radiance for this scene is 622, and \frac{622}{1+622} \approx 0.9981+622622​≈0.998, so the difference between the extended and the simple variants in this case is imperceptible - simple reinhard was already making full use of the low dynamic range. If we used a different white point, the TMO would look noticeably different.

    Luminance and Color Theory

    Okay, so I must admit that I've lied to you slightly. Reinhard's formulas actually operate on a thing called luminance rather than operating on RGB-triples as I implied. Luminance is a single scalar value which measures how bright we view something. It may not be obvious, but for example we perceive green as much brighter than blue. In other words, (0.0, 0.7, 0.0)(0.0,0.7,0.0) appears much brighter than (0.0, 0.0, 0.7)(0.0,0.0,0.7).

    Converting a linear RGB triple to a luminance value is easy:

    L = 0.2126R + 0.7152G + 0.0722BL=0.2126R+0.7152G+0.0722B

    So far we've effectively been applying our TMOs to each RGB channel individually. However this can cause a 'shift' in hue or saturation which can significantly change the color appearance.

    Instead, what Reinhard's formula entails is to convert our linear RGB radiance to luminance, apply tone mapping the luminance, then somehow scale our RGB value by the new luminance. The simplest way of doing that final scaling is:

    C_{\mathrm{out}} = C_{\mathrm{in}} \frac{L_{\mathrm{out}}}{L_{\mathrm{in}}}Cout​=Cin​Lin​Lout​​

    In C++:

    float luminance(vec3 v)
    {
        return dot(v, vec3(0.2126f, 0.7152f, 0.0722f));
    }
    
    vec3 change_luminance(vec3 c_in, float l_out)
    {
        float l_in = luminance(c_in);
        return c_in * (l_out / l_in);
    }
    

    Two other more complex luminance-adjusting methods are discussed in a paper by Mantiuk et al., but the simple method is perfectly acceptable.

    Don't confuse luminance with luma - luma is the equivalent of the luminance computed from an sRGB pixel (i.e gamma-corrected). The coefficients used to convert to luma are the same as luminance, they just operate on sRGB components instead. See Wikipedia for more details.

    Note that the hue / saturation preservation which results from this method isn't always desirable. Reinhard tone mapping in particular was created with the intention of being applied to luminance only, so it looks much better when this is done (see the following section for details on that). Other tone mapping curves look better when applied to RGB components separately.

    Reinhard was on the wrong track in applying his tone mapping curve to luminance; his curve was inspired by film, but film curves are applied to each color channel separately. And that is a good thing - [...] the "hue and saturation shifts" (really mostly saturation shifts) resulting from applying nonlinear curves per channel are an important feature, not a bug.

    -- Naty Hoffman, co-author of Real Time Rendering

    Extended Reinhard (Luminance Tone Map)

    Let's apply the extended Reinhard TMO to luminance only:

    vec3 reinhard_extended_luminance(vec3 v, float max_white_l)
    {
        float l_old = luminance(v);
        float numerator = l_old * (1.0f + (l_old / (max_white_l * max_white_l)));
        float l_new = numerator / (1.0f + l_old);
        return change_luminance(v, l_new);
    }
    

    Here's the difference it makes - the stained glass windows and the column on the left are noticeably more orange and less 'washed out' in the luminance mapped version:

     

    Reinhard applied to RGB channels

     

    Reinhard applied to luminance

    Note that in fact with Reinhard, the luminance calculation can be simplified somewhat:

    \begin{aligned} L_\mathrm{out} &= \frac{L_\mathrm{in}}{1.0 + L_\mathrm{in}} \\ \implies \frac{L_\mathrm{out}}{L_\mathrm{in}} &= \frac{1.0}{1.0 + L_\mathrm{in}} \end{aligned}Lout​⟹Lin​Lout​​​=1.0+Lin​Lin​​=1.0+Lin​1.0​​

    Hence

    \begin{aligned} \mathrm{TMO_{reinhardextlum}}(C_\mathrm{in}) &= C_\mathrm{in} \frac{L_\mathrm{out}}{L_\mathrm{in}} \\ &= \frac{C_\mathrm{in}}{1.0 + L_\mathrm{in}} \end{aligned}TMOreinhardextlum​(Cin​)​=Cin​Lin​Lout​​=1.0+Lin​Cin​​​

    Reinhard-Jodie

    You aren't necessarily forced to choose between tone mapping RGB individually and tone mapping luminance. Here's one such Reinhard variant by shadertoy user Jodie which does that:

    vec3 reinhard_jodie(vec3 v)
    {
        float l = luminance(v);
        vec3 tv = v / (1.0f + v);
        return lerp(v / (1.0f + l), tv, tv);
    }
    

    The difference between this and the luminance-only tone map is barely noticeable in this scene (you can see that the stain glass windows are slightly less orange), but in other scenes (especially those with colored lights) the difference is more obvious. Note that there's also no way to set the white point with this TMO, but you could add a way yourself.

     

    Reinhard-Jodie tone mapping

    Filmic Tone Mapping Operators

    So-called 'filmic' TMOs are designed to emulate real film. Other than that, their defining feature is the distinctive 'toe' at the bottom end of the curve (radiance is on the xx-axis, final pixel brightness is on the yy-axis):

     

    Note that the other end of the tone mapping curve is usually described as the 'shoulder'.

    Uncharted 2

    A popular TMO for real-time graphics is the Uncharted 2 TMO devised by John Hable (sometimes known as 'Hable Tone Mapping' or 'Hable Filmic' etc). It has some parameters which can be tweaked, but the basic operator is given by the following code:

    vec3 uncharted2_tonemap_partial(vec3 x)
    {
        float A = 0.15f;
        float B = 0.50f;
        float C = 0.10f;
        float D = 0.20f;
        float E = 0.02f;
        float F = 0.30f;
        return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
    }
    
    vec3 uncharted2_filmic(vec3 v)
    {
        float exposure_bias = 2.0f;
        vec3 curr = uncharted2_tonemap_partial(v * exposure_bias);
    
        vec3 W = vec3(11.2f);
        vec3 white_scale = vec3(1.0f) / uncharted2_tonemap_partial(W);
        return curr * white_scale;
    }
    

     

    Uncharted 2 tone mapping

    For some further reading, see John Hable's blog post about creating an improved curve which offers more intuitive controls.

    ACES

    Another popular filmic tone mapping curve is ACES (Academy Color Encoding System). This is the default TMO used by Unreal Engine 4 so would be a perfectly good choice to use in your own real-time engine. The TMO curve is calculated through a couple of matrix transformations and formulae adapted from Stephen Hill's fit, shown below:

    static const std::array<vec3, 3> aces_input_matrix =
    {
        vec3(0.59719f, 0.35458f, 0.04823f),
        vec3(0.07600f, 0.90834f, 0.01566f),
        vec3(0.02840f, 0.13383f, 0.83777f)
    };
    
    static const std::array<vec3, 3> aces_output_matrix =
    {
        vec3( 1.60475f, -0.53108f, -0.07367f),
        vec3(-0.10208f,  1.10813f, -0.00605f),
        vec3(-0.00327f, -0.07276f,  1.07602f)
    };
    
    vec3 mul(const std::array<vec3, 3>& m, const vec3& v)
    {
        float x = m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2];
        float y = m[1][0] * v[1] + m[1][1] * v[1] + m[1][2] * v[2];
        float z = m[2][0] * v[1] + m[2][1] * v[1] + m[2][2] * v[2];
        return vec3(x, y, z);
    }
    
    vec3 rtt_and_odt_fit(vec3 v)
    {
        vec3 a = v * (v + 0.0245786f) - 0.000090537f;
        vec3 b = v * (0.983729f * v + 0.4329510f) + 0.238081f;
        return a / b;
    }
    
    vec3 aces_fitted(vec3 v)
    {
        v = mul(aces_input_matrix, v);
        v = rtt_and_odt_fit(v);
        return mul(aces_output_matrix, v);
    }
    

     

    ACES tone mapping

    You can also use this approximated ACES fit by Krzysztof Narkowicz for something a bit more performant. Note that this approximation oversaturates bright colors slightly compared to the better fit (you can see that in the middle stained glass window).

    vec3 aces_approx(vec3 v)
    {
        v *= 0.6f;
        float a = 2.51f;
        float b = 0.03f;
        float c = 2.43f;
        float d = 0.59f;
        float e = 0.14f;
        return clamp((v*(a*v+b))/(v*(c*v+d)+e), 0.0f, 1.0f);
    }
    

     

    ACES tone mapping (worse fit)

    Real Camera Response Functions

    Sometimes it might be desireable to exactly reproduce the 'tone mapping' response of a real camera. Fortunately, the University of Columbia has released a database of camera response functions which we can use for this purpose. Each camera response curve has two axes: irradiance (essentially the energy coming into the camera) and intensity (essentially the value of color component at that point on the film).

    Since irradiance is the input and intensity is the output, you can visualise irradiance and intensity on the xx and yy axes respectively. The data given to us has the points on both axes normalized into the range [0.0, 1.0][0.0,1.0]. For intensity, this is exactly what we want, as we can just scale the output by 256 to get the corresponding 8-bit color component. For irradiance, we will need to decide the 'width' of the curve. Since the point with the largest xx value is (1.0, 1.0)(1.0,1.0), if we stretch this curve horizontally by a factor of aa then the new furthest point will be (a, 1.0)(a,1.0). We can map any irradiance value greater than aa to 1.01.0 and hence we can effectively control the white point by stretching or squashing the irradiance axis. Instead of 'white point', we'll call this parameter 'ISO' which mimics the function of the ISO setting on a real-world camera.

    The data is given as two associative arrays. Given a irradiance value in [0.0, 1.0][0.0,1.0] we need to find the index into the irradiance array (ideally using a binary search). Then, we can compute the output value by using the index we got from that binary search to look up the intensity value from the intensity array. We'll repeat this procedure for each RGB channel individually to compute the final color.

    For real-time graphics you might want to find a way to bake a LUT instead of doing a relatively expensive binary search.

    float camera_get_intensity(float f, float iso)
    {
        f = clamp(f, 0.0f, iso); // Clamp to [0.0, iso]
        f /= iso; // Convert to [0.0, 1.0]
    
        // Returns 1.0 if the index is out-of-bounds
        auto get_or_one = [](const auto& arr, size_t index)
        {
            return index < arr.size() ? arr[index] : 1.0;
        };
    
        // std::upper_bound uses a binary search to find the position of f in camera_irradiance
        auto upper = std::upper_bound(camera_irradiance.begin(), camera_irradiance.end(), f);
        size_t idx = std::distance(camera_irradiance.begin(), upper);
    
        double low_irradiance = camera_irradiance[idx];
        double high_irradiance = get_or_one(camera_irradiance, idx + 1);
        double lerp_param = (f - low_irradiance) / (high_irradiance - low_irradiance);
    
        double low_val = camera_intensity[idx];
        double high_val = get_or_one(camera_intensity, idx + 1);
    
        // Lerping isn't really necessary for RGB8 (as the curve is sampled with 1024 points)
        return clamp(lerp((float)low_val, (float)high_val, (float)lerp_param), 0.0f, 1.0f);
    }
    
    vec3 camera_tonemap(vec3 v, float iso)
    {
        float r = camera_get_intensity(v.r(), iso);
        float g = camera_get_intensity(v.g(), iso);
        float b = camera_get_intensity(v.b(), iso);
        return vec3(r, g, b);
    }
    

     

    DSCS315-R1 response curve, ISO = 6.0

    This method is available as an option in Indigo Renderer, for the curious.

    Local Tone Mapping Operators

    So far, all the TMOs I've discussed have been global tone mapping operators. This means that the computation they do is only based on the input radiance value and global image parameters like the average luminance. Tone mapping operators which are a function of position are known as local tone mapping operators. These are generally far more expensive and hence unsuitable for real time graphics - yet widely used in digital photography. Local TMOs can also give strange results when applied to video.

    One local tone mapping operator is described in the same paper by Reinhard et al. - it involves a digital simulation of a process known as 'dodging and burning' in real photography, which essentially applies different exposure to different regions of the image, often resulting in more detailed images than global tone mapping operators are able to produce.

    Conclusion

    Hopefully this guide was instructive. I've placed a grid of all TMOs used here for comparison, but keep in mind that these can be adjusted in certain ways through exposure or other parameters, so this isn't entirely a fair comparison.

     

    Clamp

     

    Reinhard simple

     

    Reinhard luminance (white point = max luminance)

     

    Reinhard-Jodie

     

    Uncharted 2

     

    ACES

     

    ACES (worse fit)

     

    DSCS315-R1 (ISO = 6.0)

    Further Reading

    Appendix

    The full source code for this post can be found on my GitHub. For the vec3 class, I'm using a slightly modified version of the one found in Shirley's 'Ray Tracing in One Weekend' book.

    展开全文
  • Tone mapping进化论

    千次阅读 2018-04-21 21:36:27
    这些内容如果需要显示到LDR的设备上,就需要一个称为tone mapping的过程,把HDR变成LDR。现在高端的显示器和电视也可以直接显示出HDR的内容。然而和LDR不同之处在于,LDR就是一个确定的范围,HDR是一个非常宽广的...
  • toneMapping.m

    2020-09-17 15:27:12
    程序是对Reinhard论文“Photographic Tone Reproduction for Digital Images”的实现,具体的讲解可参考我的博客https://blog.csdn.net/u014230360/article/details/108527016
  • Original ImageOriginal1Original2Original3Original4 Local Tone mapping ProcessIntensity ImageBase ImageDetail ImageFinal Tone mapping + gamma correction Result ComparisonNaive...
  • 快速理解Tone Mapping

    千次阅读 2019-05-21 10:34:44
    整个Tone Mapping的过程就是首先要根据当前的场景推算出场景的平均亮度,再根据这个平均亮度选取一个合适的亮度域,再将整个场景映射到这个亮度域得到正确的结果。其中最重要的几个参数: Middle grey:整个场景的...
  • Filmic Tonemapping DELUXE v2.0.2.unitypackage
  • 代码和逻辑真的懒得粘贴了,工程中都有 (3)bloom 中控制阈值threshold = 1: 即 只有颜色值大于1的才会被处理(HDR) 用ToneMapping 将HDR 最后转换到 LDR 显示 最后的关系 : Bloom 作用HDR 的结果 , 然后 被 ...
  • Inverse Tone Mapping

    2021-10-09 15:24:45
    逆色调映射方法(inverse Tone Mapping Operator, iTMO或reverse Tone Mapping Operator, rTMO) SDR转化HDR过程的算法称为扩展算子(Expand Operator),它可以被定义为一个SDR图像I的函数g: 其中 是SDR图像...
  • 浅析Tone mapping

    2021-08-07 23:10:51
    Tone mapping所遇到的问题首先是在摄影学中所提出,并由相关摄影师解决了部分问题。直到在20世纪40年代,由安塞尔. 亚当斯(Ansel Adams) 创立提出的运用于传统黑白摄影的分区曝光法(Zone System),能在拍摄...
  • 该开源代码在实现“Photographic Tone Reproduction for Digital Images”论文算法时有一处错误。论文本身有一个符号错误和一个参数设置不合理。这些我都在程序中做了修正。关于该论文的色调映射算法讲解可参见我的...
  • Tonemapping常用的几种算法

    千次阅读 2017-09-19 10:15:32
    SimpleReinhard : float4 fragSimpleReinhard(v2f i) : SV_Target { float4 texColor = tex2D(_MainTex, i.uv); float lum = Luminance(texColor.rgb);  float lumTm = lum * _ExposureAdjustment;...
  • (2)toneMapping:色调映射 作用:最大限度的保留亮度信息(主要是作用将HDR 和bloom的效果修饰的更自然) 因为HDR的亮度是超过1 普通的颜色 是8位存储 0-255中表示 HDR的颜色 是32位存储,所以用toneMapping可以是...
  • 图形学实验 tonemapping

    2013-12-10 19:43:37
    天大图形学实验tonemapping,程序可以用vc++运行,
  • 翻看查阅了很多博文,发现了以下几篇文章,比较全面的介绍了色调映射概念及过程,我仅当一名搬运工,记录在此。 自己一直在做高动态范围图像的研究,对色调映射稍有了解,但对于二者之间的关系一直没有做过深入的...
  • 色调映射(Tone Mapping),即压缩其动态范围至输出设备的动态范围以下,使高动态范围HDR图像能够适应低动态范围LDR显示器。
  • 神秘海域ToneMapping公式

    千次阅读 2017-12-25 10:37:11
    这是神秘海域的公式: float3 ACESFilm( float3 x ) {  float a = 2.51f;  float b = 0.03f;  float c = 2.43f;  float d = 0.59f;  float e = 0.14f;...saturate((x*(a*x+b))/(x*(c*x+
  • In order to make better use of the advantages of both global mapping and local mapping to improve the performance of HDRI(High Dynamic Range Image) mapping,a group of 20 HDRIs with different mean ...
  • 简述ToneMapping发展史

    千次阅读 2019-03-06 14:39:20
    简述ToneMapping发展史HDR2002年Reinhard ToneMapping2007年CryEngine22010年Uncharted2(神秘海域2)公开算法目前最好的ToneMapping算法---来自学术界的藐视,Academy Color Encoding System(ACES) HDR 为什么会...
  • HDR&ToneMapping

    万次阅读 2011-09-04 10:55:35
    实际中也有把这个exposure adjustment并到tone mapping中去,其实还是分开比较好,因为这些概念是从摄像技术中来的,曝光就是曝光,tone mapping就是tone mapping。   ReinhardToneMapping tone mapping方面比较...
  • 3、Tone Mapping色调映射(Advanced High Dynamic Range Imaging) 如今,大多数可用的显示设备无法本地显示HDR内容。 入门级显示器的对比度仅为200:1。 尽管高端液晶电视具有更高的对比度,但平均对比度约为10,000...
  • 在程序中提出了一种利用亮度直方图构造色调映射查找表(LUT)的色调映射算法。融合人类视觉系统(HVS)的特性,在构造直方图的同时,更加重视视觉可分辨度的缩放范围,有利于更好地保留原始图像的细节。...
  • 计算机图形学课上的实验程序,可以读取BMP图片并进行tone mapping处理

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,286
精华内容 914
关键字:

mappingtone