三种基于 PDE 的图像修复(Inpainting)模型分析与 MATLAB 实现

图像修复的核心问题是:在缺损区域 DDD 内,用周围已知信息 Ω∖D\Omega\setminus DΩ∖D 推断补全。PDE 流派从 1990s 起形成三条主线:

模型 提出者 核心思想
TV 修复 Rudin--Osher--Fatemi 推广到 inpainting (Chan--Shen 1999) 缺损区内最小化 TV 范数,$\partial_t u = \mathrm{div}\big(\frac{\nabla u}{
BSCB Bertalmio--Sapiro--Caselles--Ballester 2000 信息沿等照度线 方向向洞内输运,ut=∇⊥u⋅∇gu_t = \nabla^\perp u \cdot \nabla gut=∇⊥u⋅∇g(各向异性扩散)
CDD Chan--Kang 2000 (Curvature-Driven Diffusion) 在 TV 基础上引入曲率项,使大尺度结构 / 细长条能被"拉"进洞

一、三种模型方程对照

记缺损域为 DDD,已知域为 Ω∖D\Omega\setminus DΩ∖D,u0u_0u0 为观测图像。

1、TV 修复(Chan--Shen)

min⁡u∫Ω∣∇u∣ dxs.t. u∣Ω∖D=u0∣Ω∖D \min_u \int_{\Omega} |\nabla u| \,dx\quad \text{s.t. } u|{\Omega\setminus D}=u_0|{\Omega\setminus D} umin∫Ω∣∇u∣dxs.t. u∣Ω∖D=u0∣Ω∖D

Euler--Lagrange(加人工时间 ∂tu=−δEδu\partial_t u = -\frac{\delta E}{\delta u}∂tu=−δuδE):

∂tu=div ⁣(∇u∣∇u∣β)+λ(x)(u0−u),x∈D \partial_t u = \mathrm{div}\!\left(\frac{\nabla u}{|\nabla u|_\beta}\right) + \lambda(x)(u_0-u),\quad x\in D ∂tu=div(∣∇u∣β∇u)+λ(x)(u0−u),x∈D

  • ∣∇u∣β=∣∇u∣2+β2|\nabla u|_\beta = \sqrt{|\nabla u|^2+\beta^2}∣∇u∣β=∣∇u∣2+β2 ,β\betaβ 防除零
  • λ(x)\lambda(x)λ(x) 是指示函数:在 DDD 内为 1,已知区为 0(或松弛)
  • 直觉 :缺损区里做"最小 TV"填充,边缘会被保留成分段光滑,但对大洞/细长结构不友好------TV 偏爱短路,把细线"切"断了

2、BSCB(等照度线输运)

等照度线:{x:u(x)=c}\{x:u(x)=c\}{x:u(x)=c},其法向是 ∇u\nabla u∇u,切向是 ∇⊥u=J∇u\nabla^\perp u = J\nabla u∇⊥u=J∇u(JJJ 是 90° 旋转)。

BSCB 核心方程:

ut=∇⊥u⋅∇g=⟨∇g,  ∇⊥u⟩ u_t = \nabla^\perp u \cdot \nabla g = \langle \nabla g,\; \nabla^\perp u\rangle ut=∇⊥u⋅∇g=⟨∇g,∇⊥u⟩

  • ggg 通常是 g=∣∇u∣g=|\nabla u|g=∣∇u∣ 或高斯平滑后的梯度模,用来控制权重大小
  • 直觉:信息沿着等照度线"流"进洞------因为自然图像里等照度线=结构走向(发丝、栏杆、织物),顺着它传最合理
  • 实现关键:∇⊥u=(−uy,ux)\nabla^\perp u = (-u_y, u_x)∇⊥u=(−uy,ux)

3、CDD(曲率驱动)

TV 的扩散系数是 1/∣∇u∣1/|\nabla u|1/∣∇u∣,对平坦/陡峭都一样;但大洞需要"长距离"输运,靠曲率引导:

∂tu=div ⁣(d(κ)∇u∣∇u∣β),d(κ)=11+∣∇u∣(1+αsin⁡κπ) \partial_t u = \mathrm{div}\!\left(d(\kappa)\frac{\nabla u}{|\nabla u|_\beta}\right),\qquad d(\kappa)=\frac{1}{1+|\nabla u|}\bigl(1+\alpha\sin\frac{\kappa}{\pi}\bigr) ∂tu=div(d(κ)∣∇u∣β∇u),d(κ)=1+∣∇u∣1(1+αsinπκ)

  • κ=div(∇u/∣∇u∣)\kappa = \mathrm{div}(\nabla u/|\nabla u|)κ=div(∇u/∣∇u∣) 是曲率
  • sin⁡(κ/π)\sin(\kappa/\pi)sin(κ/π) 让凸/凹结构被"拉长"进洞
  • 直觉:曲率项让细线、长边缘在大缺损下也能连通,是 TV 在大洞场景的升级

二、MATLAB 实现(三者同框对比)

2.1 主脚本 pde_inpainting_comparison.m

matlab 复制代码
%% 三种基于 PDE 的图像修复模型对比
clear; clc; close all;

%% ===== 1. 准备图像 + 缺损掩膜 =====
% 用内置 lena 或自己图
img_orig = im2double(imread('lena.png'));  % 请自备图,或换成 cameraman
if size(img_orig,3)==3, img_orig = rgb2gray(img_orig); end

% 造缺损:文字 + 划痕
mask = ones(size(img_orig));  % 1=已知, 0=缺损
% 中央文字块
mask(120:180, 140:260) = 0;
% 一条斜划痕
for i = 1:size(mask,1)
    j = round(i*0.8 + 50);
    if j>0 && j<=size(mask,2)
        mask(max(1,i-2):min(size(mask,1),i+2), max(1,j-2):min(size(mask,2),j+2)) = 0;
    end
end

% 缺损图
img_damaged = img_orig .* mask;
img_damaged(mask==0) = 0.5;  % 缺损区填个灰色便于看

%% ===== 2. 参数 =====
params.nIter = 3000;
params.dt = 0.2;           % TV / CDD 时间步(需 CFL: dt < h^2/4 量级)
params.beta = 1e-2;        % TV 分母防零
params.alpha_cdd = 2.0;    % CDD 曲率权重
params.gauss_sigma = 1.0;  % BSCB 里 g 的高斯平滑

fprintf('缺损像素占比: %.1f%%\n', 100*sum(mask(:)==0)/numel(mask));

%% ===== 3. 分别修复 =====
tic;  u_tv = tv_inpaint(img_damaged, mask, params);  t_tv = toc;
tic;  u_bscb = bscb_inpaint(img_damaged, mask, params); t_bscb = toc;
tic;  u_cdd = cdd_inpaint(img_damaged, mask, params);  t_cdd = toc;

fprintf('TV: %.1fs, BSCB: %.1fs, CDD: %.1fs\n', t_tv, t_bscb, t_cdd);

%% ===== 4. 误差(只看缺损区,已知区强制一样)=====
mse_tv   = mean((u_tv(mask==0)   - img_orig(mask==0)).^2);
mse_bscb = mean((u_bscb(mask==0) - img_orig(mask==0)).^2);
mse_cdd  = mean((u_cdd(mask==0)  - img_orig(mask==0)).^2);
psnr = @(mse) 10*log10(1/mse);

fprintf('缺损区 MSE / PSNR:\n');
fprintf('  TV:   %.4e / %.2f dB\n', mse_tv, psnr(mse_tv));
fprintf('  BSCB: %.4e / %.2f dB\n', mse_bscb, psnr(mse_bscb));
fprintf('  CDD:  %.4e / %.2f dB\n', mse_cdd, psnr(mse_cdd));

%% ===== 5. 可视化 =====
figure('Color','w','Position',[100 100 1400 500]);
subplot(1,4,1); imshow(img_damaged); title('缺损图');
subplot(1,4,2); imshow(u_tv);   title(sprintf('TV 修复 (PSNR=%.1f)',psnr(mse_tv)));
subplot(1,4,3); imshow(u_bscb);  title(sprintf('BSCB 修复 (PSNR=%.1f)',psnr(mse_bscb)));
subplot(1,4,4); imshow(u_cdd);   title(sprintf('CDD 修复 (PSNR=%.1f)',psnr(mse_cdd)));
sgtitle('三种 PDE 图像修复对比', 'FontSize',14, 'FontWeight','bold');

% 局部放大看结构连通性
figure('Color','w');
yrange = 160:220; xrange = 100:200;
subplot(2,2,1); imshow(img_orig(yrange,xrange));   title('原图局部');
subplot(2,2,2); imshow(u_tv(yrange,xrange));      title('TV:细线易断');
subplot(2,2,3); imshow(u_bscb(yrange,xrange));    title('BSCB:等照度输运');
subplot(2,2,4); imshow(u_cdd(yrange,xrange));     title('CDD:曲率拉通');

2.2 TV 修复(分裂写成显式,好懂)

matlab 复制代码
function u = tv_inpaint(img_damaged, mask, params)
% TV inpainting: u_t = div( grad(u)/|grad(u)| ) in D, Dirichlet outside
u = img_damaged;
known = (mask > 0.5);
[h,w] = size(u);
dt = params.dt; beta = params.beta;

for iter = 1:params.nIter
    % 梯度
    [ux, uy] = gradient(u);
    norm_grad = sqrt(ux.^2 + uy.^2 + beta^2);
    
    % 散度项 div( grad/|grad| )
    px = ux ./ norm_grad;
    py = uy ./ norm_grad;
    [pxx, ~] = gradient(px);
    [~, pyy] = gradient(py);
    div = pxx + pyy;
    
    % 只在缺损区演化,已知区拉回原值
    u = u + dt * div;
    u(known) = img_damaged(known);  % 硬约束
    
    if mod(iter,1000)==0, fprintf('  TV iter %d\n',iter); end
end
end

2.3 BSCB(等照度线输运)

matlab 复制代码
function u = bscb_inpaint(img_damaged, mask, params)
% BSCB: u_t = < grad(g), J grad(u) >, J = [0 -1; 1 0]
u = img_damaged;
known = (mask > 0.5);
dt = params.dt;

for iter = 1:params.nIter
    % 梯度 + 旋转 90° = 等照度线切向
    [ux, uy] = gradient(u);
    % J grad(u) = (-uy, ux)
    Jgu = cat(3, -uy, ux);
    
    % g = |grad(u)| 高斯平滑(控制权重大小)
    g = sqrt(ux.^2 + uy.^2);
    g = imgaussfilt(g, params.gauss_sigma);
    [gx, gy] = gradient(g);
    
    % 点积 <grad(g), J grad(u)>
    rhs = gx .* Jgu(:,:,1) + gy .* Jgu(:,:,2);
    
    u = u + dt * rhs;
    u(known) = img_damaged(known);
    
    if mod(iter,1000)==0, fprintf('  BSCB iter %d\n',iter); end
end
end

2.4 CDD(曲率驱动)

matlab 复制代码
function u = cdd_inpaint(img_damaged, mask, params)
% CDD: u_t = div( d(κ) * grad(u)/|grad(u)| )
% κ = div( grad(u)/|grad(u)| )
u = img_damaged;
known = (mask > 0.5);
[h,w] = size(u);
dt = params.dt; beta = params.beta;
alpha = params.alpha_cdd;

for iter = 1:params.nIter
    [ux, uy] = gradient(u);
    norm_gu = sqrt(ux.^2 + uy.^2 + beta^2);
    
    % 单位法向 n = grad(u)/|grad(u)|
    nx = ux ./ norm_gu;
    ny = uy ./ norm_gu;
    
    % 曲率 κ = div(n)
    [nx_x, nx_y] = gradient(nx);
    [ny_x, ny_y] = gradient(ny);
    kappa = nx_x + ny_y;  % div(n)
    
    % 扩散系数 d(κ)
    dk = (1./(1+sqrt(ux.^2+uy.^2+1e-3))) .* (1 + alpha*sin(kappa/pi));
    
    % div( dk * n )
    px = dk .* nx; py = dk .* ny;
    [pxx, ~] = gradient(px);
    [~, pyy] = gradient(py);
    div = pxx + pyy;
    
    u = u + dt * div;
    u(known) = img_damaged(known);
    
    if mod(iter,1000)==0, fprintf('  CDD iter %d\n',iter); end
end
end

纯显式对 TV/CDD 的 dt 要小心:CFL 大致 dt < h²/4 ≈ 0.06(h=1 像素)。上面 dt=0.2 能跑但会稍抖,想要稳可以改 dt=0.05 或把 TV 换成 Split-Bregman(快 10× 且无条件稳)。

参考代码 分析三种基于PDE的图像修复模型 www.youwenfan.com/contentcsw/82220.html

三、三种模型对比

维度 TV BSCB CDD
物理直觉 缺损区最小 TV,边缘保边 信息沿等照度线"流"进洞 TV + 曲率项,长结构被拉通
对小缺损 很好,边缘锐 纹理走向自然 略重
对细长结构 易断(分段常数倾向) △ 靠等照度方向,但无曲率仍会散 曲率把细线"连"回来
对大洞 拉不通,出现分片平台 △ 能传但易模糊 最优
计算代价 低(可 Split-Bregman 加速) 高(曲率二阶,需小 dt)
典型场景 划痕、小文字擦除 纹理/发丝/织物修补 大缺损 + 结构连通(人脸、建筑线脚)