4. 原始图像数据获取与格式解析
在汽车图像质量调试中,ISP 输出的 YUV 或 RGB 图像已经过降噪、去马赛克、色调映射等处理,很多缺陷会被掩盖或改变形态。要真正定位镜头阴影、坏点、暗电流、色彩串扰、噪声底等问题,必须回到传感器输出的 RAW 数据。本节围绕“拿到 RAW、看懂 RAW、导出可分析图像”这条主线展开,重点讲清 Bayer 排列、位深、打包模式以及 onsemi 私有元数据的解析方法。
一、RAW 数据的本质:Bayer 阵列与 CFA 排列
绝大多数车载 CMOS 传感器在每个像素上只放置一个颜色滤光片,形成 Color Filter Array(CFA)。最常见的排列是 2×2 重复单元,即 Bayer 模式。四种典型排列为 RGGB、BGGR、GRBG、GBRG,命名方式是按 2×2 单元从左到右、从上到下的颜色顺序读取。例如 RGGB 表示第一行是 R、G,第二行是 G、B。
RAW 文件本身通常不携带颜色信息,只有每个像素的灰度值。如果 Bayer 相位判断错误,去马赛克后会出现明显的红绿或蓝绿偏色,且偏色区域呈棋盘格状。因此解析 RAW 的第一步是确认 CFA 排列,并与传感器寄存器中 CFA_PATTERN 或 onsemi 元数据中的 Bayer 字段交叉验证。
二、位深与打包模式:从 8bit 到 16bit 容器
位深决定每个像素的量化精度。车载传感器常见 10bit、12bit、14bit,部分高动态场景使用 16bit 或压缩 RAW。位深越高,暗部层次和动态范围保留越好,但数据量也越大。例如 1920×1080、12bit 的 RAW 单帧约 1920×1080×12/8 ≈ 3.11MB;若用 16bit 容器存储,则约 4.15MB。
打包模式是解析 RAW 时最容易出错的地方。常见模式包括:
- Unpacked(非打包):每个像素占 2 字节,低 12 位有效,高 4 位补零或填充。解析时直接按 16bit 读取再右移或掩码即可。
- Packed(紧凑打包):多个像素的有效位连续拼接,例如 12bit packed 中每 3 字节承载 2 个像素。解析时必须按位域提取,不能按字节对齐读取。
- MIPI RAW10/RAW12:每 4 个像素一组,额外字节存放低位。RAW10 中 5 字节承载 4 个像素,RAW12 中 3 字节承载 2 个像素。这类格式在 CSI-2 传输中很常见,解析时要按组还原。
判断打包模式是否解析正确,可以观察直方图:若低位被错误截断,直方图会出现梳状间隙;若字节序错误,图像会出现规律性条纹或整体亮度异常。
三、onsemi 私有元数据解析
onsemi 传感器通常在 RAW 帧的头部或尾部嵌入私有元数据(Embedded Data / Metadata),用于记录曝光、增益、温度、帧号、坏点校正状态、CFA 相位、黑电平等信息。这些数据不在标准 MIPI 像素流中,而是通过额外的数据行或独立寄存器读出。
解析 onsemi 元数据时,需要先确认三个关键参数:元数据起始地址、每行字节数、字段偏移表。常见做法是从传感器数据手册中获取 Embedded Data 的行数和每行长度,再按字段定义逐位解析。例如某型号在帧头嵌入 4 行元数据,每行 16 字节,其中第 0~1 字节为帧号,第 2~3 字节为曝光行数,第 4~5 字节为模拟增益,第 6~7 字节为数字增益,第 8~9 字节为传感器温度,第 10 字节为 CFA 相位。
解析时要注意字节序(onsemi 多为小端或大端需查手册确认)和有效位宽。例如模拟增益字段可能只有 12 位有效,高 4 位为保留位。若直接按 16 位读取,会得到错误增益值,进而影响后续噪声分析。
四、使用工具导出可分析图像
拿到 RAW 后,通常需要导出为可分析的图像格式,如 16bit TIFF、PNG 或 NumPy 数组。以 Python 为例,若已知图像宽高、位深、Bayer 相位和打包模式,可以用 NumPy 按位域解包,再重塑为二维数组。以下是一个 12bit packed 的解包示例:
import numpy as np
def unpack_12bit_packed(data, width, height):
# 每 3 字节承载 2 个 12bit 像素
data = np.frombuffer(data, dtype=np.uint8)
data = data.reshape(-1, 3)
p0 = (data[:, 0].astype(np.uint16) << 4) | (data[:, 1].astype(np.uint16) >> 4)
p1 = ((data[:, 1].astype(np.uint16) & 0x0F) << 8) | data[:, 2].astype(np.uint16)
pixels = np.empty(data.shape[0] * 2, dtype=np.uint16)
pixels[0::2] = p0
pixels[1::2] = p1
return pixels.reshape(height, width)
解包后,建议先做黑电平扣除和增益归一化,再导出 16bit TIFF。若使用 rawpy,可以直接读取 DNG 或 RAW 文件,并通过 raw.postprocess() 控制输出色彩空间和位深。但要注意,rawpy 默认会应用白平衡和去马赛克,若要做纯 RAW 分析,应关闭这些选项,或直接读取 raw.raw_image。
五、结果判读与常见问题
导出图像后,可以从以下几个维度判断解析是否正确:
- 偏色:若整体偏红或偏蓝,可能是 CFA 相位错误或白平衡未归一化。
- 棋盘格:若出现规律性明暗交替,通常是 Bayer 相位与去马赛克算法不匹配。
- 条纹:若出现水平或垂直条纹,可能是打包模式解析错误或字节序问题。
- 直方图间隙:若直方图出现梳状间隙,说明低位被截断或位深映射错误。
- 元数据异常:若曝光、增益、温度字段明显偏离合理范围,应检查字节序和有效位宽。
完成 RAW 解析和导出后,后续的噪声分析、坏点检测、色彩校正才有可靠的数据基础。下一阶段将基于这些可分析图像,展开具体的图像质量评估与调优。