3、RAW图预处理流程:黑电平校正、Lens Shading校正、坏点校正、去噪预处理
各位,咱们直接进入正题。RAW图从传感器出来,说白了就是一堆「带噪声的原始电压值」。你不能直接拿它做颜色处理,必须先过一遍预处理流水线。我习惯把这四个步骤叫做「RAW图的四道安检」——黑电平校正、Lens Shading校正、坏点校正、去噪预处理。顺序基本固定,但每家芯片厂商的微调策略不太一样。
核心观点:预处理做不好,后面所有调优都是白费功夫。你想想看,一个坏点没修掉,后面插值完就是一块彩色污渍,再好的AWB也救不回来。
3.1 黑电平校正(Black Level Correction)
先说黑电平。传感器在没有光照时,像素并不是输出0,而是有一个固定的偏置电压。这个偏置就是黑电平。我见过不少新手直接忽略这步,结果暗部偏色严重,怎么调都调不回来。
校正公式很简单:
// 黑电平校正
pixel_out = pixel_raw - black_level;
if (pixel_out < 0) pixel_out = 0;
但实际项目里没这么简单。不同传感器、不同增益下的黑电平不一样。我做过一个项目,传感器在低增益下黑电平是64,高增益下漂到了72。你如果只用一个固定值,高ISO下暗部就发紫。
我的经验:建议在产线标定时,至少采集3个不同增益下的黑电平值,做线性插值。有些高端传感器还会逐行变化,那就得做逐行校正。
| 增益档位 | 黑电平值(12bit) | 备注 |
|---|---|---|
| ISO 100 | 64 | 基准值 |
| ISO 400 | 68 | 线性插值 |
| ISO 1600 | 72 | 高增益漂移 |
避坑指南:我曾经在一个项目中,黑电平校正后忘了做clip,导致暗部出现负值,后面插值直接溢出成白色噪点。查了两天才找到原因——从此我养成了校正后立即clip的习惯。
3.2 Lens Shading校正(Lens Shading Correction)
镜头边缘进光量天生比中心少,这就是Lens Shading。说白了就是「中心亮、四周暗」。另外还有颜色shading——边缘偏色。这两个问题必须一起修。
校正原理:预先存一张增益网格图(Gain Map),每个像素位置乘一个对应的增益系数。
// Lens Shading校正
pixel_out = pixel_in * gain_table[row][col];
// 注意:R、Gr、Gb、B四个通道的增益表不同
我个人习惯用 9x13 或 17x25 的网格,然后双线性插值到全分辨率。网格太密浪费内存,太疏校正不平滑。嗯,这里要注意——网格的边界处理,我踩过坑。
关键参数对比:
| 网格密度 | 内存占用(12MP) | 校正精度 | 适用场景 |
|---|---|---|---|
| 9x13 | ~4KB | 中等 | 手机摄像头 |
| 17x25 | ~16KB | 良好 | 车载/安防 |
| 33x49 | ~64KB | 优秀 | 专业相机 |
我的建议:颜色shading校正最好在Bayer域做,等插值成RGB后再做,边缘的色差已经被放大,校正效果会打折扣。
3.3 坏点校正(Bad Pixel Correction)
传感器生产过程中难免有坏点。坏点分两种:静态坏点(永远亮或永远暗)和动态坏点(只在特定条件下出现)。
静态坏点校正:产线标定时生成一张坏点坐标表,运行时直接替换。
// 静态坏点替换
if (is_bad_pixel[row][col]) {
pixel_out = median_filter(raw, row, col, 3x3);
} else {
pixel_out = raw[row][col];
}
动态坏点检测:实时判断当前像素是否异常。我常用的方法是——比较当前像素与周围8个像素的差值,如果超过阈值,就标记为坏点。
避坑指南:我曾经在一个项目中,动态坏点检测阈值设得太低,结果把正常的纹理细节当成了坏点,画面变得像磨皮过度一样。后来我改用自适应阈值——根据局部方差动态调整检测门限,效果好了很多。
你想想看,坏点校正放在Lens Shading之后是有道理的——因为Lens Shading会放大边缘的噪声和坏点,先校正亮度不均,再做坏点检测,准确率更高。
3.4 去噪预处理(Denoising Preprocessing)
最后一步是去噪。为什么叫「预处理」?因为这里只做轻度去噪,保留细节,后面还有专门的降噪模块做精细处理。
常用的Bayer域去噪方法:
- 空域滤波:双边滤波、非局部均值(NLM)——效果好但计算量大
- 时域滤波:多帧叠加、运动补偿——适合视频,静态场景效果极佳
- 变换域滤波:小波阈值、BM3D——质量最高,但实时性差
我个人在手机项目里最常用的是「自适应双边滤波」。为什么?因为它在边缘保持和噪声抑制之间平衡得最好。而且可以在Bayer域直接操作,不用先插值成RGB,省掉一步开销。
// 自适应双边滤波(简化版)
for each pixel (i, j) {
weight_sum = 0;
value_sum = 0;
for each neighbor (m, n) {
w_spatial = exp(-((i-m)^2 + (j-n)^2) / (2 * sigma_s^2));
w_range = exp(-(I(i,j) - I(m,n))^2 / (2 * sigma_r^2));
weight = w_spatial * w_range;
value_sum += I(m,n) * weight;
weight_sum += weight;
}
I_out(i,j) = value_sum / weight_sum;
}
去噪参数调优口诀:
- sigma_s(空间域):越大越模糊,一般取 1.5~3.0
- sigma_r(值域):越大越保留细节,一般取 10~30(12bit数据)
- 迭代次数:1次足够,多了细节全丢
我的经验:去噪预处理不要做得太狠。我见过有人把sigma_r设到5,结果暗部噪声是没了,但头发丝纹理也糊了。记住——预处理的目标是「让后面的插值和颜色处理不放大噪声」,不是一步到位降干净。
好了,这四个步骤走完,RAW图才算「干净」了。接下来才能放心地做白平衡、去马赛克、颜色校正。每一步都有坑,但只要你理解了原理,加上项目经验积累,这些都不是问题。