2. Bayer RAW 格式与预处理:Bayer Pattern详解、黑电平校正(BLC)、镜头阴影校正(LSC)

好,我们正式开始 ISP 流水线的第一站——RAW 域预处理。很多刚入行的朋友觉得这步简单,不就是读个数据嘛。其实不然,预处理做不好,后面 3A 算法再牛也救不回来。我见过不少项目,调试几个月发现暗角去不掉,最后追根溯源,是 LSC 的增益表没对齐 Bayer 格式。

这一节,我们把三个最基础也最关键的概念讲透:Bayer Pattern、黑电平校正、镜头阴影校正。

2.1 Bayer Pattern——传感器是怎么“看”颜色的

先问一个问题:一个像素点,能不能同时感知红绿蓝?
答案是:不能。单个像素只能感知光的强度,分不清颜色。

那彩色照片怎么来的?靠滤光片阵列。最经典的就是 Bayer Pattern,由 Kodak 的 Bryce Bayer 在 1976 年发明。说白了,就是在每个像素上盖一层微小的滤色片,只让特定波长的光通过。

2.1.1 标准 RGGB 排列

最常见的 Bayer 格式是 RGGB,也就是 2×2 的单元里,1个R、2个G、1个B。为什么绿色多一倍?因为人眼对绿色最敏感,多采样绿色能提升视觉分辨率。

位置 像素 (0,0) 像素 (0,1) 像素 (1,0) 像素 (1,1)
颜色 R G G B

当然,也有其他排列方式,比如 RCCC(用于安防)、RCCB(用于低照度)、RGBW(用于高动态)。但 ISP 领域,90% 以上的场景还是 RGGB。

我的习惯:拿到一颗新 sensor,第一件事就是确认 Bayer 排列顺序。有些 sensor 输出是 BGGR,有些是 GRBG。如果 Bayer 顺序搞反了,demosaic 出来的颜色会完全错乱。我曾经在调试一款安防摄像头时,因为 datasheet 写错了 Bayer 顺序,浪费了整整两天。

2.1.2 Bayer RAW 的数据格式

RAW 数据就是 sensor 直接输出的原始值,没有经过任何处理。每个像素通常用 10bit、12bit 或 14bit 表示。比如 12bit RAW,每个像素的取值范围是 0~4095。

存储时,一般按行排列。以 1920×1080 的 RGGB 为例:

  • 第 0 行:R G R G R G ...
  • 第 1 行:G B G B G B ...
  • 第 2 行:R G R G R G ...
  • 以此类推

注意,这里每个像素只存一个值,不是 RGB 三个值。所以 RAW 图看起来是灰蒙蒙的,别担心,那是正常的。

2.2 黑电平校正(BLC)——把“假黑”拉回真黑

你想想看,sensor 在完全无光的情况下,输出值是不是 0?
不是。因为 sensor 本身有暗电流,即使没有光子入射,也会产生少量电荷。这个非零的底噪,就叫黑电平(Black Level)。

黑电平校正的目的很简单:把无光时的输出值减去一个固定的偏移量,让真正的黑色对应 0。

2.2.1 黑电平的测量方法

sensor 通常会在像素阵列的边缘预留一些被遮挡的像素(Optical Black 像素),这些像素永远接收不到光。ISP 会统计这些像素的平均值,作为当前帧的黑电平值。

举个例子:

  • OB 像素平均值 = 64(12bit RAW)
  • 有效像素原始值 = 100
  • 校正后值 = 100 - 64 = 36
注意:黑电平校正后,可能会出现负值。比如原始值 50,减去 64 得到 -14。这时候需要做 clamp 处理,把负值截断到 0。但 clamp 会丢失信息,所以有些 ISP 会保留一个小的偏移量,比如减去 64 后再加上 16,保证数据不出现负值。

2.2.2 BLC 的代码实现(伪代码)

// 假设 raw_data 是 12bit 的 Bayer RAW 数组
// ob_level 是当前帧的黑电平值
for (int i = 0; i < width * height; i++) {
    int corrected = raw_data[i] - ob_level;
    if (corrected < 0) corrected = 0;
    if (corrected > 4095) corrected = 4095;  // 12bit 上限
    raw_data[i] = corrected;
}

实际工程中,BLC 通常放在 ISP 流水线的最前面,因为后续的 LSC、AWB 等模块都假设黑电平已经被正确移除。

2.3 镜头阴影校正(LSC)——拯救暗角

你有没有拍过一张纯白墙壁的照片,结果发现四角发暗、中间亮?这就是镜头阴影,也叫暗角(Vignetting)。

原因有两个:

  1. 光学原因:镜头边缘的光线入射角大,到达 sensor 的光强比中心弱。
  2. 像素原因:边缘像素的微透镜接收效率比中心低。

LSC 的做法就是给边缘像素乘以一个大于 1 的增益,把亮度补回来。

2.3.1 增益表的生成

LSC 的核心是一张增益表(Gain Table)。这张表记录了每个像素位置应该乘多少增益。增益表通常是在模组标定阶段生成的。

标定方法:

  • 拍摄一张均匀的灰卡或白板
  • 统计每个像素位置的亮度值
  • 以中心亮度为基准,计算每个位置的增益 = 中心亮度 / 当前位置亮度

举个例子:

位置 中心 左上角 右下角
原始亮度 2000 1600 1500
增益 1.0 1.25 1.33
避坑指南:我曾经在一个项目中,LSC 增益表用的是 8bit 定点数,精度不够,导致校正后出现明显的带状条纹。后来换成 12bit 定点数,问题就解决了。增益表的精度直接影响校正效果,建议至少用 10bit 以上。

2.3.2 LSC 的实现方式

实际工程中,不会为每个像素都存一个增益值,那样太占内存。常用的做法是:

  • 网格增益:把图像分成 16×16 或 32×32 的网格,每个网格存一个增益值。像素点的增益通过双线性插值得到。
  • 多项式拟合:用多项式拟合出增益曲面,运行时直接计算。

网格增益是主流方案。比如 1920×1080 的图像,分成 16×16 的网格,只需要 120×68 个增益值,内存占用很小。

2.3.3 LSC 的注意事项

LSC 校正时,不同颜色通道的阴影程度可能不同。所以 R、G、B 通道需要分别使用不同的增益表。有些 sensor 的阴影在红色通道特别严重,如果统一用一张表,会出现颜色偏移。

关键点:LSC 必须在 BLC 之后执行。如果黑电平没校正,暗区的增益会被放大,导致黑电平也被放大,画面会发灰。

2.4 本章知识体系

下面这张图展示了 Bayer RAW 预处理的核心流程和三个模块的关系:

Bayer RAW 预处理流程 Bayer RAW 预处理核心流程 Sensor RAW 输出 黑电平校正 (BLC) 镜头阴影校正 (LSC) 校正后 RAW 数据 Bayer Pattern RGGB / BGGR / GRBG 每个像素只存一个颜色 12bit RAW: 0~4095 BLC 关键点 减去 OB 像素平均值 防止负值:clamp 或偏移 必须在 LSC 之前执行 LSC 关键点 网格增益 + 双线性插值 R/G/B 通道分别标定 增益精度 ≥ 10bit

从图中可以看到,Bayer RAW 数据先经过 BLC 去除暗电流偏移,再经过 LSC 补偿镜头阴影,最后输出干净的 RAW 数据给后续的 demosaic、AWB 等模块。

一句话总结:BLC 解决的是“黑不黑”的问题,LSC 解决的是“亮不匀”的问题。两者都是 RAW 域预处理的基础,缺一不可。