Criminisi图像修复MATLAB实现,这是目前**纹理/结构型破损(划痕、物体移除、大面积缺损)**的经典算法,比传统的TV/Inpainting模型更擅长保持纹理连续性,也是Adobe Photoshop“内容识别填充”的早期核心算法之一。
一、Criminisi算法核心逻辑
Criminisi是基于纹理合成的优先级修复算法,核心解决“先修哪里、用什么修”两个问题:
- 优先级计算:优先修复结构明显(梯度强)且周围已知信息可靠的区域,符合人类视觉“先看轮廓、再看纹理”的习惯。
- 块匹配:从完好区域找和待修复块最相似的纹理块,复制到破损区域,保证纹理一致性。
核心公式
1. 优先级P(p)=C(p)⋅D(p)P(p) = C(p) \cdot D(p)P(p)=C(p)⋅D(p)
- C(p)C(p)C(p):置信项,衡量待修复块周围已知像素的可靠性(初始已知区域C=1C=1C=1,破损区域C=0C=0C=0)。
- D(p)D(p)D(p):数据项,衡量待修复点所在边界的结构强度(梯度在边界法向上的投影,值越大结构越明显)。
2. 置信项更新
每次修复完一个块Ψp^\Psi_{\hat{p}}Ψp^,块内新修复像素的置信度统一设为当前块的置信度C(p^)C(\hat{p})C(p^)。
二、MATLAB实现
1. 主函数:criminisi_inpaint.m
function[inpainted_img,mask]=criminisi_inpaint(img,mask,patch_size)% CRIMINISI_INPAINT 基于Criminisi算法的图像修复% img: 输入图像 (灰度图,uint8/ double)% mask: 破损掩码 (0=破损区域,255=完好区域,和img同尺寸)% patch_size: 修复块大小 (奇数,推荐9/11/15)% inpainted_img: 修复后图像% mask: 更新后的掩码%% ========== 参数初始化 ==========ifnargin<3,patch_size=9;endhalf_patch=floor(patch_size/2);max_iter=1000;% 最大迭代次数,防止死循环alpha=1e-6;% 归一化常数,避免除零% 转double便于计算ifisa(img,'uint8'),img=im2double(img);endifisa(mask,'uint8'),mask=im2double(mask);endmask=im2bw(mask,0.5);% 确保mask是二值(0/1)[rows,cols]=size(img);% 置信度矩阵:完好区域=1,破损区域=0C=double(mask);% 破损区域标记:Φ=破损,Ω=完好Phi=~mask;% 逻辑矩阵,破损为trueOmega=mask;% 逻辑矩阵,完好的为true%% ========== 预计算图像梯度 ==========% 用Sobel算子计算梯度(x/y方向)[gx,gy]=imgradientxy(img,'sobel');[~,grad_mag]=imgradient(gx,gy);% 梯度幅值grad_mag_max=max(grad_mag(:));% 梯度幅值最大值,用于归一化%% ========== 主修复循环 ==========fprintf('开始Criminisi修复...\n');iter=0;whileany(Phi(:))&&iter<max_iter iter=iter+1;% ---------- 1. 找破损区域的边界δΦ ----------% 形态学操作:腐蚀完好区域,得到边界eroded_Omega=imerode(Omega,strel('disk',1));boundary=Omega&~eroded_Omega;% 边界点:完好区域的外围[b_y,b_x]=find(boundary);% 边界点的坐标ifisempty(b_y),break;end% 无边界,修复完成% ---------- 2. 计算每个边界点的优先级 ----------P=zeros(length(b_y),1);% 优先级数组fori=1:length(b_y)p_y=b_y(i);p_x=b_x(i);% ---- 2.1 计算置信项C(p) ----% 取以p为中心的块,裁剪边界y_min=max(1,p_y-half_patch);y_max=min(rows,p_y+half_patch);x_min=max(1,p_x-half_patch);x_max=min(cols,p_x+half_patch);% 块内的已知像素的置信度平均值block_C=C(y_min:y_max,x_min:x_max);block_Omega=Omega(y_min:y_max,x_min:x_max);ifsum(block_Omega(:))==0C_p=0;elseC_p=mean(block_C(block_Omega));end% ---- 2.2 计算数据项D(p) ----% 边界法向量n:指向破损区域内部(掩码梯度的反方向)% 用中心差分计算掩码梯度ifp_x==1,nx=mask(p_y,p_x+1)-mask(p_y,p_x);elseifp_x==cols,nx=mask(p_y,p_x)-mask(p_y,p_x-1);elsenx=mask(p_y,p_x+1)-mask(p_y,p_x-1);endifp_y==1,ny=mask(p_y+1,p_x)-mask(p_y,p_x);elseifp_y==rows,ny=mask(p_y,p_x)-mask(p_y-1,p_x);elseny=mask(p_y+1,p_x)-mask(p_y-1,p_x);endn_norm=sqrt(nx^2+ny^2+eps);nx=-nx/n_norm;% 取反,指向破损内部ny=-ny/n_norm;% 图像梯度在法向量上的投影grad_dot_n=abs(gx(p_y,p_x)*nx+gy(p_y,p_x)*ny);D_p=grad_dot_n/(alpha+grad_mag_max);% 归一化% ---- 2.3 总优先级 ----P(i)=C_p*D_p;end% ---------- 3. 选优先级最高的点 ----------[~,idx]=max(P);p_hat_y=b_y(idx);p_hat_x=b_x(idx);% ---------- 4. 取以待修复点为中心的块Ψ_p̂ ----------y_min=max(1,p_hat_y-half_patch);y_max=min(rows,p_hat_y+half_patch);x_min=max(1,p_hat_x-half_patch);x_max=min(cols,p_hat_x+half_patch);patch_phi=Phi(y_min:y_max,x_min:x_max);% 块内的破损区域patch_omega=Omega(y_min:y_max,x_min:x_max);% 块内的完好区域% ---------- 5. 在完好区域搜索最佳匹配块 ----------min_ssd=inf;best_q_y=p_hat_y;best_q_x=p_hat_x;% 遍历所有完好区域的可能块中心[omega_y,omega_x]=find(Omega);fori=1:length(omega_y)q_y=omega_y(i);q_x=omega_x(i);% 块不能超出图像边界q_ymin=max(1,q_y-half_patch);q_ymax=min(rows,q_y+half_patch);q_xmin=max(1,q_x-half_patch);q_xmax=min(cols,q_x+half_patch);% 块尺寸必须和待修复块一致if(q_ymax-q_ymin)~=(y_max-y_min)||...(q_xmax-q_xmin)~=(x_max-x_min)continue;end% 计算SSD(平方差和)src_block=img(q_ymin:q_ymax,q_xmin:q_xmax);dst_block=img(y_min:y_max,x_min:x_max);ssd=sum((src_block(patch_omega)-dst_block(patch_omega)).^2,'all');ifssd<min_ssd min_ssd=ssd;best_q_y=q_y;best_q_x=q_x;best_q_ymin=q_ymin;best_q_ymax=q_ymax;best_q_xmin=q_xmin;best_q_xmax=q_xmax;endend% ---------- 6. 复制匹配块到破损区域 ----------src_block=img(best_q_ymin:best_q_ymax,best_q_xmin:best_q_xmax);% 只复制破损的部分img(y_min:y_max,x_min:x_max)=src_block.*double(patch_phi)+...img(y_min:y_max,x_min:x_max).*double(patch_omega);% ---------- 7. 更新置信度和掩码 ----------C(y_min:y_max,x_min:x_max)=C(y_min:y_max,x_min:x_max).*double(patch_omega)+...P(idx).*double(patch_phi);Phi(y_min:y_max,x_min:x_max)=false;% 修复的区域不再是破损Omega(y_min:y_max,x_min:x_max)=true;% 进度提示ifmod(iter,10)==0fprintf('迭代 %d 次,剩余破损像素: %d\n',iter,sum(Phi(:)));endendfprintf('修复完成,总迭代次数: %d\n',iter);inpainted_img=img;mask=Omega;end2. 测试脚本:demo_criminisi.m
%% demo_criminisi.m Criminisi算法示例clear;clc;close all;%% ========== 1. 准备输入图像和破损掩码 ==========% 读入测试图像(Lena图,确保路径正确)img=imread('lena.png');ifsize(img,3)==3img_gray=rgb2gray(img);% 转为灰度图,彩色图见下文说明elseimg_gray=img;end% 生成破损掩码:在图像中心创建一个50x50的矩形破损mask=ones(size(img_gray));rect_center=[size(img_gray,2)/2,size(img_gray,1)/2];% [x, y]rect_size=[50,50];% [宽, 高]x1=rect_center(1)-rect_size(1)/2;x2=rect_center(1)+rect_size(1)/2;y1=rect_center(2)-rect_size(2)/2;y2=rect_center(2)+rect_size(2)/2;mask(y1:y2,x1:x2)=0;% 破损区域设为0% 可视化破损图像broken_img=img_gray;broken_img(~mask)=0;% 破损区域显示为黑色%% ========== 2. 运行Criminisi修复 ==========patch_size=9;% 修复块大小,奇数,推荐9/11/15[inpainted_img,~]=criminisi_inpaint(img_gray,mask,patch_size);%% ========== 3. 结果可视化 ==========figure('Color','white','Position',[1001001200400])subplot(1,3,1)imshow(img_gray)title('原始图像')subplot(1,3,2)imshow(broken_img)title('破损图像(中心矩形区域)')subplot(1,3,3)imshow(inpainted_img)title(sprintf('Criminisi修复结果(块大小=%d)',patch_size))%% ========== 4. (可选)计算修复质量指标 ==========% 如果有原始完整图像,可以计算PSNRpsnr_val=psnr(inpainted_img,img_gray);fprintf('修复图像PSNR: %.2f dB\n',psnr_val);参考代码 图像修复算法www.youwenfan.com/contentcsw/82890.html
三、关键参数说明
| 参数 | 作用 | 推荐值 |
|---|---|---|
patch_size | 修复块大小 | 9/11/15(纹理越丰富,块越大;结构越简单,块越小) |
max_iter | 最大迭代次数 | 1000(破损区域大时适当增大) |
| 掩码制作 | 定义破损区域 | 用画图工具手动标注,或代码生成(矩形/圆形/自定义形状) |
四、彩色图像修复方法
上面的代码是灰度图版本,彩色图像修复有两种常用方案:
方案1:逐通道修复(简单但可能颜色失真)
img_color=imread('lena.png');mask=imread('mask.png');inpainted_color=zeros(size(img_color));forc=1:3[inpainted_color(:,:,c),~]=criminisi_inpaint(img_color(:,:,c),mask,9);end方案2:HSV空间修复(推荐,颜色更自然)
img_hsv=rgb2hsv(img_color);% 只在V(明度)通道修复,H(色调)和S(饱和度)保持不变[V_inpainted,~]=criminisi_inpaint(img_hsv(:,:,3),mask,9);img_hsv(:,:,3)=V_inpainted;inpainted_color=hsv2rgb(img_hsv);五、算法优缺点
优点
- 擅长修复大面积纹理/结构破损(如移除照片中的路人、修复划痕)
- 能保持结构的连续性,修复结果更符合人类视觉习惯
- 无需训练数据,通用性强
缺点
- 计算量大(全图搜索匹配块,512x512图像约需1-5分钟)
- 平滑区域容易出现块效应
- 对复杂语义(如人脸五官)修复效果差
六、与其他修复算法的对比
| 算法 | 适用场景 | 优缺点 |
|---|---|---|
| Criminisi | 大面积纹理/结构破损 | 结构保持好,速度慢 |
| TV-Inpainting | 小面积平滑区域破损 | 速度快,纹理模糊 |
| PatchMatch | 纹理合成 | 速度快,结构保持弱 |
| 深度学习(LaMa/Stable Diffusion) | 复杂语义破损 | 效果好,需要训练数据 |
如果你的破损区域是不规则形状(如划痕、文字遮挡),可以告诉我,我帮你修改掩码生成代码;如果是彩色图像/实时性要求高,也可以给你对应的优化版本。