3. 光响应非均匀性校准:PRNU 产生原因、平场校正算法实现、增益矩阵计算与存储

好,咱们接着聊传感器校准。上一章讲了暗电流和 DSNU,这次轮到它的「好兄弟」—— PRNU 了。

PRNU,全称 Photo Response Non-Uniformity,光响应非均匀性。说白了,就是同一个场景,均匀光照下,每个像素的输出值不一样。你想想看,如果拍一张纯灰卡,结果图像上有的地方亮、有的地方暗,那肯定不行。

3.1 PRNU 是怎么来的?

PRNU 的产生原因,我归纳为三类:

  • 工艺偏差:每个像素的光电二极管尺寸、微透镜对准、滤光片厚度,都存在纳米级的差异。别小看这点差异,量产时它就是 PRNU 的主要来源。
  • 材料缺陷:硅衬底里的杂质、晶格缺陷,会导致少数像素的量子效率偏低。这类缺陷通常是「死像素」或「坏点」的前身。
  • 光学系统影响:镜头边缘的渐晕(vignetting)、微透镜阵列的耦合效率不均匀,也会叠加到 PRNU 上。严格来说这属于光学非均匀性,但校正时通常一起处理。
我的经验: 区分「传感器本身 PRNU」和「光学 PRNU」很重要。我曾经遇到一个项目,平场校正后边缘还是偏暗,折腾了两周才发现是镜头遮光罩设计有问题。先测裸片 PRNU,再装镜头测一遍,能省很多 debug 时间。

PRNU 的数学模型很简单:

Y(i,j) = G(i,j) * X(i,j) + D(i,j) + N(i,j)

其中 G(i,j) 就是像素 (i,j) 的增益因子。理想情况下 G=1,但实际会在 0.9~1.1 之间波动。我们的任务,就是把这个 G 矩阵算出来,然后乘回去。

3.2 平场校正算法:从采集到计算

平场校正(Flat Field Correction, FFC)的流程,我习惯分四步走:

  1. 采集平场图像:用均匀光源照射传感器,拍 N 帧(通常 16~64 帧),取平均去噪。
  2. 减去暗场:别忘了先做 DSNU 校正。平场图像减去暗场图像,得到纯光响应部分。
  3. 计算增益矩阵:用每个像素的值除以全图均值,得到增益因子。
  4. 存储与应用:把增益矩阵存到非易失存储器里,运行时逐像素相乘。

这里有个关键点:光源的均匀性。如果光源本身不均匀,你校正的就是「光源+传感器」的联合非均匀性,而不是传感器本身的 PRNU。

避坑指南: 我曾经用积分球做光源,结果积分球出口有个小灰尘,拍出来的平场图像上有个暗斑。我以为是传感器坏点,折腾了半天才发现是灰尘。所以,采集前一定要清洁光路,并且用多角度验证光源均匀性。

3.3 增益矩阵计算:两种主流方法

增益矩阵的计算,说白了就是求每个像素相对于全图均值的比例。但具体实现有讲究:

方法 公式 适用场景 注意事项
全局均值法 G(i,j) = μ / P(i,j) 光照均匀、传感器无大区域缺陷 对边缘渐晕敏感
局部均值法 G(i,j) = μ_local / P(i,j) 有渐晕、或传感器有区域响应差异 需要更大的存储空间

我个人习惯用全局均值法做第一版,简单、快速。如果发现校正后图像有区域性偏差,再切换到局部均值法。

代码实现(伪代码):

// 采集平场
uint16_t flat[HEIGHT][WIDTH];
uint32_t sum = 0;
for (int y = 0; y < HEIGHT; y++) {
    for (int x = 0; x < WIDTH; x++) {
        sum += flat[y][x];
    }
}
float mean = (float)sum / (HEIGHT * WIDTH);

// 计算增益矩阵
float gain[HEIGHT][WIDTH];
for (int y = 0; y < HEIGHT; y++) {
    for (int x = 0; x < WIDTH; x++) {
        gain[y][x] = mean / (float)flat[y][x];
    }
}
注意: 实际工程中,增益矩阵通常用定点数存储(如 Q8.8 格式),避免浮点运算的开销。而且,增益值一般限制在 [0.5, 2.0] 范围内,防止异常像素导致图像失真。

3.4 增益矩阵的存储策略

增益矩阵的存储,是个典型的「空间换时间」问题。全分辨率存储(比如 1920x1080 的 16 位增益值)需要约 4MB 空间。对于嵌入式系统来说,这有点奢侈。

我常用的三种存储方案:

  • 全分辨率存储:精度最高,但占用 Flash 大。适合高端工业相机。
  • 下采样存储:把增益矩阵缩小到 1/4 或 1/8,运行时双线性插值恢复。精度略降,但存储量减少 16~64 倍。
  • 多项式拟合:用二阶或三阶多项式拟合增益曲面,只存几个系数。最省空间,但对复杂非均匀性拟合效果差。

我做过一个项目,用的是 1/8 下采样 + 双线性插值。效果和全分辨率几乎没区别,但存储从 4MB 降到了 64KB。嗯,这个 trade-off 很划算。

3.5 PRNU 校准的完整流程

下面这张图,是我自己总结的 PRNU 校准流程,你可以参考一下:

PRNU 校准流程图 PRNU 校准完整流程 采集平场图像 减去暗场 (DSNU) 计算增益矩阵 存储到 Flash 运行时逐像素校正 注意:平场采集时,光源强度应控制在传感器线性响应区的 50%~70% 避免饱和或信噪比不足

3.6 工程中的几个坑

最后,分享几个我踩过的坑:

  • 温度漂移:PRNU 会随温度变化。如果产品工作温度范围宽,建议做多温度点校准,运行时查表插值。
  • 增益饱和:校正后的像素值可能超过原始位深。比如 10-bit 传感器,校正后可能得到 11-bit 的值。需要做 clamp 或重新量化。
  • 坏点处理:PRNU 校正不能替代坏点校正。增益矩阵里如果某个像素增益异常大(比如 >2.0),那它大概率是个坏点,应该标记出来单独处理。
小技巧: 我习惯在增益矩阵里同时存储一个「置信度掩码」。如果某个像素的增益偏离均值超过 3 个标准差,就把置信度标记为 0,运行时用邻域插值代替。这样既保留了 PRNU 校正的精度,又避免了坏点放大。

好了,PRNU 校准这部分就聊到这儿。记住,平场校正不是一劳永逸的——传感器老化、镜头更换、温度变化,都可能需要重新校准。设计系统时,最好留一个「在线校准」的接口。