4、RAW图预处理与Bayer格式优化

RAW图预处理,说白了就是图像质量的第一道防线。我做了这么多年嵌入式相机,发现很多后期调不好的问题,根子都在RAW阶段没处理好。今天咱们就聊聊Bayer格式、坏点校正和黑电平校正这几个硬骨头。

4.1 Bayer Pattern格式:RGGB与GRBG

先说说Bayer格式。CMOS传感器每个像素只能感知亮度,要得到彩色图像就得靠滤色片阵列。最常见的Bayer Pattern有RGGB和GRBG两种。

你想想看,一个2x2的像素块里,RGGB是左上角红色、右上和左下绿色、右下蓝色。GRBG呢?左上绿色、右上红色、左下蓝色、右下绿色。就这么个排列差异,搞不好整个图像就偏色了。

关键点: Bayer格式决定了后续插值算法的参考方向。选错Pattern,图像会整体偏移一个像素。

我在项目中遇到过一件事:某款传感器手册写的是RGGB,结果实际输出是GRBG。团队按RGGB做了三个月算法,一上硬件全偏色。后来我让测试组直接拍一张纯白画面,用脚本统计四个通道的均值,才揪出这个问题。

Bayer类型 2x2排列 常见传感器 插值起点
RGGB R G / G B Sony IMX系列 左上R
GRBG G R / B G OmniVision多数型号 左上G
BGGR B G / G R 部分安森美传感器 左上B
GBRG G B / R G 少数工业相机 左上G

我个人习惯在驱动初始化阶段,用寄存器读取Bayer ID,而不是硬编码。这样换传感器时不用改算法层代码。

4.2 坏点校正(DPC)算法优化

坏点,就是传感器上那些永远亮或永远暗的像素。每个传感器都有,只是多少的问题。DPC算法要做的,就是把这些坏点找出来,然后用周围像素的值把它填上。

DPC分两步:检测和校正。检测阶段,我常用的是中值比较法。拿当前像素和周围8个邻域比,如果差值超过阈值,就标记为坏点。

// 简化的坏点检测逻辑
uint8_t is_dead_pixel(uint16_t center, uint16_t neighbors[8]) {
    uint16_t median = get_median(neighbors, 8);
    uint16_t diff = abs(center - median);
    uint16_t threshold = 40;  // 经验值,需根据传感器调整
    
    return (diff > threshold) ? 1 : 0;
}

校正方法呢?我推荐用加权平均。别用简单的均值,那样会模糊边缘。加权平均给距离近的像素更高权重,效果好很多。

经验之谈: 阈值不要设死。我习惯根据ISO值动态调整阈值——ISO越高,噪声越大,阈值也要跟着放大。否则高ISO下全是假坏点。

算法优化上,有几个坑要注意:

  • 不要逐帧全图检测:坏点位置是固定的,检测一次存下来就行。我见过有人每帧都跑检测,CPU占用直接飙到30%。
  • 坏点表要持久化:第一次上电检测后,把坏点坐标存到Flash里。下次开机直接加载,省掉检测时间。
  • 边界处理要小心:图像边缘的像素没有完整邻域,我一般直接跳过不检测,或者用镜像填充。

注意: 坏点校正不能替代传感器标定。如果坏点数量超过传感器总像素的0.1%,建议直接联系供应商换货。算法只能补救,不能根治。

4.3 黑电平校正(BLC)的硬件加速思路

黑电平校正,说白了就是消除暗电流带来的偏置。传感器在全黑环境下应该输出0,但实际上会有个固定偏置值。BLC就是把这个偏置减掉。

软件做BLC很简单:每个像素减去一个固定值。但问题是,这个减法在嵌入式平台上很费时间。一张200万像素的RAW图,逐像素做减法,ARM Cortex-A7上大概要花8-10ms。

怎么加速?我给出三个思路:

  1. 查表法:把减法结果预计算好,存成LUT。输入像素值直接查表输出。代价是额外占用256字节或1024字节的RAM。
  2. SIMD优化:ARM平台的NEON指令集,一次可以处理16个像素。我实测过,NEON版本的BLC比C循环快4-5倍。
  3. ISP硬件模块:如果SoC集成了ISP,通常都有硬件BLC模块。配置几个寄存器就行,零CPU开销。
// NEON加速BLC示例(伪代码)
void blc_neon(uint16_t *raw, int size, uint16_t offset) {
    uint16x8_t vec_offset = vdupq_n_u16(offset);
    for (int i = 0; i < size; i += 8) {
        uint16x8_t vec_pixel = vld1q_u16(&raw[i]);
        uint16x8_t vec_result = vsubq_u16(vec_pixel, vec_offset);
        vst1q_u16(&raw[i], vec_result);
    }
}

我个人最推荐第三种方案。硬件加速不是非得用专用ISP,很多MCU的DMA模块也能做简单的算术操作。我曾经在一个STM32H7项目上,用DMA2D的填充加偏移功能,实现了零CPU开销的BLC。

核心思路: BLC的本质是逐像素减法。任何能并行处理多个像素的硬件单元——DMA、GPU、NEON、FPGA——都可以用来加速。别只盯着CPU。

4.4 本章知识体系

下面这张图把Bayer格式、DPC和BLC的关系串起来了。你看,RAW图进来后,先做BLC消除偏置,再做DPC修复坏点,然后才能做后续的Demosaic和颜色校正。顺序不能乱。

RAW图预处理流程 RAW图输入 黑电平校正 (BLC) 消除暗电流偏置 坏点校正 (DPC) 检测+加权平均修复 预处理后RAW图 注意事项 • Bayer格式需与传感器匹配 • DPC阈值随ISO动态调整 • BLC优先用硬件加速 • 坏点表需持久化存储 • 预处理顺序不可颠倒 Bayer Pattern RGGB: R G / G B GRBG: G R / B G BGGR: B G / G R GBRG: G B / R G 选错Pattern = 全图偏色

嗯,到这里RAW图预处理的核心内容就讲完了。Bayer格式选对、DPC算法优化好、BLC用硬件加速,这三件事做好了,后面的图像处理流程会顺畅很多。我曾经在一个项目上只优化了BLC和DPC,整条ISP流水线的帧率就从15fps提到了30fps——有时候性能瓶颈就在这些不起眼的预处理环节。