ISP核心模块解析(上):Bayer域处理
好,我们正式开始啃ISP最核心的模块。这一节我打算拆成上下两部分来讲,上部分聚焦在Bayer域——也就是Sensor原始数据刚进来还没做任何处理的那个阶段。
很多人觉得Bayer域处理就是“预处理”,没什么技术含量。其实不然。我做过几个项目,前期Bayer域没处理好,后面调再好的算法也救不回来。说白了,Bayer域是画质的地基。
Bayer域是什么?
先简单回顾一下。CMOS Sensor每个像素只能感知亮度,不能直接感知颜色。为了获取彩色信息,Sensor表面覆盖了一层滤色阵列——最常见的就是Bayer Pattern。
Bayer Pattern的典型排列(RGGB):
每个2x2单元包含1个R、2个G、1个B。人眼对绿色最敏感,所以绿色像素占了一半。
Sensor输出的就是这种马赛克状的RAW数据。后续所有处理,都要在这个基础上展开。
黑电平校正(Black Level Correction)
黑电平校正,简称BLC。这是ISP流水线的第一关。
为什么要做?
理想情况下,Sensor在全黑环境下的输出应该是0。但实际不是。由于暗电流、电路噪声等因素,Sensor会有一个固定的偏移量。这个偏移量就是黑电平。
如果不校正,暗部会发灰,动态范围也会被压缩。
怎么做?
常见的做法是:Sensor内部会预留一些光学黑像素(Optical Black, OB),这些像素被遮挡,不感光。ISP读取这些OB像素的平均值,作为当前帧的黑电平基准。
// 伪代码示例:黑电平校正
uint16_t black_level = get_ob_average(sensor_raw);
for (each pixel in frame) {
pixel = (pixel > black_level) ? (pixel - black_level) : 0;
}
我的经验:黑电平不是一成不变的。温度升高、增益变大,黑电平都会漂移。我建议做动态黑电平校正——每帧都重新计算OB均值,而不是用固定值。曾经有个项目,客户反映暗部偏色,查了两天才发现是黑电平没随增益联动。
Bayer域去噪
去噪是ISP里最考验功力的模块之一。Bayer域去噪,顾名思义,是在数据还是马赛克状态时就做降噪。
为什么要在Bayer域做?
因为去马赛克之后,噪声会被插值算法放大和扩散。先降噪,再插值,效果会好很多。
常用方法:
- 空域滤波:双边滤波、非局部均值(NLM)。双边滤波保边效果好,但计算量大。
- 变换域滤波:小波变换、BM3D。效果顶级,但硬件实现成本高。
- 时域滤波:多帧融合。适合静态场景,运动区域容易产生鬼影。
| 方法 | 降噪效果 | 细节保留 | 计算量 | 硬件友好度 |
|---|---|---|---|---|
| 双边滤波 | 中等 | 好 | 中 | 高 |
| NLM | 好 | 好 | 高 | 低 |
| BM3D | 极好 | 极好 | 极高 | 极低 |
| 时域滤波 | 好 | 中等 | 低 | 高 |
避坑指南:我曾经在一个低光项目中,为了追求降噪效果,把Bayer域去噪强度拉得很高。结果发现暗部细节全糊了,而且出现了伪色。后来才意识到,Bayer域去噪要克制——它的主要目标是去除传感器读出噪声和光子散粒噪声,而不是把纹理也抹掉。降噪强度要和ISO联动,ISO越高,强度越大,但要有上限。
去马赛克(Demosaic)
去马赛克,也叫颜色插值。它的任务是从Bayer Pattern中恢复出每个像素的R、G、B三个通道值。
为什么难?
因为每个像素只知道自己一个通道的值,另外两个通道要靠邻居猜。猜错了,就会出现伪色、锯齿、摩尔纹。
经典算法演进:
- 最近邻插值:最简单,但效果最差。锯齿严重。
- 双线性插值:取周围同色像素的平均值。速度快,但边缘模糊。
- 边缘导向插值:先判断边缘方向,再沿边缘方向插值。效果明显提升。
- 自适应插值:结合色差恒定假设(色差在局部区域变化平缓)。这是目前主流方案的基础。
我个人的习惯是,在项目中优先采用基于色差恒定的边缘导向插值。它在画质和计算量之间取得了很好的平衡。
// 伪代码:基于色差恒定的G通道插值(简化版)
// 假设当前像素是R位置,需要插值G
// 色差:K = G - R(在R位置,G未知,R已知)
// 利用周围G-R色差的平均值来估计当前G值
float k_h = abs(G_left - R_left) + abs(G_right - R_right); // 水平方向色差变化
float k_v = abs(G_up - R_up) + abs(G_down - R_down); // 垂直方向色差变化
if (k_h < k_v) {
// 水平边缘,沿水平方向插值
G_current = R_current + ( (G_left - R_left) + (G_right - R_right) ) / 2;
} else if (k_v < k_h) {
// 垂直边缘,沿垂直方向插值
G_current = R_current + ( (G_up - R_up) + (G_down - R_down) ) / 2;
} else {
// 平坦区域,取平均
G_current = R_current + ( (G_left - R_left) + (G_right - R_right) + (G_up - R_up) + (G_down - R_down) ) / 4;
}
我的经验:去马赛克算法对边缘方向判断的准确性非常敏感。方向判断错了,伪色就出来了。我建议在判断边缘方向时,不要只看单一通道的梯度,可以结合G通道的梯度信息。G通道采样率最高,信息最可靠。
Bayer域处理流程总览
这三个模块在ISP流水线中是串联的:BLC → Bayer去噪 → Demosaic。顺序不能乱。
下面这张图展示了Bayer域处理的核心流程和关键节点:
嗯,到这里,Bayer域的三个核心模块就讲完了。你想想看,这三个模块环环相扣,任何一个环节出问题,都会直接影响最终画质。我个人建议,在做ISP调优时,先把Bayer域的参数调稳,再往后走。地基打牢了,上层建筑才稳。