ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

传感器RAW噪声标定全流程:从PTC到DSNU/PRNU校正

传感器RAW噪声标定全流程:从PTC到DSNU/PRNU校正 很多人在刚开始接触传感器数据时都有一个误解RAW是最原始、最“干净”的数据是相机传感器“看到”的真相。实际恰好相反RAW是噪声最多、最未经修饰的数据里面除了你要的光信号还堆满了暗电流、读出噪声、固定模式噪声以及黑电平偏移带来的底色。没有做过噪声标定的RAW直接拿去做高精度测量、拼接、HDR融合或者训练算法结果基本都是被这些“杂质”牵着鼻子走。所以这篇文章我想认真聊一聊RAW数据的噪声到底从哪来应该用什么模型去描述它以及怎么通过一套完整的标定流程把传感器的真实性能参数增益、读出噪声、暗电流、DSNU、PRNU全部算出来再把这些参数变成可落地的校正方案。内容主要面向做图像质量调优的工程师、做机器视觉和计算摄影的开发者以及那些想在自家相机或工业相机上做底层图像处理、但对噪声标定还停留在“听说过”阶段的朋友。我会把原理和实操放在一起讲尽量做到你看完就能照着搭一套自己的标定流程。1. 为什么说“没做过噪声标定的RAW和废片差不多”先把一个容易混的概念说清楚这里的RAW指的是CMOS或CCD图像传感器直接输出的原始像素数据不是U盘和存储卡里那个“raw格式无法格式化”的raw。后者是文件系统层面的问题跟图像传感器噪声完全是两码事。我们要处理的是传感器把光子转成电子、再把电子转成电压和数字码值的过程中混入的各种干扰信号。1.1 RAW数据里的“杂质”比你以为的多得多一个RAW像素的最终值可以粗略写成DN_output k * (N_photo N_dark) DN_offset N_read N_fpn其中k是系统增益单位是DN/e-码值/电子数N_photo是光生电子数N_dark是暗电流产生的电子数DN_offset是黑电平偏移N_read是每一行读出时引入的随机噪声N_fpn是固定模式噪声。也就是说你从RAW文件里读到的每个像素值等于“真实光信号”叠了一大堆东西之后的结果。如果你不标定、不校正直接拿这个值去做白平衡、做颜色矩阵、做亮度统计这些杂质会被一并放大甚至在某些场景下喧宾夺主。比如拍一张暗部占主体的夜景照片RAW里暗部区域的码值方差可能有一大半来自读出噪声和暗电流而不是真实的光子涨落。1.2 噪声模型和标定到底是什么关系噪声模型解决的是“为什么会有噪声、噪声有多大、噪声之间怎么叠加”的问题。标定解决的是“把模型里那些未知参数具体量化出来”的问题。拿一个常说的情况举例你发现传感器在某个增益档位下暗部出现明显的竖条纹这是FPN里的列噪声。要想去掉它你必须先知道每一列相对于平均值的偏移量是多少这个偏移量的测定过程就是标定。同理你想知道传感器在低光照下的极限灵敏度就必须测出读出噪声的等效电子数这同样依赖标定。所以噪声模型是理论框架标定是把这个框架从公式变成一个个可用的数值。两者缺一不可。1.3 什么场景必须做什么场景可以偷懒我说了这么多并不是要劝所有人在任何项目里都搞一套严丝合缝的噪声标定。实际工作中要先判断需求做普通拍照、手机摄影、日常视频ISP里的3A和去噪模块已经帮你做了一部分隐式处理这时候可以不额外标定。做机器视觉测量、科学成像、医学影像、天文摄影或者把RAW喂给深度学习模型做低光照增强那噪声标定几乎是必须的。因为你要么需要精确的辐射值要么需要逼真的噪声分布来训练网络。一句话你的下游任务越依赖像素值的准确性噪声标定的优先级就越高。2. 把噪声拆开散粒噪声、读出噪声、暗电流和FPN各管哪一段想标定噪声先得认识噪声。传感器里的噪声来源可以分两大类随机噪声和固定模式噪声。随机噪声每次拍都不一样不能通过扣同一帧模板去掉只能通过统计方法估计固定模式噪声每次拍都长一个样是传感器的“指纹”只要测到模板就能干净地减掉。2.1 散粒噪声光的粒子性决定的“物理底线”光子到达传感器是离散事件不是绝对均匀的流动。这个报数可以用泊松过程来描述其标准差等于信号均值的平方根。也就是说信号越强散粒噪声的标准差也越大但信噪比反而越高因为信号增加的速度比噪声增加的速度快。在RAW码值域来看散粒噪声导致的方差会随信号均值线性增长这一条是后面PTC光子转移曲线拟合的核心依据。它与其他噪声不一样是你不管怎么做电子学设计都消不掉的属于传感器的“物理底线”。2.2 读出噪声和暗电流电子学层面的“底噪”和“漏电”读出噪声read noise是传感器把电荷转成电压、再经过模拟前端和ADC量化时引入的随机噪声和曝光时间无关拍一帧全黑图像也有。它主要由源极跟随器的热噪声、复位噪声、列放大器噪声、ADC量化噪声组成通常用电子数e-来描述数值越小说明传感器底噪越干净。暗电流dark current是半导体材料在没有光照时因为热激发产生的漏电。曝光时间越长、传感器温度越高暗电流累积越多。暗电流本身具有空间不均匀性它里面均匀的那部分叫暗电流均值不均匀的那部分会形成固定模式噪声同时暗电流的产生过程是随机的所以也有一个泊松性质的散粒噪声分量叫暗电流散粒噪声。2.3 DSNU和PRNU固定模式噪声其实是“指纹”固定模式噪声一般分两种DSNUDark Signal Non-Uniformity暗场下各像素暗电流大小的不一致性。即使完全挡光、同一曝光时间各个像素的输出码值也不完全相同。PRNUPhoto Response Non-Uniformity亮场下各像素对同样光强响应的不一致性。比如一束完全均匀的光打到传感器上不同像素的转换效率有细微差别导致输出值不同。DSNU和PRNU都是空间固定的不会随时间改变温度、增益改变会影响大小但模式不变。所以它们可以通过拍一张暗场/亮场模板逐像素做减法或除法来校正。这也是“标定”最直观、最立竿见影的用途。2.4 不同噪声在图像上长什么样实际看图像时这些噪声各有各的“长相”散粒噪声表现为颗粒感随亮度增大而变得不明显放大看像细沙。读出噪声暗部尤其明显通常表现为全画面的颗粒条带如果行噪声占主导会出现横向或纵向的细腻条纹。暗电流长时间曝光时画面整体发亮且亮度不均匀发热严重的传感器角落更亮或者出现固定的一片模糊亮斑。列噪声/行噪声整列或整行像素有近似一致的偏移表现为竖条纹或横条纹。这是FPN最讨厌的地方人眼对这类结构纹理特别敏感。理解这些特征之后再回到标定流程你就能明白每一步到底在针对哪种噪声做处理。3. 用PTC光子转移曲线把传感器参数全部算出来讲完噪声类型接下来是重头戏怎么把这些噪声用数字精确描述出来。最标准的做法是拍一组均匀光场图像做均值-方差分析画出PTCPhoton Transfer Curve光子转移曲线再从中提取传感器的关键参数。3.1 一套完整的传感器参数清单标定结束之后你最后应该得到这些参数参数符号单位含义系统增益kDN/e-一个电子对应多少个码值读出噪声σ_re-暗场下的随机底噪满阱容量FWCe-像素能容纳的最大电子数暗电流De-/s单位时间热激发产生的电子数暗场非均匀性DSNUDN或%暗场下像素间偏移的STD光响应非均匀性PRNU%均匀光场下像素响应的STD/均值黑电平偏移DN_offsetDN零光强时ADC输出的码值有了这组参数你对这颗传感器的性能就有了完全量化的把握后面做图像处理才有依据。3.2 PTC实验的基本逻辑拍一系列均匀亮场PTC实验的思路不复杂让传感器分别在不同光照强度或不同曝光时间下成像保证输出的平均码值从一个很小的值一直延伸到接近饱和然后对每一帧图像统计均值和方差。对于一张只受散粒噪声和读出噪声影响的图像总方差的表达式可以写成σ_total² k * μ_all σ_read²其中μ_all是总信号均值包含黑电平偏移和暗信号k是系统增益σ_read是读出噪声包括暗电流散粒噪声的总和但暗电流很小时可忽略。所以如果你用一组不同亮度的均匀光场图把每个图的方差和均值做线性拟合斜率就是系统增益k纵轴截距就是读出噪声σ_read²。原理和计算都非常直接这也是EMVA 1288标准的核心做法之一。3.3 关键公式的拟合与计算细节这里重点讲一下拟合时的细节。实际PTC数据里方差在低均值区和饱合区都会偏离线性所以拟合时不能无脑全用。低均值区方差被读出噪声主导如果暗电流散粒噪声不小会使得曲线在低均值段有上翘拟合时容易高估增益。通常做法是把均值低于某个阈值的数据点去掉阈值取50个码值或根据暗场方差动态设定。线性区散粒噪声主导方差和均值呈线性这一段才是拟合系统增益的黄金区域。饱和区像素接近满阱时方差不再增长甚至会下降因为电荷被截断涨落被压缩了。这部分数据一定不能纳入拟合。实际拟合我建议用加权最小二乘方差较小的点权重更高避免低亮度点把斜率拉偏。如果你用Python实现可以直接用numpy.polyfit但记得先做截取和加权。3.4 实测PTC时容易翻车的四个细节第一光源必须绝对稳定。PTC假设光信号在时间上是平稳的光源一闪烁方差里就会混入光源的波动导致增益被严重高估。我吃过这个亏用便宜的LED灯板做PTC结果算出来的“系统增益”比标称值大了将近一倍后来换了大面积积分球光源才稳定下来。第二曝光时间范围要覆盖得足够宽。只拍暗场和接近饱和的几个点线性区根本拟合不出来。第三均值最低点不要取太暗。有些相机会做黑电平钳位暗部信息被截断后方差会异常偏小。第四用均值而不是中位数来代表信号水平。虽然在均匀光场下两者差别不大但均值对噪声的统计特性更敏感和方差的对应关系更直接。4. 实操标定流程从暗场到亮场一步步算出校正参数原理说了这么多接下来是完整实操流程。我按一套工业相机标准光源的典型环境来写手机模组、单反的流程也基本一样只需要根据接口和软件做调整。4.1 准备工作环境、光源和相机设置标定需要在稳定的环境下进行环境温度尽量控制在25℃左右恒温或者至少记录下当前传感器温度因为暗电流对温度极度敏感。准备可调亮度的均匀光源最好用积分球或者高均匀度灯箱均匀性要优于98%。如果实在没有用硫酸纸加平板光源多叠几层也能凑合但后面PRNU结果会偏大。先把相机固定好拧紧镜头或直接裸传感器测试。有镜头的话一定盖好镜头盖后再拍暗场。相机参数设为手动模式关闭自动曝光、自动白平衡、自动增益。所有图像格式设为RAW输出关闭相机内所有降噪功能。4.2 暗场序列采集与暗电流、读出噪声分离暗场的目的是测出没有光时传感器的底噪。盖上镜头盖或者用完全不透光的盖子遮住镜头确保没有一丝光漏进去。设一个较短的曝光时间比如1ms或尽量接近0拍10张以上图像这些图像的平均值就是黑电平偏移DN_offset标准差就是总读出噪声的近似值。然后设置一组递增的曝光时间比如1ms、10ms、100ms、1s、5s、10s……每档拍5-10张分别计算图像均值和方差。把均值随曝光时间的变化做线性拟合斜率为暗电流D单位DN/s再加上3.3节算出的增益k就能换算成e-/s。这里有一步容易被忽略暗电流只会增加暗信号不会影响读出噪声的绝对值它自己会引入散粒噪声。要分离两者需要看方差和曝光时间的关系。如果曝光时间增长时方差增长明显说明暗电流散粒噪声占比大如果方差基本不随曝光时间变化说明读出噪声主导。用公式表达就是σ_dark_total² σ_read² k * μ_dark其中μ_dark就是暗电流均值所以暗电流散粒噪声 k * μ_dark读出噪声σ_read²减去这一项。实际操作中我会直接用短曝光暗场算σ_read再用长曝光暗场算暗电流比较清晰。4.3 亮场序列采集与增益、PRNU计算拍亮场时保持曝光时间固定通过调节光源亮度或中性密度滤光片来改变光强。我给一个可复用的参数模板曝光时间选一个中间值保证线性区有足够空间比如10ms。调整光源亮度使图像平均码值分别落在满量程的5%、10%、20%、40%、60%、80%左右。每档拍10帧最好边拍边看直方图避免截断或饱和。每帧统计平均值μ和空间方差σ²。注意这里的空间方差是“每一帧图像内所有像素的方差”然后对多帧取平均能抑制时间随机噪声对统计的干扰。把μ和σ²画出来线性拟合后得到增益k。PRNU的计算更简单取亮度落在饱和值80%左右的均匀图对多帧平均后算所有像素的STD再除以均值就得到PRNU百分比。DSNU则是暗场图长曝光STD/均值注意DSNU只算空间方差不对时间噪声做平均。4.4 标定结果落成文件保存哪些内容、什么格式标定完成后数据要保存成方便后续程序加载的格式。我习惯用JSON或NumPy的.npz保存内容包含增益k、读出噪声σ_read、暗电流D、黑电平偏移DN_offsetDSNU模板一帧大小与RAW相同的暗场均值图像PRNU模板一帧与RAW相同尺寸的增益校正矩阵或直接保存一个亮度校正系数矩阵传感器型号、固件版本、温度、增益档位、曝光时间、文件名前缀等元数据这里有个关键点DSNU和PRNU模板最好按增益档位分别保存。因为不同增益下读出的偏置和增益匹配会略有不同跨增益使用同一套模板会引入新的误差。4.5 用Python快速验证标定效果标定结果好不好最直接的办法是验证。我这里的思路是拿标定参数对拍摄的暗场和亮场做一次校正再统计校正前后的方差和条纹是否改善。下面是核心验证代码思路import numpy as np from numpy.fft import fft2, fftshift def correct_raw(raw, offset, gain_map, dark_fpn, prnu_map): # 1. 减黑电平 corrected raw.astype(np.float32) - offset # 2. 减暗场固定模式噪声 corrected - dark_fpn # 3. 除以PRNU增益图 corrected / prnu_map return corrected def check_noise(raw, n_row50): # 计算某一行内相邻像素差的均方根衡量行噪声是否减弱 diff raw[:, 1:] - raw[:, :-1] return np.std(diff)验证时看两件事一是校正后图像的方差是否显著下降特别是暗部二是在频域上看竖条纹/横条纹的能量有没有消失。如果校正后顺条纹依然明显说明DSNU模板没建准需要重新做多帧平均把随机噪声影响压下去。5. 标定数据写进图像管线减暗场、增益归一化、FPN校正的顺序别搞反标定参数算出来了真正的考验才刚开始怎么把它们正确地用进图像处理管线。顺序一搞错轻则校正效果打折重则引入新的噪声和人造纹理。5.1 标准校正顺序先减偏置再减固定模式噪声最后做增益归一化我在实际调试中总结出来的推荐顺序是先对RAW减去黑电平偏移DN_offset把零点对齐。再减去暗场固定模式噪声模板DSNU模板。这个模板必须在同一温度、同一曝光时间条件下拍摄否则里面有暗电流差减了反而引入新的残差。然后把每个像素除以PRNU增益图把不同像素的响应校准到同一个基准。最后才是做白平衡、颜色矩阵、去马赛克、降噪等上层处理。为什么不能把增益归一化放在减暗场之前道理很简单如果像素间的增益不一致你直接减去偏置残留的噪声幅度也会被放大然后你再除以一个不准确的增益图可能会在原来的噪声结构上又叠加一层新的伪差。5.2 和去马赛克、降噪模块的关系先校正再降噪ISP管线中典型的顺序是RAW校正 → 黑电平 → 去马赛克 → 白平衡 → 颜色校正 → Gamma → 降噪 → 锐化。但你拿到RAW做离线处理时顺序可以更灵活。我个人建议在去马赛克之前做减暗场和FPN校正因为此时噪声还是单通道逐像素的校正模板和像素一一对应去马赛克之后像素变成了插值结果再做FPN校正就容易出现彩色的伪影。降噪模块放哪里取决于你是用传统算法还是AI算法。传统BM3D或频域去噪放在Gamma之前比较好基于深度学习的降噪模型如果训练时喂的是RAW域数据就要在RAW域做而且模型输入的归一化要跟你标定数据的分布保持一致。5.3 一个可上手的“标定降噪”流水线示例我整理一个相对通用的流水线步骤适合工业相机离线处理场景# 步骤概览 1. 加载RAW按Bayer格式排列 2. 对每个通道分别减offset减DSNU乘PRNU校正系数 3. 输出标定后的RAW保存成16bit tiff 4. 用去马赛克算法如RCD转成RGB 5. 白平衡用灰卡标定值 6. 做色域变换sRGB/Adobe RGB 7. 在亮度通道上做温和降噪保护边缘 8. Tone Mapping Gamma每一步的具体参数和算法都可以根据自己的场景调整但骨架顺序别乱动。5.4 校正不完全时的症状竖条纹、亮度不均到彩色伪影如果校正文件有问题图像会表现出一些典型的症状通过这些症状可以反向排查竖条纹残留DSNU模板里的列噪声没扣干净大概率是模板平均帧数不够或者相机行读出增益有变化。整体亮度沿一个方向渐变PRNU模板可能算错了常见原因是光源均匀性太差或者亮场采集时光源本身有渐变。暗部有彩色斑点黑电平没有精确减掉导致去马赛克后暗部各通道偏置不一致。固定位置出现亮暗环镜头阴影和PRNU混在一起了需要先做镜头阴影校正lens shading correction再做PRNU校正。我在项目里曾经遇到过一种很隐蔽的情况同一颗传感器在不同姿态横放、竖放、倒置下DSNU表现一致但PRNU会因为重力导致的微小微距形变出现几个像素的偏移校正模板一错位就出现鬼影一样的边缘。所以如果相机结构是柔性的标定模板的像素位置对齐一定要做校验。6. 我实测翻过车的几个坑希望你绕开最后这部分我把我这些年做传感器标定踩过的坑集中说一下每一条都是真金白银换来的经验希望能帮你少走一些弯路。6.1 温度漂移暗电流标定没控温冬天夏天两套结果暗电流随温度的变化近似遵循阿伦尼乌斯关系温度每升高6-8℃暗电流差不多翻一倍。我第一次做长曝光标定时在空调房和室外各测了一组数据结果暗电流差了快两倍。后来每次标定我都先让相机在目标温度下开机预热半小时以上等传感器温度稳定后再采集并且把温度记录在标定文件里。如果你做的是车载或户外相机最好在标定阶段就覆盖工作温度范围做多温度点插值。6.2 黑电平钳位在“吃”你的暗场方差很多相机的ISP会在传感器层做黑电平钳位把暗场均值强制拉回0附近低于某个阈值的像素直接钳成0。这会导致你拍出来的RAW方差显著偏小直接影响PTC拟合和读出噪声计算。遇到这种情况先去关闭黑电平钳位如果闭不掉就要在采集暗场图像时避开极短曝光并且用中位数而不是平均值来估计真实黑电平偏移量。我试用过一款模组它的黑电平钳位会在某些增益档位自动启用不仔细看文档根本发现不了浪费了我整整一周时间。6.3 光源闪烁和色温漂移对PRNU的影响做PRNU标定用的是单色光或白光光源如果有轻微闪烁PRNU计算值会被污染。有些LED光源在低亮度下PWM频率不足虽然肉眼看不出闪烁但传感器1/60s曝光下已经出现明显的亮度条纹。我现在的做法是亮场采集前先拍几帧看均值的时间序列波动如果STD超过0.3%就换光源或者延长曝光时间覆盖多个PWM周期。色温漂移虽然对单通道PRNU影响不大但对彩色传感器来说不同通道的响应会随光源色温变化标准做法是用特定波段的滤光片或标准光源D65做单通道标定。6.4 别拿标定参数硬套别的相机和别的增益档位这个问题我在协助别的团队做联合标定时经常遇到。他们在A相机上做了完整的噪声标定然后直接把参数套到B相机上结果出厂前发现暗部竖条纹异常明显。传感器个体差异比想象中大得多同一批晶圆出来的芯片PRNU都可能差0.5%以上。所以每台相机、每个增益档位都要单独标定。批量生产时至少要做抽检并建立参数追溯库发现偏离超出阈值就重新标定。6.5 标定参数在深度学习训练集里的另类用法最近在带一个低光照增强项目时我发现一个有意思的用法把标定参数当作数据增强工具。具体来说训练网络时输入的不是绝对干净的RAW而是先用噪声模型模拟出噪声版本再把标定测得的降质模板加到干净图上。这样网络见过足够多的真实噪声分布后泛化能力明显好于只用高斯白噪声模拟的结果。具体做法是把PTC模型拟合成一个仿真器每次迭代时对干净图像做noisy clean Poisson(clean * k) Gaussian(0, sigma_r^2) dark_fpn prnu * clean这个仿真器还能用来预估算法的极限性能噪声降到多少 dB 后算法的精度不会再提升从而在硬件选型和算法设计之间找到一个平衡点。我做噪声标定这些年最大的体会是标定不是一次性的数学作业它是一个需要反复迭代、不断和物理世界对表的过程。你算出来的每一个参数都要拿到真实图像上去验证竖条纹消了吗暗部方差降了吗不同亮度下的一致性达标了吗只有这些肉眼可见的改善落地了标定才算真正完成。希望这篇文章里的流程和经验能帮你把手上的RAW数据变成真正信得过、拿得出手的结果。
返回列表