ISP核心模块:黑电平校正、镜头阴影校正、坏点校正的原理与调优
图像传感器拍出来的原始数据,说白了就是一堆「带病」的RAW图。直接拿去做后续处理,效果肯定惨不忍睹。今天咱们聊聊ISP流水线最前面的三个模块——BLC、LSC、DPC。这三个家伙负责把RAW图从「能用」变成「好用」。
我个人习惯把这三个模块称为「预处理三件套」。它们解决的问题各不相同,但目标一致:让传感器数据尽可能真实地反映外界光线。
2.1 黑电平校正(BLC)
先说说黑电平。你想想看,传感器在完全无光的环境下,理论上应该输出0。但实际呢?它会输出一个非零的数值。这个数值就是黑电平。
为什么会这样?传感器电路本身有暗电流,ADC转换也有偏移。这些因素叠加起来,就形成了黑电平。如果不校正,暗部区域会发灰,对比度也会受影响。
2.1.1 原理与实现
BLC的原理很简单:减去一个固定的偏移量。但具体怎么减,有讲究。
// 最简单的全局BLC
pixel_out = pixel_in - blc_offset;
// 带饱和保护的BLC
pixel_out = (pixel_in > blc_offset) ? (pixel_in - blc_offset) : 0;
我在项目中遇到过一个问题:有些传感器不同通道的黑电平不一样。R、Gr、Gb、B四个通道各有各的偏移。这时候就得用通道独立的BLC。
| BLC模式 | 适用场景 | 精度 | 硬件开销 |
|---|---|---|---|
| 全局BLC | 低端传感器,暗电流均匀 | 低 | 1个减法器 |
| 通道独立BLC | 主流CMOS传感器 | 中 | 4个减法器 |
| 动态BLC | 温度变化大的场景 | 高 | 查表+减法器 |
2.1.2 调优要点
调BLC的时候,我建议你关注两个点:
- 暗场采集:盖上镜头盖,拍一张全黑图。取平均值作为blc_offset。注意要拍多帧取平均,消除随机噪声。
- 温度补偿:黑电平会随温度漂移。我曾经在户外测试时发现,中午和晚上的黑电平能差3-5个DN。如果产品工作温度范围大,建议做温度查表补偿。
2.2 镜头阴影校正(LSC)
镜头阴影,也叫亮度衰减。说白了就是画面四周比中心暗。这是光学系统的物理特性,没法完全避免,但可以通过数字手段补偿。
你想想看,镜头中心的光线是垂直入射的,边缘的光线是斜入射的。斜入射的光路更长,衰减更严重。再加上镜头本身的渐晕效应,就形成了阴影。
2.2.1 原理与实现
LSC的核心思路是:给边缘像素乘以一个大于1的增益。增益的大小取决于像素到中心的距离。
// 基于网格的LSC
// 预先存储一个增益网格,例如 17x13 个网格点
// 运行时通过双线性插值计算每个像素的增益
gain = bilinear_interpolation(grid, x, y);
pixel_out = pixel_in * gain;
我记得第一次做LSC时,用了最简单的多项式模型。结果发现边缘偏色严重。后来才意识到,不同颜色通道的衰减曲线不一样。R通道衰减最快,B通道次之,G通道最慢。所以LSC必须分通道处理。
2.2.2 调优流程
调LSC,我一般按这个步骤来:
- 拍灰卡:用均匀光源照射灰卡,拍一张RAW图。
- 分析衰减曲线:提取中心到边缘的亮度变化曲线。
- 生成增益网格:根据衰减曲线反推增益值。
- 验证校正效果:校正后,画面亮度均匀性应该达到95%以上。
- 检查偏色:如果边缘偏色,调整各通道的增益比例。
我曾经在一个项目中,LSC调了整整两周。问题出在光源不均匀上。灰卡拍出来本身就不均匀,导致增益网格全是错的。后来换了积分球光源,一次就调好了。
2.3 坏点校正(DPC)
坏点,就是传感器上那些「不听话」的像素。有的永远亮着(亮点),有的永远暗着(暗点),有的忽亮忽暗(闪烁点)。
坏点分两种:静态坏点和动态坏点。静态坏点是出厂就有的,位置固定。动态坏点是使用过程中产生的,受温度、电压影响。
2.3.1 原理与实现
DPC的基本思路是:检测坏点,然后用周围像素的值替换它。
// 简单的坏点检测与校正
// 检测:当前像素与周围8邻域的差值超过阈值
if (abs(pixel_center - pixel_neighbor) > threshold) {
// 校正:用中值或均值替换
pixel_out = median_filter(neighbors);
} else {
pixel_out = pixel_in;
}
这里有个坑:阈值设得太小,会把正常细节误判为坏点;设得太大,又漏检。我建议用自适应阈值,根据局部亮度动态调整。
| 坏点类型 | 检测方法 | 校正方法 | 适用场景 |
|---|---|---|---|
| 静态亮点 | 暗场检测 | 查表替换 | 出厂标定 |
| 静态暗点 | 亮场检测 | 查表替换 | 出厂标定 |
| 动态坏点 | 邻域比较 | 中值滤波 | 实时处理 |
2.3.2 调优要点
调DPC,我总结了几条经验:
- 静态坏点用查表法:出厂时做一次坏点标定,把坏点坐标存到OTP里。运行时直接查表替换,效率最高。
- 动态坏点要防误判:纹理丰富的区域容易误检。我习惯在检测前先算局部方差,方差大的区域放宽阈值。
- 校正后要平滑:替换后的像素可能与周围不连续,造成「斑点」。建议做一次3x3均值平滑。
2.4 三个模块的协同关系
BLC、LSC、DPC在ISP流水线中的顺序是固定的:BLC → DPC → LSC。为什么是这个顺序?
先做BLC,把黑电平归零,后续的增益计算才准确。再做DPC,因为坏点校正依赖邻域像素,如果先做LSC,增益会把坏点放大,影响检测。最后做LSC,因为增益计算需要准确的像素值。
下面这张图展示了三个模块在ISP流水线中的位置和关系:
2.5 实战调优建议
最后,给几个实战中的调优建议:
- 先调BLC,再调LSC,最后调DPC。BLC不准,LSC的增益计算就是错的。LSC没调好,DPC的坏点检测阈值就不好设。
- 用工具看RAW图。我习惯用ImageJ或者Python的rawpy库,直接看校正前后的RAW数据。只看JPEG是看不出问题的。
- 留余量。BLC的偏移量不要减到0,留1-2个DN的余量。LSC的增益不要超过3倍。DPC的阈值设得宽松一点,宁可漏检也不要误判。
好了,预处理三件套就聊到这儿。这三个模块虽然基础,但调好了能让后续的ISP处理事半功倍。记住一句话:地基不牢,地动山摇。