第4章:RAW域数据格式——从RAW8到RAW16,以及Bayer的那些事儿
好,咱们进入第四章。前面聊了MIPI的物理层和协议层,现在该聊聊RAW域里最核心的东西——数据格式了。
说白了,传感器输出的原始数据长什么样?你怎么把它从一堆比特流里还原成有意义的像素值?这就是本章要解决的问题。
我个人习惯把RAW数据格式比作“传感器的母语”。你调试ISP,不懂这个,后面全是瞎蒙。
4.1 RAW位深:RAW8、RAW10、RAW12、RAW14、RAW16
先看位深。位深决定了每个像素用多少个比特来表示。
| 格式 | 位深 | 典型应用场景 | 动态范围(理论) |
|---|---|---|---|
| RAW8 | 8 bit | 低端摄像头、USB摄像头 | 48 dB |
| RAW10 | 10 bit | 手机主摄、安防IPC | 60 dB |
| RAW12 | 12 bit | 车载摄像头、工业相机 | 72 dB |
| RAW14 | 14 bit | 高端工业、医疗影像 | 84 dB |
| RAW16 | 16 bit | 科学级相机、天文摄影 | 96 dB |
你可能会问:位深越高越好吗?理论上是的。但实际项目中,你得考虑带宽、存储、处理速度。
关键认知:RAW10是消费电子领域的“黄金位深”。它比RAW8多了一档动态范围,又不像RAW12那样对带宽压力太大。我经手的项目中,80%的调试工作都集中在RAW10上。
举个例子。一个200万像素的传感器,30fps输出:
- RAW8:带宽约 480 Mbps
- RAW10:带宽约 600 Mbps
- RAW12:带宽约 720 Mbps
你看,从RAW10到RAW12,带宽涨了20%。对于MIPI D-PHY来说,这20%可能就意味着要从1-lane切换到2-lane,或者从低速模式切到高速模式。
我的经验:选位深时,先算带宽预算。别一上来就上RAW12,结果发现MIPI跑不动,最后还得降帧率。我曾经在一个车载项目里就吃过这个亏——选了RAW14,结果D-PHY的时钟频率死活上不去,最后只能降回RAW12。
4.2 Bayer模式:RGGB/GRBG/BGGR/GBRG
好,位深聊完了。接下来是Bayer模式。
大部分彩色传感器用的都是Bayer滤色阵列。每个像素只感测一种颜色——红、绿或蓝。然后通过插值算法恢复全彩。
Bayer模式有四种排列方式:
- RGGB:第一行 R G R G...,第二行 G B G B...
- GRBG:第一行 G R G R...,第二行 B G B G...
- BGGR:第一行 B G B G...,第二行 G R G R...
- GBRG:第一行 G B G B...,第二行 R G R G...
你可能会觉得:这不就是四种排列嘛,随便选一个不就行了?
嗯,这里有个坑。ISP的demosaic模块是跟Bayer模式绑定的。你传感器输出的是RGGB,ISP却配成了GRBG,那出来的图像颜色就全乱了。
避坑指南:我曾经在调试一款OV sensor时,发现图像偏绿严重。查了半天,最后发现是Bayer模式配错了。传感器手册上写的是“BGGR”,我默认配成了“RGGB”。就这一个字节的差异,折腾了我两天。
怎么确认Bayer模式?我建议你这么做:
- 看传感器数据手册,找到Bayer pattern的说明
- 拍一张纯白画面,看RAW数据的排列
- 用工具(比如RAW分析器)验证
另外,Bayer模式跟传感器的物理方向也有关系。传感器旋转90度或180度,Bayer模式也会变。所以做模组时,一定要确认镜头的安装方向。
4.3 数据打包与解包
接下来是实操环节——数据打包与解包。
MIPI CSI-2协议里,RAW数据不是直接裸传的。它需要打包成特定的格式。常见的打包方式有:
- RAW8:每个像素1字节,直接传,最简单
- RAW10:4个像素打包成5字节(4×10=40 bit = 5 byte)
- RAW12:2个像素打包成3字节(2×12=24 bit = 3 byte)
- RAW14:4个像素打包成7字节(4×14=56 bit = 7 byte)
- RAW16:每个像素2字节,直接传
我重点说说RAW10的打包,因为这是最常用的。
RAW10打包规则
假设你有4个像素:P0, P1, P2, P3,每个10 bit。打包成5字节:
Byte 0: P0[9:2] // P0的高8位
Byte 1: P1[9:2] // P1的高8位
Byte 2: P2[9:2] // P2的高8位
Byte 3: P3[9:2] // P3的高8位
Byte 4: P3[1:0] P2[1:0] P1[1:0] P0[1:0] // 4个像素的低2位拼在一起
你看,第4字节里,P0的低2位在最低位,P3的低2位在最高位。这个顺序不能搞反。
解包时要注意:很多初学者解包时,会把低2位的顺序搞反。比如把P0的低2位放到最高位去了。结果就是图像出现周期性的条纹噪声。我见过不止一个工程师在这个问题上栽跟头。
RAW12打包规则
RAW12的打包更简单:2个像素打包成3字节。
Byte 0: P0[11:4] // P0的高8位
Byte 1: P1[11:4] // P1的高8位
Byte 2: P1[3:0] P0[3:0] // P0和P1的低4位拼在一起
嗯,这里也是,P0的低4位在低位,P1的低4位在高位。
解包代码示例(C语言风格)
我写个简单的RAW10解包函数,你感受一下:
// 输入:packed_data - 5字节的打包数据
// 输出:pixels - 4个10-bit像素值
void unpack_raw10(uint8_t *packed_data, uint16_t *pixels) {
pixels[0] = (packed_data[0] << 2) | ((packed_data[4] >> 0) & 0x03);
pixels[1] = (packed_data[1] << 2) | ((packed_data[4] >> 2) & 0x03);
pixels[2] = (packed_data[2] << 2) | ((packed_data[4] >> 4) & 0x03);
pixels[3] = (packed_data[3] << 2) | ((packed_data[4] >> 6) & 0x03);
}
注意看,第4字节的移位操作。我习惯用宏定义把这种操作封装起来,避免手写出错。
调试技巧:如果你不确定解包是否正确,可以拍一张均匀光照的灰卡。解包后,检查相邻像素的值是否连续变化。如果出现周期性的跳变,那多半是解包顺序错了。
4.4 知识结构图
下面这张图总结了本章的核心知识体系:
4.5 本章小结
好,总结一下本章的核心要点:
- 位深:RAW10是主流,选型时先算带宽
- Bayer模式:四种排列,配错就偏色,务必确认
- 打包解包:注意低2位/低4位的顺序,用灰卡验证
这些看起来都是基础,但我在实际项目中见过太多人在这上面翻车。你想想看,一个ISP pipeline调了几个月,最后发现是RAW数据解包错了——那得多冤?
所以,我的建议是:在开始调试ISP之前,先把RAW数据的抓取和解包验证做扎实。这一步稳了,后面的工作才有意义。