4、黑电平与线性化:校正传感器暗电流噪声,恢复线性响应

好,咱们进入第四讲。黑电平校正和线性化,这两个东西在 ISP 流水线里排得很靠前。说白了,它们解决的是同一个问题:让传感器输出的数字信号,真实反映进入像素的光子数量。

你想想看,如果传感器在完全黑暗的环境下,输出值不是 0,而是 64、128 甚至更高,那这张图还能看吗?暗部全是灰蒙蒙一片。这就是暗电流噪声在作怪。

核心目标:从原始 RAW 数据中减去传感器固有的暗电流偏置,并校正光电响应曲线的非线性,使后续的增益、白平衡、去马赛克等模块能基于线性信号工作。

4.1 暗电流噪声的来源与特性

暗电流,英文叫 Dark Current。它是半导体器件在无光照条件下,由于热激发产生的电子-空穴对,被像素的势阱收集后形成的信号。

我刚开始做 sensor 选型时,吃过一次亏。有一款 sensor 标称暗电流很小,但实际在 60°C 环境下测试,暗电流涨了将近 10 倍。嗯,这里要注意:暗电流对温度极其敏感。温度每升高 6-8°C,暗电流大约翻一倍。

特性参数 典型值范围 影响因素
暗电流密度 10-500 e⁻/pixel/s 工艺、温度、像素尺寸
温度系数 每 6-8°C 翻倍 硅材料本征载流子浓度
空间分布 固定模式噪声 (FPN) 像素间工艺差异
时间分布 散粒噪声 (Shot Noise) 服从泊松分布

暗电流噪声包含两部分:固定模式噪声 (FPN) 和 随机噪声。FPN 是每个像素固定的偏置差异,可以通过黑电平校正消除。随机噪声是时间上的波动,只能通过多帧平均来抑制。

4.2 黑电平校正的两种主流方法

黑电平校正,就是找到传感器在无光照时的输出值,然后从每个像素中减去这个值。怎么做?主要有两种方法。

4.2.1 光学黑电平 (OB, Optical Black)

大多数 sensor 会在像素阵列的边缘,用金属层遮挡几行或几列像素。这些像素永远接收不到光,专门用来测量暗电流。

我个人习惯优先使用 OB 方法。因为它能实时跟踪暗电流的变化,尤其是温度变化时。

// 伪代码:OB 黑电平校正
// 假设 sensor 有 16 行 OB 像素,位于阵列顶部
uint16_t ob_sum = 0;
int ob_count = 0;

for (int row = 0; row < 16; row++) {
    for (int col = 0; col < sensor_width; col++) {
        ob_sum += ob_buffer[row][col];
        ob_count++;
    }
}

uint16_t black_level = ob_sum / ob_count;

// 对有效像素进行校正
for (int row = 16; row < sensor_height; row++) {
    for (int col = 0; col < sensor_width; col++) {
        raw_data[row][col] = (raw_data[row][col] > black_level) ?
                             (raw_data[row][col] - black_level) : 0;
    }
}

经验之谈:OB 区域的行数不要取太少。我曾经遇到过一个项目,只用了 4 行 OB,结果因为行间噪声波动,黑电平估计值抖动很大,导致画面出现横纹。后来改成 16 行,问题解决。建议至少取 8-16 行做平均。

4.2.2 暗帧减法 (Dark Frame Subtraction)

另一种方法是在完全黑暗的环境下(比如盖上镜头盖),拍摄一张或多张暗帧,然后取平均作为校正模板。

这种方法精度高,但缺点也很明显:不能实时跟踪温度变化。如果 sensor 温度变了,暗帧就不准了。

方法 优点 缺点 适用场景
OB 实时校正 实时跟踪温度变化,无需额外拍摄 占用像素面积,OB 区域可能受光晕影响 手机、安防、车载等量产产品
暗帧减法 精度高,可消除 FPN 无法实时更新,对温度敏感 实验室测量、科学相机、天文摄影

4.3 线性化校正:为什么需要它?

理想情况下,传感器的光电转换应该是线性的:光子数翻倍,输出数字值也翻倍。但现实不是这样的。

