2. ISP Pipeline基础
说实话,刚入行那会儿,我觉得 ISP 就是个黑盒子——RAW 图进去,漂亮照片出来。直到第一次调一个 200 万像素的安防芯片,才发现这中间每一步都是坑。今天咱们就把 ISP Pipeline 最核心的几个模块掰开揉碎聊一遍。
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 |
2.2 Demosaic:猜出丢失的颜色
每个像素只有一种颜色信息,另外两种颜色需要靠周围像素「猜」出来。这个过程就叫 Demosaic(去马赛克)。
最简单的做法是双线性插值——取周围同色像素的平均值。但这样做边缘会糊,还会出现彩色伪影。实际工程中常用的是:
- 边缘导向插值:先判断当前像素是在水平边缘还是垂直边缘,然后沿着边缘方向插值,不跨边缘。
- 自适应插值:结合色差恒定假设(R-G 和 B-G 的差值在局部区域变化缓慢),效果更好。
- AI 辅助 Demosaic:用轻量 CNN 网络做插值,我见过在 FPGA 上跑 3 层卷积的方案,效果确实比传统方法好,但资源开销大。
2.3 去噪:在细节和干净之间找平衡
传感器噪声来源很多:热噪声、读出噪声、光子散粒噪声……低光环境下尤其明显。去噪是 ISP 里最吃算力的模块之一。
我习惯把去噪分成两个阶段:
- Bayer 域去噪:处理 RAW 图上的固定模式噪声(FPN)和行噪声。常用方法有中值滤波、BM3D 的简化版。
- 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 里,查表输出就行。一个周期搞定,延迟极低。
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 个乘加器,流水线设计下每个时钟周期可以处理一个像素。
2.7 本章知识结构
下面这张图把 ISP Pipeline 的核心流程串起来了。每个模块的输入输出、依赖关系一目了然。
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,其他模块可以共享算力单元。