
做逆色调映射这几年我最大的一个感受是很多人把SDR转HDR想得太简单了觉得无非是拉高亮度和对比度稍微调一下曲线出来的画面“鲜艳”一点就算成功。真正上手之后才发现这活儿说白了是在跟丢失的信息作斗争——HDR视频技术里最难啃的硬骨头之一就是怎么把已经压缩到普通动态范围SDR里的画面重新“撑回”高动态范围HDR而且还得撑得自然、撑得有细节。这套技术有个专门的名字逆色调映射Inverse Tone Mapping。这篇文章不讲虚的我会把逆色调映射到底是什么、业界有哪些主流路线、深度学习方案怎么建模、以及我实际处理SDR转HDR时踩过的坑和参数取舍一次说透。1. 逆色调映射的本质SDR转HDR不是调亮而是补全丢失的动态范围1.1 从信号链路理解SDR与HDR的本质差异要理解逆色调映射得先搞清楚SDR和HDR在信号层面差在哪。SDR内容按BT.709/BT.601标准编码采用Gamma曲线也就是电光转换函数大致是一条2.4次幂曲线。这套标准诞生于CRT显示器时代亮度范围设计为大约0.1到100nit极限也就到100nit出头。而HDR内容按BT.2100标准主流使用PQ曲线Perceptual Quantizer对应SMPTE ST 2084峰值亮度可以到1000nit、4000nit甚至10000nit而且它是按人眼视觉感知特性量化亮度的在10bit或12bit精度下每一级亮度步进都小于人眼刚好能分辨的差JND。常规的色调映射Tone Mapping是把HDR内容压缩到SDR显示设备能表现的范围这是向下兼容。逆色调映射恰恰反过来把SDR信号扩展成HDR信号是向上“无中生有”。问题就出在“无中生有”这四个字上。SDR内容的亮度上限就那么高一个本该在HDR下爆发出上千nit高光的区域在SDR编码里早就被硬裁剪成纯白255或者被压制得非常平缓里面的细节是实实在在丢失了。像素值255就是255你把它乘以任何系数都得不到能在HDR屏幕上表现出自然层次的高光渐变。逆色调映射本质上是个病态问题丢失的信息不会自动回来一切要靠算法用周围的信息去“猜”、去“重建”。我经常用一个类比来解释这个过程色调映射像是把阳光房里的所有家具搬进一间小黑屋东西太多搬不下就得丢弃一些逆色调映射则是只给一张小黑屋里家具摆放的照片让你推算出原来阳光房的落地窗、吊灯和窗外风景长什么样。所以这不是简单的数值运算而是一个重建与推理过程。1.2 为什么不能直接做数值放大很多第一次接触SDR转HDR的人第一个想法都是把像素亮度乘个系数不就行了比如把SDR的峰值亮度100nit映射到HDR的1000nit按10倍放大。实际操作一下就会发现三个问题。第一直接放大后暗部也跟着亮了黑位抬升整个画面发灰。SDR里编码为16/255的“黑”区域放大后在HDR下可能变成了几十nit的“灰”观感上完全没了对比度。第二高光区域没有任何恢复。SDR里已经裁剪成纯白的部分放大后还是纯白没有过渡、没有层次在HDR屏幕上反而更难看因为HDR屏幕的动态范围让你一眼就能看出这坨白不正常。第三很多人在Gamma编码域里直接做运算这是概念性错误。SDR像素值跟物理亮度不是线性关系sRGB编码本身带一个非线性转换正确做法是先做去Gamma/去ELG把数据线性化在线性光照域里处理最后再编码回PQ传输函数。直接在非线性的编码值上做乘除相当于在已经扭曲的尺度上操作出来的色彩和亮度关系必然是错的。所以逆色调映射的完整管线通常长这样解码SDR到线性RGB —— 在光照域设计扩展曲线或交给网络推理 —— 色域转换 —— 按PQ编码写HDR。每一步都有讲究后面的章节我会逐段拆。2. 三条技术路线全局扩展、分层重建与深度学习的取舍逆色调映射发展了十几年主流方案大致能分成三类各有各的适用场景也各有各的硬伤。2.1 全局扩展函数最直接也最容易发灰的路线全局扩展是整个领域最早期的做法代表工作是Banterle等人在2007年前后提出的方案。核心思路很简单构造一个从SDR亮度到HDR亮度的全局映射函数对整帧所有像素做同样的变换。比较常见的做法是先计算一个扩展因子再结合曝光补偿。比如给定输入亮度L输出亮度可以用类似L a · L^k的形式或者用sigmoid函数把原本被压缩的亮部重新拉开。这种方法的优点非常突出计算量小可以做到实时适合在播放器、视频转换棒这类低算力设备上跑而且时间上很稳定不会出现逐帧跳动。但缺点也同样致命。它没有空间自适应性整幅画面统一处理亮的地方整体变亮暗部也被同步提亮高光区域该没层次还是没层次画面看起来灰蒙蒙的在HDR屏幕上反而暴露出更多缺点。我用全局方法做过几次试验结论很明确作为学术铺垫有价值产线上基本不够用。2.2 分层与曝光堆叠局部细节的恢复尝试既然全局方法不行研究者开始考虑“局部处理”。一个自然的思路是把图像拆成基础层和细节层基础层承载大尺度的亮度变化细节层保存边缘和小尺度纹理。扩展只作用在基础层上细节层原地保留这样处理完的高光区域能保住纹理不像全局方法那样糊成一团。还有一类跟“局部”沾边但容易混淆的方法多曝光HDR合成也就是曝光堆叠。拿同一场景的多张不同曝光时间的SDR照片提取各自的暗部细节和高光细节加权合并成一张HDR图。严格说这属于HDR重建不是单图逆色调映射因为它依赖多帧信息。但在实际项目里“SDR转HDR”的工具箱经常把这些方法混在一起用比如对着同一个静态场景拍三张不同曝光的照片然后合成HDR这套流程成熟而且稳定。只不过一进入视频领域多层合成的方法就吃不开因为视频要求帧间一致不同帧的融合权重一旦变化就会闪烁。2.3 基于学习的方法从数据中学会“高光该长什么样”2017年Eilertsen等人提出HDRCNN之后逆色调映射开始进入深度学习时代。核心思路很简单粗暴让卷积神经网络看大量由HDR母版生成的SDR版本学会从SDR画面里预测出丢失的HDR高光渐变。这类方法的优势非常明显它不再依赖人工设计的先验规则而是从数据里学出“什么样的边缘、什么样的纹理、什么样的几何结构大概率对应一个多少亮的高光”。实测下来深度学习方案在主观观感、高光自然度、细节恢复程度上都大幅领先前两代方法尤其在处理像太阳、镜面高光、灯带这类“干净高光”时推断出来的衰减曲线非常接近真实光照。代价也很大离线训练成本高推理阶段在GPU上跑还勉强能用CPU上实时基本别想另外逐帧独立推理很容易产生时间闪烁。下面这张表是我在项目选型时经常拿出来的对比维度维度全局扩展分层/局部方法深度学习方法计算开销低中高高光细节恢复弱中强时间稳定性好中等弱需额外约束适用场景播放器实时转换、低端设备静态图处理、离线修复离线批量修复、高质量重制3. 膨胀卷积与感知损失深度学习逆色调映射建模细节既然深度学习已经是目前效果上限最高的方向我把它的建模细节展开讲讲。很多开源代码跑出来效果一般问题往往不是模型本身而是训练数据和损失函数没有对齐。3.1 成对数据怎么造渲染器加色调映射的模拟链路深度学习训练需要成对的数据输入是SDR画面目标是HDR真值。问题在于真实世界里你几乎不可能拿到严格配对的SDR和HDR版本——对着同一个场景同时拍SDR和HDR传感器、镜头、曝光差异都会造成像素级不对齐。业界最常用的做法是“渲染模拟链路”用Blender Cycles或者Unreal Engine这类支持物理正确光照的渲染器输出高动态范围的HDR画面当作真值然后对这些HDR画面做一次色调映射生成对应的SDR版本再把这个SDR版本当作训练输入。这个模拟过程把“HDR真值是怎么经过色调映射变成SDR的”完全可控而且色调映射的参数可以变着花样来模型便能在多样性的SDR形态里学到鲁棒的特征。我实际跑训练的时候还会额外加一步合成扰动给SDR输入补一些轻微的量化噪声、色度采样偏移模拟真实老片经压缩、解码后的脏数据效果。这一步对模型落地到实际素材时的泛化能力提升非常明显。3.2 网络结构与感受野为什么膨胀卷积管用逆色调映射网络通常采用全卷积结构输入一张SDR图输出亮度扩展后的HDR图。HDRCNN使用的就是类似U-Net的编码器-解码器结构中间用膨胀卷积增加感受野。感受野在这里是个非常关键的问题。一个被裁剪成纯白的高光区它的自然衰减可能横跨几百个像素比如太阳周围的辉光、灯箱周围的泛光。如果网络只看得到局部几像素的上下文它根本没法推断这个高光该有多亮、衰减到多宽。膨胀卷积的作用就是不降分辨率的情况下指数级扩大有效感受野让网络能看到更大范围的亮度变化趋势。我自己的经验是最后的特征层感受野至少得覆盖到输入尺寸的1/4以上否则结果会显得“小气”高光周围缺少舒展的过渡带。编码器-解码器结构还有个隐藏优点skip connection能把浅层的高频边缘信息直接传给重建层这样高光边缘不会糊成一片亮部和暗部的边界还能保持锋利。3.3 损失函数要用HDR域思维别在sRGB编码域里算L2这是我反复踩过的一个坑。早期训练时为了省事直接让网络预测sRGB编码域的像素值然后用L2损失约束。训练跑完指标还挺好看但生成的画面怪怪的——高光区域总有一种“说不上来的糊”暗部又容易出现色斑。原因在于sRGB编码域跟人眼感知的亮度差异不是线性的编码域里的L2距离并不能代表视觉差异。正确做法是在线性HDR域算损失。通常先把网络输出的线性HDR值经过一次色调映射回到SDR域再和参考SDR算空间域的感知损失同时在线性HDR域也直接算L1/L2损失。HDR域损失负责约束整体的数值合理性感知损失负责约束肉眼观感。这样一来网络不再为了取悦编码域数值而牺牲视觉质量高光的自然度和暗部的纯净度都上了一个台阶。4. 谷歌浏览器HDR截图过曝一次“伪逆映射”的真实事故开头提到的“谷歌浏览器HDR截图过曝”你看搜索热词都能发现它是高频困扰。这个现象表面上看跟逆色调映射没关系但实际排查一圈就会发现它本质上是动态范围转换链路出了问题正好是理解逆色调映射方向感的好素材。4.1 现象屏幕正常截图灰白死白典型场景是这样的系统是Windows显示器支持HDR系统设置里已经开启了使用HDR。浏览器打开一段HDR视频屏幕上播放的观感完全正常高光有层次、色彩也饱满。然后你随手用微信截图或者QQ截图想截一帧分享截出来的图却整体发灰白高光区域一片死白像被暴力过曝了一样跟屏幕上看到的完全两个画面。更迷惑的是同一时刻用Xbox Game Bar截图或者按WinPrtScr系统截图得到的图有时是正常的有时也过曝取决于具体环境。很多人第一反应是显示器坏了或者显卡驱动有问题结果换台设备还是这样。4.2 排查链路从显示系统到截图工具的逐层定位遇到这类问题我的排查顺序是这样的先确认是“截图过曝”而不是“显示过曝”。让提问的人拍屏幕照片如果拍屏幕的照片色彩正常、只有截图文件过曝问题就锁定在截取/转换链路而不是显示器。确认系统HDR开关Win Alt B能否切换HDR模式打开了没有。如果系统HDR没开浏览器不会进入HDR播放链路。确认浏览器硬件加速已开启。Chrome的地址栏进chrome://settings/system查看“使用硬件加速模式如果可用”是否打开。HDR播放依赖GPU合成关掉硬件加速会让浏览器退回SDR处理。在视频画面上右键打开统计信息确认正在播放的是HDR内容。YouTube的话看“Stats for nerds”如果编码显示VP9.2/AV1.10bit PQ基本就能锁定是HDR流。对比不同截图工具的差异。微信/QQ这类第三方截图工具走的是老的GDI/DirectX捕获路径拿到的是系统合成器里还没做显示适配的HDR帧缓冲而Xbox Game Bar在HDR模式下截出的图是经过系统HDR处理的。走到这一步原因已经很清楚了第三方截图工具抓取到的是PQ编码的高动态范围信号但它在内部用sRGB/Gamma曲线去解析这个PQ信号等于把PQ的数值当成了普通SDR亮度来输出。PQ曲线的数值分布是为感知均匀编码设计的直接拿去按sRGB显示高光部分当然会暴力溢出这就是我们看到的灰白一片。4.3 和逆色调映射的关系错误的动态范围扩展随时在发生这种过曝本质上是一种“错误的动态范围扩展”——把一个本应显示在HDR链路里的信号生硬地拉回到SDR链路里而且没做任何转换补偿。正常播放时系统或者浏览器内部的显示映射模块会把HDR内容转换到屏幕实际支持的色彩空间和亮度范围截图工具绕过了这套转换等于拿错解码表去解另一套编码体系。这一点恰恰说明了逆色调映射的核心难点动态范围扩展不是简单的数值放大任何扩展都要有合理的映射逻辑、要有感知依据否则就会像截图过曝这样把好好的画面搞废。反过来如果你用播放器或者电视自带的“SDR转HDR”功能看普通SDR视频遇到那种高光一片死白、画面发灰的情况原因也大同小异——它内部的逆色调映射模块没做好把高光区域扩展过头了。所以下次再看到“SDR转HDR效果发白”你基本可以断定是逆色调映射的扩展强度失控不是显示设备的问题。5. 实战调参从素材摸排到目标亮度设定的完整流程前面讲了原理接下来讲讲我自己处理SDR转HDR项目时的完整流程和参数取舍。5.1 先分辨素材原生SDR还是降级SDR决定处理思路拿到一个待处理的SDR视频第一件事不是直接丢进模型而是先判断素材类型。我把它分成两类原生SDR素材这是最大的坑。老电影、老剧集、大部分电视节目从拍摄到后期都是SDR流程画面里本来就不存在HDR级别的动态范围信息高光区域更是早就被压平或剪裁掉了。处理这类素材逆色调映射只能靠先验“编造”高光目标是编得自然、有层次而不是去还原某个真实存在的HDR场景。降级SDR素材有些视频是从HDR母版经过色调映射转成SDR播出的。这类素材的高光区域往往保留着微弱但可辨识的梯度变化没有完全剪平专业术语叫“近裁剪区域信息”。处理这类素材逆色调映射更像是“还原”网络有机会重建出接近母版的动态范围。分辨方法很简单看高光区域的渐变。用专业工具或者直接放大看如果太阳、灯泡、高光反射周围有细腻的亮度过渡大概率是降级素材如果是一整块死白没有过渡基本是原生SDR。处理策略完全不同原生SDR要克制扩展强度低一些尽量保住整体观感降级SDR可以激进一点目标是尽可能把丢失的层次拉回来。5.2 目标峰值亮度、扩展强度与色域映射的设定参数设定上最核心的是三个目标峰值亮度、扩展强度、色域映射策略。目标峰值亮度应该根据最终播放设备来定而不是盲目奔着4000nit去。如果是制作给1000nit显示器看的版本目标峰值就设在800到1000nit附近如果面向手机屏幕这类峰值亮度有限的设备可以压到600nit左右。设定太高画面为了迁就峰值会把中灰拉低整体偏暗暗部细节还容易丢。扩展强度一般取0.8到1.2之间。0.8意味着扩展保守适合原生SDR老片1.0是正常档超过1.0适合低照度素材比如夜景演唱会原本暗部密度很高加大扩展能把氛围提起来。超过1.2我基本不用因为暗部噪声会被放大到肉眼可见的程度画质得不偿失。色域映射也不容忽视。SDR素材是BT.709色域HDR目标是BT.2020。主流做法是用标准矩阵把RGB转换过去同时适当提升饱和度补偿。需要注意的是BT.2020色域远大于BT.709直接转换容易导致肤色偏红、绿色过艳所以我一般会把肤色区域单独保护起来或者采用一种带亮度适应的色域映射算法让高光区域的色彩不那么“烧”。5.3 一版可直接上手的SDR转HDR处理流程如果你手头有N卡且想快速跑通一条SDR转HDR流程下面这条管线可以作为起点用FFmpeg或MediaInfo检查输入的色彩空间。确认像素格式、色深、色彩原色了解素材的编码状态。解码到16bit float线性RGB。这是所有正确颜色处理的前提绝不能在Gamma编码域做扩展。应用逆色调映射模型。如果是离线高质量处理我会优先用训练好的深度学习模型推理亮度扩展层如果是直播推流这种实时需求才退回全局或分层算子。做色域转换BT.709到BT.2020。注意转换矩阵要带颜色适应必要时手动微调饱和度。编码输出用x265写HEVC 10bit带上HDR元数据。我常用的参数是这样x265 --input-depth 16 --input-csp i444 --colorprim bt2020 --transfer smpte2084 --colormatrix bt2020nc --master-display G(13250,34500)B(7500,3000)R(34000,16000)WP(15635,16450)L(10000000,50) --max-cll 1000,400 -o output.hevc input.y4m这里的关键参数是把传输函数设为smpte2084PQ色彩原色设为bt2020并写入Mastering Display和MaxCLL/MaxFALL元数据。这些信息对播放器正确显示HDR至关重要很多自己压出来的HDR视频在播放器里发灰就是因为少了这些元数据。6. 翻车重灾区光晕、断阶、肤色偏移与时间闪烁最后这部分是我觉得比原理更值钱的部分——逆色调映射实际处理时最容易翻车的几个地方以及怎么修。6.1 光晕和断阶边缘伪影与量化台阶的来源光晕大概是最常见的伪影。表现是强明暗交界处出现一圈发亮的边像是物体最外圈被谁用画笔描了一遍光。原因分两类一类是局部扩展时边缘像素被错误拉高另一类是深度学习模型在高对比边缘处产生了过冲。修复思路通常是做边缘检测把扩展系数在高梯度区域降下来或者用导向滤波来约束扩展值的空间平滑性。断阶banding是另一个高频问题。SDR素材本身是8bit扩展到HDR后如果扩展函数的斜率在暗部区域过大原本连续的亮度台阶就会被放大成肉眼可见的横条纹。我一般会做两步一是在扩展曲线设计时对暗部区域压低斜率二是对输出做轻微的抖动或带噪。很多播放器也有去带噪选项但那是给SDR信号用的在HDR上还是从源头解决比较靠谱。6.2 时间闪烁与噪声放大逐帧模型的软肋时间闪烁是深度学习方案最被人诟病的一点。网络对每一帧独立推理同一像素在不同帧里的亮度可能忽高忽低这种闪烁尤其容易出现在大面积高光区域人类视觉对这类低频亮度波动非常敏感比空间模糊还难受。解决办法大致分三类训练时加入时序约束让网络看到相邻帧推理后用光流做时域滤波或者对高光区域做一个时间上的滑动窗口平均再混合回原始帧。我实际用的最多的是光流约束加后滤波结合效果好但代价是处理速度变慢。噪声放大也容易出问题。SDR素材在暗部本来就有一定的传感器噪声或压缩噪声扩展之后噪声能量被连着放大原本不太明显的噪点会变成密密麻麻的“彩虹点”。所以很多逆色调映射流程里都有一个前置去噪步骤以及在扩展层面对暗部加以更温和的增益。6.3 质量核验与综合修复建议处理完了不能只看一帧我通常会从三个维度检查。主观上我会在一台峰值亮度符合目标的HDR显示器上逐场景检查重点看三样东西高光区域有没有层次和过渡肤色有没有偏移暗部有没有发灰。客观上如果是实验项目可以用HDR-VDP2这类感知质量指标做数值评估但说实话这个指标在预测主观满意度上还没那么可靠我还是更相信逐场景人工复核。最后还有一个经常被忽视的点把处理出来的HDR版本重新做一次色调映射回SDR看看画面是否还保有原来的对比度关系。如果回映射后观感跟原始SDR差很多说明扩展时把画面原有的亮度结构破坏了需要回头调参数。做逆色调映射我这些年下来最深的一条体会是所有参数都要服从画面的“亮度叙事”。一个画面为什么会亮、亮到什么程度、周围怎么衰减这些在真实光照里是有逻辑的。技术哪个强、模型哪个新最后都要回归到一个问题——一个完全没有接触过原始HDR场景的观众会不会觉得这个画面是自然可信的。处理老片修复项目时我甚至会把原作摄影风格也纳入参数考虑逆色调映射不是为了把每帧都弄成高亮炫技而是让对比度和动态范围服务于内容本身。操作上我的建议是小项目先跑全局或分层方案快速出效果画面不对劲再上深度学习大项目则反过来直接上模型但参数从保守档起步优先保证不翻车再逐步逼近理想效果。