2. ISP Pipeline基础

说实话,刚入行那会儿,我觉得 ISP 就是个黑盒子——RAW 图进去,漂亮照片出来。直到第一次调一个 200 万像素的安防芯片,才发现这中间每一步都是坑。今天咱们就把 ISP Pipeline 最核心的几个模块掰开揉碎聊一遍。

一句话总结:ISP 的本质是把传感器输出的 Bayer 格式原始数据,一步步变成人眼看着舒服的 RGB/YUV 图像。每一步都在做「修正」和「增强」。

2.1 Bayer 格式:传感器的原始语言

CMOS 传感器每个像素只能感知亮度,那颜色怎么来的?答案是在像素表面覆盖一层彩色滤光片阵列(CFA)。最常见的模式就是 Bayer 模式。

Bayer 模式的基本规律:每个 2x2 单元里,有 1 个红色(R)、2 个绿色(G)、1 个蓝色(B)像素。为什么绿色多一倍?因为人眼对绿色最敏感。这个设计很聪明,对吧?

Bayer 模式 排列方式 常见场景
RGGB 第一行 R G R G…
第二行 G B G B…
最主流,索尼、OV 传感器默认
BGGR 第一行 B G B G…
第二行 G R G R…
部分安防传感器
GRBG 第一行 G R G R…
第二行 B G B G…
某些老款 Omnivision
我的习惯:拿到新传感器,第一件事就是确认 Bayer 排列顺序。我曾经因为搞反了 RGGB 和 BGGR,调了三天 Demosaic,画面全是紫绿色伪影——后来发现是 Bayer 顺序配错了。

2.2 Demosaic:猜出丢失的颜色

每个像素只有一种颜色信息,另外两种颜色需要靠周围像素「猜」出来。这个过程就叫 Demosaic(去马赛克)。

最简单的做法是双线性插值——取周围同色像素的平均值。但这样做边缘会糊,还会出现彩色伪影。实际工程中常用的是:

  • 边缘导向插值:先判断当前像素是在水平边缘还是垂直边缘,然后沿着边缘方向插值,不跨边缘。
  • 自适应插值:结合色差恒定假设(R-G 和 B-G 的差值在局部区域变化缓慢),效果更好。
  • AI 辅助 Demosaic:用轻量 CNN 网络做插值,我见过在 FPGA 上跑 3 层卷积的方案,效果确实比传统方法好,但资源开销大。
避坑:Demosaic 和去噪的顺序很关键。先 Demosaic 再去噪,噪声会被插值放大,产生彩色噪点。先做简单去噪再 Demosaic,边缘细节又容易丢失。我一般建议:在 Bayer 域做轻量去噪,Demosaic 之后再做精细去噪。

2.3 去噪:在细节和干净之间找平衡

传感器噪声来源很多:热噪声、读出噪声、光子散粒噪声……低光环境下尤其明显。去噪是 ISP 里最吃算力的模块之一。

我习惯把去噪分成两个阶段:

  1. Bayer 域去噪:处理 RAW 图上的固定模式噪声(FPN)和行噪声。常用方法有中值滤波、BM3D 的简化版。
  2. RGB 域去噪:处理高斯噪声和彩色噪声。经典算法有双边滤波、非局部均值(NLM),现在很多芯片用 CNN 去噪。

举个例子,双边滤波的公式很简单:

// 双边滤波核心思想
// 输出像素 = 邻域内像素的加权平均
// 权重 = 空间距离权重 × 像素值相似度权重

for each pixel (i, j):
    sum_weight = 0
    sum_value = 0
    for each neighbor (k, l):
        w_spatial = exp(-((i-k)^2 + (j-l)^2) / (2 * sigma_s^2))
        w_range  = exp(-(I(i,j) - I(k,l))^2 / (2 * sigma_r^2))
        weight = w_spatial * w_range
        sum_weight += weight
        sum_value += I(k,l) * weight
    output(i,j) = sum_value / sum_weight

sigma_s 控制空间范围,sigma_r 控制边缘保留程度。调参的时候你会发现:sigma_r 设大了,边缘也糊了;设小了,噪声去不干净。这个平衡点,每个传感器都不一样。

2.4 白平衡:让白色真的白

人眼有色彩恒常性——不管在日光灯下还是烛光下,看白纸都觉得是白的。但传感器没这个能力。白平衡就是强制把画面中的中性色(灰、白)校正到标准色温下。

经典做法是「灰度世界假设」:认为整幅图像所有颜色的平均值应该是灰色。于是:

  • 计算 R、G、B 三个通道的平均值
  • 算出 R_gain = G_avg / R_avg,B_gain = G_avg / B_avg
  • 每个像素的 R 乘以 R_gain,B 乘以 B_gain

