还没开源点赞已经几十个的HDR转SDR库终于开源了

背景

在3月份开始更新HDR转SDR实践之旅,当时代码还没写只是建了库,没想到更新了几篇文章这么受欢迎,代码还没上传已经有几十个赞了,还有小伙伴提了个issue每个月催我更新代码,这个领域资料太少了,边学边写,现在总算完成了,不辜负大家的等待,如果你觉得有所收获,来给HDR转SDR开源代码点个赞吧,你的鼓励是我前进最大的动力。

现有功能实现如下,供大家一起学习一起上进

  1. 输出模式(直接输出到Surface、经过OpenGL中转)
  2. 视图模式(无缝切换SurfaceView和TextureView)
  3. 多种纹理来源配置(Auto、YUV420Buffer、外部纹理OES、Y2Y)、纹理位深配置(8位、10位、16位)
  4. HDR转SDR CubeLut配置,PQ转SDR12种、HLG转SDR4种
  5. HDR转SDR Shader配置,该Shader支持对PQ视频和HLG视频进行色度矫正、色调参考、色调映射、色域转换、Gamma压缩
  6. 色调映射已支持Android8的Tonemap、Android13的Tonemap、BT2446A、BT2446C、Hable
  7. 色域转换已支持BT2020转BT709Clip、Compress、adpative_l0_cusp
  8. 10个测试视频无缝切换

这篇文章是本系列的第8篇文章,主要讲HDR转SDR中最重要知识点色调映射。

什么是色调映射

HDR视频的高亮度内容在低亮度显示器上还能保留图像细节和颜色,这门技术就叫做色调映射(Tone mapping)。色调映射本质上就是模拟相机把HDR内容从高亮度映射到低亮度从而实现压缩动态范围,如下图所示调整亮度同时改变了图像对比度和动态范围,从而让图像效果更好。

色调映射分为三种全局、局部、时域,全局色调映射速度快在视频处理中比较常用,后文主要围绕这个展开。

色调映射 解释 算法 优点 缺点
全局 直接映射每个像素的亮度 曲线函数、直方图、自适应对数 1. 实现简单运行速度快 2. 适合用在视频中 偏主观,不同视频效果不一定好
局部 不同区域的亮度映射不一样 快速双边滤波、Retinex 1. 效果好 2. 适合用在图片中 1.复杂度高运行速度慢 2.容易出现光晕失真现象
时域 根据连续帧得到亮度的时域相关性 动作域的光流预测 1. 消除失真 2.防止视频闪烁 单独效果比局部色调映射弱,适合在局部后运行

全局色调映射

全局色调映射是调整暗部和亮部的曲线函数。Tonemap operator收集了常见的Tmo色调映射操作符(Tone mapping operator),如下图所示所有曲线有个普遍的特点就是S型,前半部分就是"toe"防止暗的部分太亮,后半部分就是"shoulder"把亮部映射到接近最大亮度,中间部分就是"mid tone"接近线性。 色调映射曲线本质上就是一个缩放函数起到调节亮度的作用,传递函数也起到了调节亮度的作用,它和色调曲线的区别是传递函数不改变范围,色调曲线改变了范围。

缩放规则

直接对RGB的3个通道做色调映射会导致颜色失真,为了映射前后保持色调一致需要对RGB进行统一缩放,缩放步骤如下所示:

  1. 放大成绝对亮度(也可以是参考白值为1的相对亮度,为了表述方便用绝对亮度)
  2. 把RGB的亮度和参考白的亮度代入色调曲线相除得到色调映射后的亮度
  3. 色调映射后的亮度除以色调映射前的亮度得到缩放值
  4. RGB乘以缩放值就是色调映射的RGB
  5. 归一化RGB

RGB亮度有多种表示方式,缩放方法自然也有多种,根据BT2408的附录五有以下5个缩放方式,MaxRGB在代码实践使用更多一点。

