资讯详情

资讯详情

相机噪声模型与标定:泊松噪声、光子转移曲线及张正友法

做计算成像这几年我越来越觉得“标定”这个词被严重低估了。很多人拿到一台相机第一反应是拍棋盘格、跑一遍脚本、拿到内参和畸变系数然后就当完事等到算法在低光照下突然崩掉或者重建出来的三维点云在边缘处成片扭曲才会回头补课原来传感器输出的每一个灰度值都不是一个确定性的数而是一个带方差的观测值。相机噪声模型与标定就是把“这个观测值有多可信”和“这个观测值是从哪条几何路径来的”这两件事一起解决掉的功课。这篇文章对应我计算成像系列的第6讲核心三块泊松噪声的物理来源与数学模型、光子转移曲线PTC的实测与参数提取、张正友标定法的原理拆解与实操纪律。讲完之后我会把这三块串回同一条流水线说说噪声模型怎么反过来影响几何标定的精度以及我这两年在实际项目里攒下的踩坑记录。适合谁在校学生做课程项目之前应该读视觉算法工程师准备自建相机评测流程之前应该读甚至只是想知道“相机里面的噪声到底能不能测出来”的硬件爱好者也可以读。读完你应该能自己动手搭一套 PTC 标定流程并且能解释清楚为什么同一个传感器在暗光下标出来的棋盘格角点位置会飘。1. 信号链全景光子、电子、电压、灰度噪声插在每一环1.1 为什么标定之前先要理解噪声计算成像的前提是你拿到的每一张图像都是一组物理量的测量结果。测量就一定有不确定度这是物理决定的不是算法能绕过去的。很多刚接触成像的同学会把传感器输出的 DNDigital Number灰度值当成一个“精确的读数”然后直接拿去做反演、重建、匹配。这种假设在高亮、长曝光、低增益的条件下勉强能撑住一旦进入暗光、短曝光、高增益模型的输出就开始剧烈振荡而且你很难分辨到底是算法错了还是输入数据本身就不靠谱。所以我的习惯是动几何标定之前先把噪声模型搞清楚。原因很朴素——只有知道每一步观测值有多大方差你才知道哪些像素值得信任、哪些像素只是巧合。而光子转移曲线就是干这件事的它能把传感器的增益每个 DN 对应多少电子、读出噪声、满阱容量一次性标出来。有了这套参数你再去看泊松噪声、去设计去噪算法、去评估角点检测精度都会有一个统一的度量单位而不是停留在“噪声大不大”这种模糊感觉上。1.2 从光子到灰度每个环节的噪声来源一条典型的 CMOS 成像信号链可以拆成六段入射光子 → 光电转换产生电子 → 电荷在势阱中积累 → 读出电路把电荷转成电压 → 放大器 → ADC 量化为 DN。每一段都会往信号里掺入自己的不确定性。第一段是光子本身的随机性。光子到达传感器表面的过程可以看成独立随机事件单位时间内到达的数量服从泊松分布这是泊松噪声的根源也是本讲最重要的一个噪声项。第二段是光电转换效率QE量子效率它决定有多少光子最终变成电子。即使 QE 是 1电子数仍然是泊松随机的QE 小于 1 只会等比例降低信号均值不改变“方差等于均值”这个性质。第三段是暗电流即使完全遮光半导体内部热激发也会产生电子这部分电子同样服从泊松分布而且随温度指数增长。第四到第六段是读出链主要贡献读出噪声近似高斯、固定模式噪声像素间增益与暗电平不一致和 ADC 量化噪声。我把主要噪声源整理成一张表方便对照着看噪声来源产生环节统计特性主要对策光子散粒噪声光电转换泊松方差均值电子数增加曝光或采用多帧平均暗电流噪声暗电子热激发泊松随温度升高制冷、暗帧扣除读出噪声读出放大链路近似高斯与曝光无关慢速读出、相关双采样固定模式噪声像素工艺差异空间固定随光照线性/偏置变化平场校正、暗帧扣除ADC 量化噪声模数转换均匀分布幅度约 1/√12 LSB提高位深这张表里最容易被忽略的是暗电流。我做低光照项目时吃过好几次亏晚上实验室空调关了传感器温度从 25℃ 升到 35℃PTC 标出来的读出噪声倒没变多少但暗电流翻了一两倍导致所有长曝光暗场全是“假信号”。所以后面我会反复强调标定噪声参数时温度是必须记录的环境变量。1.3 时间噪声与空间噪声先分清再测噪声从“帧与帧之间”和“空间分布”两个维度看性质完全不同。时间噪声是指同一像素在不同帧之间的随机涨落包括散粒噪声、暗电流随机部分和读出噪声空间噪声则是固定的比如某个像素天生偏亮或偏暗每一帧都在同一个位置出现同样的纹路这就是固定模式噪声FPN它内部又分为暗信号非均匀性DSNU和光响应非均匀性PRNU。这个区分对测量至关重要。如果你只拍一张均匀亮场图直接计算整幅图的方差你得到的是“时间噪声 空间噪声”的混合值。在均匀光源下PRNU 引起的方差往往比散粒噪声还大这时候你算出来的曲线斜率就会偏离真实增益。解决办法非常朴素同一个曝光条件下连拍两张两张相减固定模式噪声在差分中精确抵消剩下的方差是两倍的时间噪声除以 2 就还原成单帧时间噪声方差。我在下一节讲 PTC 时会默认采用这个配对差分的做法这是整条测量流程的地基。2. 泊松噪声的数学本质为什么光子数是随机变量2.1 泊松分布是怎么来的理解泊松噪声先要从光子到达的物理过程说起。假设一束稳定光打在传感器上单位时间内到达某个像素的光子平均数是 λ。光子与光子之间是相互独立的——上一个光子到达后下一个光子的“到达概率”不会因此变大或变小。在这样一个“独立、稀疏、速率恒定”的随机过程中任意时间窗口内到达的光子数会服从泊松分布P(k) λ^k · e^(-λ) / k!这个分布最漂亮的性质是均值等于方差都等于 λ。也就是说如果你测了 100 次曝光平均每次收到 100 个光子那么单次接收到的光子数并不是稳定的 100而是围绕 100 上下波动标准差是 √100 10 个光子。这种波动不是测量误差而是光本身的粒子性带来的你再怎么优化电路也消不掉。我经常用一个雨滴的类比给学生讲想象一块平整的瓷砖地面雨下得很均匀每块瓷砖平均每秒落 10 滴雨。但你数任意一块瓷砖的真实雨滴数会发现有的 7 滴、有的 13 滴这种差异不是哪块瓷砖有问题而是雨滴本身就按泊松规律随机降落。相机传感器就是那片瓷砖。2.2 SNR 与 √N 定律泊松分布直接给出一个残酷的标尺信噪比 SNR λ / √λ √λ。这里的 λ 是光子数或者电子数二者定量关系只差一个量子效率系数。换句话说信号涨 4 倍信噪比才涨 2 倍要想把 SNR 从 10 提到 20光子数要变成原来的 4 倍提到 100光子数要变成原来的 100 倍。这个“√N 定律”解释了计算成像里几乎所有关于曝光的经验法则。为什么暗光下图像看起来“糊”不是因为镜头不锐而是因为每个像素收到的光子太少散粒噪声直接把信号细节淹没了。为什么天文摄影动辄单帧曝光十几分钟因为要积累光子数把 λ 堆上去才能让暗弱天体的 SNR 压过读出噪声。为什么像素合并binning能在暗光下改善图像因为合并 N 个像素等效λ变成 NλSNR 提升 √N 倍代价是分辨率下降。下面这张表可以帮你建立直觉平均光子数 λ散粒噪声标准差 √λSNR线性相对噪声%103.163.1631.6100101010100031.631.63.16100001001001所以当你看到一张平均亮度只有 100 个电子/像素的图时请记住哪怕传感器本身零瑕疵、电路零噪声这张图的理论极限信噪比也只有 20dB。这就是为什么低光照成像的关键永远是“多收光子”而不是“多加增益”——增益只是把噪声和信号一起放大并不能提高 SNR。2.3 仿真把泊松噪声加进一张理想图像理解归理解最好还是动手仿真一次。下面这段 Python 代码把“光子到达 → 光电转换 → 读出噪声 → 量化成 DN”的完整链路模拟出来你可以调参观察散粒噪声的行为import numpy as np def simulate_exposure(photons, qe0.6, gain1.0, read_noise2.0): 模拟一次曝光。 photons: 每个像素的入射光子数二维数组 qe: 量子效率 gain: 系统增益单位 e-/DN read_noise: 读出噪声单位电子 返回 DN 图像 electrons np.random.poisson(photons * qe) # 泊松散粒噪声 total_e electrons np.random.normal(0.0, read_noise, photons.shape) dn np.clip(total_e / gain, 0, 65535) return dn, electrons # 均匀照明每像素平均 100 光子 photons np.full((256, 256), 100.0) dn, e simulate_exposure(photons) print(电子均值:, e.mean().round(1), 电子标准差:, e.std().round(1))跑一次你会发现标准差和均值几乎是同一个量级这正是“方差等于均值”在数据上的体现。你还可以把 photons 换成一张真实的纹理图分档设置平均光子数10、100、1000观察同一场景在不同光照下的噪声表现。做这个实验最大的收获是建立直觉噪声的绝对大小随信号强度变化暗部方差小但相对噪声大亮部方差大但相对噪声小这决定了任何“与灰度无关的固定高斯去噪”都是错的去噪权重必须按像素亮度动态调整。3. 光子转移曲线实测半小时摸清传感器的家底3.1 均值和方差之间的一条直线光子转移曲线的名字听起来很学术原理却只要一行公式。在 DN 单位下单一像素的时间噪声方差可以写成Var(DN) mean(DN) / K σ_read²其中 K 是系统增益单位是 e-/DN它描述了“平均多少个电子对应一个 DN”σ_read² 是读出噪声方差单位是 DN²。为什么方差里会有 mean(DN)/K 这一项因为均值每增加 1 个 DN实际增加的是 K 个电子而电子数的泊松方差等于 K·mean(DN)换算回 DN² 再除以 K²正好得到 mean(DN)/K。把不同曝光量下拍摄的平场图像逐幅计算均值和方差再以 log(mean) 为横轴、log(variance) 为纵轴画图就得到经典的 PTC。曲线上有三个特征区段低信号端散粒噪声远小于读出噪声方差是一条水平线log-log 斜率为 0中段散粒噪声主导方差与均值成正比log-log 斜率为 1高信号端接近满阱电荷溢出导致方差回落出现一个“膝盖”状拐点。很多传感器厂商会把这条曲线画在 datasheet 里但自己实测一遍才是真的懂。3.2 拍摄要点平场、暗帧、配对差分PTC 测量本身不复杂但细节决定成败。我按固定顺序走下面的流程供你照着抄关闭一切自动功能自动曝光、自动白平衡、ISP 降噪、HDR 合成全部关掉输出 raw 或未处理的 sensor 原始数据。JPEG 里藏了太多非线性处理会直接毁掉线性方差关系。准备均匀光源首选积分球或用磨砂玻璃充分散射的 LED 面光源。光源要稳定注意市电驱动的灯具有 100Hz/120Hz 频闪两帧之间亮度波动会污染方差估计最好用直流供电或高频 PWM 的 LED点亮后预热 10 分钟再开拍。记录暗场盖上镜头盖用最短曝光时间拍 1 张偏置帧再用实际曝光时间拍 1 张暗场。后续所有平场都先减掉这张暗场消除偏置电平和暗电流。平场序列从最低曝光开始每档曝光连拍 2 张配对然后逐步增加曝光时间直到画面中心接近饱和。档位建议覆盖 8~12 个亮度级。计算方差取画面中央约 200×200 的区域避免边缘渐晕影响。对每一对图像计算 diff (im1 - im2) / √2然后取 diff 的方差作为该亮度下的时间噪声方差同时用单帧图像的方差作为含固定模式噪声的总方差两个值都记录下来后面能对比看 PRNU 的贡献。这里有个容易踩的坑配对差分时除以 √2 经常被漏掉。两帧独立图像的差方差是单帧的两倍不除以 √2 直接当单帧方差用K 值会翻倍偏大满阱容量也跟着错。我见过不少从网上抄的代码漏掉这一步结果标出来的增益和厂商手册对不上。3.3 从曲线里读出 K、读出噪声、满阱容量拿到均值和方差序列后拟合直线提取参数。我习惯在散粒噪声主导的中段避开低端读出噪声平台和高端饱和拐点做线性拟合import numpy as np from numpy.polynomial import polynomial as P def ptc_fit(means, vars_, k_min, k_max): 在线性区拟合 Var a * mean b k_min/k_max: 拟合区间 [DN]需要肉眼从曲线中段选 mask (means k_min) (means k_max) coeff, _ P.polyfit(means[mask], vars_[mask], 1) a, b coeff[1], coeff[0] # a1/K, b读出噪声方差(DN²) K 1.0 / a # 系统增益, e-/DN read_noise np.sqrt(b) * K # 读出噪声, 电子 return K, read_noise拟合结果按以下方式解读K系统增益单位 e-/DN。K 越小每个 DN 对应的电子越少等效 ISO 增益越高图像越“亮”但噪声也被放得越大。σ_read √b × K读出噪声的电子数。工业相机通常能做到 2~5 e-消费级相机在 5~10 e-高增益档位还会更高。满阱容量FWC在 log-log 曲线上找到方差开始回落的拐点对应的均值 DNFWC K × mean_DN_knee。这个值决定了一个像素最多能装多少电子直接影响 HDR 设计的曝光分层策略。交叉点散粒噪声等于读出噪声的电子数 N_cross σ_read²。比如读出噪声 3 e-那平均信号只要到 9 个电子散粒噪声就开始压过读出噪声。这个交叉点是选择曝光时间的下限依据。如果你想去对标厂商手册行业内普遍采用一套被简称为“1288”的评测规范它的核心思想和这里完全一致只是把测试条件、统计置信区间、坏点剔除规则标准化了。我第一次按 1288 的思路重跑了自己的 PTC 流程后才发现之前用的单帧方差把 PRNU 也算进去了导致 K 系统性偏小约 15%。从那以后配对差分就成了我的默认动作。4. 张正友标定法从一块棋盘格里抠出相机几何4.1 问题设定与单应矩阵几何标定解决的是另一个问题图像上的二维坐标和三维空间点之间是什么关系。针孔模型写出来是 s·x̃ K·[R|t]·Xx̃ 是图像齐次坐标X 是三维点K 是内参矩阵包含焦距 fx、fy 和主点 cx、cyR 和 t 是相机相对标定板的外参。张正友标定法的高明之处在于用一个平面标定板替代了传统方法里昂贵的三维标定块。当标定板平面取 Z0 时模型简化为 s·x̃ K·[r1 r2 t]·[X Y 1]ᵀ这个 3×3 矩阵 H 就是单应矩阵。每一张棋盘格照片通过角点检测和直接线性变换DLT都能求出一个 H。而 H 内部包含了两部分信息一部分是内参 K另一部分是这一姿态下的外参。接下来的关键问题是如何从一堆 H 里把 K 单独剥出来。4.2 从约束到内参闭式解单应矩阵本身给不出 K但旋转矩阵的列向量是标准正交的这个几何性质提供了额外约束。记 B K⁻ᵀK⁻¹B 是对称矩阵只有 6 个独立未知数。每一张棋盘格姿态的旋转正交性能给出两个线性方程h1ᵀ·B·h2 0 h1ᵀ·B·h1 h2ᵀ·B·h2理论上 3 张姿态就能解出全部 6 个未知数实际一般要求 3 张以上解出 B 后通过 Cholesky 分解反推出内参 K再逐姿态求出外参 R、t。这个闭式解的价值在于它不需要任何非线性优化的初值猜测直接用线性代数就能拿到一个足够好的起点。拿到初值后真正的精度来自最后的非线性优化。把所有角点残差堆起来用列文伯格-马夸尔特法同时精化 K、畸变系数和每张图的外参目标函数是Σᵢⱼ ‖ x_ij − x̂(K, d, R_i, t_i, X_j) ‖²其中 x̂ 是把棋盘格角点 X_j 投影到图像上、再经过畸变模型扭曲后的预测坐标。径向畸变和切向畸变的标准形式如下k1、k2 是径向p1、p2 是切向x_dist x · (1 k1·r² k2·r⁴) 2·p1·x·y p2·(r² 2x²) y_dist y · (1 k1·r² k2·r⁴) p1·(r² 2y²) 2·p2·x·y其中 x、y 是归一化无畸变坐标r² x² y²。这里有个细节畸变模型作用在归一化坐标上而不是像素坐标上。如果你自己实现务必先把像素坐标减主点、除焦距再套畸变公式否则优化会不收敛或者收敛到错误的畸变参数。4.3 采集纪律和验收标准张正友标定法对数学的要求不高对数据采集的要求相当高。方法本身再漂亮喂进去烂数据也白搭。我给自己定了几条硬纪律数量10~20 张有效姿态。少于 8 张内参的置信区间会大得没法用多于 20 张收益递减。姿态多样性棋盘格必须在俯仰绕 x 轴和偏航绕 y 轴方向都有明显倾斜同时覆盖画面中心和四角。如果你只是把棋盘格放在不同距离、同一朝向平移fx 和 fy 会互相牵连标出来焦距天天变。对焦与光圈手动对焦锁死在实测工作距离光圈收到中档比如 f/5.6~f/8保证倾斜时棋盘格整个幅面都足够锐利。大光圈浅景深会让倾斜姿态的半张棋盘格离焦角点位置会系统性偏移。光照均匀明亮的照明最低 ISO 拍摄。这里就和前面讲的噪声模型接上了——角点定位误差和噪声标准差成正比暗光下角点检测的 jitter 会让每张图的单应矩阵都带随机扰动。表面平整度棋盘格必须贴在绝对平整的刚性板上纸板微弯一点点标出来的畸变参数就会带进弯曲量而且这种误差是系统性的多拍几张也平均不掉。验收时不要只盯着总体 RMS 重投影误差。我会额外做两件事第一统计每张图各自的平均残差如果某张图明显偏大多半是这张姿态拍虚了或曝光不足直接剔除重拍第二把残差画到图像坐标系热图上如果四角残差系统性偏大说明畸变模型和实际镜头不匹配或者标定板没有覆盖到画面四角。合格的项目级标定总体 RMS 应该压在 0.2~0.5 像素以内差于 1 像素的标定在后续三维重建里基本没法看。5. 噪声与几何标定合流一份我这两年攒下的实战清单5.1 噪声如何悄悄污染角点检测很多人标完内参就再也不看噪声参数了这是不对的。角点检测的本质是在梯度方向找灰度跳变的位置定位精度与信噪比直接挂钩。一个简化的误差模型可以写成定位误差 ∝ 图像噪声标准差 / 局部梯度强度。也就是说棋盘格角点越锐利、图像噪声越小定位越准。我在同一台相机上对比过ISO 100 时读出噪声约 2.5 e-角点 jitter 大概 0.05 像素开到 ISO 3200读出噪声涨到 8 e-同时散粒噪声也按 √N 增长角点 jitter 直接翻到 0.15~0.2 像素。这 0.1 像素的差异在单目测距和双目三角化中会被放大成厘米级的深度误差。所以我的原则是几何标定永远在最高 SNR 的条件下做——最低 ISO、最强均匀光、保证标定板表面不反光。如果你必须在低照度下标定至少先用 PTC 测出该条件下的噪声方差把角点检测的阈值和子像素窗口按噪声水平调大。5.2 把噪声方差变成权重既然知道每个像素的噪声方差几何标定里的重投影误差就不应该给所有角点同等的权重。噪声模型告诉我们灰度值为 y_DN 的像素其时间噪声方差近似是σ²(y) max(y / K, 0) σ_read²DN²于是加权最小二乘的目标函数变成Σᵢⱼ w_ij · ‖ x_ij − x̂(…) ‖²其中 w_ij 1 / σ²(y_ij)这等于在说亮部角点定位更可信给它更大的权重暗部角点噪声大别让它把优化结果往错误方向拖。实现起来并不难主流标定工具大多支持传入权重矩阵不支持的话手写一个列文伯格-马夸尔特也就半天功夫。类似的权重思路还可以用到双目三角化把二维观测协方差通过雅可比矩阵前向传播到三维点协方差得到一个带不确定度的三维坐标而不是一个“假装精确”的点。对于做计量级三维重建的朋友这一步是必需品不是锦上添花。5.3 踩坑记录我这两年被各种标定问题折磨出来的经验大部分都适合记录成一张“现象-根因-对策”表现象根因对策夏天和冬天标出的 K 值对不上暗电流随温度漂移方差估计被污染标定前热机 30 分钟记录传感器温度长曝光必须实时扣暗帧PTC 标出的增益比手册偏小单帧方差混入了 PRNU用配对差分计算时间噪声方差张正友标定的 fx/fy 反复横跳棋盘格只有平移缺少俯仰/偏航旋转增加姿态多样性覆盖俯仰±30°画面边缘畸变怎么标都不对棋盘格有效覆盖只在中央区域让标定板铺满四角必要时用多尺寸板JPEG 标定数据全部偏一个方向自动白平衡改变了颜色通道增益角点位置随通道权重漂移永远用 raw 输出关掉一切 ISP标定和实际采集用不同 ISO重建误差大内参不随增益变化但角点噪声和权重变了标定环境和实测环境保持同一增益档这张表里的每一条都是我在真实项目里交过学费换来的。尤其是温度那条我第一次发现 K 值漂移时还以为是代码 bug排查了两天才意识到是下午实验室空调关了传感器升温导致暗电流变化。从那以后PTC 采集数据时我都会顺手在笔记本上记一栏传感器温度哪怕只是“开机半小时/刚开机”这种粗略标记。5.4 现在的固定流程把噪声模型和几何标定串起来之后我现在每到一个新项目都会按下面的流程走一遍半小时到一小时就能完成热机 30 分钟让传感器温度稳定选好镜头、光圈、焦距并锁定。分别对常用 ISO 档位跑一遍 PTC记录 K、读出噪声、满阱容量写进项目配置表。在最低 ISO、均匀强光下采集 15 组棋盘格覆盖中心与四角、俯仰与偏航跑张正友标定并验收。把噪声模型写成权重对重投影误差做加权优化如果需要再做协方差传播。每次外场测试前花 5 分钟拍一组平场加一组暗帧快速拟合一个 K 值和配置表对比。偏差超过 10% 就重标。最后再分享一个小习惯我会把每次 PTC 的 log-log 曲线截图存成一个“传感器体检档案”按日期命名。时间长了能看出传感器老化、污染、甚至镜头镀膜劣化的趋势。这本来是无心插柳后来发现它比任何手册都有说服力——当曲线斜率在半年内漂移超过 5% 时就该重新评估这台设备还能不能用于定量测量了。噪声模型和几何标定从来不是一次性的仪式而是一套需要持续维护的度量体系。希望这篇第 6 讲能帮你在项目里少走点弯路也让你下次看到满屏噪点的时候知道它们到底是从哪来的。
觉得有用,分享给同行:

为您的企业打造数字门面

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

立即咨询 →