
简介这份资源围绕Criminisi图像修复算法的改进实现展开面向图像处理、图像修复方向的研究人员与开发者尤其适合需要在Matlab环境下验证算法效果、评估修复质量的学习者。其核心思路是以P-Laplacian算子替代原始算法中的数据项从而提升优先级计算的精确度增强边缘保持能力并减少噪声传播。压缩包共5个文件包含3个m脚本、1个c源码与1个dll动态库整体约6KB其中m文件承担主流程与PSNR计算c与dll文件支撑底层辅助逻辑。资源提供了可直接运行的修复流程与PSNR定量评估功能便于读者对比改进前后的修复效果理解优先级计算、边界处理与性能优化等关键环节。目前已有212人学习下载适合作为图像修复算法入门与改进实验的参考素材。1. 拆开 p-laplace.rar一份把 Criminisi 数据项换成 P-Laplacian 的 MATLAB 修复包图像修复里有个绕不开的经典Criminisi 算法。2004 年那篇论文把「先补哪块、从哪块抄」拆成优先级排序加样本块匹配两步成了后来一大票修复方法的基线。但基线归基线真拿它去补一条穿过纹理区域的划痕边缘经常糊成一团噪声还会顺着补丁往里爬。p-laplace.rar 这份资源干的事就是把原算法里算优先级的那个数据项从普通梯度换成 P-Laplacian 算子让边缘和细节在填充过程中被更狠地保住。包里是 MATLAB 实现核心文件是 Demo_inpaint.m、inpaint.m、bestexemplarhelper.c 及其编译产物 bestexemplarhelper.dll外加一个 PSNR.m 用来量化修复质量。适合正在做图像修复、去划痕、老照片修补或者拿 Criminisi 当对比基线的人——你不需要从零复现论文但得知道它改了哪、怎么跑、哪里会翻车。2. Criminisi 的优先级机制与 P-Laplacian 到底改了什么2.1 原版 Criminisi 的优先级公式拆解Criminisi 的核心是给破损区域边界上每个待补像素算一个优先级 P(p)挑最高的先补。公式是 P(p) C(p) · D(p)其中 C(p) 是置信度项衡量这个像素周围有多少已知信息D(p) 是数据项衡量等照度线isophote在边界上的强度说白了就是「这个位置有没有明显的边缘方向」。数据项 D(p) 的原始定义是D(p) |∇I⊥(p) · n(p)| / α∇I⊥(p) 是 p 点梯度的正交方向等照度线方向n(p) 是边界法向量α 是归一化因子灰度图取 255。这个式子的物理含义是如果等照度线方向和边界法向越接近垂直说明这里有强边缘穿过应该优先补这样纹理和结构才能延续进去。问题出在 ∇I 上。原版用的是普通梯度对噪声敏感而且在线性结构交叉或者光照不均的区域梯度方向容易算歪。一旦 D(p) 算错优先级排序就乱补丁顺序跟着错最后修复结果要么边缘断裂要么纹理错位。2.2 P-Laplacian 算子为什么能顶替梯度P-Laplacian 算子的定义是Δ_p u div(|∇u|^(p-2) ∇u)当 p2 时退化成普通 Laplacianp2 时对梯度大的区域边缘惩罚更重p2 时对梯度小的区域平滑区更敏感。在图像修复场景里通常取 p 在 1 到 2 之间目的是让算子在平坦区域做各向同性扩散在边缘附近做各向异性扩散——也就是沿着边缘方向扩散、垂直边缘方向抑制。把它塞进 Criminisi 的数据项本质是用 P-Laplacian 的扩散通量替代原始梯度方向让优先级计算对边缘的判定更稳。具体来说改进后的数据项不再直接取 ∇I⊥而是用 P-Laplacian 在局部窗口内的响应来加权邻域信息。这样做的直接好处是噪声引起的伪梯度被压制真实边缘的响应被放大优先级排序更贴近人眼期望的「先补结构、再补纹理」。2.3 包里文件的分工与调用关系拿到压缩包先别急着跑把文件角色理清楚文件类型作用Demo_inpaint.m脚本入口读图、设掩膜、调 inpaint、算 PSNRinpaint.m函数Criminisi 主循环含优先级计算和样本块搜索bestexemplarhelper.cC 源码样本块匹配的加速实现bestexemplarhelper.dll编译产物Windows 下供 MATLAB 调用的 MEX 文件PSNR.m函数计算修复前后峰值信噪比调用链是 Demo_inpaint → inpaint → bestexemplarhelper通过 MEX 接口。如果你在非 Windows 平台dll 用不了得自己用 mex 重新编译 bestexemplarhelper.c这一步后面避坑章节会细说。2.4 优先级计算里 P-Laplacian 的接入点在 inpaint.m 里找优先级计算那段通常会看到一个循环遍历破损边界上的像素。原版代码大概长这样% 原版数据项计算示意 [Ix, Iy] gradient(img); grad_perp [-Iy, Ix]; % 梯度正交方向 normal compute_normal(mask, p); D abs(sum(grad_perp .* normal)) / 255;改进版会把 gradient 那步换成 P-Laplacian 响应% P-Laplacian 加权后的数据项示意 p 1.5; % P-Laplacian 阶数常用 1.2~1.8 [Ix, Iy] gradient(img); grad_mag sqrt(Ix.^2 Iy.^2) eps; weight grad_mag.^(p - 2); % P-Laplacian 非线性权重 Ix_w weight .* Ix; Iy_w weight .* Iy; grad_perp [-Iy_w, Ix_w]; normal compute_normal(mask, p); D abs(sum(grad_perp .* normal)) / 255;逻辑说明weight 这一项就是 P-Laplacian 的核心。当 p2 时grad_mag 越小平坦区weight 越大扩散越强grad_mag 越大边缘weight 越小扩散被抑制。这样算出来的 grad_perp 在边缘处更干净不会被噪声带偏。参数 p 是唯一需要调的默认 1.5 是个稳妥起点纹理密集的图可以往 1.2 压结构为主的图可以往 1.8 提。3. 跑通 Demo从掩膜制作到 PSNR 输出3.1 环境准备与 MEX 编译MATLAB 版本没有硬性要求但 bestexemplarhelper.dll 是预编译的位数必须和你的 MATLAB 一致。64 位 MATLAB 配 64 位 dll32 位配 32 位混了直接报「invalid MEX-file」。如果你在 Windows 上且 dll 能用跳过编译否则在 MATLAB 命令行执行% 编译 MEX 文件需提前配置好 C 编译器 mex -O bestexemplarhelper.c-O 开优化编译完会生成 bestexemplarhelper.mexw64Windows或 .mexa64Linux。如果报「No supported compiler」去 MATLAB 的附加功能里装一个 MinGW-w64 编译器这是常见做法装完再跑一次 mex -setup 选它。3.2 制作掩膜决定修复范围的关键输入Criminisi 需要一个二值掩膜白色1表示待修复区域黑色0表示已知区域。掩膜质量直接决定修复效果别拿画图工具随手涂。常见做法是用阈值分割加形态学处理% 从原图生成掩膜示例假设修复区域是亮色划痕 img imread(damaged.png); gray rgb2gray(img); mask gray 200; % 阈值提取亮色划痕 mask imdilate(mask, strel(disk, 2)); % 膨胀 2 像素留余量 imwrite(mask, mask.png);参数说明阈值 200 是经验值具体看划痕亮度和背景对比度可以用 imtool 交互看灰度直方图定。膨胀半径 2 是防止掩膜边缘刚好压在真实边缘上导致修复时把好像素也覆盖掉。掩膜宁可稍微大一点也别漏掉破损边缘。3.3 调用 inpaint 主函数Demo_inpaint.m 里通常已经写好了调用模板核心就几行% 主修复调用 img imread(damaged.png); mask imread(mask.png); mask logical(mask); patch_size 9; % 样本块大小奇数 result inpaint(img, mask, patch_size); imwrite(result, restored.png); psnr_val PSNR(img, result, mask); fprintf(PSNR %.2f dB\n, psnr_val);逻辑说明patch_size 是样本块边长9 是 Criminisi 论文里的推荐值。块太小纹理匹配容易碎块太大结构延续会拖沓。inpaint 内部会先算优先级、再搜最佳样本块、然后拷贝像素并更新置信度循环直到掩膜区域填满。PSNR 计算时注意第三个参数 mask——只算修复区域的 PSNR 才有意义全图算会被未修改区域拉高。3.4 PSNR 评估的正确用法PSNR.m 的调用签名一般是 PSNR(original, restored, mask)。如果你手上没有无损原图PSNR 就没法算这时候只能靠目视评估。有原图的情况下修复区域的 PSNR 能到 30 dB 以上算不错28 以下基本能看出明显瑕疵。但别迷信 PSNR它和主观质量的相关性有限边缘糊没糊、纹理断没断还是得放大看。提示跑 Demo 前先把 MATLAB 当前目录切到解压后的文件夹否则 inpaint 找不到 bestexemplarhelper会报 undefined function。4. 避坑与排查dll 报错、掩膜越界、优先级反转4.1 MEX 文件加载失败现象运行 inpaint 时报「Invalid MEX-file bestexemplarhelper.mexw64: 找不到指定的模块」或「不是有效的 Win32 应用程序」。原因dll 位数和 MATLAB 不匹配或者缺少运行时依赖库。预编译的 dll 可能链接了特定版本的 MSVCRT。解决先确认 MATLAB 位数computer(arch)然后自己用 mex 重新编译 bestexemplarhelper.c。编译前装好 MinGW-w64mex -setup 选它。如果还报缺库用 Dependency Walker 查 dll 依赖补上对应运行库。4.2 掩膜全白或全黑导致死循环现象程序卡住不结束或者瞬间跑完但结果没变化。原因掩膜全白意味着所有像素都待修复没有已知信息可用优先级计算里置信度全为零样本块搜索找不到有效源掩膜全黑则循环条件直接不成立。解决跑之前用 sum(mask(:)) 检查掩膜像素数确保既不为 0 也不等于图像总像素数。正常掩膜占比在 5% 到 30% 之间比较合理超过 50% 修复质量会急剧下降这不是算法问题是信息量不够。4.3 优先级反转导致结构断裂现象修复结果里长边缘补到一半突然拐弯或者纹理方向错乱。原因P-Laplacian 的 p 参数设得不对。p 太大接近 2退化成普通 Laplacian边缘保持效果消失p 太小接近 1扩散过强平坦区被过度平滑优先级排序被噪声主导。解决p 从 1.5 开始试每次调 0.1观察边缘连续性。另外检查 inpaint.m 里 P-Laplacian 权重那行有没有加 eps没加的话 grad_mag 为零时会出现 Inf 或 NaN直接污染整个优先级矩阵。4.4 PSNR 计算出负值或异常大现象PSNR 输出 -Inf 或者 100 以上。原因-Inf 通常是修复结果和原图在掩膜区域完全一致MSE0或者数据类型不匹配导致减法溢出。异常大则是掩膜区域选错了比如把未修复区域也算进去。解决确认 PSNR.m 里先把图像转成 double 再算 MSEuint8 相减会截断。检查掩膜是不是 logical 类型double 类型的掩膜在索引时会被当成数值而不是逻辑导致区域选错。4.5 大图跑不动或内存爆掉现象处理 1024×1024 以上的图时 MATLAB 卡死或报 Out of Memory。原因Criminisi 的样本块搜索是暴力匹配复杂度随图像尺寸和掩膜面积平方增长加上 P-Laplacian 权重计算额外开销内存占用比原版高。解决先把图降采样到 512 宽跑通流程确认参数后再上原图。或者把掩膜分成若干连通区域逐块修复再合并。bestexemplarhelper.c 本身就是加速用的确保它被正确调用别退回到纯 MATLAB 的慢速搜索。5. 进阶调参与验证把 P-Laplacian 阶数当成旋钮来拧跑通 Demo 只是起点这份资源真正的价值在于 P-Laplacian 阶数 p 给了你一个连续可调的边缘保持旋钮。我一般会做一组对比实验固定掩膜和 patch_size让 p 从 1.1 到 1.9 步进 0.2每个值跑一遍把修复区域的 PSNR 和目视效果都记下来。% p 参数扫描脚本 p_list 1.1:0.2:1.9; psnr_results zeros(size(p_list)); for i 1:length(p_list) result inpaint(img, mask, 9, p, p_list(i)); psnr_results(i) PSNR(img, result, mask); imwrite(result, sprintf(restored_p%.1f.png, p_list(i))); end plot(p_list, psnr_results, -o); xlabel(P-Laplacian order p); ylabel(PSNR (dB));这段脚本假设 inpaint 支持名值对参数传 p如果你的版本里 p 是硬编码的就去 inpaint.m 里把它提成函数参数。跑完看曲线通常会看到一个先升后降的趋势峰值对应的 p 就是这张图的最优值。纹理密集的图峰值偏左p 小结构为主的图峰值偏右p 大。验证环节别只看 PSNR。把修复结果和原图在掩膜区域做差分放大看边缘过渡带有没有振铃或模糊。P-Laplacian 的强项是保边如果差分图里边缘处出现明显亮暗条纹说明 p 太小、扩散过强如果边缘处出现阶梯状伪影说明 p 太大、退化成线性扩散了。还有一个容易忽略的点bestexemplarhelper.c 里的搜索窗口大小。默认可能是全图搜索大图上极慢。常见做法是限制搜索半径比如只在待补块周围 50 像素范围内找最佳样本速度能快一个数量级质量损失通常可接受。改完 C 源码记得重新 mex 编译。从那以后我每次拿到一个新的修复算法包都强制先跑一遍参数扫描再下结论绝不拿默认参数的结果去对比别人的最优结果。希望这份拆解帮到你少走点我当年踩过的弯路。本文还有配套的精品资源点击获取