4. RAW图预处理:Bayer格式解析、黑电平校正、LSC镜头阴影校正、坏点校正、白平衡预处理

各位同学,欢迎来到多帧HDR融合实战的第一道关卡——RAW图预处理。

说实话,很多做算法的人容易忽略这一步。他们觉得HDR融合的核心是“对齐”和“融合”,预处理嘛,随便调调库就行。但我得告诉你,这种想法在工程落地时往往会吃大亏。我在项目里见过太多次了——预处理没做好,后面融合出来的图像全是偏色、暗角、或者一堆闪烁的坏点。嗯,今天我们就来把这些坑一个一个填上。

4.1 Bayer格式解析:从Sensor拿到的第一手数据

你从摄像头Sensor直接读出来的数据,不是RGB,而是Bayer格式。说白了,每个像素只记录一种颜色——红、绿、蓝中的一个。最常见的排列是RGGB,也就是一个2x2的单元里,左上和右下是绿,右上红,左下蓝。

为什么Sensor要这么干? 因为人眼对绿色最敏感,所以绿色采样点多一倍。这是仿生学的设计,不是拍脑袋定的。

我刚开始做ISP时,犯过一个低级错误:直接把Bayer数据当成RGB去显示,结果图像全是马赛克。后来才明白,必须先做去马赛克(Demosaic),把缺失的颜色插值出来。

不过在我们HDR融合的场景里,预处理阶段通常不做Demosaic。为什么?因为融合需要在RAW域做,保持原始信息。你想想看,一旦插值成RGB,很多细节就丢了,后面再融合效果会打折扣。

Bayer排列 常见Sensor 特点
RGGB Sony IMX系列 最主流,绿色双倍采样
BGGR 部分OmniVision 与RGGB只是顺序不同
RCCC 安防/车载 红色为主,低光性能好
我的习惯: 拿到新Sensor时,第一件事就是确认Bayer排列。用一张均匀光照的白色场景拍一张RAW,然后看每个通道的均值——哪个通道值高,哪个就是绿色通道。这招百试百灵。

4.2 黑电平校正:把“零”找回来

Sensor在完全无光的情况下,输出的像素值不是0。因为电路本身有暗电流,会有一个基础偏移量,这就是黑电平(Black Level)。

黑电平校正其实很简单:

// 伪代码示例
uint16_t black_level = 64;  // 10-bit RAW常见值
for (int i = 0; i < width * height; i++) {
    raw[i] = (raw[i] > black_level) ? (raw[i] - black_level) : 0;
}

但这里有个坑——不同通道的黑电平可能不一样。我记得有一次调试一个国产Sensor,红通道和蓝通道的黑电平差了8个DN(Digital Number)。如果统一减同一个值,暗部就会偏色。

避坑指南: 我曾经因为没做逐通道黑电平校正,导致HDR融合后的暗部区域出现明显的绿色色偏。排查了整整两天才发现是黑电平的问题。所以,一定要查Sensor datasheet,确认每个通道的黑电平值。

4.3 LSC镜头阴影校正:消除暗角

镜头阴影(Lens Shading),说白了就是画面四周比中心暗。这是物理规律——镜头边缘进光量天生就少。再加上Sensor的微透镜角度匹配问题,四周的响应也会下降。

校正方法是用一个增益矩阵,对每个像素乘以一个系数。系数从中心到边缘逐渐增大。

// 简化的LSC校正
float gain = lens_shading_table[y][x];  // 查表得到增益
raw[y * width + x] = clamp(raw[y * width + x] * gain, 0, max_val);

实际工程中,LSC表是怎么来的?有两种方式:

  • 标定法: 拍一张均匀光照的灰卡,然后反推每个像素需要的增益。这是最准的。
  • 经验公式法: 用多项式拟合,比如中心增益为1.0,边缘增益为1.2~1.5。适合快速原型验证。
我的经验: 在HDR融合场景下,LSC校正尤其重要。因为多帧融合时,如果不同帧的阴影不一致(比如镜头位置有微小变化),融合后会出现“鬼影”状的亮度不均匀。我建议在预处理阶段就做LSC,而且最好用标定法。

4.4 坏点校正:别让一个像素毁了整张图

Sensor的坏点分两种:

  • 死点(Dead Pixel): 永远输出0或接近0,怎么都不亮。
  • 亮点(Hot Pixel): 永远输出最大值,或者比正常值高很多。

坏点校正的核心思路是“用邻居的值来替代”。最简单的做法是中值滤波:

// 坏点检测与校正
if (is_defect_pixel(raw[y][x], neighbors)) {
    raw[y][x] = median_filter(neighbors);
}

但这里有个关键问题——怎么判断一个像素是不是坏点?

我常用的方法是:

  1. 计算当前像素与周围8个像素的差值。
  2. 如果差值超过某个阈值(比如50个DN),就标记为候选坏点。
  3. 再检查这个像素在相邻帧中是否持续异常——如果是,才确定为坏点。
注意: 在HDR多帧场景下,坏点检测可以跨帧做。比如同一位置在3帧中都是异常值,那基本可以确定是坏点。但如果只有一帧异常,可能是噪声,不要误判。

4.5 白平衡预处理:给颜色定调

白平衡(White Balance)的目的是让白色物体在任何光源下都呈现白色。在RAW域,我们通常用增益调整来实现:

// 白平衡增益应用
float r_gain = 1.8;  // 红色通道增益
float b_gain = 1.4;  // 蓝色通道增益
// 绿色通道通常保持1.0

for (int i = 0; i < width * height; i++) {
    if (is_red_pixel(i)) {
        raw[i] = clamp(raw[i] * r_gain, 0, max_val);
    } else if (is_blue_pixel(i)) {
        raw[i] = clamp(raw[i] * b_gain, 0, max_val);
    }
}

增益怎么算?常见方法有:

  • 灰度世界法: 假设场景中所有颜色的平均是灰色,然后调整增益使R、G、B均值相等。
  • 白点法: 找到场景中最亮的区域(假设是白色),然后调整增益使该区域R=G=B。

但在HDR融合中,我建议不要做完整的白平衡。为什么?因为HDR融合需要多帧之间的颜色一致性。如果每帧都独立做白平衡,不同帧的增益可能不一样,融合后就会出现颜色闪烁。

我的做法: 在预处理阶段只做“白平衡预处理”——用一个固定的、预先标定好的增益值。这个增益值是在标准光源下(比如D65)标定的。真正的自适应白平衡,放到融合之后再做。

4.6 本章知识体系总览

下面这张图总结了RAW图预处理的完整流程和各个模块的关系:

RAW图预处理流程 RAW图预处理流程 Sensor RAW数据 ① Bayer格式解析 ② 黑电平校正 ③ LSC镜头阴影校正 ④ 坏点校正 ⑤ 白平衡预处理 预处理后RAW数据 关键原则 • 保持RAW域处理 • 逐通道独立校正 • 固定增益避免闪烁 常见陷阱 • 黑电平通道不一致 • LSC表未标定 • 坏点误判为噪声

这五个步骤是串行执行的,顺序不能乱。Bayer解析是基础,黑电平校正是前提,LSC和坏点校正是质量保障,白平衡预处理是最后的调色准备。每一步都做扎实了,后面的HDR融合才能有好的数据基础。

最后说一句: 预处理阶段的代码,我建议写成模块化的,每个步骤独立一个函数。这样调试的时候可以单独开关每个模块,快速定位问题。我在项目中就是这么干的,排查效率至少提升一倍。