为什么会这样?主要有两个原因:

  • 光电二极管响应非线性:在低照度或高照度区域,光电转换效率会下降。
  • 读出电路的非线性:ADC、放大器等模拟电路,在大信号输入时会出现饱和或压缩。

我记得有一次调试一款高动态 sensor,发现暗部细节很好,但亮部出现了奇怪的偏色。查了半天,发现是线性化参数没配好,导致 R、G、B 三个通道在亮部的响应曲线不一致。校正之后,颜色就正常了。

4.4 线性化校正的工程实现

线性化校正通常采用分段线性插值或多项式拟合的方法。sensor 厂商会提供一张 LUT(查找表),描述输入光强与输出数字值之间的映射关系。

校正流程如下:

  1. 采集数据:在均匀光源下,拍摄不同曝光时间或不同照度的灰阶卡。
  2. 提取响应曲线:对每个通道(R、Gr、Gb、B),统计灰阶卡各灰阶的 RAW 值。
  3. 拟合校正曲线:以理想线性响应为目标,计算每个输入值对应的校正系数。
  4. 生成 LUT:将校正系数写入 sensor 或 ISP 的寄存器。
// 伪代码:分段线性线性化校正
// lut_table 包含 256 个校正后的输出值(假设 10-bit RAW)
uint16_t linearized_value[1024]; // 输入 10-bit,输出 16-bit

for (int i = 0; i < 1024; i++) {
    // 查找输入值 i 对应的分段区间
    int segment = find_segment(i, lut_table);
    // 线性插值
    linearized_value[i] = interpolate(i, segment, lut_table);
}

// 对每个像素应用校正
for (int row = 0; row < height; row++) {
    for (int col = 0; col < width; col++) {
        raw_data[row][col] = linearized_value[raw_data[row][col]];
    }
}

避坑指南:我曾经遇到过一个 case,线性化 LUT 在低照度区域校正过度,导致暗部噪声被放大。后来发现是 LUT 在低端区域的斜率太大。解决方案是在 LUT 的低端加入一个阈值,低于该阈值的像素不做线性化校正,只做黑电平减除。

4.5 黑电平与线性化的协同工作流程

在 ISP 流水线中,这两个模块的执行顺序是固定的:先做黑电平校正,再做线性化校正。

为什么?因为线性化校正假设输入信号已经去除了暗电流偏置。如果先做线性化,暗电流偏置会被当作有效信号一起被校正,导致校正曲线偏移。

下面这张图展示了完整的流程:

黑电平校正与线性化协同工作流程 RAW 传感器数据 步骤 1:黑电平校正 减去 OB 均值或暗帧模板 步骤 2:线性化校正 通过 LUT 或分段线性插值恢复线性响应 线性化 RAW 数据 ⚠️ 注意事项 • 顺序不可颠倒 • 黑电平校正必须在 线性化之前执行 • 否则校正曲线会偏移

4.6 调优实战中的常见问题

在实际项目中,黑电平和线性化调优经常会遇到几个坑:

  • OB 区域被光晕污染:镜头边缘的杂散光打到 OB 像素上,导致黑电平估计值偏高。解决方案是增加 OB 区域的屏蔽层,或者在软件中剔除边缘的 OB 像素。
  • 线性化 LUT 在拼接处不连续:分段线性插值的分段点处,如果斜率变化太大,会出现亮度跳变。我建议在分段点处做平滑过渡,比如用三次样条插值。
  • 温度变化导致黑电平漂移:如果 sensor 工作温度范围很宽(比如车载 -40°C 到 105°C),建议做温度补偿。可以在不同温度下标定黑电平,然后根据温度传感器读数动态切换参数。

一个小技巧:在调试黑电平时,可以拍一张全黑图,然后看 RAW 数据的直方图。理想情况下,直方图应该是一个窄峰,峰值位置就是黑电平值。如果直方图出现多个峰,说明 OB 区域可能有问题,或者 sensor 存在列 FPN。

好了,这一讲的内容就是这些。黑电平和线性化是 ISP 调优的基础,看似简单,但做扎实了,后面的模块才能发挥出最佳效果。