1. ISP基础与图像传感器
做嵌入式图像处理这些年,我最大的感触是:不懂传感器,就别谈ISP优化。很多工程师一上来就调参数,结果换了颗传感器,之前的经验全废了。这一章,咱们先把地基打牢。
1.1 ISP流水线概述
ISP,全称Image Signal Processor。说白了,就是把传感器输出的原始数据,变成人眼看着舒服的图像。你想想看,手机拍一张照片,背后其实跑了一整套流水线。
我个人习惯把ISP流水线分成三个阶段:
- 前端处理:坏点校正、黑电平补偿、镜头阴影校正。这些操作都在RAW域完成。
- 中间处理:去马赛克、白平衡、颜色校正、伽马校正。这是最核心的部分。
- 后端处理:边缘增强、降噪、色调映射。输出最终图像。
核心观点:ISP不是一条固定的流水线。不同平台、不同传感器,流水线的顺序和模块都可能不一样。我在项目中遇到过客户非要先做降噪再做去马赛克,结果细节全糊了——顺序真的很重要。
下面这张图,是我自己画的一个典型ISP流水线框架。你可以把它当作本章的地图。
1.2 CMOS传感器工作原理
CMOS传感器,现在几乎一统天下了。CCD?除了极少数工业场景,基本看不到了。
它的工作原理,我总结成三步:
- 光电转换:光子打到像素上,产生电子。光越强,电子越多。
- 电荷读出:通过行选和列选,把每个像素的电压读出来。
- 模数转换:ADC把模拟电压转成数字值,也就是RAW数据。
实战经验:我曾经调试一款安防摄像头,发现暗光下图像偏绿。查了半天,原来是传感器增益曲线没配好。CMOS的模拟增益和数字增益,对色彩影响很大——这个坑我踩过。
这里有个关键参数表,做ISP调试时经常用到:
| 参数 | 典型值 | 影响 |
|---|---|---|
| 像素尺寸 | 1.4µm - 2.0µm | 越大,感光越好,但分辨率受限 |
| 满阱容量 | 10000e⁻ - 30000e⁻ | 决定动态范围 |
| 读出噪声 | 2e⁻ - 10e⁻ | 影响暗光信噪比 |
| 量子效率 | 50% - 80% | 越高,灵敏度越好 |
1.3 Bayer阵列与色彩滤波
CMOS传感器本身是色盲的——它只能感知光的强度,分不清颜色。那彩色照片怎么来的?靠的是Bayer阵列。
Bayer阵列,说白了就是在每个像素上盖一层滤色片。最常见的格式是RGGB:
- 50%的像素是绿色(G)
- 25%的像素是红色(R)
- 25%的像素是蓝色(B)
为什么绿色占一半?因为人眼对绿色最敏感。这个设计很巧妙,我每次讲到这里都觉得挺佩服当年发明Bayer的人。
⚠️ 注意:Bayer阵列不是唯一的色彩滤波方案。还有RCCC(用于车规)、RCCB(用于低照度)等变体。选型时一定要确认传感器的Bayer模式,否则ISP配置全错。
常见的Bayer排列有四种:
| 模式 | 第一行 | 第二行 | 常见场景 |
|---|---|---|---|
| BGGR | B G B G | G R G R | Sony传感器 |
| RGGB | R G R G | G B G B | OmniVision传感器 |
| GBRG | G B G B | R G R G | 部分安防传感器 |
| GRBG | G R G R | B G B G | 部分车载传感器 |
1.4 RAW图格式解析
RAW图,就是传感器直接输出的原始数据。没有经过任何ISP处理。我经常跟团队说:RAW图是ISP调试的起点,也是终点——调试好了,后面都是锦上添花;调试不好,后面再怎么调也救不回来。
RAW图有几个关键属性:
- 位深:常见8bit、10bit、12bit、14bit。位深越大,动态范围越好。
- Bayer模式:上面讲过的四种之一。
- 黑电平:传感器在无光时的输出值,通常不是0。
- 白电平:传感器饱和时的输出值。
举个例子,一个10bit RAW图,黑电平可能是64,白电平是1023。有效数据范围只有64~1023,而不是0~1023。这个偏移量,ISP前端必须补偿掉。
关键公式:校正后的像素值 = (RAW值 - 黑电平) × 增益
这个公式虽然简单,但我在项目中见过有人把黑电平设成0,结果暗部全是噪声——嗯,细节决定成败。
下面是一个简单的RAW图读取代码示例,用Python演示:
import numpy as np
def read_raw_10bit(file_path, width, height, bayer='RGGB'):
"""
读取10bit RAW图,返回numpy数组
"""
raw_data = np.fromfile(file_path, dtype=np.uint16)
raw_data = raw_data.reshape((height, width))
# 10bit数据,右移6位得到有效值
raw_data = raw_data & 0x03FF
# 分离Bayer通道
if bayer == 'RGGB':
R = raw_data[0::2, 0::2]
G1 = raw_data[0::2, 1::2]
G2 = raw_data[1::2, 0::2]
B = raw_data[1::2, 1::2]
return raw_data, {'R': R, 'G1': G1, 'G2': G2, 'B': B}
# 使用示例
raw, channels = read_raw_10bit('image.raw', 1920, 1080)
print(f"RAW数据范围: {raw.min()} - {raw.max()}")
调试技巧:拿到一颗新传感器,我第一件事就是拍一张全黑图和一张全白图。全黑图看黑电平是否稳定,全白图看有没有坏点或阴影。这个习惯帮我省了不少排查时间。
最后,我把本章的知识点整理成一张结构图,方便你回顾:
嗯,这一章的内容就到这里。记住一句话:ISP调试,从理解传感器开始。后面章节我们会深入每个模块的实战调优,到时候这些基础会反复用到。