Bayer格式详解:从阵列到RAW数据读取
做图像处理的人,天天跟Bayer格式打交道。说白了,Bayer就是CMOS传感器输出的一种原始数据格式。为什么叫Bayer?因为发明它的人叫Bryce Bayer,柯达的工程师。1976年提出的方案,到现在还在用,你想想看这有多经典。
我个人习惯把Bayer格式理解成「传感器的语言」。传感器本身分不清颜色,它只能感知光的强度。那怎么得到彩色图像?靠的就是在像素表面覆盖一层彩色滤光片阵列——这就是Bayer阵列。
Bayer阵列模式
标准的Bayer阵列,每2x2个像素为一组。每个像素只允许一种颜色的光通过。绿色占一半,红色和蓝色各占四分之一。为什么绿色多?因为人眼对绿色最敏感。这个设计很聪明,对吧?
常见的Bayer模式有四种:
| 模式名称 | 2x2排列 | 常见传感器 |
|---|---|---|
| RGGB | R G / G B | Sony IMX系列 |
| GRBG | G R / B G | OmniVision OV系列 |
| GBRG | G B / R G | 部分三星传感器 |
| BGGR | B G / G R | 部分安森美传感器 |
这里有个坑。我曾经在项目中接过一批RAW图,怎么处理颜色都不对。折腾了两天,最后发现是Bayer模式搞错了。传感器文档写的是RGGB,实际输出是GRBG。从那以后,我拿到新传感器第一件事就是拍一张纯白画面,检查Bayer模式。
Bayer阵列的物理结构
你想想看,一个2000万像素的传感器,实际上有2000万个感光点。每个点上面盖着一层微小的滤光片。这些滤光片的排列方式,就是Bayer阵列。
我画了一张图,帮你理解Bayer阵列的结构:
RAW数据读取
RAW数据,说白了就是传感器直接输出的原始数据。没有经过任何处理,没有去马赛克,没有白平衡,没有伽马校正。每个像素就是一个数字,代表该点的光强度。
读取RAW数据,我一般用Python。下面是我常用的读取代码:
import numpy as np
import struct
def read_raw_bayer(file_path, width, height, bpp=10):
"""
读取Bayer RAW数据
file_path: RAW文件路径
width, height: 图像宽高
bpp: 每像素位数,常见10bit/12bit/14bit
"""
with open(file_path, 'rb') as f:
raw_data = f.read()
# 计算每像素占用的字节数
if bpp == 10:
# 10bit数据,5个字节存4个像素
pixel_count = width * height
expected_size = (pixel_count * 10 + 7) // 8
raw_data = raw_data[:expected_size]
# 解包10bit数据
raw_10bit = np.frombuffer(raw_data, dtype=np.uint16)
# 这里需要做位操作,略
elif bpp == 12:
# 12bit数据,3个字节存2个像素
raw_12bit = np.frombuffer(raw_data, dtype=np.uint16)
# 位操作提取12bit值
else:
# 8bit或16bit直接读取
dtype = np.uint8 if bpp == 8 else np.uint16
raw_array = np.frombuffer(raw_data, dtype=dtype)
# 重塑为2D数组
bayer_image = raw_array.reshape((height, width))
return bayer_image
Bayer数据的内存布局
嗯,这里要注意。Bayer数据在内存里是逐行存储的。第一行是RGRGRG...,第二行是GBGBGB...,以此类推。你想想看,如果搞错了行顺序,出来的图像就是错位的。
我整理了一个表格,帮你理解不同Bayer模式的行数据:
| 模式 | 奇数行 | 偶数行 |
|---|---|---|
| RGGB | R G R G R G ... | G B G B G B ... |
| GRBG | G R G R G R ... | B G B G B G ... |
| GBRG | G B G B G B ... | R G R G R G ... |
| BGGR | B G B G B G ... | G R G R G R ... |
我曾经犯过一个低级错误。读取RAW数据时,把行顺序搞反了。结果出来的图像颜色完全不对,红色变成了蓝色。排查了半天,才发现是numpy的reshape方向错了。
Bayer数据的可视化
读取完RAW数据,我习惯先看一眼数据分布。用matplotlib显示一下:
import matplotlib.pyplot as plt
def visualize_bayer(bayer_image, mode='RGGB'):
plt.figure(figsize=(10, 8))
# 显示原始Bayer数据
plt.subplot(2, 2, 1)
plt.imshow(bayer_image, cmap='gray')
plt.title('Bayer RAW Data')
# 分离通道
h, w = bayer_image.shape
r_channel = np.zeros((h//2, w//2))
g_channel = np.zeros((h//2, w//2))
b_channel = np.zeros((h//2, w//2))
if mode == 'RGGB':
r_channel = bayer_image[0:h:2, 0:w:2]
g_channel = bayer_image[0:h:2, 1:w:2]
b_channel = bayer_image[1:h:2, 1:w:2]
plt.subplot(2, 2, 2)
plt.imshow(r_channel, cmap='Reds')
plt.title('Red Channel')
plt.subplot(2, 2, 3)
plt.imshow(g_channel, cmap='Greens')
plt.title('Green Channel')
plt.subplot(2, 2, 4)
plt.imshow(b_channel, cmap='Blues')
plt.title('Blue Channel')
plt.tight_layout()
plt.show()
常见Bayer格式的差异
不同厂商的传感器,Bayer格式可能不一样。我整理了一些常见传感器的参数:
| 传感器型号 | Bayer模式 | 位深 | 常见应用 |
|---|---|---|---|
| Sony IMX378 | RGGB | 10bit | 手机摄像头 |
| OmniVision OV5640 | GRBG | 10bit | 嵌入式摄像头 |
| Sony IMX290 | RGGB | 12bit | 安防监控 |
| ON Semi AR0230 | BGGR | 12bit | 车载摄像头 |
这里有个技巧。拿到一个新传感器,如果文档没写Bayer模式,你可以拍一张纯白画面。然后分别用四种模式去解析,看哪个出来的颜色最正常。我经常用这个方法快速确认。
Bayer数据的预处理
读取完RAW数据,我一般会做几步预处理:
- 黑电平校正:减去暗电流带来的偏移量
- 坏点校正:修复传感器上的坏像素
- 增益校正:补偿不同通道的灵敏度差异
- 数据归一化:将数据映射到0-1范围
这些预处理步骤,说白了就是为后面的去马赛克和白平衡做准备。数据质量不好,后面的算法再牛也白搭。
def preprocess_bayer(bayer_image, black_level=64, white_level=1023):
"""
Bayer数据预处理
"""
# 黑电平校正
bayer_corrected = bayer_image.astype(np.float32) - black_level
# 裁剪到有效范围
bayer_corrected = np.clip(bayer_corrected, 0, white_level - black_level)
# 归一化到0-1
bayer_normalized = bayer_corrected / (white_level - black_level)
return bayer_normalized
嗯,到这里Bayer格式的基础知识就差不多了。你想想看,理解了Bayer阵列的结构,知道了怎么读取RAW数据,后面的去马赛克和白平衡处理就有了基础。这些内容看起来简单,但实际项目中踩坑的人不少。我建议你动手写代码试试,用真实的RAW数据跑一遍,感受一下。