图像传感器基础:CMOS 与 CCD 传感器、彩色滤波阵列(CFA)、Bayer 阵列详解

做去马赛克算法,第一件事不是调模型,而是搞懂图像传感器是怎么「看」颜色的。我早年踩过一个坑——直接用 RGB 三通道的数据训练网络,结果效果奇差。后来才发现,传感器压根没给我完整的 RGB,它只给了一个单通道的拜耳阵列。嗯,今天我们就从最底层开始,把这件事彻底讲清楚。

2.1 两种主流传感器:CCD 与 CMOS

先说说传感器本身。目前市面上主流的图像传感器就两种:CCD 和 CMOS。它们干的活一样——把光信号转成电信号,但实现方式完全不同。

对比维度 CCD(电荷耦合器件) CMOS(互补金属氧化物半导体)
读出方式 逐行转移,统一输出 每个像素独立读出
功耗 较高(需要多组电压驱动) 低(单电压供电)
噪声 低(读出噪声小) 较高(像素级放大器引入噪声)
速度 较慢(串行读出) 快(并行读出,支持高帧率)
成本 高(工艺复杂) 低(标准 CMOS 工艺)
主流应用 科研、天文、高端工业 手机、相机、安防、自动驾驶

我个人习惯用 CMOS 传感器做项目。为什么?说白了,现在深度学习去马赛克面对的主要数据源就是手机和消费级相机,这些设备清一色全是 CMOS。CCD 虽然画质干净,但帧率低、功耗高,不适合实时处理。

我的经验: 如果你在实验室用 CCD 采集的数据训练模型,部署到手机 CMOS 上大概率会翻车。两者的噪声分布完全不同——CMOS 有更明显的行噪声和固定模式噪声(FPN),去马赛克网络必须适应这种噪声特性。

2.2 彩色滤波阵列(CFA)——传感器为什么是「色盲」的?

你可能会问:传感器不是能感知颜色吗?其实单个像素只能感知光的强度,它分不清红绿蓝。那彩色照片怎么来的?答案是——在传感器表面盖一层彩色滤波阵列(CFA)。

每个像素上方只放一种颜色的滤光片,让这个像素只响应特定波段的光。这样一来,传感器输出的就是一张单通道的马赛克图像,每个位置只记录了 R、G、B 中的一种。

核心理解: 传感器输出的原始数据(RAW)不是 RGB 三通道图像,而是一张单通道的 CFA 图像。去马赛克算法的任务,就是从这张单通道图恢复出完整的 RGB 三通道图。

我曾经见过刚入行的同学直接把 RAW 数据当成灰度图丢进网络,结果训练出来的模型输出全是偏色的。嗯,这里要记住:RAW 数据虽然看起来像灰度图,但每个像素的「含义」不同——有的代表红色亮度,有的代表绿色亮度,有的代表蓝色亮度。

2.3 Bayer 阵列——最经典的 CFA 模式

CFA 有很多种排列方式,但最最常用的就是 Bayer 阵列。它由 Bryce Bayer 在 1976 年发明,到今天仍然是主流。

Bayer 阵列的排列规则很简单:

  • 每 2×2 的像素块中,有 2 个绿色(G)、1 个红色(R)、1 个蓝色(B)
  • 绿色占 50%,红色和蓝色各占 25%
  • 排列方式有两种变体:RGGB 和 BGGR

为什么绿色多一倍?因为人眼对绿色最敏感,绿色通道携带了最多的亮度信息。你想想看,一张彩色图转成灰度图,绿色通道的权重本来就最高。

注意: 不同相机厂商的 Bayer 排列起始位置可能不同。比如索尼常用 RGGB,有些手机传感器用 BGGR。如果你的去马赛克网络写死了排列方式,换一个传感器数据就直接崩了。我建议在数据预处理阶段就做归一化——统一转成 RGGB 排列。

2.4 Bayer 阵列的数学表示

假设传感器分辨率为 H×W,那么 RAW 数据可以表示为:

# 以 RGGB 排列为例
# 坐标 (i, j) 处的像素值
# i 为行索引,j 为列索引

