Criminisi圖像修復(fù)的MATLAB實(shí)現(xiàn)與調(diào)參指南)
簡(jiǎn)介這份資源圍繞Criminisi圖像修復(fù)算法的改進(jìn)實(shí)現(xiàn)展開(kāi)面向圖像處理、圖像修復(fù)方向的研究人員與開(kāi)發(fā)者尤其適合需要在Matlab環(huán)境下驗(yàn)證算法效果、評(píng)估修復(fù)質(zhì)量的學(xué)習(xí)者。其核心思路是以P-Laplacian算子替代原始算法中的數(shù)據(jù)項(xiàng)從而提升優(yōu)先級(jí)計(jì)算的精確度增強(qiáng)邊緣保持能力并減少噪聲傳播。壓縮包共5個(gè)文件包含3個(gè)m腳本、1個(gè)c源碼與1個(gè)dll動(dòng)態(tài)庫(kù)整體約6KB其中m文件承擔(dān)主流程與PSNR計(jì)算c與dll文件支撐底層輔助邏輯。資源提供了可直接運(yùn)行的修復(fù)流程與PSNR定量評(píng)估功能便于讀者對(duì)比改進(jìn)前后的修復(fù)效果理解優(yōu)先級(jí)計(jì)算、邊界處理與性能優(yōu)化等關(guān)鍵環(huán)節(jié)。目前已有212人學(xué)習(xí)下載適合作為圖像修復(fù)算法入門(mén)與改進(jìn)實(shí)驗(yàn)的參考素材。1. 拆開(kāi) p-laplace.rar一份把 Criminisi 數(shù)據(jù)項(xiàng)換成 P-Laplacian 的 MATLAB 修復(fù)包圖像修復(fù)里有個(gè)繞不開(kāi)的經(jīng)典Criminisi 算法。2004 年那篇論文把「先補(bǔ)哪塊、從哪塊抄」拆成優(yōu)先級(jí)排序加樣本塊匹配兩步成了后來(lái)一大票修復(fù)方法的基線。但基線歸基線真拿它去補(bǔ)一條穿過(guò)紋理區(qū)域的劃痕邊緣經(jīng)常糊成一團(tuán)噪聲還會(huì)順著補(bǔ)丁往里爬。p-laplace.rar 這份資源干的事就是把原算法里算優(yōu)先級(jí)的那個(gè)數(shù)據(jù)項(xiàng)從普通梯度換成 P-Laplacian 算子讓邊緣和細(xì)節(jié)在填充過(guò)程中被更狠地保住。包里是 MATLAB 實(shí)現(xiàn)核心文件是 Demo_inpaint.m、inpaint.m、bestexemplarhelper.c 及其編譯產(chǎn)物 bestexemplarhelper.dll外加一個(gè) PSNR.m 用來(lái)量化修復(fù)質(zhì)量。適合正在做圖像修復(fù)、去劃痕、老照片修補(bǔ)或者拿 Criminisi 當(dāng)對(duì)比基線的人——你不需要從零復(fù)現(xiàn)論文但得知道它改了哪、怎么跑、哪里會(huì)翻車(chē)。2. Criminisi 的優(yōu)先級(jí)機(jī)制與 P-Laplacian 到底改了什么2.1 原版 Criminisi 的優(yōu)先級(jí)公式拆解Criminisi 的核心是給破損區(qū)域邊界上每個(gè)待補(bǔ)像素算一個(gè)優(yōu)先級(jí) P(p)挑最高的先補(bǔ)。公式是 P(p) C(p) · D(p)其中 C(p) 是置信度項(xiàng)衡量這個(gè)像素周?chē)卸嗌僖阎畔(p) 是數(shù)據(jù)項(xiàng)衡量等照度線isophote在邊界上的強(qiáng)度說(shuō)白了就是「這個(gè)位置有沒(méi)有明顯的邊緣方向」。數(shù)據(jù)項(xiàng) D(p) 的原始定義是D(p) |?I⊥(p) · n(p)| / α?I⊥(p) 是 p 點(diǎn)梯度的正交方向等照度線方向n(p) 是邊界法向量α 是歸一化因子灰度圖取 255。這個(gè)式子的物理含義是如果等照度線方向和邊界法向越接近垂直說(shuō)明這里有強(qiáng)邊緣穿過(guò)應(yīng)該優(yōu)先補(bǔ)這樣紋理和結(jié)構(gòu)才能延續(xù)進(jìn)去。問(wèn)題出在 ?I 上。原版用的是普通梯度對(duì)噪聲敏感而且在線性結(jié)構(gòu)交叉或者光照不均的區(qū)域梯度方向容易算歪。一旦 D(p) 算錯(cuò)優(yōu)先級(jí)排序就亂補(bǔ)丁順序跟著錯(cuò)最后修復(fù)結(jié)果要么邊緣斷裂要么紋理錯(cuò)位。2.2 P-Laplacian 算子為什么能頂替梯度P-Laplacian 算子的定義是Δ_p u div(|?u|^(p-2) ?u)當(dāng) p2 時(shí)退化成普通 Laplacianp2 時(shí)對(duì)梯度大的區(qū)域邊緣懲罰更重p2 時(shí)對(duì)梯度小的區(qū)域平滑區(qū)更敏感。在圖像修復(fù)場(chǎng)景里通常取 p 在 1 到 2 之間目的是讓算子在平坦區(qū)域做各向同性擴(kuò)散在邊緣附近做各向異性擴(kuò)散——也就是沿著邊緣方向擴(kuò)散、垂直邊緣方向抑制。把它塞進(jìn) Criminisi 的數(shù)據(jù)項(xiàng)本質(zhì)是用 P-Laplacian 的擴(kuò)散通量替代原始梯度方向讓優(yōu)先級(jí)計(jì)算對(duì)邊緣的判定更穩(wěn)。具體來(lái)說(shuō)改進(jìn)后的數(shù)據(jù)項(xiàng)不再直接取 ?I⊥而是用 P-Laplacian 在局部窗口內(nèi)的響應(yīng)來(lái)加權(quán)鄰域信息。這樣做的直接好處是噪聲引起的偽梯度被壓制真實(shí)邊緣的響應(yīng)被放大優(yōu)先級(jí)排序更貼近人眼期望的「先補(bǔ)結(jié)構(gòu)、再補(bǔ)紋理」。2.3 包里文件的分工與調(diào)用關(guān)系拿到壓縮包先別急著跑把文件角色理清楚文件類(lèi)型作用Demo_inpaint.m腳本入口讀圖、設(shè)掩膜、調(diào) inpaint、算 PSNRinpaint.m函數(shù)Criminisi 主循環(huán)含優(yōu)先級(jí)計(jì)算和樣本塊搜索bestexemplarhelper.cC 源碼樣本塊匹配的加速實(shí)現(xiàn)bestexemplarhelper.dll編譯產(chǎn)物Windows 下供 MATLAB 調(diào)用的 MEX 文件PSNR.m函數(shù)計(jì)算修復(fù)前后峰值信噪比調(diào)用鏈?zhǔn)?Demo_inpaint → inpaint → bestexemplarhelper通過(guò) MEX 接口。如果你在非 Windows 平臺(tái)dll 用不了得自己用 mex 重新編譯 bestexemplarhelper.c這一步后面避坑章節(jié)會(huì)細(xì)說(shuō)。2.4 優(yōu)先級(jí)計(jì)算里 P-Laplacian 的接入點(diǎn)在 inpaint.m 里找優(yōu)先級(jí)計(jì)算那段通常會(huì)看到一個(gè)循環(huán)遍歷破損邊界上的像素。原版代碼大概長(zhǎng)這樣% 原版數(shù)據(jù)項(xiàng)計(jì)算示意 [Ix, Iy] gradient(img); grad_perp [-Iy, Ix]; % 梯度正交方向 normal compute_normal(mask, p); D abs(sum(grad_perp .* normal)) / 255;改進(jìn)版會(huì)把 gradient 那步換成 P-Laplacian 響應(yīng)% P-Laplacian 加權(quán)后的數(shù)據(jù)項(xiàng)示意 p 1.5; % P-Laplacian 階數(shù)常用 1.2~1.8 [Ix, Iy] gradient(img); grad_mag sqrt(Ix.^2 Iy.^2) eps; weight grad_mag.^(p - 2); % P-Laplacian 非線性權(quán)重 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;邏輯說(shuō)明weight 這一項(xiàng)就是 P-Laplacian 的核心。當(dāng) p2 時(shí)grad_mag 越小平坦區(qū)weight 越大擴(kuò)散越強(qiáng)grad_mag 越大邊緣weight 越小擴(kuò)散被抑制。這樣算出來(lái)的 grad_perp 在邊緣處更干凈不會(huì)被噪聲帶偏。參數(shù) p 是唯一需要調(diào)的默認(rèn) 1.5 是個(gè)穩(wěn)妥起點(diǎn)紋理密集的圖可以往 1.2 壓結(jié)構(gòu)為主的圖可以往 1.8 提。3. 跑通 Demo從掩膜制作到 PSNR 輸出3.1 環(huán)境準(zhǔn)備與 MEX 編譯MATLAB 版本沒(méi)有硬性要求但 bestexemplarhelper.dll 是預(yù)編譯的位數(shù)必須和你的 MATLAB 一致。64 位 MATLAB 配 64 位 dll32 位配 32 位混了直接報(bào)「invalid MEX-file」。如果你在 Windows 上且 dll 能用跳過(guò)編譯否則在 MATLAB 命令行執(zhí)行% 編譯 MEX 文件需提前配置好 C 編譯器 mex -O bestexemplarhelper.c-O 開(kāi)優(yōu)化編譯完會(huì)生成 bestexemplarhelper.mexw64Windows或 .mexa64Linux。如果報(bào)「No supported compiler」去 MATLAB 的附加功能里裝一個(gè) MinGW-w64 編譯器這是常見(jiàn)做法裝完再跑一次 mex -setup 選它。3.2 制作掩膜決定修復(fù)范圍的關(guān)鍵輸入Criminisi 需要一個(gè)二值掩膜白色1表示待修復(fù)區(qū)域黑色0表示已知區(qū)域。掩膜質(zhì)量直接決定修復(fù)效果別拿畫(huà)圖工具隨手涂。常見(jiàn)做法是用閾值分割加形態(tài)學(xué)處理% 從原圖生成掩膜示例假設(shè)修復(fù)區(qū)域是亮色劃痕 img imread(damaged.png); gray rgb2gray(img); mask gray 200; % 閾值提取亮色劃痕 mask imdilate(mask, strel(disk, 2)); % 膨脹 2 像素留余量 imwrite(mask, mask.png);參數(shù)說(shuō)明閾值 200 是經(jīng)驗(yàn)值具體看劃痕亮度和背景對(duì)比度可以用 imtool 交互看灰度直方圖定。膨脹半徑 2 是防止掩膜邊緣剛好壓在真實(shí)邊緣上導(dǎo)致修復(fù)時(shí)把好像素也覆蓋掉。掩膜寧可稍微大一點(diǎn)也別漏掉破損邊緣。3.3 調(diào)用 inpaint 主函數(shù)Demo_inpaint.m 里通常已經(jīng)寫(xiě)好了調(diào)用模板核心就幾行% 主修復(fù)調(diào)用 img imread(damaged.png); mask imread(mask.png); mask logical(mask); patch_size 9; % 樣本塊大小奇數(shù) result inpaint(img, mask, patch_size); imwrite(result, restored.png); psnr_val PSNR(img, result, mask); fprintf(PSNR %.2f dB\n, psnr_val);邏輯說(shuō)明patch_size 是樣本塊邊長(zhǎng)9 是 Criminisi 論文里的推薦值。塊太小紋理匹配容易碎塊太大結(jié)構(gòu)延續(xù)會(huì)拖沓。inpaint 內(nèi)部會(huì)先算優(yōu)先級(jí)、再搜最佳樣本塊、然后拷貝像素并更新置信度循環(huán)直到掩膜區(qū)域填滿。PSNR 計(jì)算時(shí)注意第三個(gè)參數(shù) mask——只算修復(fù)區(qū)域的 PSNR 才有意義全圖算會(huì)被未修改區(qū)域拉高。3.4 PSNR 評(píng)估的正確用法PSNR.m 的調(diào)用簽名一般是 PSNR(original, restored, mask)。如果你手上沒(méi)有無(wú)損原圖PSNR 就沒(méi)法算這時(shí)候只能靠目視評(píng)估。有原圖的情況下修復(fù)區(qū)域的 PSNR 能到 30 dB 以上算不錯(cuò)28 以下基本能看出明顯瑕疵。但別迷信 PSNR它和主觀質(zhì)量的相關(guān)性有限邊緣糊沒(méi)糊、紋理斷沒(méi)斷還是得放大看。提示跑 Demo 前先把 MATLAB 當(dāng)前目錄切到解壓后的文件夾否則 inpaint 找不到 bestexemplarhelper會(huì)報(bào) undefined function。4. 避坑與排查dll 報(bào)錯(cuò)、掩膜越界、優(yōu)先級(jí)反轉(zhuǎn)4.1 MEX 文件加載失敗現(xiàn)象運(yùn)行 inpaint 時(shí)報(bào)「Invalid MEX-file bestexemplarhelper.mexw64: 找不到指定的模塊」或「不是有效的 Win32 應(yīng)用程序」。原因dll 位數(shù)和 MATLAB 不匹配或者缺少運(yùn)行時(shí)依賴庫(kù)。預(yù)編譯的 dll 可能鏈接了特定版本的 MSVCRT。解決先確認(rèn) MATLAB 位數(shù)computer(arch)然后自己用 mex 重新編譯 bestexemplarhelper.c。編譯前裝好 MinGW-w64mex -setup 選它。如果還報(bào)缺庫(kù)用 Dependency Walker 查 dll 依賴補(bǔ)上對(duì)應(yīng)運(yùn)行庫(kù)。4.2 掩膜全白或全黑導(dǎo)致死循環(huán)現(xiàn)象程序卡住不結(jié)束或者瞬間跑完但結(jié)果沒(méi)變化。原因掩膜全白意味著所有像素都待修復(fù)沒(méi)有已知信息可用優(yōu)先級(jí)計(jì)算里置信度全為零樣本塊搜索找不到有效源掩膜全黑則循環(huán)條件直接不成立。解決跑之前用 sum(mask(:)) 檢查掩膜像素?cái)?shù)確保既不為 0 也不等于圖像總像素?cái)?shù)。正常掩膜占比在 5% 到 30% 之間比較合理超過(guò) 50% 修復(fù)質(zhì)量會(huì)急劇下降這不是算法問(wèn)題是信息量不夠。4.3 優(yōu)先級(jí)反轉(zhuǎn)導(dǎo)致結(jié)構(gòu)斷裂現(xiàn)象修復(fù)結(jié)果里長(zhǎng)邊緣補(bǔ)到一半突然拐彎或者紋理方向錯(cuò)亂。原因P-Laplacian 的 p 參數(shù)設(shè)得不對(duì)。p 太大接近 2退化成普通 Laplacian邊緣保持效果消失p 太小接近 1擴(kuò)散過(guò)強(qiáng)平坦區(qū)被過(guò)度平滑優(yōu)先級(jí)排序被噪聲主導(dǎo)。解決p 從 1.5 開(kāi)始試每次調(diào) 0.1觀察邊緣連續(xù)性。另外檢查 inpaint.m 里 P-Laplacian 權(quán)重那行有沒(méi)有加 eps沒(méi)加的話 grad_mag 為零時(shí)會(huì)出現(xiàn) Inf 或 NaN直接污染整個(gè)優(yōu)先級(jí)矩陣。4.4 PSNR 計(jì)算出負(fù)值或異常大現(xiàn)象PSNR 輸出 -Inf 或者 100 以上。原因-Inf 通常是修復(fù)結(jié)果和原圖在掩膜區(qū)域完全一致MSE0或者數(shù)據(jù)類(lèi)型不匹配導(dǎo)致減法溢出。異常大則是掩膜區(qū)域選錯(cuò)了比如把未修復(fù)區(qū)域也算進(jìn)去。解決確認(rèn) PSNR.m 里先把圖像轉(zhuǎn)成 double 再算 MSEuint8 相減會(huì)截?cái)唷z查掩膜是不是 logical 類(lèi)型double 類(lèi)型的掩膜在索引時(shí)會(huì)被當(dāng)成數(shù)值而不是邏輯導(dǎo)致區(qū)域選錯(cuò)。4.5 大圖跑不動(dòng)或內(nèi)存爆掉現(xiàn)象處理 1024×1024 以上的圖時(shí) MATLAB 卡死或報(bào) Out of Memory。原因Criminisi 的樣本塊搜索是暴力匹配復(fù)雜度隨圖像尺寸和掩膜面積平方增長(zhǎng)加上 P-Laplacian 權(quán)重計(jì)算額外開(kāi)銷(xiāo)內(nèi)存占用比原版高。解決先把圖降采樣到 512 寬跑通流程確認(rèn)參數(shù)后再上原圖?;蛘甙蜒谀し殖扇舾蛇B通區(qū)域逐塊修復(fù)再合并。bestexemplarhelper.c 本身就是加速用的確保它被正確調(diào)用別退回到純 MATLAB 的慢速搜索。5. 進(jìn)階調(diào)參與驗(yàn)證把 P-Laplacian 階數(shù)當(dāng)成旋鈕來(lái)擰跑通 Demo 只是起點(diǎn)這份資源真正的價(jià)值在于 P-Laplacian 階數(shù) p 給了你一個(gè)連續(xù)可調(diào)的邊緣保持旋鈕。我一般會(huì)做一組對(duì)比實(shí)驗(yàn)固定掩膜和 patch_size讓 p 從 1.1 到 1.9 步進(jìn) 0.2每個(gè)值跑一遍把修復(fù)區(qū)域的 PSNR 和目視效果都記下來(lái)。% p 參數(shù)掃描腳本 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));這段腳本假設(shè) inpaint 支持名值對(duì)參數(shù)傳 p如果你的版本里 p 是硬編碼的就去 inpaint.m 里把它提成函數(shù)參數(shù)。跑完看曲線通常會(huì)看到一個(gè)先升后降的趨勢(shì)峰值對(duì)應(yīng)的 p 就是這張圖的最優(yōu)值。紋理密集的圖峰值偏左p 小結(jié)構(gòu)為主的圖峰值偏右p 大。驗(yàn)證環(huán)節(jié)別只看 PSNR。把修復(fù)結(jié)果和原圖在掩膜區(qū)域做差分放大看邊緣過(guò)渡帶有沒(méi)有振鈴或模糊。P-Laplacian 的強(qiáng)項(xiàng)是保邊如果差分圖里邊緣處出現(xiàn)明顯亮暗條紋說(shuō)明 p 太小、擴(kuò)散過(guò)強(qiáng)如果邊緣處出現(xiàn)階梯狀偽影說(shuō)明 p 太大、退化成線性擴(kuò)散了。還有一個(gè)容易忽略的點(diǎn)bestexemplarhelper.c 里的搜索窗口大小。默認(rèn)可能是全圖搜索大圖上極慢。常見(jiàn)做法是限制搜索半徑比如只在待補(bǔ)塊周?chē)?50 像素范圍內(nèi)找最佳樣本速度能快一個(gè)數(shù)量級(jí)質(zhì)量損失通??山邮?。改完 C 源碼記得重新 mex 編譯。從那以后我每次拿到一個(gè)新的修復(fù)算法包都強(qiáng)制先跑一遍參數(shù)掃描再下結(jié)論絕不拿默認(rèn)參數(shù)的結(jié)果去對(duì)比別人的最優(yōu)結(jié)果。希望這份拆解幫到你少走點(diǎn)我當(dāng)年踩過(guò)的彎路。本文還有配套的精品資源點(diǎn)擊獲取