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流水线中的位置和关系,我画了一张图,方便你理解。
从图上可以看得很清楚:传感器输出Bayer RAW图后,先做BLC去掉暗电流偏移,再做LSC补偿镜头阴影,最后做DPC修复坏点。这个顺序不能乱。如果先做LSC再做BLC,会把黑电平的偏移也放大,导致暗部偏色。
总结一下:Bayer格式是传感器的“语言”,RAW图是原始数据。BLC、LSC、DPC是ISP流水线最前端的三个预处理模块。它们解决的是传感器和镜头的物理缺陷。只有把这些基础打好,后面的去马赛克、白平衡、颜色校正才能正常工作。