Bayer RAW格式详解:从像素排列到数据基础
做图像传感器调试这些年,我越来越觉得——Bayer RAW就是整个ISP pipeline的起点。你想想看,所有后续的去马赛克、坏点校正、降噪、锐化,全都是在跟这堆原始数据打交道。如果连RAW格式都没吃透,后面调出来的效果大概率是空中楼阁。
这一章,咱们就把Bayer RAW的底裤扒干净。从Pattern排列到数据格式,再到黑电平与白平衡的基础概念,我会结合自己踩过的坑来讲。
2.1 四种常见的Bayer Pattern排列
先说个基本事实:单颗CMOS传感器每个像素只能感知亮度,分不清颜色。那怎么拍出彩色照片?答案就是在像素表面覆盖彩色滤光片阵列(CFA),也就是Bayer Pattern。
Bayer Pattern的核心思路很简单:人眼对绿色最敏感,所以绿色像素占一半,红蓝各占四分之一。但具体怎么排列?业界主要有四种变体:
| Pattern名称 | 2x2单元排列 | 常见传感器厂商 | 我的备注 |
|---|---|---|---|
| RGGB | R G G B |
Sony、OmniVision | 最主流,我调得最多 |
| BGGR | B G G R |
部分Onsemi、老款CCD | 跟RGGB就是镜像关系 |
| GRBG | G R B G |
Samsung部分型号 | 第一行第一个是绿,容易搞混 |
| GBRG | G B R G |
部分安防传感器 | 少见,但遇到了别慌 |
💡 我的小技巧:拿到一个新传感器,第一件事就是确认Pattern。我习惯用纯色均匀光拍一张RAW,然后看2x2块的像素值分布——哪个通道值最高,基本就能反推Pattern。这招比看datasheet快多了。
你可能会问:这四种Pattern到底有啥本质区别?说白了,它们只是CFA排列的起始相位不同。对于去马赛克算法来说,Pattern决定了插值时的参考像素位置。比如RGGB的R通道在(0,0),而BGGR的R通道在(1,1)。
⚠️ 我曾经踩过的坑:有一次项目赶进度,我直接复用上一款传感器的去马赛克代码,没改Pattern配置。结果出来的图像全是彩色摩尔纹,R和B通道完全错位。排查了整整两天才发现是Pattern没对上。从那以后,我每次换传感器都会在代码里显式打印Pattern信息。
2.2 RAW10 / RAW12 数据格式
聊完Pattern,咱们看看数据怎么存的。传感器的ADC(模数转换器)把每个像素的模拟电压转成数字值,这个值的位深决定了动态范围和精度。
常见的RAW位深有这些:
| 格式 | 位深 | 最大值 | 存储方式 | 典型应用 |
|---|---|---|---|---|
| RAW8 | 8-bit | 255 | 1字节/像素 | 低端摄像头、预览 |
| RAW10 | 10-bit | 1023 | 5字节/4像素(packed) | 手机、安防主流 |
| RAW12 | 12-bit | 4095 | 3字节/2像素(packed) | 工业相机、高端安防 |
| RAW14/16 | 14/16-bit | 16383/65535 | 2字节/像素 | 专业摄影、科学相机 |
这里有个关键点:RAW10和RAW12不是直接存10位或12位。因为内存寻址最小单位是字节(8位),所以需要做packing。我举个例子:
// RAW10 Packed格式:5个字节存4个像素
// 像素0: [9:2] 高8位
// 像素1: [9:2] 高8位
// 像素2: [9:2] 高8位
// 像素3: [9:2] 高8位
// 第5字节: [像素3[1:0], 像素2[1:0], 像素1[1:0], 像素0[1:0]]
// 解包代码示例(Python)
def unpack_raw10(packed_data):
"""将RAW10 packed数据解包为16位整数数组"""
import numpy as np
raw10 = np.frombuffer(packed_data, dtype=np.uint8)
pixel_count = len(raw10) * 4 // 5
result = np.zeros(pixel_count, dtype=np.uint16)
for i in range(0, len(raw10), 5):
# 高8位
result[i*4//5] = (raw10[i] << 2) | ((raw10[i+4] >> 0) & 0x03)
result[i*4//5 + 1] = (raw10[i+1] << 2) | ((raw10[i+4] >> 2) & 0x03)
result[i*4//5 + 2] = (raw10[i+2] << 2) | ((raw10[i+4] >> 4) & 0x03)
result[i*4//5 + 3] = (raw10[i+3] << 2) | ((raw10[i+4] >> 6) & 0x03)
return result
🔑 关键理解:RAW10的1023级灰阶 vs RAW12的4095级灰阶,差距不是4倍,而是16倍的精度提升。在暗光场景下,RAW12能保留更多暗部细节,减少banding(条带噪声)。我个人做安防项目时,能上RAW12绝不选RAW10。
2.3 黑电平(Black Level)基础
好,现在你拿到了RAW数据。但有个问题:传感器在完全无光(盖上镜头盖)时,输出的数字值不是0。为什么?
原因有几个:
- 暗电流:半导体本身的热激发产生电子,温度越高越明显
- ADC偏移:模数转换器有固定的偏置电压
- 电路噪声:读出电路引入的直流分量
这个非零的基准值,就是黑电平(Black Level)。通常用OB(Optical Black)像素来测量——传感器边缘有一圈被金属遮挡的像素,它们输出的平均值就是黑电平。
| 参数 | 典型值(RAW10) | 说明 |
|---|---|---|
| 黑电平 | 64 | 对应0 lux的基准值 |
| 白电平 | 1023 | 饱和时的最大值 |
| 有效动态范围 | 64 ~ 1023 | 实际可用的960级 |
⚠️ 注意:黑电平不是固定不变的!温度升高、增益改变、曝光时间变长,黑电平都会漂移。我见过一个案例:某款传感器在60℃环境下黑电平从64漂到了78,结果暗部偏绿——因为RGB三个通道的黑电平漂移量不一致。所以动态黑电平校正是必须的。
2.4 白平衡(White Balance)基础
白平衡,说白了就是让白色物体在任何光源下都呈现白色。但RAW数据本身没有白平衡——传感器对R、G、B的响应天然不平衡。
举个例子:在D65(6500K)标准光源下,拍一张白纸。你去看RAW数据,G通道的值可能是800,R通道只有600,B通道只有500。这不是传感器坏了,而是CFA的光谱透过率和硅的光电响应特性导致的。
白平衡校正就是给每个通道乘一个增益系数:
// 白平衡增益计算(简化版)
// 目标:让R、G、B通道对白纸的响应一致
// 假设G通道为参考
R_gain = G_mean / R_mean
B_gain = G_mean / B_mean
// 校正后的像素值
R_corrected = R_raw * R_gain
G_corrected = G_raw * 1.0 // G通道通常不做增益
B_corrected = B_raw * B_gain
💡 实战经验:白平衡增益不是越大越好。增益太大会放大噪声,尤其是B通道——因为B通道的量子效率通常最低,增益最大。我一般会限制最大增益不超过4倍(约12dB),超过这个值就考虑换光源或者用多帧融合。
这里有个容易混淆的概念:黑电平校正必须在白平衡之前做。为什么?你想想看,如果黑电平是64,白平衡增益是2倍,那么64*2=128的偏移会被放大。正确的顺序是:
- 减黑电平:pixel -= black_level
- 白平衡增益:pixel *= gain
- 后续处理:去马赛克、色彩校正等
📌 本章核心总结:
- Bayer Pattern有4种排列,确认Pattern是调试第一步
- RAW10/12的packing格式必须正确解包,否则数据全错
- 黑电平是传感器的“零点”,必须动态校正
- 白平衡增益要合理,先减黑电平再乘增益
嗯,这一章的内容就到这里。这些基础概念看起来简单,但我在实际项目中见过太多因为Pattern搞错、黑电平没减干净、白平衡顺序不对导致的奇葩问题。把这些基础打牢,后面调去马赛克和坏点校正的时候,你会少走很多弯路。