方法 公式 优点 缺点
MaxRGB <math xmlns="http://www.w3.org/1998/Math/MathML"> r g b ∗ T o m ( M a x ( r g b ) ) T o m ( W ) ∗ M a x ( r g b ) rgb*\frac{Tom(Max(rgb))}{Tom(W)*Max(rgb)} </math>rgb∗Tom(W)∗Max(rgb)Tom(Max(rgb)) 不会产生超出目标色域的颜色 牺牲亮度保留色度、可能会产生没有饱和度变化的伪影
RGB <math xmlns="http://www.w3.org/1998/Math/MathML"> T o m ( r g b ) T o m ( W ) \frac{Tom(rgb)}{Tom(W)} </math>Tom(W)Tom(rgb) 不会产生超出目标色域的颜色 饱和度可能过低
YRGB <math xmlns="http://www.w3.org/1998/Math/MathML"> r g b ∗ T o m ( Y ) T o m ( W ) ∗ Y rgb*\frac{Tom(Y)}{Tom(W)*Y} </math>rgb∗Tom(W)∗YTom(Y) 保留色度 产生超出目标色域的颜色
YCbcr float Y2 = Tom(YCbCr.x)/Tom(W) YCbCr.yz *= min(YCbCr.x / Y2, Y2 / YCbCr.x) YCbCr.x = Y2; YCbcr To RGB 去饱和功能、包含YCbCr的色调 产生超出目标色域的颜色
ICtCp float I2 = Tom(ICtCp.x)/Tom(W) ICtCp.yz *= min(ICtCp.x / I2, I2 / ICtCp.x) ICtCp.x = I2 ICtCp To RGB 感知色差空间、去饱和功能 产生超出目标色域的颜色

注意:

  1. 为了方便公式中省略了放大成绝对亮度和归一化的步骤
  2. W表示参考白的颜色值
  3. 上述公式和BT2408的附录五略微有点不同,本质是一样的

曲线

色调曲线 解释 优点 缺点
reinhard reinhard的论文"Photographic Tone Reproduction for Digital Images"中提到了该公式 简单 灰暗
hable John hable在神秘海域2中提出曲线拟合艺术家处理后的图片 艺术家的角度调整效果 两个多项式相除,计算量相对其他曲线慢一点
aces aces本身是美国电影艺术与科学学会为解决颜色空间转换问题而发明的,Krzysztof Narkowicz为了使用aces用曲线拟合简化了代码 暗处亮处的细节保留较好,色彩鲜艳 较aces原本的算法偏鲜艳
Android8 Android8.0开始使用的色调映射 Hermitian曲线插值 Android8比Android13的色调映射亮一点
Android13 Android13.0开始使用的色调映射 PQOETF曲线拟合 Android13比Android8的色调映射暗一点
BT2446A BT2446推荐的A方法用来转换HDR、SDR 适合电影电视剧,使用YCbCr转换 反复HDR和SDR转换相对于BT2446C有色差
BT2446C BT2446推荐的C方法用来转换HDR、SDR 适合直播内容,使用Yxy转换,适应肤色 肉眼感觉比BT2446A亮一点

reinhard

公式可视化
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> T M O ( x ) = x ⋅ ( 1 + x x w 2 ) 1 + x = x 1 + x + ( x x w ) 2 1 + x TMO(x) =\frac{x \cdot (1+\frac{x}{x_w^{2}})}{1+x} = \frac{x}{1+x}+\frac{(\frac{x}{x_w})^2}{1+x} </math>TMO(x)=1+xx⋅(1+xw2x)=1+xx+1+x(xwx)2

注意:

<math xmlns="http://www.w3.org/1998/Math/MathML"> x w {x_w} </math>xw代表色调映射白色时X的值,也就是说x的范围从 <math xmlns="http://www.w3.org/1998/Math/MathML"> [ 0 , 1 ] [0,1] </math>[0,1]变成了 <math xmlns="http://www.w3.org/1998/Math/MathML"> [ 0 , X w ] [0,X_w] </math>[0,Xw],除以1+x是为了让Tom(x)的范围变成 <math xmlns="http://www.w3.org/1998/Math/MathML"> [ 0 , 1 ] [0,1] </math>[0,1],加上最右边 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( x x w ) 2 (\frac{x}{x_w})^2 </math>(xwx)2是为了让曲线的后半部分不要太暗,个人感觉平方和Gamma2.0曲线的道理一样都是调节暗部和亮部

hable

公式可视化
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> T o m ( x ) = a x 2 + b ⋅ c ⋅ x + d ⋅ e a x 2 + b x + d ⋅ f − e f Tom(x)=\frac{ax^{2}+b\cdot c\cdot x+d\cdot e}{ax^{2}+bx+d\cdot f}-\frac{e}{f} </math>Tom(x)=ax2+bx+d⋅fax2+b⋅c⋅x+d⋅e−fe

注意:

  1. 注意 <math xmlns="http://www.w3.org/1998/Math/MathML"> a = 0.15 , b = 0.50 , c = 0.10 , d = 0.20 , e = 0.0 f , f = 0.30 a = 0.15, b = 0.50, c = 0.10, d = 0.20, e = 0.0f, f = 0.30 </math>a=0.15,b=0.50,c=0.10,d=0.20,e=0.0f,f=0.30
  2. hable曲线本身是拟合曲线,参数控制曲线中的变化,可以用上述公式可视化改变参数试试就明白了

