3、RAW数据存储格式:10bit/12bit/14bit/16bit RAW、MIPI RAW格式、DNG格式解析

3.1 位深度的本质:从10bit到16bit

RAW数据的位深度,说白了就是每个像素用多少个bit来表示。我刚开始接触ISP时,总觉得位深越大越好,后来踩过坑才明白——位深的选择是个系统工程。

位深 每像素bit数 动态范围 常见场景
10bit 10 约60dB 手机摄像头、MIPI CSI-2主流
12bit 12 约72dB 安防监控、工业相机
14bit 14 约84dB 单反相机、高端工业相机
16bit 16 约96dB 科学相机、天文摄影

关键认知:位深每增加1bit,理论上动态范围提升约6dB。但实际传感器噪声会限制有效位深,很多12bit传感器实际有效位深只有10-11bit。

3.2 存储格式的物理布局

RAW数据在内存里怎么摆?这里有个容易搞混的点:位深和存储字节数不是一一对应的。

3.2.1 10bit RAW的两种存储方式

10bit数据比较特殊,因为它不是8bit的整数倍。我见过两种主流方案:

  • 打包模式(Packed):4个10bit像素打包成5个字节(40bit)。节省带宽,但处理时需要解包。
  • 对齐模式(Aligned):每个10bit像素占用16bit(高6位填充0)。浪费空间,但CPU处理方便。

我的经验:在MIPI传输时用打包模式,在DDR存储时用对齐模式。这样既省带宽,又方便DMA搬运。

3.2.2 12bit/14bit/16bit的存储

12bit和16bit相对简单:

  • 12bit:通常按16bit对齐存储(高4位填充),也有按24bit打包3个像素的。
  • 14bit:几乎都是16bit对齐,高2位填充。我在项目中遇到过14bit传感器,但后端ISP只支持12bit,只能截断处理——嗯,这其实浪费了传感器的能力。
  • 16bit:直接按16bit存储,没有填充问题。但要注意,很多16bit RAW其实是14bit数据左对齐的。
// 10bit打包模式示例:4个像素 -> 5字节
// 像素0: 0x3FF, 像素1: 0x2AA, 像素2: 0x155, 像素3: 0x000
// 存储: [0xFF, 0x0F, 0xAA, 0x56, 0x00]
// 字节0: 像素0[9:2]
// 字节1: 像素0[1:0] | 像素1[9:4] << 2
// 字节2: 像素1[3:0] | 像素2[9:6] << 4
// 字节3: 像素2[5:0] | 像素3[9:8] << 6
// 字节4: 像素3[7:0]

3.3 MIPI RAW格式解析

MIPI CSI-2是摄像头和SoC之间最常用的接口。RAW数据在MIPI链路上传输时,有自己的一套规矩。

3.3.1 MIPI RAW的数据类型

数据类型 描述 每像素bit数
RAW6 6bit RAW 6
RAW7 7bit RAW 7
RAW8 8bit RAW 8
RAW10 10bit RAW 10
RAW12 12bit RAW 12
RAW14 14bit RAW 14
RAW16 16bit RAW 16
RAW20 20bit RAW 20

避坑指南:我曾经在调试MIPI RAW10时,发现图像有条纹。查了三天,最后发现是MIPI的VC(Virtual Channel)配置错了,导致数据错位。MIPI的包结构一定要仔细核对:PH(Packet Header)+ RAW Data + PF(Packet Footer)。

3.3.2 MIPI RAW10的传输格式

MIPI RAW10使用打包模式传输。具体来说:

  • 每4个像素组成一个5字节的传输单元
  • 传输顺序:先传像素0的高8位,再传像素0的低2位+像素1的高6位...
  • 每行数据以帧起始码(FS)和帧结束码(FE)包裹

你想想看,如果MIPI时钟是500MHz,4条lane,RAW10模式下理论带宽能到多少?嗯,500M * 4 * 10 / 8 = 2.5GB/s。但实际要扣除包开销,大概能到2GB/s左右。

3.4 DNG格式解析

DNG(Digital Negative)是Adobe推出的RAW通用格式。我个人觉得,DNG最大的价值在于标准化——不用为每个传感器写单独的解析器。

3.4.1 DNG的核心结构

DNG基于TIFF格式,包含以下关键标签:

标签 作用 我的备注
DNGVersion DNG规范版本 目前主流是1.4.0.0
CFAPattern Bayer模式定义 0=R, 1=G, 2=B, 3=G
ColorMatrix1 颜色校正矩阵 用于D50光源
AsShotNeutral 白平衡参考点 归一化后的RGB值
BlackLevel 黑电平值 每个通道可以不同
WhiteLevel 白电平值 通常为2^bitdepth - 1

重要提示:DNG的BlackLevel和WhiteLevel是ISP处理的关键参数。我见过有人把这两个值搞反,结果图像整体偏色。BlackLevel通常是64-256之间的值,WhiteLevel是4095(12bit)或16383(14bit)。

3.4.2 DNG的压缩方式

DNG支持两种压缩:

  • 线性压缩(Linear):无损失,但压缩率低,约1.5:1
  • 差值压缩(Delta):基于JPEG-LS,压缩率约2:1,但会引入微小误差

我在项目中一般用线性压缩,因为后续ISP处理需要精确的RAW数据。差值压缩虽然省空间,但解压后和原始数据有差异,对高精度应用不友好。

3.5 格式转换实战

不同RAW格式之间的转换是嵌入式视觉的常见需求。下面是一个10bit打包到16bit对齐的转换示例:

// 10bit packed to 16bit aligned
void raw10_to_raw16(uint8_t *src, uint16_t *dst, int width, int height) {
    int pixels = width * height;
    for (int i = 0; i < pixels; i += 4) {
        int byte_idx = (i / 4) * 5;
        // 解包4个10bit像素
        dst[i]     = (src[byte_idx] << 2) | (src[byte_idx+1] >> 6);
        dst[i+1]   = ((src[byte_idx+1] & 0x3F) << 4) | (src[byte_idx+2] >> 4);
        dst[i+2]   = ((src[byte_idx+2] & 0x0F) << 6) | (src[byte_idx+3] >> 2);
        dst[i+3]   = ((src[byte_idx+3] & 0x03) << 8) | src[byte_idx+4];
    }
}

性能优化建议:在ARM Cortex-A系列上,用NEON指令可以一次处理16个像素。我实测过,NEON版本比纯C快4-5倍。对于4K分辨率,这个优化能省下好几毫秒。

3.6 知识结构总览

下面这张图总结了RAW数据存储格式的核心关系:

RAW数据存储格式知识结构 RAW数据存储 位深选择 10bit 12bit 14bit 16bit MIPI RAW 打包模式 对齐模式 数据类型 DNG格式 TIFF基础 关键标签 压缩方式 选择依据:传感器能力 → 带宽限制 → 处理精度 → 存储成本 10bit适合移动端,12bit是主流,14bit以上用于专业领域

核心总结:RAW数据存储格式的选择,本质是在数据精度、传输带宽、处理效率、存储成本之间做权衡。10bit适合移动端,12bit是工业主流,14bit以上留给专业领域。MIPI RAW是传输层的规范,DNG是存储层的规范,两者互补。