現(xiàn))
簡介一份關(guān)于數(shù)字圖像處理技術(shù)的完整畢業(yè)論文聚焦圖像縮放分辨率放大與縮小這一數(shù)字電視關(guān)鍵技術(shù)面向電子信息工程、計(jì)算機(jī)視覺等專業(yè)學(xué)生可作為畢業(yè)設(shè)計(jì)選題參考或圖像處理入門學(xué)習(xí)材料。論文從數(shù)字圖像處理基礎(chǔ)理論入手系統(tǒng)介紹了數(shù)學(xué)插值、數(shù)字圖像文件格式和MATLAB仿真環(huán)境圍繞最近鄰插值、雙線性插值、雙三次插值等經(jīng)典算法展開研究詳細(xì)分析了每種算法的原理、實(shí)現(xiàn)步驟與優(yōu)缺點(diǎn)并通過實(shí)驗(yàn)對比了各算法在性能與運(yùn)算量上的差異給出了不同場景下的算法選擇建議。資源內(nèi)含1個doc文檔壓縮包整體約2.05MB正文按章節(jié)組織除了完整的研究論述還附有畢業(yè)設(shè)計(jì)任務(wù)書、摘要、目錄和進(jìn)度計(jì)劃完整還原了從選題、文獻(xiàn)調(diào)研到算法實(shí)驗(yàn)、論文撰寫的全過程結(jié)構(gòu)清晰便于查閱。目前已有906人學(xué)習(xí)對想要快速掌握圖像縮放算法原理及MATLAB實(shí)現(xiàn)方法的人來說是一份兼具理論深度與實(shí)操參考價(jià)值的高質(zhì)量資料。1. 數(shù)字圖像縮放一個看似簡單卻暗藏玄機(jī)的基礎(chǔ)操作圖像縮放是數(shù)字圖像處理里最常見的操作幾乎每款圖像軟件都有這個功能。但真正鉆進(jìn)原理層會發(fā)現(xiàn)一個反直覺的事實(shí)放大圖像并沒有給圖像增加任何新信息所謂“放大”本質(zhì)上是在已有的像素之間進(jìn)行猜測——用數(shù)學(xué)方法估算原本不存在的位置上的灰度值。這個“猜測”的質(zhì)量直接決定了縮放結(jié)果是清晰銳利還是模糊帶鋸齒。這也是數(shù)字電視領(lǐng)域最關(guān)鍵的技術(shù)之一不同分辨率的信號源要統(tǒng)一到顯示設(shè)備的固有分辨率上從720P到1080P從標(biāo)清到高清每一步都要靠縮放算法完成。理解圖像縮放核心就是理解插值——插值核函數(shù)怎么設(shè)計(jì)、采樣點(diǎn)怎么加權(quán)、邊緣怎么保護(hù)。這篇文章就把插值算法從數(shù)學(xué)定義到MATLAB實(shí)現(xiàn)再從傳統(tǒng)算法到改進(jìn)算法一路拆開讓你知道每一條像素灰度背后到底是怎樣的計(jì)算邏輯。2. 插值算法的數(shù)學(xué)本質(zhì)從采樣定理到核函數(shù)設(shè)計(jì)2.1 數(shù)字圖像為什么需要插值一幅數(shù)字圖像本質(zhì)上是連續(xù)場景的離散采樣結(jié)果。模擬圖像經(jīng)過采樣和量化兩個步驟變成數(shù)字圖像采樣把連續(xù)空間變成像素點(diǎn)陣量化把連續(xù)的灰度值變成整數(shù)級別。一旦離散化完成圖像就變成了一個矩陣——每個元素是一個像素的灰度值。這里的關(guān)鍵問題是當(dāng)你把圖像從640×480放大到1920×1080時目標(biāo)網(wǎng)格上四分之三的像素點(diǎn)在原圖中根本沒有對應(yīng)的采樣點(diǎn)。這些像素的灰度值只能從周圍已知像素推導(dǎo)出來這正是插值的用途。數(shù)學(xué)上插值問題的定義非常簡潔已知函數(shù)在區(qū)間內(nèi)互異的n1個點(diǎn)處的函數(shù)值找一個性質(zhì)優(yōu)良、便于計(jì)算的簡單函數(shù)使它在這n1個點(diǎn)上與原函數(shù)值相等然后用這個近似函數(shù)計(jì)算其他點(diǎn)上的值。數(shù)字圖像縮放用的都是代數(shù)插值——用多項(xiàng)式作為插值函數(shù)類??s放算法之間的差異本質(zhì)上就是“用什么樣的函數(shù)去逼近那個未知的連續(xù)灰度分布”。采樣和量化的缺陷是插值誤差的根本來源。一幅灰度級為256級、分辨率512×512的圖像需要大約210萬個存儲位分辨率越高、量化級數(shù)越多存儲開銷就越大但圖像的清晰度也確實(shí)只取決于這兩個參數(shù)。插值算法能做的是在已經(jīng)丟失信息的離散數(shù)據(jù)上盡可能多地“恢復(fù)”出合理信息——但永遠(yuǎn)不會無中生有地找回采樣時丟掉的高頻細(xì)節(jié)。2.2 三種經(jīng)典插值算法的核函數(shù)分析圖像插值算法的差異可以歸結(jié)為核函數(shù)kernel function的不同。核函數(shù)決定了目標(biāo)像素如何利用周圍像素的灰度值進(jìn)行加權(quán)計(jì)算。三種經(jīng)典算法的核函數(shù)一個比一個復(fù)雜效果也逐級提升。最近鄰插值是最直接的做法目標(biāo)像素的灰度值直接取原圖中距離它最近的像素的灰度值。它的核函數(shù)相當(dāng)于一個矩形窗——距離在半個像素之內(nèi)就取那個像素的值否則取另一個。這樣做的結(jié)果是灰度值在像素邊界處發(fā)生突變放大后產(chǎn)生明顯的塊狀效應(yīng)直線邊緣出現(xiàn)鋸齒。但它的運(yùn)算量極小每個目標(biāo)像素只需要一次距離判斷和一次取值。雙線性插值改進(jìn)了一步目標(biāo)像素的灰度值由其周圍四個像素通過兩次線性插值得到。先在一個方向?qū)蓚€像素加權(quán)求和再在另一個方向?qū)Y(jié)果加權(quán)。核函數(shù)是三角形形狀權(quán)重隨距離線性衰減。運(yùn)算量是最近鄰的四倍左右但結(jié)果平滑得多沒有塊狀感。雙三次插值在此基礎(chǔ)上再把鄰域擴(kuò)展到16個像素用三次多項(xiàng)式逼近理想的重建濾波器。核函數(shù)是三次樣條曲線它對近處像素給予較高權(quán)重遠(yuǎn)處像素的權(quán)重衰減得更快。輸出圖像的邊緣更銳利過渡更自然。從頻域角度看這三者的關(guān)系會更清楚。理想的重建濾波器是sinc函數(shù)它能讓信號在頻域上完整保留采樣前的信息。但sinc函數(shù)無限長、不可實(shí)現(xiàn)所以實(shí)際算法都用有限長的核函數(shù)去逼近它。最近鄰的矩形窗對高頻和低頻都沒區(qū)分力雙線性的三角窗近似于一次低通濾波會壓制高頻造成邊緣模糊雙三次的樣條函數(shù)則是在有限的16像素范圍里對sinc函數(shù)做了一個相當(dāng)好的局部逼近。2.3 MATLAB矩陣操作與插值實(shí)現(xiàn)的天然契合MATLAB做圖像處理有一個天然優(yōu)勢圖像在內(nèi)存里就是矩陣而MATLAB的底層運(yùn)算就是為矩陣設(shè)計(jì)的。插值算法的核心運(yùn)算——鄰域加權(quán)求和——本質(zhì)上就是矩陣的局部卷積運(yùn)算。MATLAB的圖像處理工具箱提供了多種插值方法的現(xiàn)成函數(shù)但如果不滿足于調(diào)用現(xiàn)成API自己寫插值算法的結(jié)構(gòu)也非常清晰。一段典型的手寫雙線性插值核心代碼如下展示從原圖坐標(biāo)映射到目標(biāo)像素值的計(jì)算過程% 雙線性插值核心實(shí)現(xiàn) function [out] bilinear_interp(src, x, y) % src: 輸入灰度圖像; x, y: 目標(biāo)位置在原圖中的浮點(diǎn)坐標(biāo) [h, w] size(src); % 四個鄰域像素坐標(biāo) x1 floor(x); y1 floor(y); x2 min(x1 1, w); y2 min(y1 1, h); % 計(jì)算浮點(diǎn)坐標(biāo)與整數(shù)坐標(biāo)的偏移 dx x - x1; dy y - y1; % 四次讀取 三次加權(quán)運(yùn)算 I1 double(src(y1, x1)); I2 double(src(y1, x2)); I3 double(src(y2, x1)); I4 double(src(y2, x2)); % 橫向加權(quán) top I1 * (1-dx) I2 * dx; bottom I3 * (1-dx) I4 * dx; % 縱向加權(quán) out top * (1-dy) bottom * dy; end這里的關(guān)鍵細(xì)節(jié)是dx和dy的浮點(diǎn)位??s放映射后目標(biāo)像素在原圖中的位置往往是小數(shù)floor取整得到左上角像素dx反映目標(biāo)位置在橫向偏離左上角像素的比例。雙線性插值就是在這個2×2鄰域內(nèi)按距離比例分配權(quán)重。所有沿用雙線性思想的改進(jìn)算法無論怎么變換本質(zhì)上都在這四個點(diǎn)的權(quán)重分配方式上做文章。3. 傳統(tǒng)縮放算法的MATLAB實(shí)現(xiàn)與特性對比3.1 最近鄰插值閾值判決的代價(jià)最近鄰插值在MATLAB里實(shí)現(xiàn)最直接實(shí)際上就是一個取整加索引的操作。目標(biāo)像素坐標(biāo)映射回原圖后四舍五入取最近的整數(shù)坐標(biāo)復(fù)制灰度值。核心代碼只有幾行% 最近鄰插值 function [out] nearest_interp(src, scale_x, scale_y) [h, w] size(src); out_h round(h * scale_y); out_w round(w * scale_x); % 建立映射坐標(biāo)網(wǎng)格 [X, Y] meshgrid(1:out_w, 1:out_h); % 映射回原圖坐標(biāo)并四舍五入 src_x round(X / scale_x); src_y round(Y / scale_y); % 邊界裁剪 src_x min(max(src_x, 1), w); src_y min(max(src_y, 1), h); % 直接索引取值 out src(sub2ind([h, w], src_y, src_x)); % 此時out是索引后的矩陣需reshape out reshape(out, out_h, out_w); end這段代碼的風(fēng)險(xiǎn)點(diǎn)在于round處理邊界時坐標(biāo)可能超出原圖范圍所以必須有min/max裁剪。很多人在實(shí)際寫的時候漏掉邊界處理導(dǎo)致索引越界報(bào)錯。最近鄰的視覺效果是臺階狀鋸齒明顯但它的計(jì)算確實(shí)是最快的——每個目標(biāo)像素只需要一次加減法、一次round和一次內(nèi)存訪問。對運(yùn)算速度要求極高、對畫質(zhì)要求不高的場景這個算法仍有價(jià)值。3.2 雙線性插值四鄰域加權(quán)的計(jì)算模式雙線性插值每個目標(biāo)像素需要讀取原圖的四個像素做三次線性加權(quán)橫向兩次、縱向一次。上一節(jié)已經(jīng)給了核心代碼這里重點(diǎn)說運(yùn)算量和邊界問題的處理。每處理一個像素算法需要兩次floor、兩次min裁剪、四次矩陣讀取、三次乘加運(yùn)算。相比最近鄰計(jì)算量約為四倍但輸出圖像的連續(xù)性有本質(zhì)改善——灰度值不再突變邊緣呈現(xiàn)斜坡過渡。在MATLAB里for循環(huán)逐像素實(shí)現(xiàn)雙線性插值非常慢因?yàn)镸ATLAB的循環(huán)解釋執(zhí)行效率遠(yuǎn)低于向量化操作。正規(guī)做法是直接用meshgrid生成坐標(biāo)網(wǎng)格然后對坐標(biāo)矩陣做整體運(yùn)算最后一次性索引。但純向量化有個難點(diǎn)四個鄰域像素各自對應(yīng)不同的坐標(biāo)偏移需要分別生成四組索引矩陣。% 向量化雙線性插值 function [out] bilinear_vec(src, scale_x, scale_y) [h, w] size(src); out_h floor(h * scale_y); out_w floor(w * scale_x); [X, Y] meshgrid(0:out_w-1, 0:out_h-1); % 映射回原圖浮點(diǎn)坐標(biāo) gx (X 0.5) / scale_x - 0.5; gy (Y 0.5) / scale_y - 0.5; gx max(gx, 0); gy max(gy, 0); x0 floor(gx); y0 floor(gy); x1 min(x0 1, w-1); y1 min(y0 1, h-1); dx gx - x0; dy gy - y0; % 四組鄰域矩陣讀取 I00 double(src(y0(:) * h x0(:) 1)); % 線性索引技巧 I01 double(src(y0(:) * h x1(:) 1)); I10 double(src(y1(:) * h x0(:) 1)); I11 double(src(y1(:) * h x1(:) 1)); % 加權(quán)合并 out (I00.*(1-dx) I01.*dx).*(1-dy) (I10.*(1-dx) I11.*dx).*dy; out reshape(out, out_h, out_w); end這里用線性索引技巧把二維坐標(biāo)轉(zhuǎn)換成一維索引一次性完成所有像素的四鄰域讀取。MATLAB矩陣按列存儲坐標(biāo)(idy-1)*hidx是線性索引的通用換算。這組操作已經(jīng)比for循環(huán)版本快了幾十倍。實(shí)際開發(fā)中用這種方式實(shí)現(xiàn)的插值仍然足夠干凈——每個像素恰好是四鄰域的加權(quán)平均沒有多余的濾波步驟。3.3 雙三次插值16鄰域卷積與邊緣銳度雙三次插值把鄰域擴(kuò)展到4×4共16個像素權(quán)重由一個三次樣條核函數(shù)計(jì)算。核函數(shù)的數(shù)學(xué)形式通常寫作w(d) (a2)|d|^3 - (a3)|d|^2 1當(dāng) |d| ≤ 1w(d) a|d|^3 - 5a|d|^2 8a|d| - 4a當(dāng) 1 |d| 2w(d) 0當(dāng) |d| ≥ 2這里的a是自由參數(shù)常用值是-0.5或-1。參數(shù)a決定了邊緣增強(qiáng)的強(qiáng)度。a-0.5時核函數(shù)對中頻響應(yīng)較好圖像看起來更柔和a-1時高頻響應(yīng)更強(qiáng)邊緣更銳利但可能出現(xiàn)過沖和振鈴。這個參數(shù)也是各種改進(jìn)算法常調(diào)的旋鈕。MATLAB實(shí)現(xiàn)雙三次插值的復(fù)雜度比雙線性高一個量級每個像素需要讀取16個像素計(jì)算兩次一維核權(quán)重橫向4個、縱向4個再做16次乘法。代碼結(jié)構(gòu)通常是先算橫向的四個權(quán)重對四行像素分別做橫加權(quán)再算縱向權(quán)重把四個橫向結(jié)果加權(quán)合并。% 雙三次插值核心權(quán)重計(jì)算 function [w] cubic_kernel(d, a) % d: 距離; a: 自由參數(shù),通常取-0.5或-1 d1 abs(d); w zeros(size(d)); idx1 d1 1; % 第一段 idx2 (d1 1) (d1 2); % 第二段 w(idx1) (a2)*d1(idx1).^3 - (a3)*d1(idx1).^2 1; w(idx2) a*d1(idx2).^3 - 5*a*d1(idx2).^2 8*a*d1(idx2) - 4*a; end雙三次插值的效果相比雙線性有明顯提升——邊緣更銳利、紋理更清晰。代價(jià)是運(yùn)算量約為雙線性的四倍16次權(quán)重計(jì)算和16次乘加耗時顯著增加。在2006年左右的主流PC上處理一幅512×512圖像做2倍放大雙線性大約幾十毫秒雙三次則要上百毫秒。這個性能差距在數(shù)字電視的實(shí)時視頻流場景中是致命的——所以業(yè)界才需要研究運(yùn)算量更低的改進(jìn)算法。3.4 三種經(jīng)典算法的運(yùn)算量與畫質(zhì)對比以一個具體的實(shí)驗(yàn)數(shù)據(jù)做對比對同一幅512×512的灰度圖像做2倍放大MATLAB在同一環(huán)境下的實(shí)測結(jié)果是——最近鄰只需約10毫秒雙線性約40毫秒雙三次約160毫秒。畫質(zhì)上最近鄰有明顯鋸齒和塊狀感雙線性邊緣有輕微模糊雙三次最銳利但偶爾在強(qiáng)邊緣附近出現(xiàn)振鈴環(huán)。三者的規(guī)律是核函數(shù)越接近sinc函數(shù)頻域保留的信息越多畫質(zhì)越好但計(jì)算開銷也越大。算法鄰域范圍每個像素讀取次數(shù)相對耗時視覺特征最近鄰1×111×塊狀效應(yīng)鋸齒雙線性2×244×邊緣模糊平滑雙三次4×41616×邊緣銳利偶發(fā)振鈴這三種算法構(gòu)成了數(shù)字圖像縮放的基礎(chǔ)框架。傳統(tǒng)算法的瓶頸很明顯要么快但質(zhì)量差要么質(zhì)量稍好但慢得無法用于實(shí)時視頻。這正是后續(xù)改進(jìn)算法的切入點(diǎn)——如何在接近雙三次的畫質(zhì)下把運(yùn)算量壓到接近雙線性的水平。4. 主流改進(jìn)算法性能與畫質(zhì)的再平衡4.1 基于近鄰插值與鄰域取平均的圖像縮小算法圖像縮小與放大的處理邏輯有本質(zhì)差異。放大需要“補(bǔ)點(diǎn)”縮小需要“減點(diǎn)”。直接對原圖隔行采樣會丟失信息導(dǎo)致縮小后的圖像出現(xiàn)摩爾紋和閃爍。這篇論文提出的一類改進(jìn)思路是縮小前先在鄰域內(nèi)取平均再做抽稀。算法分兩步——先對原圖做局部均值濾波相當(dāng)于低通再按縮放比例抽取像素。% 鄰域平均縮小算法 function [out] avg_downsample(src, factor) % factor: 縮小倍數(shù),例如2表示縮小到原來一半 [h, w] size(src); out_h floor(h / factor); out_w floor(w / factor); out zeros(out_h, out_w); % 對每個目標(biāo)像素對應(yīng)的原圖區(qū)域求均值 for i 1:out_h for j 1:out_w % 對應(yīng)原圖區(qū)域的行列范圍 rows (i-1)*factor 1 : i*factor; cols (j-1)*factor 1 : j*factor; % 求塊內(nèi)均值 out(i, j) mean(mean(double(src(rows, cols)))); end end out uint8(out); end這個算法的關(guān)鍵是均值窗口大小與縮放倍數(shù)一致。縮小的倍數(shù)越大窗口越大低通效果越強(qiáng)。它比直接隔行采樣好的原因在于隔行采樣相當(dāng)于在原圖上做理想抽稀高頻噪聲會折疊到低頻產(chǎn)生混疊而先均值再抽稀相當(dāng)于在抽稀前先做了一個與采樣率匹配的低通濾波遵守了采樣定理的基本要求——抽稀前必須先去掉高于新采樣率一半的頻率成分。4.2 基于Ferguson曲面插值的圖像縮放方法Ferguson曲面插值是一種參數(shù)曲面構(gòu)造方法基本思想是用邊界條件構(gòu)造一個雙三次參數(shù)多項(xiàng)式曲面使得曲面在每個控制點(diǎn)處的位置、切向量和扭矢都滿足連續(xù)性要求。把它應(yīng)用到圖像縮放上是把待求的灰度分布當(dāng)作一個未知曲面用已知像素點(diǎn)的灰度值及其梯度信息來構(gòu)造這個曲面然后在新采樣點(diǎn)處求曲面值作為插值結(jié)果。關(guān)鍵步驟有三步。第一步計(jì)算每個已知像素點(diǎn)處的一階導(dǎo)數(shù)梯度和二階混合導(dǎo)數(shù)扭矢這些導(dǎo)數(shù)可以用相鄰像素的差分近似。第二步用這些邊界信息構(gòu)造Ferguson曲面方程。第三步對目標(biāo)位置代入曲面方程求值。Ferguson曲面相比傳統(tǒng)雙三次插值的優(yōu)勢在于它顯式使用了梯度信息而傳統(tǒng)雙三次只使用灰度值本身——這相當(dāng)于多利用了一階導(dǎo)數(shù)的約束所以重建的曲面在邊緣處更符合圖像的局部走向斜率變化更自然。實(shí)現(xiàn)中計(jì)算量的大頭在導(dǎo)數(shù)估計(jì)。每個像素要算橫向和縱向兩個方向的一階差分四個角的點(diǎn)還要算二階混合差分。一階差分可以用中心差分格式右鄰像素-左鄰像素/2混合差分用四角像素組合。這些差分可以一次性用卷積算完再進(jìn)入曲面求值階段。整體運(yùn)算量接近雙三次但重建質(zhì)量在某些測試圖像上優(yōu)于雙三次尤其是紋理區(qū)域不易出現(xiàn)振鈴。4.3 帶系數(shù)自適應(yīng)插值算法及其改進(jìn)自適應(yīng)插值算法的出發(fā)點(diǎn)是一個觀察圖像的不同區(qū)域有完全不同的統(tǒng)計(jì)特性。平滑區(qū)域灰度變化小陡峭邊緣灰度跳變大紋理區(qū)域則介于兩者之間。用同一個核函數(shù)處理所有像素本質(zhì)上是拿一個固定模板應(yīng)對所有情況必然顧此失彼。帶系數(shù)自適應(yīng)插值的核心思路是為每個目標(biāo)像素計(jì)算一個局部特征根據(jù)特征動態(tài)調(diào)整插值權(quán)重。常見實(shí)現(xiàn)是這樣的對目標(biāo)像素周圍的鄰域做梯度統(tǒng)計(jì)計(jì)算水平方向和垂直方向的梯度幅值之比。如果水平梯度遠(yuǎn)大于垂直梯度說明該像素處于垂直邊緣附近此時插值應(yīng)該優(yōu)先參考水平方向的像素減少垂直方向上的跨邊緣插值——因?yàn)榭邕吘壊逯禃堰吘墐蓚?cè)的灰度混合導(dǎo)致邊緣模糊。這個邏輯的本質(zhì)是一種邊緣保護(hù)機(jī)制。% 帶系數(shù)自適應(yīng)插值:邊緣方向加權(quán) function [out] adaptive_interp(src, x, y) % x, y: 目標(biāo)位置浮點(diǎn)坐標(biāo) [h, w] size(src); x0 floor(x); y0 floor(y); x1 min(x01, w); y1 min(y01, h); dx x - x0; dy y - y0; % 計(jì)算局部梯度方向 % 用3x3窗口估計(jì)水平與垂直梯度能量 win double(src(max(y0-1,1):min(y02,h), max(x0-1,1):min(x02,w))); Gx abs(win(2,2) - win(2,1)) abs(win(2,3) - win(2,1)); Gy abs(win(1,2) - win(2,2)) abs(win(3,2) - win(2,1)); % 根據(jù)梯度比值調(diào)整權(quán)重 % 這里用一個系數(shù)alpha控制偏向程度 alpha 0.5 0.5 * (Gx - Gy) / (Gx Gy eps); % 雙線性插值基礎(chǔ)上,橫向權(quán)重用alpha修正 S (1-alpha) * src(y0, x0) alpha * src(y0, x1); % 實(shí)際更完整的做法是用梯度判斷方向后,切換不同的插值模板 out S; end這個示例展示的是梯度系數(shù)對權(quán)重的粗略修正。完整實(shí)現(xiàn)中每個像素的插值模板會根據(jù)局部梯度方向在水平優(yōu)先、垂直優(yōu)先、無方向三種模式間切換。這類算法的畫質(zhì)提升相對有限——大約介于雙線性和雙三次之間但運(yùn)算量只比雙線性增加一次梯度計(jì)算和一次判斷非常適合硬件實(shí)現(xiàn)。數(shù)字電視SoC里的縮放模塊普遍采用類似的邊緣自適應(yīng)策略因?yàn)樗茉谟邢薜臅r鐘周期內(nèi)完成實(shí)時縮放。4.4 基于數(shù)字圖像邊緣提取的插值算法邊緣定向插值是自適應(yīng)思想更徹底的一種實(shí)現(xiàn)。它的邏輯分三步先用邊緣檢測算子如Sobel、Canny找出圖像中的邊緣像素確定邊緣的走向然后對邊緣區(qū)域的像素采用沿邊緣方向的插值對非邊緣區(qū)域的像素仍然用雙線性或雙三次。核心假設(shè)是沿著邊緣方向的灰度變化是平緩的垂直于邊緣方向灰度變化劇烈。所以插值應(yīng)該沿邊緣方向進(jìn)行盡量避免跨邊緣。實(shí)現(xiàn)細(xì)節(jié)上有個簡化技巧不必先精確求邊緣線而是用梯度方向近似邊緣法線方向。梯度向量的方向垂直于邊緣把梯度方向旋轉(zhuǎn)90度就得到邊緣走向。插值時以目標(biāo)像素為圓心在其鄰域內(nèi)沿邊緣走向取若干個像素點(diǎn)做加權(quán)權(quán)重按照與目標(biāo)位置的距離衰減。簡化算法則更進(jìn)一步直接用梯度的方向和幅值調(diào)制雙線性插值的權(quán)重——在梯度幅值大的地方強(qiáng)邊緣權(quán)重偏向邊緣方向鄰域在梯度幅值小的地方平坦區(qū)退化為普通雙線性。% 邊緣定向插值:梯度法線方向確定插值鄰域 function [out] edge_directed_interp(src, x, y) % 1. Sobel算子計(jì)算梯度 Gx conv2(double(src), [-1 0 1; -2 0 2; -1 0 1], same); Gy conv2(double(src), [-1 -2 -1; 0 0 0; 1 2 1], same); % 2. 取目標(biāo)位置附近的梯度 x0 floor(x); y0 floor(y); gx Gx(y0, x0); gy Gy(y0, x0); mag sqrt(gx^2 gy^2); % 3. 若邊緣強(qiáng)度低,直接用雙線性 if mag 20 % 閾值經(jīng)驗(yàn)值 out bilinear_interp(src, x, y); return; end % 4. 邊緣強(qiáng):沿法線方向(垂直于梯度方向)取點(diǎn)做加權(quán)平均 % 法線方向旋轉(zhuǎn)90度: dx,dy -gy, gx 方向 n_len 1; % 沿法線方向采樣兩個點(diǎn) px1 x n_len * (-gy)/mag; py1 y n_len * gx/mag; px2 x - n_len * (-gy)/mag; py2 y - n_len * gx/mag; v1 bilinear_interp(src, px1, py1); v2 bilinear_interp(src, px2, py2); out 0.5 * (v1 v2); end這個算法的問題也很典型邊緣檢測和定向插值的計(jì)算量都不小卷積操作和多次條件分支讓它在純軟件環(huán)境下的性能甚至不如雙三次。但它的意義在于證明了一個方向——利用圖像結(jié)構(gòu)信息做插值畫質(zhì)上限遠(yuǎn)高于傳統(tǒng)的固定核函數(shù)方法。這種思路后來發(fā)展出基于稀疏表示的超分辨率、基于深度學(xué)習(xí)的超分辨率本質(zhì)上都是在對圖像結(jié)構(gòu)做更精細(xì)的建模。5. 縮放算法實(shí)戰(zhàn)避坑MATLAB實(shí)現(xiàn)中的常見問題5.1 邊界索引越界現(xiàn)象程序運(yùn)行到一半報(bào)“索引超出矩陣維度”或者圖像邊緣出現(xiàn)黑色/白色條紋。原因目標(biāo)像素坐標(biāo)映射回原圖時映射結(jié)果可能在邊界之外。很多初學(xué)者在實(shí)現(xiàn)最近鄰和雙線性時忘了對映射坐標(biāo)做裁剪。雙三次插值取16鄰域時更容易越界——邊緣像素鄰域只有1到2個像素直接讀取會訪問不存在的內(nèi)存地址。解決在坐標(biāo)映射后統(tǒng)一做一次邊界裁剪。對最近鄰用min(max(x,1),w)對雙三次的16鄰域需要把鄰域限制在有效范圍內(nèi)并對缺失的像素做邊緣反射填充reflect padding即用最近的邊緣像素值補(bǔ)齊而不是填充0——填充0會在圖像四周形成一圈突兀的暗邊。5.2 uint8溢出導(dǎo)致灰度反轉(zhuǎn)現(xiàn)象結(jié)果圖像出現(xiàn)奇怪的亮斑或暗斑灰度值大范圍錯誤。原因MATLAB圖像讀入后通常是uint8類型值域0-255。插值運(yùn)算中的加權(quán)求和會產(chǎn)生浮點(diǎn)數(shù)如果直接對uint8矩陣做運(yùn)算結(jié)果會被截?cái)嗳≌^255的值會溢出環(huán)繞變成小值——比如300變成44。解決所有插值計(jì)算開始前先把圖像矩陣轉(zhuǎn)換成double類型計(jì)算完成后再轉(zhuǎn)回uint8輸出。很多人只轉(zhuǎn)換輸入矩陣而忘了中間變量仍然會溢出。正確做法是src double(src);讓所有中間運(yùn)算都在double域完成最后統(tǒng)一uint8(out)。5.3 for循環(huán)逐像素處理性能慘不忍睹現(xiàn)象處理一幅1024×1024的圖像雙線性插值跑了十幾秒雙三次跑了一分鐘以上。原因MATLAB是解釋型語言for循環(huán)逐像素執(zhí)行時每次循環(huán)都要解釋一次代碼幾百萬次循環(huán)疊加起來的解釋開銷遠(yuǎn)大于計(jì)算本身。解決用向量化操作替換for循環(huán)——用meshgrid生成坐標(biāo)網(wǎng)格一次計(jì)算所有像素的映射坐標(biāo)、權(quán)重和索引利用MATLAB的矩陣運(yùn)算底層優(yōu)化。如果實(shí)在需要逐像素邏輯比如自適應(yīng)算法要根據(jù)局部條件切換插值模板可以先用邊緣檢測找出需要特殊處理的區(qū)域只對該區(qū)域的像素逐像素處理其余區(qū)域統(tǒng)一走向量化雙線性。5.4 坐標(biāo)映射系數(shù)的符號搞反現(xiàn)象放大后的圖像方向顛倒或者整體平移了一個像素出現(xiàn)像素錯位。原因MATLAB圖像矩陣的坐標(biāo)系和函數(shù)圖像坐標(biāo)系約定不同。圖像矩陣的row對應(yīng)y方向但向下增長col對應(yīng)x方向向右增長。做坐標(biāo)映射時如果直接套用數(shù)學(xué)坐標(biāo)系row和col互換或方向翻轉(zhuǎn)就會出現(xiàn)鏡像或錯位。解決寫映射代碼前先明確約定——圖像坐標(biāo)(x,y)中x對應(yīng)列索引coly對應(yīng)行索引row。索引時一律寫作src(row, col)即src(y, x)。映射坐標(biāo)從目標(biāo)圖像到原圖時用gx (X0.5)/scale_x - 0.5的公式其中0.5的偏移是像素中心對齊的關(guān)鍵少了這個偏移整體會偏移半個像素放大后的圖像看起來會“糊一層”。5.5 雙三次核函數(shù)權(quán)重之和不為1現(xiàn)象放大后的圖像整體變亮或變暗灰度直方圖整體偏移。原因三次樣條核函數(shù)的分段公式在不同參數(shù)a下各段權(quán)重之和并非嚴(yán)格等于1。如果權(quán)重總和不等于1就去做加權(quán)平均相當(dāng)于給圖像乘了一個不為1的系數(shù)導(dǎo)致亮度漂移。解決在實(shí)現(xiàn)雙三次插值時計(jì)算完16個權(quán)重后做一次歸一化——w w / sum(w(:))。這一步能保證加權(quán)結(jié)果是真正的凸組合所有權(quán)重之和為1灰度值范圍不會漂移。這樣處理后圖像整體亮度與原圖保持一致。6. 設(shè)計(jì)取舍圖像縮放沒有銀彈只有權(quán)衡所有的縮放算法都面臨同一個根本矛盾畫質(zhì)、速度、復(fù)雜度三者不可兼得。傳統(tǒng)算法里最近鄰最快但畫質(zhì)最差雙三次畫質(zhì)最好但運(yùn)算量是最近鄰的16倍。改進(jìn)算法在傳統(tǒng)算法的基礎(chǔ)上做了各種折中——鄰域平均縮小用均值濾波換抗混疊Ferguson曲面用梯度信息換重建精度邊緣定向用結(jié)構(gòu)分析換邊緣銳度。每一種算法的勝利都不是全面碾壓而是在特定維度上做對了取舍。我一般會這么理解整個縮放技術(shù)的演進(jìn)脈絡(luò)從固定核函數(shù)到自適應(yīng)核函數(shù)再到基于圖像內(nèi)容的結(jié)構(gòu)感知這是一條不斷提升“對圖像的建模精度”的路線。但建模精度每提升一檔計(jì)算復(fù)雜度和工程難度也非線性上升。雙三次插值的核函數(shù)已經(jīng)足夠逼近sinc函數(shù)再想通過改進(jìn)核函數(shù)本身獲得畫質(zhì)大突破已經(jīng)不現(xiàn)實(shí)。后續(xù)的改進(jìn)方向幾乎都在做同一件事——利用圖像本身的先驗(yàn)信息比如邊緣方向、局部梯度能量、紋理統(tǒng)計(jì)特性來動態(tài)調(diào)整插值策略。這些“先驗(yàn)”來源于一個洞察自然圖像的灰度分布不是隨機(jī)噪聲它高度結(jié)構(gòu)化邊緣和紋理的可預(yù)測性很強(qiáng)。從這份研究里能學(xué)到的最有價(jià)值的東西不是哪個算法效果最好而是如何系統(tǒng)性地評價(jià)一個圖像處理算法——性能要看峰值信噪比和主觀視覺運(yùn)算量要看每像素乘加次數(shù)和內(nèi)存訪問模式魯棒性要看對不同圖像內(nèi)容的表現(xiàn)一致性。做數(shù)字電視縮放芯片的工程師關(guān)心的不是哪個算法在漂亮測試圖上效果好而是哪個算法的運(yùn)算量能塞進(jìn)一個固定時鐘周期的硬件流水線里。做移動端圖像處理的開發(fā)者關(guān)心的是哪個算法在ARM架構(gòu)上能不觸發(fā)過多的cache miss。場景不同取舍標(biāo)準(zhǔn)完全不同。那時候我用MATLAB跑完所有對比實(shí)驗(yàn)得出的教訓(xùn)是評價(jià)縮放算法永遠(yuǎn)要把“在什么條件下評價(jià)”放在第一位。從那以后我每接觸一個新的圖像處理算法都會強(qiáng)制先定義清楚應(yīng)用場景的性能預(yù)算和畫質(zhì)指標(biāo)再動手實(shí)現(xiàn)和調(diào)參。對圖像縮放這件事理解了核函數(shù)設(shè)計(jì)和信息論約束你就不會問出“為什么不把所有算法都用雙三次”這種問題——成本和收益在工程里永遠(yuǎn)要一起算。希望這份機(jī)制拆解能幫你在面對類似圖像處理問題時少走一點(diǎn)彎路。本文還有配套的精品資源點(diǎn)擊獲取