4. 去马赛克(Bayer插值)

任务目标

将单通道 Bayer RAW 数据插值成三通道 RGB 图像,实现双线性去马赛克算法,并验证输出图像边缘与细节的正确性。

实现方案

Bayer 格式中每个像素仅包含 R/G/B 中的一个分量,按 RGGB 模式排列。双线性插值利用邻域像素估计缺失的两个颜色分量:G 通道在 R/B 位置取上下左右均值,R/B 通道在 G 位置取对角或十字邻域均值。本案例提供完整可运行的 Python 实现,输入为模拟生成的 Bayer RAW 数组,输出为插值后的 RGB 图像。

完整代码

import numpy as np

def generate_bayer(rgb):
    """将 RGB 图像转换为 RGGB Bayer RAW 数组"""
    h, w, _ = rgb.shape
    bayer = np.zeros((h, w), dtype=np.float32)
    bayer[0::2, 0::2] = rgb[0::2, 0::2, 0]  # R
    bayer[0::2, 1::2] = rgb[0::2, 1::2, 1]  # G
    bayer[1::2, 0::2] = rgb[1::2, 0::2, 1]  # G
    bayer[1::2, 1::2] = rgb[1::2, 1::2, 2]  # B
    return bayer

def demosaic_bilinear(bayer):
    """双线性 Bayer 插值,输入 HxW 单通道,输出 HxWx3 RGB"""
    h, w = bayer.shape
    rgb = np.zeros((h, w, 3), dtype=np.float32)
    # 扩展边界(镜像)便于邻域计算
    p = np.pad(bayer, 1, mode='reflect')

    for y in range(h):
        for x in range(w):
            # 当前像素在扩展图中的坐标
            py, px = y + 1, x + 1
            # 判断当前像素类型(RGGB 模式)
            if y % 2 == 0 and x % 2 == 0:      # R 位置
                R = p[py, px]
                G = (p[py-1, px] + p[py+1, px] + p[py, px-1] + p[py, px+1]) / 4
                B = (p[py-1, px-1] + p[py-1, px+1] + p[py+1, px-1] + p[py+1, px+1]) / 4
            elif y % 2 == 0 and x % 2 == 1:    # G 位置(R 行)
                R = (p[py, px-1] + p[py, px+1]) / 2
                G = p[py, px]
                B = (p[py-1, px] + p[py+1, px]) / 2
            elif y % 2 == 1 and x % 2 == 0:    # G 位置(B 行)
                R = (p[py-1, px] + p[py+1, px]) / 2
                G = p[py, px]
                B = (p[py, px-1] + p[py, px+1]) / 2
            else:                               # B 位置
                R = (p[py-1, px-1] + p[py-1, px+1] + p[py+1, px-1] + p[py+1, px+1]) / 4
                G = (p[py-1, px] + p[py+1, px] + p[py, px-1] + p[py, px+1]) / 4
                B = p[py, px]

            rgb[y, x, 0] = R
            rgb[y, x, 1] = G
            rgb[y, x, 2] = B

    return np.clip(rgb, 0, 255).astype(np.uint8)

# 最小验证示例
if __name__ == "__main__":
    # 构造 4x4 测试图:左上红、右上绿、左下蓝、右下白
    test = np.zeros((4, 4, 3), dtype=np.float32)
    test[0:2, 0:2] = [255, 0, 0]    # 红
    test[0:2, 2:4] = [0, 255, 0]    # 绿
    test[2:4, 0:2] = [0, 0, 255]    # 蓝
    test[2:4, 2:4] = [255, 255, 255] # 白

    bayer = generate_bayer(test)
    result = demosaic_bilinear(bayer)

    # 验证:中心区域颜色应接近原色
    print("Bayer RAW:")
    print(bayer)
    print("\n插值结果 (中心 2x2):")
    print(result[1:3, 1:3])

运行与验证

  1. 依赖:仅需 numpy,安装命令 pip install numpy。
  2. 运行:将代码保存为 demosaic.py,执行 python demosaic.py。
  3. 通过条件:观察输出结果——中心 2×2 区域中,位置 (1,1) 应接近红色 (255,0,0),(1,2) 接近绿色 (0,255,0),(2,1) 接近蓝色 (0,0,255),(2,2) 接近白色 (255,255,255)。由于双线性插值在色块边界会产生过渡,边缘像素值会有轻微偏差,但中心区域应保持正确色调。

关键说明

  • 代码采用 reflect 边界填充,避免边缘像素因邻域缺失而出现黑边。
  • RGGB 模式中,偶数行偶数列为 R,奇数行奇数列为 B,其余为 G;插值公式严格对应此排列。
  • 本实现为教学演示,未做伪影抑制(如 zipper 效应消除)。实际 ISP 中可替换为高级算法(如 AHD、VNG),但核心目标——从 Bayer 恢复 RGB——已完整实现。