3. RAW域噪声特性分析

RAW域噪声特性分析 学习卡片

任务目标

本案例通过对RAW格式图像添加已知参数的高斯噪声与泊松噪声,定量分析亮度通道与色度通道的噪声分布特性,建立RAW域噪声模型的基本认知。

核心原理

RAW域噪声主要由两部分构成:光子散粒噪声(泊松分布,与信号强度相关)和读出噪声(近似高斯分布,与信号无关)。在亮度通道上,噪声随信号强度增大而增大(泊松分量主导);在色度通道上,由于色彩插值(demosaic)的邻域运算,噪声呈现空间相关性且幅度通常低于亮度噪声。本案例通过合成噪声RAW图,分别统计亮度与色度通道的噪声标准差随信号强度的变化曲线,验证上述特性。

环境依赖

  • Python 3.8+
  • numpy、scipy(仅用于读取TIFF格式RAW)
  • opencv-python(用于色彩插值)

完整实现代码

import numpy as np
import cv2

# ---------- 1. 生成无噪声RAW(Bayer BGGR,14bit) ----------
def generate_clean_raw(h=256, w=256):
    """生成平滑亮度渐变的RAW图,便于统计不同信号强度下的噪声"""
    # 亮度从暗到亮渐变(覆盖0~16383的14bit范围)
    gradient = np.linspace(0, 16383, w, dtype=np.float32)
    raw = np.tile(gradient, (h, 1))
    # 模拟彩色场景:R/G/B通道增益不同
    raw[0::2, 0::2] *= 0.8    # R位置
    raw[0::2, 1::2] *= 1.0    # Gr位置
    raw[1::2, 0::2] *= 1.0    # Gb位置
    raw[1::2, 1::2] *= 1.2    # B位置
    return raw

# ---------- 2. 添加泊松+高斯噪声 ----------
def add_raw_noise(raw, gain=1.0, sigma_read=2.0):
    """
    模拟RAW域噪声:
    - 泊松分量:光子到达率与信号强度成正比,经增益gain转换为电子数
    - 高斯分量:读出噪声,标准差sigma_read(ADU)
    """
    # 电子数 = 信号(ADU) / gain
    electrons = raw / gain
    # 泊松噪声(光子散粒噪声)
    noisy_electrons = np.random.poisson(electrons).astype(np.float32)
    # 读出噪声(高斯)
    noisy_electrons += np.random.normal(0, sigma_read, raw.shape)
    # 转回ADU
    noisy_raw = noisy_electrons * gain
    return np.clip(noisy_raw, 0, 16383)

# ---------- 3. 色彩插值(简单双线性) ----------
def demosaic_bayer(raw_bggr):
    """将Bayer BGGR格式转为RGB三通道(使用OpenCV)"""
    raw_8bit = (raw_bggr / 16383 * 255).astype(np.uint8)
    rgb = cv2.cvtColor(raw_8bit, cv2.COLOR_BayerBG2RGB)
    return rgb.astype(np.float32) / 255.0

# ---------- 4. 亮度/色度噪声统计 ----------
def analyze_noise(clean_rgb, noisy_rgb, bin_edges):
    """
    统计不同亮度区间内,亮度噪声与色度噪声的标准差
    返回: (亮度均值bin, 亮度噪声std, 色度噪声std)
    """
    # 亮度 = RGB均值,色度 = RGB与亮度之差
    clean_lum = clean_rgb.mean(axis=2)
    noisy_lum = noisy_rgb.mean(axis=2)
    lum_noise = noisy_lum - clean_lum

    clean_chroma = clean_rgb - clean_lum[:, :, np.newaxis]
    noisy_chroma = noisy_rgb - noisy_lum[:, :, np.newaxis]
    chroma_noise = noisy_chroma - clean_chroma
    chroma_noise_mag = np.sqrt((chroma_noise ** 2).sum(axis=2))

    bin_centers, lum_std, chroma_std = [], [], []
    for i in range(len(bin_edges) - 1):
        mask = (clean_lum >= bin_edges[i]) & (clean_lum < bin_edges[i + 1])
        if mask.sum() < 50:
            continue
        bin_centers.append((bin_edges[i] + bin_edges[i + 1]) / 2)
        lum_std.append(lum_noise[mask].std())
        chroma_std.append(chroma_noise_mag[mask].std())
    return np.array(bin_centers), np.array(lum_std), np.array(chroma_std)

# ---------- 5. 主流程 ----------
if __name__ == "__main__":
    np.random.seed(42)
    clean_raw = generate_clean_raw(512, 512)
    noisy_raw = add_raw_noise(clean_raw, gain=2.0, sigma_read=4.0)

    clean_rgb = demosaic_bayer(clean_raw)
    noisy_rgb = demosaic_bayer(noisy_raw)

    # 亮度归一化到0~1区间,分10个bin统计
    clean_lum_norm = clean_rgb.mean(axis=2)
    bin_edges = np.linspace(0, 1, 11)
    centers, lum_std, chroma_std = analyze_noise(clean_rgb, noisy_rgb, bin_edges)

    # 打印统计结果
    print("亮度区间中心 | 亮度噪声std | 色度噪声std")
    for c, l, ch in zip(centers, lum_std, chroma_std):
        print(f"{c:.3f}       | {l:.4f}      | {ch:.4f}")

    # 验证:亮度噪声随信号增强而增大(泊松分量),色度噪声相对平坦
    # 预期输出:亮度std从暗部约0.01增至亮部约0.05,色度std约0.01~0.02

运行方式

python raw_noise_analysis.py

验证方法与判定标准

  • 运行成功标志:程序无异常退出,打印出10行左右的统计表格。
  • 噪声特性验证:观察输出的亮度噪声std列,应呈现随亮度区间中心值增大而增大的趋势(暗部约0.01,亮部约0.04~0.06),这验证了泊松噪声分量随信号增强的特性。
  • 色度噪声对比:色度噪声std应明显小于亮度噪声std(约为其1/3~1/2),且随亮度变化幅度较小,这验证了demosaic后色度通道噪声被空间平均抑制的特性。
  • 可重复性:固定随机种子(seed=42),每次运行结果应完全一致。

关键说明

  • 本案例使用合成RAW而非真实传感器数据,目的是在已知噪声参数条件下建立定量分析基准;真实RAW噪声分析需先做黑电平校正和镜头阴影校正。
  • gain参数模拟传感器模拟增益(单位e-/ADU),gain越大则泊松噪声相对读出噪声越显著。
  • demosaic算法会改变噪声统计特性,本案例使用最简单的双线性插值,实际ISP中更复杂的插值算法会进一步改变色度噪声的空间分布。