3. ISP基础概念:Bayer格式与RAW图、黑电平校正、镜头阴影校正、坏点校正

各位同学,今天我们来啃ISP流水线最前端的几个硬骨头。说白了,传感器送出来的原始数据,不能直接用。为什么?因为硬件有物理缺陷,光路有衰减,像素点还有死活。你得先把它“洗干净”,后面的算法才能正常工作。

我在高通的几个项目里,吃过不少这部分的亏。有一次调试夜景模式,画面总是偏绿,查了三天,最后发现是黑电平校正的寄存器配错了。嗯,从那以后,我对这几个基础模块再也不敢掉以轻心。

3.1 Bayer格式与RAW图

先说说传感器怎么“看”世界的。CMOS传感器每个像素只能感知光的强度,分辨不了颜色。那彩色照片怎么来的?靠滤色片。

最常见的方案是Bayer阵列。它把红、绿、蓝滤色片按一定规律铺在像素上。你想想看,人眼对绿色最敏感,所以绿色像素占一半,红色和蓝色各占四分之一。

像素位置 滤色片 输出值
(0,0) R 红色通道强度
(0,1) Gr 绿色通道强度
(1,0) Gb 绿色通道强度
(1,1) B 蓝色通道强度

传感器输出的原始数据,就是RAW图。每个像素只有12bit或10bit的灰度值,代表该位置滤色片对应的颜色强度。注意,这时候还不是RGB图像,每个像素只有一种颜色。

关键概念:RAW图 = 传感器原始输出,未经任何处理。Bayer格式 = RAW图中滤色片的排列方式。高通平台通常支持RGGB、BGGR、GRBG、GBRG四种排列。

我个人习惯在拿到新传感器时,第一件事就是确认Bayer排列。配错了,后面所有颜色都乱套。我曾经在项目里遇到过,供应商给的驱动里Bayer顺序写反了,调试了整整两天才发现。

3.2 黑电平校正(BLC)

传感器在没有光照的时候,理论上输出应该是0。但实际上不是。因为电路有暗电流,像素本身也有热噪声,所以即使全黑,输出也有一个偏移量。这个偏移量,就是黑电平。

黑电平校正,说白了就是把那个偏移减掉。

// 伪代码示例:BLC校正
// raw_input: 传感器原始值
// black_level: 黑电平值(通常从OB行读取)
// white_level: 白电平值(饱和值)

uint16_t blc_correction(uint16_t raw_input, uint16_t black_level, uint16_t white_level) {
    int32_t corrected = (int32_t)raw_input - (int32_t)black_level;
    if (corrected < 0) corrected = 0;
    if (corrected > white_level) corrected = white_level;
    return (uint16_t)corrected;
}

高通平台的做法比较灵活。它支持两种模式:

  • 全局BLC:整帧用一个黑电平值,适合均匀性好的传感器。
  • 逐行BLC:每行独立读取OB像素计算黑电平,适合温度变化大的场景。

实战技巧:我在调试暗光场景时,发现全局BLC会导致画面底部偏紫。后来改用逐行BLC,问题就解决了。原因是传感器底部发热更严重,暗电流更大。

为什么要做BLC?因为不校正的话,暗部细节会被淹没,而且会影响后面的白平衡和颜色校正。你想想看,如果黑电平是64,你拍一个暗部区域实际亮度是10,那传感器输出就是74。不减去64,算法会以为那里有光。

3.3 镜头阴影校正(LSC)

镜头不是完美的。中心进光量多,边缘进光量少。这就是镜头阴影,也叫亮度衰减。另外,不同波长的光衰减程度不一样,还会导致边缘偏色。

LSC的目的,就是补偿这种不均匀性。

高通平台的LSC实现,用的是网格增益表。把图像分成M×N个网格,每个网格存一个增益值。校正时,对每个像素,根据它所在的网格位置,插值计算出对应的增益,然后乘上去。

网格位置 R增益 Gr增益 Gb增益 B增益
中心(2,2) 1.0 1.0 1.0 1.0
边缘(0,0) 1.8 1.6 1.6 1.9
边缘(4,0) 1.7 1.5 1.5 1.8

网格增益怎么来的?通常是在产线上拍一张均匀的灰卡,然后反推每个位置需要的补偿量。

注意:LSC增益不能太大。如果边缘增益超过2.0,说明镜头本身太差,强行补偿会放大噪声。我建议增益上限控制在1.8以内,超过的话,考虑换镜头或者做裁剪。

我曾经在一个项目里,为了省成本用了很便宜的镜头。结果LSC增益拉到2.5,边缘全是噪点。后来跟硬件团队吵了一架,换了镜头才解决。

3.4 坏点校正(DPC)

传感器有几百万甚至几千万个像素,不可能每个都是好的。总会有一些像素输出异常:要么一直亮(亮点),要么一直暗(暗点),要么随机跳动(闪点)。

坏点校正,就是把这些坏点找出来,用周围正常像素的值替换掉。

高通平台支持两种模式:

  • 静态DPC:在产线标定阶段,把坏点坐标写进OTP里。运行时直接查表替换。
  • 动态DPC:运行时实时检测坏点。如果一个像素跟周围邻居差异太大,就标记为坏点。
// 动态DPC检测逻辑(简化版)
// 以当前像素为中心,取3x3窗口
// 计算中值,如果当前像素与中值的差超过阈值,则判定为坏点

bool is_dead_pixel(uint16_t center, uint16_t neighbors[8], uint16_t threshold) {
    uint16_t median = compute_median(neighbors, 8);
    int32_t diff = abs((int32_t)center - (int32_t)median);
    return (diff > threshold);
}

经验之谈:动态DPC的阈值很关键。设得太低,会把正常细节误判为坏点;设得太高,又漏检。我一般先拍一张均匀的灰卡,统计像素分布,然后取3σ作为初始阈值。

坏点替换的方法也有讲究。最简单的用均值,但容易模糊细节。高通平台推荐用中值替换,对边缘保护更好。

3.5 知识体系总览

这四个模块在ISP流水线中的位置和关系,我画了一张图,方便你理解。

ISP前端预处理流程 CMOS传感器 Bayer RAW图 黑电平校正 (BLC) 镜头阴影校正 (LSC) 坏点校正 (DPC) 校正后RAW图 处理顺序说明 BLC → LSC → DPC 先减偏移,再补亮度 最后修坏点 高通平台特点 硬件加速管线 寄存器可编程 支持在线调试 Tuning工具完善

从图上可以看得很清楚:传感器输出Bayer RAW图后,先做BLC去掉暗电流偏移,再做LSC补偿镜头阴影,最后做DPC修复坏点。这个顺序不能乱。如果先做LSC再做BLC,会把黑电平的偏移也放大,导致暗部偏色。

总结一下:Bayer格式是传感器的“语言”,RAW图是原始数据。BLC、LSC、DPC是ISP流水线最前端的三个预处理模块。它们解决的是传感器和镜头的物理缺陷。只有把这些基础打好,后面的去马赛克、白平衡、颜色校正才能正常工作。