4. 基于注意力机制的RAW去马赛克

任务目标

在RAW域去马赛克(Demosaicing)任务中,将拜耳阵列(Bayer Pattern)的RAW图恢复为全彩RGB图。本案例要求在基础U-Net去马赛克网络中引入通道注意力模块(Channel Attention),使网络能够自适应地重新校准各特征通道的权重,从而提升颜色恢复精度。

环境与依赖

Python 3.8+,PyTorch 1.10+,NumPy。数据集使用Kodak或McMaster标准测试图,按标准Bayer RGGB模式采样生成RAW输入。

核心实现

以下代码定义了一个轻量级去马赛克网络,包含编码器-解码器结构,并在跳跃连接后插入通道注意力模块(SE Block)。输入为4通道(RGGB)打包的RAW数据,输出为3通道RGB图像。

import torch
import torch.nn as nn
import torch.nn.functional as F

class ChannelAttention(nn.Module):
    """Squeeze-and-Excitation通道注意力模块"""
    def __init__(self, in_channels, reduction=8):
        super().__init__()
        self.avg_pool = nn.AdaptiveAvgPool2d(1)
        self.fc = nn.Sequential(
            nn.Linear(in_channels, in_channels // reduction),
            nn.ReLU(inplace=True),
            nn.Linear(in_channels // reduction, in_channels),
            nn.Sigmoid()
        )

    def forward(self, x):
        b, c, _, _ = x.size()
        y = self.avg_pool(x).view(b, c)
        y = self.fc(y).view(b, c, 1, 1)
        return x * y

class DoubleConv(nn.Module):
    def __init__(self, in_ch, out_ch):
        super().__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(in_ch, out_ch, 3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_ch, out_ch, 3, padding=1),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        return self.conv(x)

class DemosaicNet(nn.Module):
    """带通道注意力的U-Net去马赛克网络"""
    def __init__(self, in_channels=4, out_channels=3, features=32):
        super().__init__()
        # 编码器
        self.enc1 = DoubleConv(in_channels, features)
        self.enc2 = DoubleConv(features, features * 2)
        self.enc3 = DoubleConv(features * 2, features * 4)
        self.pool = nn.MaxPool2d(2)

        # 瓶颈
        self.bottleneck = DoubleConv(features * 4, features * 8)

        # 解码器
        self.up3 = nn.ConvTranspose2d(features * 8, features * 4, 2, stride=2)
        self.dec3 = DoubleConv(features * 8, features * 4)
        self.up2 = nn.ConvTranspose2d(features * 4, features * 2, 2, stride=2)
        self.dec2 = DoubleConv(features * 4, features * 2)
        self.up1 = nn.ConvTranspose2d(features * 2, features, 2, stride=2)
        self.dec1 = DoubleConv(features * 2, features)

        # 通道注意力(插入在解码器每个阶段之后)
        self.ca3 = ChannelAttention(features * 4)
        self.ca2 = ChannelAttention(features * 2)
        self.ca1 = ChannelAttention(features)

        self.out_conv = nn.Conv2d(features, out_channels, 1)

    def forward(self, x):
        # 编码
        e1 = self.enc1(x)
        e2 = self.enc2(self.pool(e1))
        e3 = self.enc3(self.pool(e2))

        # 瓶颈
        b = self.bottleneck(self.pool(e3))

        # 解码 + 跳跃连接 + 通道注意力
        d3 = self.up3(b)
        d3 = torch.cat([d3, e3], dim=1)
        d3 = self.dec3(d3)
        d3 = self.ca3(d3)

        d2 = self.up2(d3)
        d2 = torch.cat([d2, e2], dim=1)
        d2 = self.dec2(d2)
        d2 = self.ca2(d2)

        d1 = self.up1(d2)
        d1 = torch.cat([d1, e1], dim=1)
        d1 = self.dec1(d1)
        d1 = self.ca1(d1)

        return self.out_conv(d1)

def pack_raw_bayer(rgb, pattern='RGGB'):
    """将RGB图像按Bayer模式打包为4通道输入"""
    # rgb: [B, 3, H, W] 假设H,W为偶数
    b, c, h, w = rgb.shape
    # 按RGGB排列提取
    r = rgb[:, 0:1, 0::2, 0::2]  # R在(0,0)
    g1 = rgb[:, 1:2, 0::2, 1::2]  # G在(0,1)
    g2 = rgb[:, 1:2, 1::2, 0::2]  # G在(1,0)
    b_ = rgb[:, 2:3, 1::2, 1::2]  # B在(1,1)
    return torch.cat([r, g1, g2, b_], dim=1)

def train_step(model, raw, target, optimizer, criterion):
    """单步训练"""
    model.train()
    optimizer.zero_grad()
    output = model(raw)
    loss = criterion(output, target)
    loss.backward()
    optimizer.step()
    return loss.item()

def psnr(img1, img2, max_val=1.0):
    """计算PSNR"""
    mse = F.mse_loss(img1, img2)
    return 10 * torch.log10(max_val**2 / mse)

训练与验证

# 数据准备(示意)
# rgb_gt: [B, 3, H, W] 范围[0,1]
raw_input = pack_raw_bayer(rgb_gt)  # [B, 4, H/2, W/2]

# 模型初始化
model = DemosaicNet(in_channels=4, out_channels=3, features=32)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
criterion = nn.L1Loss()

# 训练循环(示意)
for epoch in range(50):
    for batch in dataloader:
        raw, target = batch
        loss = train_step(model, raw, target, optimizer, criterion)

# 验证
model.eval()
with torch.no_grad():
    output = model(raw_input)
    score = psnr(output, rgb_gt)
    print(f'PSNR: {score.item():.2f} dB')

验证方法与通过标准

  1. 数据准备:从Kodak数据集中选取至少10张图像,裁剪为256×256,按RGGB模式打包为4通道输入。
  2. 训练配置:batch size=8,学习率1e-3,训练50个epoch,使用Adam优化器与L1损失。
  3. 评估指标:在测试集上计算PSNR(峰值信噪比)与ΔE(色差)。
  4. 通过条件:与不含注意力模块的基线U-Net相比,PSNR提升至少0.3dB,且ΔE降低。若PSNR达到28dB以上(Kodak数据集),视为颜色恢复精度达标。

关键说明

通道注意力模块通过全局平均池化获取通道描述符,经两个全连接层学习通道间非线性关系,再用Sigmoid生成0~1的权重,对原始特征进行重标定。该机制使网络能抑制无关颜色通道的噪声响应,增强对边缘和纹理区域的颜色判别力。本实现将注意力模块置于解码器每个阶段之后,在融合跳跃连接特征后立即进行通道重标定,实验表明该位置比置于编码器或瓶颈处效果更优。