Bayer 图像、打包后的 RAW 数据流和 RGB 图像分别对应相机处理流程中的不同阶段。本文将介绍 Bayer 彩色滤光片阵列如何把感光单元映射为颜色采样、MIPI RAW10 如何保存这些采样值,以及如何用简单的双线性算法重建 RGB 图像。
Bayer CFA 与传感器感光单元
Bayer pattern 描述的是图像传感器感光单元上方实际排列的彩色滤光片阵列(Color Filter Array,CFA)。每个感光单元只测量经过一种颜色滤光片后的光强,而不是直接获得完整的 RGB 三通道数据。
标准 Bayer 阵列有四种 2x2 排列:
1 | RGGB GRBG GBRG BGGR |
该排列会在整个有效传感器区域内重复。例如,GRBG 展开后为:
1 | G R G R G R ... |
标准 Bayer CFA 中绿色采样数量是红色或蓝色的两倍:
1 | R : G : B = 1 : 2 : 1 |
绿色对人眼感知的亮度和空间细节贡献较大,因此提高绿色采样密度可以在图像质量和传感器复杂度之间取得良好平衡。两组绿色采样并不是两个独立的颜色通道,使用的也不是不同种类的绿色滤光片。
对于 RGGB 阵列,有时会根据位置进一步写成:
1 | R Gr |
Gr表示与红色采样位于同一行的绿色。Gb表示与蓝色采样位于同一行的绿色。
这种写法可以明确相邻关系:Gr 水平方向的相邻点是红色,Gb 水平方向的相邻点是蓝色。
感光单元、RAW 采样与 RGB 像素
假设传感器的有效区域为 100x100,并采用 1:1 读取:
- 每个有效感光单元都会被读取;
- 不进行像素合并、裁剪或缩放;
- 传感器输出 100x100 个 Bayer 采样值;
- 去马赛克后得到 100x100 个 RGB 像素。
整个过程中,空间尺寸保持不变:
对于输出位置 (x, y),其中一个颜色分量由对应的感光单元直接测得,另外两个颜色分量则根据周围的采样值估算。
因此,重复出现的 2x2 Bayer 块不是一个超级像素,它只是描述 CFA 的排列方式。在 1:1 处理流程中,每个感光单元在空间位置上仍然对应一个输出 RGB 像素。
Bayer 顺序与 RAW 打包相互独立
Bayer pattern 回答的是:
坐标
(x, y)处的采样值代表哪一种颜色?
RAW 打包方式回答的是:
每个采样值的比特在内存或传输数据中如何排列?
两者是相互独立的属性。数据可以是 GRBG + packed RAW10,也可以是 RGGB + unpacked RAW12,或者其他组合。
必须根据传感器、接收端或文件格式文档同时确认这两个属性。正确的 RAW10 解包程序无法弥补 Bayer pattern 选择错误的问题;同样,正确选择 Bayer pattern 也无法修复错误的字节解析。
MIPI RAW10 打包格式
RAW10 为每个感光单元保存一个 10 bit 采样值。在常见的 MIPI CSI-2 排列中,4 个像素占用 5 个字节:
1 | B0 = P0[9:2] |
可以按以下方式还原采样值:
1 | P0 = (B0 << 2) | ((B4 >> 0) & 0x03) |
下面的示例逐行解包,每次处理完整的 4 像素组:
1 |
|
为了保持示例清晰,这段代码要求图像宽度能够被 4 整除。实际解码器必须按照数据源的具体规则处理不完整像素组、行填充、行头和 stride。并非所有被称为“RAW10”的文件都是连续紧密排列的 MIPI CSI-2 payload。
Unpacked RAW10 通常会使用 16 bit 容器保存每个采样值,但字节序和有效位对齐方式由具体格式决定。10 个有效 bit 可能位于容器的低位,也可能位于高位。
根据坐标确定 Bayer 颜色
解包后,每个数组元素的取值范围为 0~1023。下一步是确定每个坐标对应的颜色。
1 | typedef enum { |
Bayer pattern 改变时,RAW10 解包代码不需要修改。只有坐标到颜色的映射以及后续去马赛克逻辑与 Bayer pattern 有关。
支持四种排列的双线性去马赛克
双线性去马赛克通过相邻采样值的平均值估算缺失的颜色分量:
- 在红色位置,绿色取上下左右四个采样的平均值,蓝色取四个对角采样的平均值;
- 在蓝色位置,绿色取上下左右四个采样的平均值,红色取四个对角采样的平均值;
- 在绿色位置,根据当前绿色属于
Gr还是Gb,从水平或垂直方向分别估算红色和蓝色。
图像最外圈需要单独处理,因为部分相邻坐标会超出图像范围。下面这个教学示例采用镜像坐标处理边界。向内镜像一个位置还能保持 Bayer pattern 的奇偶关系。
1 | static inline int mirror_coordinate(int value, int limit) |
该函数输出 RGB 交错排列的数据,并支持 RGGB、GRBG、GBRG 和 BGGR。它的目标是清晰展示基本原理,并不是生产级算法。更先进的去马赛克算法会利用梯度和色差,以减少边缘附近的拉链伪影、伪色和细节损失。
去马赛克只是图像处理流程的一步
把 10 bit 采样值直接转换为 8 bit 适合用来演示数据格式,但实际相机通常还会执行更多处理:
如果不进行黑电平校正和白平衡,输出图像可能存在明显偏色。如果不进行 Gamma 或色调映射,线性传感器数据在普通显示器上通常会显得很暗。
由于传感器或模拟读出电路的差异,两个绿色位置也可能存在轻微不一致,因此部分处理流程会分别校准 Gr 和 Gb。某些算法可能选择其中一个绿色子平面作为参考,但直接丢弃另一个子平面会损失一半绿色采样,并不具备普遍的去马赛克优势。
写入与识别 PNG、BMP 文件
文件扩展名不能完全证明文件的真实格式,应当检查文件签名:
1 | PNG: 89 50 4E 47 0D 0A 1A 0A |
在 PowerShell 7 中:
1 | Get-Content -AsByteStream -TotalCount 16 your_image.png | Format-Hex |
在 Windows PowerShell 5.1 中:
1 | Get-Content -Encoding Byte -TotalCount 16 your_image.png | Format-Hex |
PNG 使用无损的 DEFLATE 压缩。噪声较多的图像通常难以压缩,因此 PNG 文件大小接近 BMP 并不意味着它没有经过压缩。
常见的 Windows BMP 由以下部分组成:
1 | 14 字节 BITMAPFILEHEADER |
对于未压缩的 24 bit BMP,每行按照 4 字节对齐:
1 | row_size = ((width * 24 + 31) / 32) * 4 |
传统 24 bit BMP 的像素字节按照 B、G、R 顺序保存。高度为正数时,各行从下到上保存;高度为负数则表示从上到下保存。
对于 1920x1080 图像,每行数据本身已经满足 4 字节对齐:
1 | 1920 * 1080 * 3 = 6,220,800 字节 |
即大约 6.22 MB 或 5.93 MiB,再加上很小的文件头。
总结
理解相机数据时,需要把处理流程中的不同层次区分开:
最重要的几个概念是:
- 感光单元(photosite)是物理上的感光位置;
- Bayer 采样值是一个感光单元产生的数字测量结果;
- RGB 像素是在对应空间位置重建出的三通道数据;
- 2x2 Bayer 块是重复的 CFA 排列,而不是一个超级像素;
- RAW 打包方式决定字节布局,Bayer 顺序决定颜色解释。