资讯详情

资讯详情

ISP数据流后半程详解:从去马赛克到YUV输出的完整地图

小白入门ISP数据流走向这个系列上一篇我们把传感器输出的RAW底图从前端黑电平校正BLC、坏点校正DPC到镜头阴影校正LSC整个数据流的前半程捋完了。这次不聊前端清洗了直接从去马赛克开始一路经过白平衡、CCM、Gamma、降噪、锐化最后RGB转YUV输出把ISP后半程的数据流走向彻底讲明白。我接触过不少刚入行的开发者算法文档翻了一堆上手调画质的时候却总是一头雾水。原因不是不努力而是脑子里缺一张完整的数据流地图不知道每一步数据是什么格式、几个通道、多少位深、在哪个色彩空间出了问题根本不知道往哪个环节排查。这篇文章就是帮你把这张地图补全的适合刚刚接触ISP、正在跟第一个画质调试任务的初学者也适合想整体重捋一遍流程、把零散知识点串成线的老手。1. 先看全局后半程数据形态变化地图1.1 每个节点上数据长什么样在拆具体模块之前我先把后半程的数据形态变化列成一张表。这是全篇最核心的一张图建议直接保存到手机里调试的时候翻出来对照。处理阶段数据格式典型位深通道数色彩空间Sensor原始输出Bayer RAW12bit1传感器原生色彩经BLC/DPC/LSC后Bayer RAW12bit1传感器原生色彩去马赛克之后RGB12bit线性3传感器RGB白平衡增益后RGB12bit线性3传感器RGB已调平CCM之后RGB12bit线性3标准色彩空间线性态Gamma之后RGB8/10bit非线性3sRGB等非线性编码降噪锐化后RGB或亮度分量8/10bit3同上一级最终输出YUV常见4:2:08/10bit3子采样BT.601/BT.709这张表里藏着两个关键信息。第一RAW域从头到尾只有一个通道BLC、DPC、LSC这些前端模块全都在单通道上干活直到去马赛克才裂变成三个通道。第二位深下降和色彩空间切换全部发生在后半程。换句话说后半程的每一步都在改变数据形态形态一换操作的规则也跟着换。另外提醒一句不同平台对ISP流程的排列顺序会有差异有的把降噪放在去马赛克之前做RAW域降噪有的把锐化放在RGB域有的把AWB增益放在LSC之前。顺序可以有差异但数据形态变化的底层逻辑是不变的。你只要抓住当前数据是RAW还是RGB、是线性还是非线性、几位深这三个问题任何平台的流程都能很快看懂。1.2 为什么一定要分清RAW域和RGB域一句话总结在哪个域操作决定了你能做什么、不能做什么。RAW域里每个像素只代表一种颜色它本质上是一个滤色片后面测到的光强值和颜色没有直接关系。所以在RAW域里做的都是物理层面的修正把黑电平垫起来、把坏点抹掉、把镜头边缘的亮度衰减补偿掉。这些操作逐像素进行跟色彩无关也不该引入色彩相关的逻辑。进入RGB域之后每个像素终于有三个分量了这时候才可以谈颜色。但RGB域内部还要再分两层线性RGB和非线性RGB。线性RGB适合做矩阵运算比如白平衡增益、CCM因为乘法在数学上等价于物理上的光线调节非线性RGB适合给人看但不能随便做线性运算。很多新手在Gamma之后去做CCM或者在线性域直接做均值滤波结果就是各种说不清的偏色和断层都是因为没意识到数据形态已经变了。1.3 位深变化为什么是重中之重位深决定了动态范围也决定了处理过程中能不能扛住精度损失。12bit RAW的最大值是4095黑电平通常设在64左右有效范围只有4000多。如果在中间做一次除法再放大中间结果就会掉精度暗部容易出现色块或量化噪声。这也是为什么主流做法会在RAW域尽量保留高位数到Gamma甚至之后才压到8bit。道理很简单前期多留一点精度后期压缩时才有余量不至于把细节和色阶一起压没了。你如果看到一个平台的ISP在12bit RAW上做了一堆运算之后到输出端还能保持顺滑的暗部过渡那多半就是位深管理做得好。2. 去马赛克单通道RAW变成三通道RGB的关键一跳2.1 Bayer排列决定了插值的基本规则传感器上每个像素只装了红、绿、蓝三种滤色片之一最常见的Bayer排列是2x2一组绿色占两个位置红蓝各占一个。这就意味着每个像素天生缺另外两种颜色信息只能靠周围像素猜出来这个过程就是去马赛克。名字叫去马赛克但实际是反着来的它是把缺颜色的像素补全。Bayer有RGGB、BGGR、GRBG、GBRG四种常见排布。排布方式直接决定插值方向调试时第一步永远是确认手里的RAW排布对不对。排布搞错了整张图会呈现一种非常统一却又说不上来的错色而且不管怎么调白平衡都救不回来。你如果第一次拿到一个平台的RAW先别急着调任何参数花五分钟确认排布能帮你省下一下午的排查时间。2.2 从双线性到方向性插值最简单的去马赛克是双线性插值用周围同色像素的平均值去填缺的颜色。优点是快、简单缺点同样明显——边缘发虚还有明显的彩色拉链和伪彩。进阶一点的做法是方向性插值。核心思路是先判断当前像素落在平坦区还是边缘区如果是边缘就顺着边缘方向插值而不是无脑平均。拿一条水平边缘举例水平方向相邻的G值高度相关垂直方向的G值变化剧烈顺着水平方向取平均显然更合理。方向判断的准确性直接决定边缘是干净的还是出现锯齿。再往上是基于深度学习的去马赛克效果确实好但落地到ISP调试时传统算法仍然是主力一是硬件友好二是行为可预测。算法选型这件事上我的观点一直是调试期可预测性比极限画质更重要。2.3 伪彩、拉链效应到底是怎么来的伪彩和拉链效应是去马赛克最容易暴露的问题。拉链效应长这样高对比度边缘附近出现一格格黑白交替的纹理像拉链一样。产生原因是插值时把边缘两侧的像素平均了G通道在边缘处出现阶梯状过渡。伪彩则是边缘处出现不该有的红、蓝、绿彩色条纹。原因是边缘附近R和B通道的插值误差比G通道大三个通道的比例一旦错乱本来应该黑白分明的边缘就染上了颜色。这个现象在黑白文字边缘特别明显你仔细看一张拍白纸黑字的照片如果字边缘泛红或者泛蓝去马赛克基本脱不了干系。伪彩和CCM、饱和度还有联动关系。如果去马赛克算法的边缘处理本来就一般再在CCM里把饱和度拉得很高伪彩会被成倍放大。所以调试时必须把去马赛克和CCM放在一起看不能各调各的。2.4 调试去马赛克质量的方法我调试去马赛克习惯用两种测试图高频斜条纹图看边缘有没有彩色拉链大面积纯色块看平坦区有没有迷宫纹。迷宫纹是平坦区域出现蛛网一样的规则纹理插值算法在几乎没有边缘信息时反复猜错就会产生。如果拉链严重优先检查方向插值的阈值是不是太敏感如果迷宫纹严重多半是缺少一个平坦区域检测的兜底逻辑。还有一个经验RAW转RGB之后先别做任何进一步的锐化直接肉眼观察边缘这样能把去马赛克的问题和后面锐化的问题隔离开省得互相甩锅。3. 白平衡先解决光再谈色3.1 为什么同一个物体在不同光源下颜色不一样人眼看一张白纸在晴天、阴天、白炽灯下都觉得是白的因为大脑做了自动补偿。传感器可没这个本事它在不同光源下看到的白纸RGB比例完全不同。白平衡要做的事就是把传感器在不同光源下拍到的RGB比例拉回一个统一的标准。这里引入色温概念。色温越高光源越偏蓝比如阴天天空光色温往7000K甚至更高走色温越低光源越偏黄红白炽灯大概2700K。白平衡算法的本质就是先估计当前光源色温再给RGB增益把这层色偏抵消掉。3.2 灰世界与白点检测经典的AWB估计方法有两类。第一类是灰世界假设认为一张正常照片里所有颜色的平均值应该接近灰色也就是说R、G、B通道的平均值应该差不多。如果统计下来R偏高就说明画面整体偏红需要压低R或者抬高G。这个方法简单粗暴但遇到大色块场景就会翻车。想象一个全是红色物体的画面平均色调本来就是红的灰世界算法会误判成光源偏红反而把红色给拉没了。第二类是白点检测在画面里找最接近白色的像素认为这些像素本来应该RGB均衡用它们来估计光源。白点检测的关键是判断阈值哪些像素能算白色需要结合亮度和色度范围一起筛。实际产品里通常把两种方法结合加上色温统计和场景识别才能在不同场景下都稳住。入门阶段你不需要把策略做得多复杂但要理解这两类方法的思路差异后面看平台代码时才不会懵。3.3 增益的计算与限制假设已经估计出当前色温下通道失衡的程度剩下的就是算增益。设G通道为基准R_gain 期望的R/G比值 ÷ 当前统计的R/G比值B_gain 期望的B/G比值 ÷ 当前统计的B/G比值然后把这两组增益乘到每个像素的R、B通道上。注意必须在线性域做这个乘法也就是去马赛克之后、Gamma之前。如果在Gamma之后的非线性域乘增益会连带改变已经编码好的亮度曲线结果就是色彩失真加亮度异常。增益一般会被限制在一定范围比如0.5到2.5倍。增益太大说明当前光源色调极端强行拉回来会带来一个不可避免的副作用噪点被同比例放大。增益本质上是把通道信号乘大信号放大了噪声也跟着放大。所以高增益场景下暗部噪点几乎无法避免这也就解释了为什么夜景照片在AWB增益大的通道上总是特别脏。3.4 混合光源是入门第一个坑AWB最怕的不是极端色温而是混合光源。同一个画面里左边窗口透进自然光右边是室内暖光照明两个区域的色温完全不一样。这时候无论AWB估计出哪个色温总有一半画面是偏的。解决思路一般是分区统计把画面切成若干区域分别统计色温再加权融合或做局部白平衡。但局部白平衡也有自己的问题区域分太细会让原本统一的场景出现颜色断层。这块在实践里没有完美解只能靠策略取舍。新手入门先记住一点看到画面一半偏蓝一半偏黄时别急着怀疑算法实现先确认光源是不是混的这是排查顺序的问题。4. 色彩校正矩阵把颜色扳回标准轨道4.1 3x3矩阵在做什么传感器自身的RGB响应曲线和人眼、和标准显示设备的RGB并不一致。所以就算白平衡做对了拍出来的颜色依然不会准红的更艳或者更暗橙的偏黄。CCM色彩校正矩阵就是用一个3x3矩阵把传感器RGB映射到标准色彩空间比如sRGB。公式的逻辑是这样的R_std m00 × R_sensor m01 × G_sensor m02 × B_sensorG_std m10 × R_sensor m11 × G_sensor m12 × B_sensorB_std m20 × R_sensor m21 × G_sensor m22 × B_sensor矩阵元素通常是主对角线最大数值可以到1.3到2.0非对角元素负责纠正通道间的串扰。整个矩阵的作用可以近似理解成把传感器看到的颜色经过剪切、拉伸之后塞进标准色域。你只要明白它是个线性变换就够了具体数值由标定流程给出。4.2 饱和度、矩阵系数和噪声代价CCM调试里最常见的操作是加大主对角线系数让颜色更鲜艳。但这里有个永恒的代价矩阵运算会放大噪声。尤其是非对角线的负系数会让暗部的色彩噪声放大得更明显。你看到的那些暗部又红又蓝的彩色噪点很多时候不是降噪模块没干活而是CCM系数太激进了。另一个典型问题是饱和度过高导致肤色失真。人脸肤色在色彩空间里是一个相对固定的区域如果CCM在这一块过度拉伸拍出来的人脸就是蜡黄或者泛红的。成熟的调试流程会单独关注肤色映射区域必要时在矩阵设计里给肤色留出保守通道让皮肤颜色保持在一个自然区间。4.3 不同色温下CCM要跟着变还有一个新手特别容易忽略的点CCM不是一组矩阵走天下的。不同色温下传感器的色彩响应有差异对应到标准色彩空间的映射关系也不一样。正规做法是标定多个色温下的CCM比如低色温一组、中间色温一组、高色温一组然后根据AWB估计出的色温在几组矩阵之间做插值。插值也要在正确的域做通常是对矩阵系数本身做插值而不是对输出RGB做插值。直接插输出RGB会出现色彩不连续的问题看着是平滑过渡实际颜色已经偏了。做画质标定时色温渐变场景是必测项看到渐变偏色先往CCM是不是只标了一组这个方向查。5. Gamma与色调映射亮度曲线决定最终观感5.1 为什么要做Gamma显示器不是线性的人眼也不是线性的。人眼对暗部变化非常敏感对亮部变化不敏感。如果直接把线性数据送去显示暗部细节会全部糊掉因为暗部的数值范围太窄了。Gamma校正的思路就是把线性亮度映射到非线性编码让暗部占据更多的数值范围。sRGB的Gamma大致是1/2.2次幂的关系实际标准用的是分段函数低亮度段是线性的高亮度段才是幂函数。Gamma曲线是整个图像风格的总开关曲线整体抬一点画面变亮压低暗部对比度变大压平高光画面变柔和。调Gamma就是调一张图的第一观感。5.2 从简单幂函数到Tone Mapping纯Gamma是全局的一整张图共用一条曲线。问题在于场景动态范围很大的时候比如逆光、夜景灯箱一条全局曲线压不住高光过曝成死白暗部压不下去还是一团黑。这时候就需要Tone Mapping色调映射出场。色调映射分两类。全局Tone Mapping对整张图用一条映射曲线但曲线可以比Gamma复杂比如S形曲线压高光的同时提亮暗部形成高对比又不丢细节的效果。局部Tone Mapping则根据不同区域动态调整映射能在超大动态范围下同时保住高光和暗部细节但复杂度更高也容易出现光晕这类伪影。现在手机上的夜景模式、HDR模式本质上就是多帧合成加局部Tone Mapping的组合拳。5.3 实操中怎么调曲线调Gamma和Tone Mapping时我习惯用灰阶卡加仪器测先看灰阶是否线性再看各亮度段的观感。有几个技巧记一下暗部提亮不要超过1档否则噪点全部浮上来。高光压制的曲线末端要平滑不能直接裁切否则会出现色阶断层。S形曲线虽然好看但中间调对比度不要拉过头不然肤色会发干发硬。另外Gamma曲线调整之后一定要回头重新看一遍AWB和CCM的结果因为曲线变化会影响整体色彩的感知这三者是联动的只调一个不管另外两个注定要返工。6. 去噪与锐化画质的最后博弈6.1 噪声从哪儿来噪声主要来源有三块。一是传感器自身的读出噪声和暗电流噪声这部分在RAW域就存在二是高增益放大产生的噪声比如夜景模式下模拟增益拉高噪声也随之放大三是ISP算法自身的误差比如去马赛克插值误差、CCM对噪声的放大效应。从视觉上分噪声分两类亮度噪声和色度噪声。亮度噪声表现为画面颗粒感色度噪声表现为花花绿绿的小彩点。人眼对色度噪声远比亮度噪声敏感彩点比颗粒更扎眼。所以降噪策略上通常是先处理色度噪声再处理亮度噪声。亮度噪声可以适当保留一点点颗粒感反而显得画面有质感全抹平就成塑料了。6.2 空域降噪与时域降噪降噪算法粗分为空域和时域。空域降噪对单帧图像做滤波常见的有双边滤波、引导滤波等核心思路是平滑的同时保留边缘。时域降噪利用多帧图像信息把连续几帧同一位置的像素做加权平均随机噪声互相抵消。时域降噪对静态场景效果极好但画面一动就会出现拖影必须配合运动检测来做强度控制。入门最容易犯的错是降噪强度拉太高画面纹理全部被抹平像蒙了一层油。这其实不是降噪模块的问题更多的是没区分亮度噪声和色度噪声、没做边缘保护。正确做法是色度降噪可以激进亮度降噪要克制边缘附近的降噪强度要主动降下来。你去看一套成熟的画质参数几乎都是这样一套分层克制的思路。6.3 锐化过冲与振铃的平衡降噪之后画面会发软所以后面通常接锐化。最常用的是Unsharp Mask原理是先模糊原图用原图减模糊图得到细节分量再把细节分量乘一个增益加回原图。锐化最容易出的问题有两个过冲和振铃。过冲是边缘周围出现一圈白边或黑边看着像给物体描边振铃是边缘附近出现多重波纹像回声一样。两个都是因为锐化在边缘区域加了过多高频能量。调锐化的时候记住一句话宁欠勿过。过冲比模糊难救模糊最多是画面软过冲在视频里非常显眼而且难以在后期磨掉。7. RGB转YUV数据出门前的最后一站7.1 为什么要转YUV到这里图像已经是一张人能看的彩色图了但ISP通常不直接输出RGB而是转成YUV。原因主要有两个。第一YUV把亮度Y和色度U、V分开可以分别编码和处理符合人眼对亮度更敏感的特性。第二YUV可以方便地做色度子采样比如4:2:0在几乎不影响观感的前提下砍掉一半以上的数据量为传输和编码省下带宽。如果你做过视频编码相关的工作会非常熟悉这套逻辑。7.2 转换公式与Full/Limited RangeRGB到YUV的转换本质上是另一个3x3矩阵变换但标准有讲究。高清视频常用BT.709标清用BT.601。以BT.601为例8bit下的亮度公式是Y 0.299 × R 0.587 × G 0.114 × B这里必须注意Full Range和Limited Range的区别。Full Range的Y和UV都在0到255Limited Range的Y只用到16到235UV只用到16到240。如果输出端设成Limited Range下游编码器却按Full Range处理画面就会灰蒙蒙一片黑的不够黑白的不够白。这是调试里非常常见也非常隐蔽的坑很多人调了半天对比度曲线结果问题出在一个Range标志位上。7.3 4:2:0子采样的常见坑4:2:0的意思是每四个亮度像素共用一对UV色度分辨率只有亮度的一半。对大多数画面来说无所谓但遇到彩色细条纹、文字边缘这类高频色度信息就会出现色度渗色比如红色文字的边缘泛出一圈红晕。解决办法是让去马赛克阶段的滤波器和YUV转换阶段的滤波器做好配合必要时在YUV域加一点色度低通把色度渗色压下去。但这也是一个博弈压太狠颜色锐度下降不压彩色边缘发糊。遇到文字边缘渗色时先检查4:2:0转换前是否有足够的抗混叠处理再考虑加滤波顺序别搞反。8. 常见问题速查与调试心得8.1 快查表现象大概率原因排查方向整张图统一偏色Bayer排布错误或AWB色温估计失准先确认RAW排布再看AWB统计结果画面一半偏蓝一半偏黄混合光源场景确认光源环境考虑分区白平衡边缘彩色条纹去马赛克方向判断或CCM饱和度过高先关闭CCM单独看去马赛克输出暗部彩色噪点明显CCM噪声放大或色度降噪不足检查CCM系数加强色度降噪暗部发灰、黑位抬升Black Level设置错误或Gamma暗部被过度提亮检查BLC值和Gamma暗部斜率整体灰蒙蒙、对比度低YUV的Full/Limited Range不匹配检查输出端与编码端的Range设置文字边缘彩色渗边4:2:0子采样导致的色度渗色YUV域加色度低通做抗混叠高光死白、暗部死黑Tone Mapping动态范围压缩不足增加HDR合成或调整TM曲线8.2 调ISP的一点个人体会最后说点经验。学ISP数据流最快的方法是拿一张自己拍的RAW图从头到尾走一遍看RAW直方图看BLC之后的直方图看LSC之后边角亮度有没有被拉平看去马赛克后的RGB图再一级一级叠加AWB和CCM。每一步输出都存成中间图画质出问题的时候对比相邻两级的中间图立刻就能锁定是哪个环节出的问题。我当年入门时最大的教训就是跳级排查。看到画面偏色直接跑去调CCM调了半天才发现是AWB的增益被限幅了。后来养成了每次只改一个变量、每一级都留中间图的习惯调试效率翻了几倍。这篇数据流走向到这里整个图像从传感器到输出端的路径就算完整了。下一篇我打算写3AAE、AWB、AF和这条数据流是怎么联动工作的那才是真正让画质活起来的部分。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →