OpenISP 模块拆解 · 第7讲:去马赛克 (CFA)
模块定位
CFA 在这里指 Color Filter Array Interpolation,也就是 demosaic。代码位于 model/cfa.py,类名 CFA。它把单通道 Bayer RAW 转成三通道 RGB,是 ISP pipeline 中非常关键的结构转换点。
在 CFA 之前,图像每个像素只有一个颜色采样;在 CFA 之后,每个像素都有 R、G、B 三个值。这个转换会显著影响清晰度、伪彩、拉链纹和颜色边缘质量。
背景原理
Bayer 阵列利用人眼对绿色更敏感的特点,用 2x2 单元采样:
text
RGGB: R Gr
Gb B
每个像素缺失两个颜色分量。Demosaic 的任务就是根据邻域采样估计这些缺失值。最简单的方法是双线性插值,但它容易在边缘处混色。更复杂的方法会利用方向梯度、颜色差分或频域模型来保护边缘。
openISP 实现
CFA 输入参数:
img: Bayer RAW 图像。mode: 当前主要支持malvar。bayer_pattern:rggb、bggr、gbrg、grbg。clip: 输出上限。
执行流程:
- 对 RAW 做 2 像素 reflect padding。
- 转成
int32,避免插值公式中负系数造成溢出。 - 按 2x2 Bayer 单元遍历。
- 根据当前位置颜色调用
malvar()。 - 输出
(H, W, 3)的 RGB 图像。
Malvar 插值思想
代码实现的是 Malvar-He-Cutler 一类线性 demosaic 思想的简化形式。它不是只拿相邻同色像素平均,而是用 5x5 邻域中的多方向采样,加上拉普拉斯修正项来估计缺失通道。
以 R 点估计 G/B 为例:
- R 点自身已知,直接保留。
- G 值由上下左右 G 采样和中心 R 的局部变化共同估计。
- B 值由对角 B 采样与中心附近结构共同估计。
这些公式中的负系数和较大中心系数,用于补偿简单平均带来的模糊。直觉上,它在利用"颜色差分比颜色值本身更平滑"的假设。
Bayer pattern 的重要性
同一段插值公式必须知道当前位置是 R、Gr、Gb 还是 B。bayer_pattern 一旦配错,算法会把 R 当 B 或把 Gr/Gb 位置颠倒,结果通常是严重偏色、棋盘纹或彩色边缘。
因此实际 ISP 中,pattern 信息通常来自 sensor driver、RAW metadata 或固定模组配置,不能靠后处理猜。
参数和调试
mode: 目前只有malvar路径有实现。clip: 控制 RGB 输出范围。bayer_pattern: 必须和输入 RAW 匹配。
调试 CFA 要重点看斜边、黑白线条、文字、织物、树枝和高饱和边缘。好的 demosaic 应在锐度、伪彩和拉链纹之间平衡。
实现注意点
当前输出数组是 np.int16,最后 clip。由于 Malvar 公式包含负项,中间使用有符号类型是必要的。
这份实现是逐 2x2 block 的 Python 循环,直观但不快。真实工程会做向量化或硬件流水线,并常加入边缘方向判断、假色抑制和噪声自适应策略。
学习重点
- CFA 是从 RAW 到 RGB 的结构性转换。
- Demosaic 会放大前级坏点、彩噪和混叠问题。
- Malvar 类方法比双线性更锐,但仍可能产生伪彩。
- Bayer pattern 错误是 demosaic 中最致命的配置错误之一。
面试问答
Q1: Demosaic 为什么是 ISP 中很重要的模块?
因为它决定了 RAW 信息如何变成完整 RGB,直接影响细节、颜色边缘、伪彩和后续颜色处理质量。
Q2: 双线性插值和 Malvar 插值有什么区别?
双线性主要做邻域平均,简单但偏软;Malvar 使用更大的邻域和修正项,锐度更好,但实现和伪影控制更复杂。
Q3: Bayer pattern 配错会怎样?
颜色通道位置全部错位,通常会出现明显偏色、棋盘纹或奇怪的彩色边缘。
Q4: CFA 前为什么要做 DPC/AAF/CNF?
因为 CFA 会扩散输入异常。坏点、混叠和彩噪如果不先控制,插值后会变成更难处理的 RGB 伪影。
Q5: 如何评价 demosaic 算法好坏?
看锐度、伪彩、拉链纹、噪声放大、边缘颜色一致性和计算成本,而不是只看单一清晰度指标。