但这个方法在单色场景下会翻车——比如拍一片蓝天,蓝色通道平均值远高于红绿,白平衡会把蓝色压下去,结果天空变灰了。

我踩过的坑:有一次做车载摄像头,傍晚路灯下画面偏黄。灰度世界算法把黄色强行拉回白色,结果路面的纹理细节全丢了。后来改用色温估计 + 查找表的方法,先判断当前光源色温,再用对应的增益矩阵,效果稳定多了。

2.5 Gamma 校正:为了显示器的非线性

人眼对暗部细节的敏感度远高于亮部。而显示器的亮度响应是非线性的——输入电压和输出亮度不是正比关系。Gamma 校正就是为了匹配这个特性。

标准公式:

V_out = V_in ^ (1 / gamma)

// 典型 gamma 值
// sRGB 标准:gamma = 2.2
// 苹果系统:gamma = 1.8
// 广电标准:gamma = 2.4

Gamma 校正的硬件实现通常用查找表(LUT)。把 0-4095(12bit RAW)映射到 0-1023(10bit 显示),提前算好 4096 个值存到 SRAM 里,查表输出就行。一个周期搞定,延迟极低。

注意:Gamma 校正必须在色彩空间转换之前做,还是在之后做?答案是:在 RGB 域做,而且要在白平衡和去噪之后。因为 Gamma 是非线性变换,如果先转 YUV 再做 Gamma,色相会偏移。

2.6 色彩空间转换:从 RGB 到 YUV

人眼对亮度变化比对颜色变化敏感得多。所以视频编码和显示系统通常把亮度和颜色分开处理——这就是 YUV 色彩空间。

从 RGB 到 YUV 的转换是线性变换:

// BT.601 标准(标清)
Y  =  0.299 * R + 0.587 * G + 0.114 * B
U  = -0.147 * R - 0.289 * G + 0.436 * B + 128
V  =  0.615 * R - 0.515 * G - 0.100 * B + 128

// BT.709 标准(高清)
Y  =  0.2126 * R + 0.7152 * G + 0.0722 * B
U  = -0.1146 * R - 0.3854 * G + 0.5000 * B + 128
V  =  0.5000 * R - 0.4542 * G - 0.0458 * B + 128

硬件实现时,这些系数通常用定点数表示(比如 Q8 格式),用乘加器阵列并行计算。一个像素的 RGB 转 YUV 只需要 3 个乘加器,流水线设计下每个时钟周期可以处理一个像素。

精度问题:系数用 8bit 定点数表示时,误差大约在 1-2 个 LSB。如果后续要做颜色分级或 HDR 合成,建议用 12bit 精度。我见过一个项目,因为系数精度不够,导致肤色在暗部出现色带,排查了两天才找到原因。

2.7 本章知识结构

下面这张图把 ISP Pipeline 的核心流程串起来了。每个模块的输入输出、依赖关系一目了然。

ISP Pipeline 核心流程 Bayer RAW Bayer 域去噪 Demosaic RGB 域去噪 白平衡 Gamma 校正 色彩空间转换 YUV 输出 关键依赖关系: ① 去噪分两段:Bayer 域做轻量去噪 → Demosaic → RGB 域做精细去噪 ② 白平衡必须在 RGB 域做,Gamma 校正必须在色彩空间转换之前做 典型数据位宽变化 Bayer RAW:12bit → 去噪后:12bit → Demosaic:12bit × 3 通道 白平衡后:12bit × 3 → Gamma 校正:10bit × 3 → YUV:10bit × 3 硬件设计时,每个模块的位宽都要单独考虑。我习惯在模块接口处加饱和截位逻辑, 防止中间计算溢出导致画面出现「雪花点」。

2.8 工程视角:Pipeline 的硬件代价

聊完算法,咱们看看硬件实现。每个模块的算力和存储开销差异很大:

模块 计算类型 典型硬件实现 延迟(@1080p)
Bayer 域去噪 统计 + 滤波 Line buffer + 乘加器 2-4 行延迟
Demosaic 插值 + 边缘检测 5x5 窗口 + 比较器 3-5 行延迟
RGB 域去噪 双边滤波 / NLM 大窗口 SRAM + 查找表 10-20 行延迟
白平衡 乘法 + 统计 乘法器 + 累加器 1 像素延迟
Gamma 校正 查找表 SRAM LUT 1 像素延迟
色彩空间转换 矩阵乘法 3 个乘加器 1 像素延迟

你看,去噪模块占了大部分延迟和存储。所以做芯片架构时,去噪模块通常要单独分配一块 SRAM,其他模块可以共享算力单元。

我的建议:如果你在做 FPGA 原型验证,先把白平衡、Gamma、色彩空间转换调通——这三个模块逻辑简单,容易出效果。去噪和 Demosaic 留到最后调,因为参数多、调试周期长。