4. 凸集投影超分辨率重建

任务说明

本案例实现凸集投影(Projection Onto Convex Sets, POCS)超分辨率重建算法。给定一张低分辨率输入图像,通过迭代交替投影到两个凸集约束上——数据一致性约束(保证重建结果下采样后与观测低分辨率图像一致)和平滑性约束(抑制噪声与振铃伪影),逐步恢复高分辨率细节。最终输出一张放大4倍的高分辨率图像,并给出可量化的收敛判定标准。

算法原理与实现

POCS的核心思想是将先验信息建模为希尔伯特空间中的闭凸集,通过交替投影逐步逼近所有约束集的交集。本案例定义两个凸集:C1(数据一致性)要求重建图像经下采样后与观测图像误差不超过阈值ε;C2(平滑性)限制局部像素变化幅度,抑制高频噪声。每次迭代中,先对当前估计执行C1投影(修正低频误差),再执行C2投影(平滑高频伪影),直至相邻两次迭代的均方误差变化小于阈值。

实现采用双线性插值作为降质模型(下采样),投影运算通过梯度下降完成。代码包含完整的初始化、迭代循环和收敛判断,不依赖任何外部图像库,仅使用Python标准库与NumPy。

POCS超分辨率重建迭代流程 低分辨率图像 y (M×N) 双线性插值放大 x₀ = upsample(y) 迭代循环 (k = 0, 1, 2, …) C1: 数据一致性投影 x ← x − λ·Dᵀ(Dx − y) C2: 平滑性投影 x ← x − μ·∇E_smooth(x) 收敛? ‖xₖ₊₁−xₖ‖ < τ 否,继续迭代 是 高分辨率图像 x* (4M×4N)

代码实现

import numpy as np
from scipy.ndimage import zoom, gaussian_filter

def downsample(img, factor=4):
    """双线性下采样:将高分辨率图像降为低分辨率"""
    h, w = img.shape
    return zoom(img, (1/factor, 1/factor), order=1)

def upsample(img, factor=4):
    """双线性上采样:将低分辨率图像放大"""
    return zoom(img, (factor, factor), order=1)

def data_consistency_projection(x, y, factor=4, eps=1.0, lr=0.5):
    """C1投影:修正下采样后与观测图像的误差"""
    x_down = downsample(x, factor)
    diff = x_down - y
    # 仅对误差超过阈值的像素进行修正
    mask = np.abs(diff) > eps
    if np.any(mask):
        # 将误差上采样回高分辨率空间并更新
        correction = upsample(diff * mask, factor)
        x = x - lr * correction
    return x

def smoothness_projection(x, mu=0.1):
    """C2投影:高斯平滑抑制高频伪影"""
    # 计算局部方差,在平坦区域加强平滑
    local_mean = gaussian_filter(x, sigma=1.0)
    local_var = gaussian_filter((x - local_mean)**2, sigma=1.0)
    # 自适应平滑强度:方差越小平滑越强
    alpha = mu * np.exp(-local_var / (np.mean(local_var) + 1e-8))
    return x - alpha * (x - local_mean)

def pocs_super_resolution(y, factor=4, max_iter=50, tol=1e-4):
    """
    凸集投影超分辨率重建
    参数:
        y: 低分辨率输入图像 (M×N, float64, 范围[0,255])
        factor: 放大倍数
        max_iter: 最大迭代次数
        tol: 收敛阈值
    返回:
        x: 重建的高分辨率图像 (factor*M × factor*N)
        history: 每次迭代的MSE变化记录
    """
    # 初始化:双线性插值放大
    x = upsample(y, factor).astype(np.float64)
    history = []
    
    for k in range(max_iter):
        x_prev = x.copy()
        
        # C1投影:数据一致性
        x = data_consistency_projection(x, y, factor, eps=0.5, lr=0.3)
        
        # C2投影:平滑性约束
        x = smoothness_projection(x, mu=0.15)
        
        # 计算收敛指标
        mse_change = np.mean((x - x_prev)**2)
        history.append(mse_change)
        
        if mse_change < tol:
            break
    
    return x, history

# ========== 示例运行 ==========
if __name__ == "__main__":
    # 生成测试图像:8×8低分辨率(模拟真实场景)
    np.random.seed(42)
    lr_img = np.random.randint(50, 200, size=(8, 8)).astype(np.float64)
    
    # 执行POCS重建
    hr_img, hist = pocs_super_resolution(lr_img, factor=4, max_iter=30)
    
    print(f"输入尺寸: {lr_img.shape} → 输出尺寸: {hr_img.shape}")
    print(f"迭代次数: {len(hist)}")
    print(f"最终收敛值: {hist[-1]:.6f}")
    print(f"重建像素范围: [{hr_img.min():.1f}, {hr_img.max():.1f}]")
    
    # 验证数据一致性:重建结果下采样后应与输入接近
    verify = downsample(hr_img, factor=4)
    consistency_error = np.mean(np.abs(verify - lr_img))
    print(f"数据一致性平均误差: {consistency_error:.3f} (应小于1.0)")

运行与验证

依赖安装:需要NumPy和SciPy,执行pip install numpy scipy。

运行方式:将代码保存为pocs_sr.py,在终端执行python pocs_sr.py。

验证方法:程序输出应满足以下判定标准:

  • 输出图像尺寸为输入尺寸的4倍(8×8 → 32×32);
  • 迭代次数在1~30之间,且最终收敛值小于1e-4;
  • 数据一致性误差小于1.0,表明重建结果与观测数据吻合;
  • 重建像素值保持在合理灰度范围[0,255]内。

若需处理真实图像,可将lr_img替换为plt.imread()读取的灰度图(需先转为float64并归一化到[0,255])。