2、RAW域基础:Bayer阵列原理、RAW数据格式解析、黑电平校正与Lens Shading

好,我们正式开始进入RAW域的世界。

很多做图像处理的工程师,做了好几年YUV域的降噪、锐化,却很少真正碰过RAW数据。说实话,这挺可惜的。RAW域才是传感器最原始的“心声”,你在这里做一点微小的调整,效果往往比在YUV域大动干戈要好得多。

这一章,我们把RAW域的四个基本功打牢:Bayer阵列、RAW格式、黑电平校正、Lens Shading。这些都是我当年刚入行时踩过坑的地方,咱们一个一个说清楚。

2.1 Bayer阵列原理

为什么传感器不直接输出RGB?说白了,成本问题。一个像素点如果同时感光R、G、B,需要三个光电二极管叠在一起,工艺复杂,良率低。所以工业界想了个巧妙的办法:每个像素只感光一种颜色,然后通过插值猜出另外两种。

最常见的排列就是Bayer阵列。它用50%的绿色、25%的红色、25%的蓝色来模拟人眼对绿色更敏感的特性。你想想看,为什么是绿多?因为人眼在亮度上的分辨率最高,而绿色对亮度的贡献最大。

核心记忆点:Bayer阵列不是“丢失”了颜色信息,而是用空间分辨率换取了色彩灵敏度。每个2x2单元里,有两个G、一个R、一个B。

常见的Bayer模式有四种,取决于哪个像素是R起点:

模式名称 第0行第0列 第0行第1列 第1行第0列 第1行第1列
RGGB R Gr Gb B
BGGR B Gb Gr R
GRBG Gr R B Gb
GBRG Gb B R Gr

这里有个细节:Gr和Gb虽然都是绿色,但位置不同。Gr在R行,Gb在B行。因为工艺差异,Gr和Gb的感光特性往往不完全一致,这就是后面要讲的“Gr/Gb不平衡校正”的由来。我在项目中遇到过,有些sensor的Gr和Gb能差3-5个DN,如果不校正,出来的图像会有明显的网格状纹理。

Bayer阵列结构示意图(RGGB模式) R Gr Gb B 关键特征 • 绿色占50% • 红蓝各占25% • Gr/Gb工艺差异 • 2x2最小重复单元

2.2 RAW数据格式解析

RAW数据不是一张“图片”,它是一堆数字。每个像素对应一个数字,这个数字代表该像素接收到的光子数量转换成的电压,再经过ADC量化后的结果。

常见的RAW位深有10bit、12bit、14bit。我个人的习惯是,先搞清楚sensor输出的是多少bit,然后决定怎么存、怎么读。

实用技巧:很多sensor虽然标称12bit,但实际有效位可能只有11.5bit。低几位往往是噪声。你可以通过统计直方图的最低有效位是否均匀分布来判断。

RAW数据的存储格式主要有两种:

  • Pack格式:比如10bit数据,5个字节存4个像素。节省带宽,但处理时需要解包。
  • Unpack格式:每个像素用2个字节(16bit)存储,高位对齐或低位对齐。浪费空间,但处理方便。

举个例子,12bit RAW数据在内存中的排列:

// 假设sensor输出12bit,采用unpack高位对齐
// 像素值 = 0xABC (12bit)
// 内存中存储为:0xAB 0xC0 (高位对齐)
// 或者:0x0A 0xBC (低位对齐)

// 读取时需要注意移位操作
uint16_t raw_value = ((uint16_t)buffer[i] << 8) | buffer[i+1];
// 如果是高位对齐,需要右移4位
raw_value = raw_value >> 4;

这里有个坑:不同sensor厂商的pack格式可能不一样。我曾经被一个sensor坑过,它的10bit pack是每4个像素用5个字节,但字节内的bit排列顺序跟标准的不一样。调试了整整一天才发现。

注意:处理RAW数据时,一定要先确认sensor的datasheet中关于数据格式的描述。重点关注:位深、pack/unpack方式、高位还是低位对齐、字节序(大端/小端)。

2.3 黑电平校正

黑电平是什么?简单说,就是sensor在完全无光的情况下,输出的数字值不是0。为什么?因为电路本身有暗电流、有偏置电压。这个值通常在几十到几百个DN之间。

黑电平校正的目的,就是把真正的“黑”拉到0附近。如果不做,你会发现图像整体偏灰,暗部细节被抬升了。

校正公式很简单:

// 黑电平校正
uint16_t ob_value = 64;  // 假设sensor的黑电平是64
uint16_t raw_in = 1000;
uint16_t raw_out;

// 直接减去黑电平
raw_out = (raw_in > ob_value) ? (raw_in - ob_value) : 0;

// 注意:有些sensor的黑电平是分通道的
// R通道OB=64, Gr通道OB=66, Gb通道OB=65, B通道OB=63
// 需要根据像素位置选择对应的OB值

为什么黑电平要分通道?因为不同颜色的像素,其光电二极管的物理特性有细微差异。我见过最夸张的一个sensor,R通道和B通道的黑电平差了20个DN。如果不分开校正,暗部会出现明显的颜色偏色。

经验之谈:黑电平校正最好在RAW域做的第一件事。而且我建议你做一个动态黑电平跟踪——sensor温度升高时,黑电平会漂移。固定值校正不够鲁棒。

2.4 Lens Shading校正

Lens Shading,也叫镜头阴影校正。你想想看,镜头是一个凸透镜,光线从中心到边缘的光程不同,再加上镜头本身的透光率不均匀,导致图像边缘比中心暗。这就是Lens Shading。

校正思路很简单:给边缘的像素乘一个大于1的增益,让边缘亮起来。但增益怎么算?

常用的模型是多项式拟合:

// 基于半径的LSC校正
float gain = 1.0f;
float r = sqrtf((x - cx)*(x - cx) + (y - cy)*(y - cy));
float r_norm = r / max_radius;  // 归一化到[0,1]

// 四阶多项式
gain = 1.0f + a1 * r_norm + a2 * r_norm * r_norm 
       + a3 * r_norm * r_norm * r_norm 
       + a4 * r_norm * r_norm * r_norm * r_norm;

// 应用增益
uint16_t corrected = (uint16_t)(raw_in * gain + 0.5f);

这里要注意几个问题:

  • 增益不能太大:边缘增益过大会放大噪声。一般控制在2.0以内。
  • 分通道校正:R、G、B的衰减曲线不一样,需要分别拟合。
  • 网格校正:有些平台用网格点存储增益值,中间值双线性插值。

避坑指南:我曾经在一个项目里,LSC校正后边缘出现了明显的色偏。查了半天,发现是R通道和B通道的增益曲线不匹配。后来我改用分通道独立拟合,每个通道单独算一组多项式系数,问题就解决了。

LSC校正的典型流程:

  1. 拍摄一张均匀光照下的灰卡图像
  2. 计算每个像素位置相对于中心的增益
  3. 用多项式或网格拟合增益曲面
  4. 将系数烧录到sensor的OTP或保存在校准文件中
  5. 运行时根据系数实时校正
Lens Shading校正流程 拍摄灰卡图像 计算增益曲面 拟合多项式 烧录/存储系数 实时校正 注意事项 • 分通道独立拟合 • 增益不超过2.0

嗯,这一章的内容就这些。Bayer阵列是基础,RAW格式是工具,黑电平校正是第一步,Lens Shading是必修课。把这些搞清楚了,后面讲降噪算法时,你才能理解为什么有些噪声在RAW域更容易处理。