1. RAW图像读取与显示
任务目标
读取一张RAW格式图像文件,将其解码为可视的RGB图像并显示,同时理解RAW数据的存储格式(Bayer排列)与基本处理流程。
RAW格式说明
RAW图像是相机传感器直接输出的原始数据,每个像素仅记录一种颜色分量,按Bayer模式排列(本案例使用RGGB排列)。RAW文件通常不包含图像头信息,需要已知宽度、高度、位深和Bayer排列方式才能正确解码。本案例使用8位深度的RAW文件,文件大小为 width × height 字节。
环境依赖
- Python 3.8+
- NumPy
- OpenCV(
pip install opencv-python numpy)
关键完整代码
import numpy as np
import cv2
def read_raw_to_bayer(filepath, width, height):
"""读取8位RAW文件,返回Bayer排列的灰度图像"""
with open(filepath, 'rb') as f:
data = np.fromfile(f, dtype=np.uint8, count=width*height)
if data.size != width*height:
raise ValueError(f"文件大小不匹配:期望 {width*height} 字节,实际 {data.size} 字节")
return data.reshape((height, width))
def demosaic_bilinear(bayer):
"""双线性插值去马赛克,输入Bayer图(RGGB),输出BGR彩色图"""
h, w = bayer.shape
# 分离四通道(每个通道对应一种滤色器位置)
r = np.zeros_like(bayer, dtype=np.uint16)
g = np.zeros_like(bayer, dtype=np.uint16)
b = np.zeros_like(bayer, dtype=np.uint16)
# RGGB排列:奇数行奇数列=R,偶数行奇数列=Gr,奇数行偶数列=Gb,偶数行偶数列=B
r[0::2, 0::2] = bayer[0::2, 0::2]
g[0::2, 1::2] = bayer[0::2, 1::2]
g[1::2, 0::2] = bayer[1::2, 0::2]
b[1::2, 1::2] = bayer[1::2, 1::2]
# 双线性插值填充缺失值(使用3x3均值核)
kernel = np.ones((3,3), np.float32) / 4
kernel[1,1] = 0 # 中心像素不参与自身均值
r_full = cv2.filter2D(r.astype(np.float32), -1, kernel, borderType=cv2.BORDER_REFLECT)
g_full = cv2.filter2D(g.astype(np.float32), -1, kernel, borderType=cv2.BORDER_REFLECT)
b_full = cv2.filter2D(b.astype(np.float32), -1, kernel, borderType=cv2.BORDER_REFLECT)
# 保留原始采样点值
mask_r = r > 0
mask_g = g > 0
mask_b = b > 0
r_full[mask_r] = r[mask_r]
g_full[mask_g] = g[mask_g]
b_full[mask_b] = b[mask_b]
# 合并为BGR图像
img = np.stack([b_full, g_full, r_full], axis=-1).astype(np.uint8)
return img
if __name__ == "__main__":
# 配置参数(根据实际RAW文件修改)
RAW_PATH = "test_raw.raw"
WIDTH, HEIGHT = 640, 480
# 1. 读取RAW为Bayer灰度图
bayer_img = read_raw_to_bayer(RAW_PATH, WIDTH, HEIGHT)
# 2. 去马赛克得到彩色图像
color_img = demosaic_bilinear(bayer_img)
# 3. 显示结果
cv2.imshow("Bayer RAW (灰度)", bayer_img)
cv2.imshow("Demosaiced RGB", color_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 4. 保存结果(可选验证)
cv2.imwrite("demosaiced_result.png", color_img)
运行与验证
- 准备一张RAW文件:可使用OpenCV从普通彩色图转换生成测试RAW:
import cv2, numpy as np
img = cv2.imread("test_color.png") # 任意彩色图
h, w = img.shape[:2]
# 模拟RGGB Bayer采样
bayer = np.zeros((h, w), dtype=np.uint8)
bayer[0::2, 0::2] = img[0::2, 0::2, 2] # R
bayer[0::2, 1::2] = img[0::2, 1::2, 1] # Gr
bayer[1::2, 0::2] = img[1::2, 0::2, 1] # Gb
bayer[1::2, 1::2] = img[1::2, 1::2, 0] # B
bayer.tofile("test_raw.raw")
print(f"已生成 {w}x{h} RAW文件")
- 运行主脚本:
python raw_display.py - 通过条件:弹出两个窗口——左侧为Bayer灰度图(呈马赛克纹理),右侧为去马赛克后的彩色图像,颜色与原始图基本一致,无明显错位或伪彩。
核心要点
- RAW文件无头信息,必须显式指定宽高和位深,否则无法正确reshape。
- Bayer排列决定了每个像素的原始颜色分量,去马赛克(Demosaic)是RAW转可视图像的关键步骤。
- 双线性插值是最基础的插值算法,实际ISP中会使用更复杂的边缘自适应算法以减少伪彩。
- 本案例使用RGGB排列,若RAW文件为其他排列(如BGGR),需调整通道赋值逻辑。