<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> a = S h o u l d e r 强度 b = L i n e a r 强度 c = L i n e a r 角度 d = T o e 强度 e = T o e 角度分子 f = T o e 角度分母 e f = T o e 角度 \begin{aligned} a &= Shoulder强度 \\ b &= Linear强度 \\ c &= Linear角度 \\ d &= Toe强度 \\ e &= Toe角度分子 \\ f &= Toe角度分母 \\ \frac{e}{f} &= Toe角度 \\ \end{aligned} </math>a b c deffe=Shoulder强度=Linear强度=Linear角度=Toe强度 =Toe角度分子 =Toe角度分母=Toe角度

aces

公式可视化

该公式是为了方便使用ACES颜色转换体系中的效果采用曲线拟合而来的,据说虚幻4用的就是这个
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> T o m ( x ) = a ⋅ x 2 + b ⋅ x c ⋅ x 2 + d ⋅ x + e Tom(x) =\frac{ a \cdot x^2+b\cdot x} {c\cdot x^2+d \cdot x+e} </math>Tom(x)=c⋅x2+d⋅x+ea⋅x2+b⋅x

注意:

  1. 注意 <math xmlns="http://www.w3.org/1998/Math/MathML"> a = 2.51 , b = 0.03 , c = 2.43 , d = 0.59 , e = 0.14 a = 2.51, b = 0.03, c = 2.43, d = 0.59, e = 0.14 </math>a=2.51,b=0.03,c=2.43,d=0.59,e=0.14
  2. 如果发现上述公式的颜色过于饱和,还可以使用拟合度更接近ACES转换的曲线ACESFitted

Android8

代码地址

个人理解和Android13其实思路是一样,按几个点插值形成的曲线进行调整
(x0,y0) x0=10,y0=17,其实就是暗部线性插值
(x1,y1) x1=y1等于屏幕最大亮度的0.75,也是线性插值
(x2,y2) x2在x1和输入最大亮度的中间,y2在y1和屏幕最大亮度的中间,然后用Hermitian曲线进行插值,Hermitian在BT2309中也有使用到

Android13

代码地址

个人理解

HLG是直接按屏幕最大亮度进行缩放

PQ是把输入的值按照给定几个点插值形成的曲线进行调整,插值按PQOETF进行拟合
(x1,y1) x1=y1等于屏幕最大亮度的0.65
(x2,y2) x2表示x1和x3之间的4.0/17.0 y2表示屏幕亮度最大亮度的0.9
(x3,y3) x3等于调整前的最大亮度,y3等于调整后的最大亮度即屏幕最大亮度

BT2446A

个人理解,代码地址

  1. RGB转换YCBCR
  2. Y亮度转换为感知线性空间
  3. 在感知域中对Y应用拐点函数
  4. 转换回伽玛域
  5. YCBCR转换回RGB

BT2446C

公式可视化

把RGB转成xyY,然后把xyY中的Y用公式进行调整,最后再转换回来,代码地址

公式个人理解是这样的,其实就是两个点进行插值

  1. (ip,YHDRip)表示SDR拐点,拐点前面的值按k1斜率插值
    ip表示SDR部分的最大亮度,由BT2408中写的HDR和SDR肤色的关系决定的也就是SDR的80%换算成100cd/m2也就是58.535cd/m2
    YHDRip是由斜率k1和ip的值算出来的,YHDRip = ip/k1=58.535/k1
  2. (HDR参考白,YSDRwp)表示SDR和HDR的高光拐点,按ln进行插值

系列文章

相关推荐
锋风Fengfeng1 小时前
安卓15预置第三方apk时签名报错问题解决
android
User_undefined1 小时前
uniapp Native.js原生arr插件服务发送广播到uniapp页面中
android·javascript·uni-app
程序员厉飞雨2 小时前
Android R8 耗时优化
android·java·前端
丘狸尾4 小时前
[cisco 模拟器] ftp服务器配置
android·运维·服务器
van叶~6 小时前
探索未来编程:仓颉语言的优雅设计与无限可能
android·java·数据库·仓颉
Crossoads10 小时前
【汇编语言】端口 —— 「从端口到时间:一文了解CMOS RAM与汇编指令的交汇」
android·java·汇编·深度学习·网络协议·机器学习·汇编语言
li_liuliu11 小时前
Android4.4 在系统中添加自己的System Service
android
C4rpeDime13 小时前
自建MD5解密平台-续
android
鲤籽鲲15 小时前
C# Random 随机数 全面解析
android·java·c#
m0_5485147718 小时前
2024.12.10——攻防世界Web_php_include
android·前端·php