if i % 2 == 0:          # 偶数行
    if j % 2 == 0:      # 偶数列 → 红色
        pixel = R(i, j)
    else:               # 奇数列 → 绿色
        pixel = G(i, j)
else:                   # 奇数行
    if j % 2 == 0:      # 偶数列 → 绿色
        pixel = G(i, j)
    else:               # 奇数列 → 蓝色
        pixel = B(i, j)

这个模式看起来简单,但实现去马赛克时,你必须精确知道每个像素的「颜色身份」。我习惯在代码里用一个 mask 矩阵来标记每个位置的颜色通道:

import numpy as np

def generate_bayer_mask(height, width, pattern='RGGB'):
    """生成 Bayer 掩码:0=R, 1=G, 2=B"""
    mask = np.zeros((height, width), dtype=np.uint8)
    
    if pattern == 'RGGB':
        mask[0::2, 0::2] = 0  # R
        mask[0::2, 1::2] = 1  # G
        mask[1::2, 0::2] = 1  # G
        mask[1::2, 1::2] = 2  # B
    elif pattern == 'BGGR':
        mask[0::2, 0::2] = 2  # B
        mask[0::2, 1::2] = 1  # G
        mask[1::2, 0::2] = 1  # G
        mask[1::2, 1::2] = 0  # R
    
    return mask
实用技巧: 训练去马赛克网络时,我通常把 mask 作为输入的一部分喂给网络。这样网络能明确知道每个位置缺什么颜色,学习效率高很多。

2.5 其他 CFA 模式简介

Bayer 虽然主流,但不是唯一。还有一些其他 CFA 模式,各有特点:

CFA 类型 排列特点 优势 劣势
Bayer (RGGB) 2×2 块,G 占 50% 简单、成熟、兼容性好 高频细节有伪色
X-Trans (富士) 6×6 块,不规则排列 减少摩尔纹 去马赛克算法复杂
RGBW 加入白色像素 (W) 低光性能好 色彩还原难度大
Quad Bayer 4×4 块,4 个同色像素一组 支持高像素合并 分辨率损失

我个人建议初学者先死磕 Bayer。把 Bayer 上的去马赛克做透了,其他模式无非是换一个排列规则。我在项目中遇到过 X-Trans 的数据,当时直接把它重采样成 Bayer 格式再处理,省了很多麻烦。

2.6 本章知识结构图

下面这张图总结了图像传感器到去马赛克的核心流程:

图像传感器与 CFA 核心流程 图像传感器 → CFA → Bayer 阵列 → 去马赛克 CCD 传感器 低噪声 / 高成本 CMOS 传感器 低功耗 / 高帧率 彩色滤波阵列 (CFA) 每个像素只记录一种颜色 Bayer 阵列(最常用 CFA) 2×2 块:R + 2G + B 绿色占 50%,红蓝各 25% RAW 数据(单通道马赛克图) 每个像素只有 R/G/B 中的一个值 去马赛克算法(Demosaicing) 从单通道 RAW 恢复完整 RGB 三通道图像 关键点 传感器是色盲 CFA 是彩色眼镜

2.7 避坑指南

最后分享几个我实际项目中踩过的坑:

  • 不要忽略黑电平校正: RAW 数据通常有一个黑电平偏移(black level),不校正的话去马赛克结果会偏色。我习惯先减掉黑电平再归一化。
  • 注意 Bayer 排列的起始位置: 不同相机可能从 (0,0) 开始是 R 或 B,写代码时一定要确认。我曾经因为这个原因,一个模型在 A 相机上效果很好,换到 B 相机直接崩了。
  • 绿色通道的两种位置要区分: Bayer 阵列中 G 有两种位置——R 行和 B 行。有些去马赛克算法对这两种 G 分别处理,效果更好。
  • 训练数据要模拟真实 CFA: 不要直接用 RGB 图像训练,一定要先模拟 CFA 采样过程,生成 RAW 数据再训练。否则模型没见过真实的马赛克模式,推理时效果会打折扣。
一句话总结: 图像传感器输出的是单通道 CFA 马赛克图,不是 RGB 三通道图。去马赛克算法的本质,就是从稀疏的彩色采样中重建完整的彩色图像。搞懂 Bayer 阵列的排列规则,是做好去马赛克的第一步。