最邻近插值、双线性插值、三次卷积插值最通俗入门理论解析,论文材料
如有任何問題,請(qǐng)聯(lián)系VX:1755337994
前言
圖像處理中有三種常用的插值算法:
-
最鄰近插值
-
雙線性插值
-
雙立方(三次卷積)插值
其中效果最好的是雙立方(三次卷積)插值,本文介紹它的原理以及使用
如果想先看效果和源碼,可以拉到最底部
本文的契機(jī)是某次基于canvas做圖像處理時(shí),發(fā)現(xiàn)canvas自帶的縮放功能不盡人意,于是重溫了下幾種圖像插值算法,并整理出來。
為何要進(jìn)行雙立方插值
-
對(duì)圖像進(jìn)行插值的目的是為了獲取縮小或放大后的圖片
-
常用的插值算法中,雙立方插值效果最好
-
本文中介紹雙立方插值的一些數(shù)學(xué)理論以及實(shí)現(xiàn)
雙立方和三次卷積只是這個(gè)插值算法的兩種不同叫法而已,可以自行推導(dǎo),會(huì)發(fā)現(xiàn)最終可以將求值轉(zhuǎn)化為卷積公式
另外,像Photoshop等圖像處理軟件中也有這三種算法的實(shí)現(xiàn)
數(shù)學(xué)理論
雙立方插值計(jì)算涉及到16個(gè)像素點(diǎn),如下圖
簡(jiǎn)單分析如下:
-
其中P00代表目標(biāo)插值圖中的某像素點(diǎn)(x, y)在原圖中最接近的映射點(diǎn)
- 譬如映射到原圖中的坐標(biāo)為(1.1, 1.1),那么P00就是(1, 1)
-
而最終插值后的圖像中的(x, y)處的值即為以上16個(gè)像素點(diǎn)的權(quán)重卷積之和
下圖進(jìn)一步分析
如下是對(duì)圖的一些簡(jiǎn)單分析
-
譬如計(jì)算插值圖中(distI, distJ)處像素的值
-
首先計(jì)算它映射到原圖中的坐標(biāo)(i + v, j + u)
-
也就是說,卷積計(jì)算時(shí),p00點(diǎn)對(duì)應(yīng)(i, j)坐標(biāo)
-
最終,插值后的圖中(distI, distJ)坐標(biāo)點(diǎn)對(duì)應(yīng)的值是原圖中(i, j)處鄰近16個(gè)像素點(diǎn)的權(quán)重卷積之和
- i, j的范圍是[i - 1, i + 2],[j - 1, j + 2]
卷積公式
-
設(shè)采樣公式為S(x)
-
原圖中每一個(gè)(i, j)坐標(biāo)點(diǎn)的值得表達(dá)式為f(i, j)
-
插值后對(duì)應(yīng)坐標(biāo)的值為F(i + v, j + u)(這個(gè)值會(huì)作為(distI, distJ)坐標(biāo)點(diǎn)的值)
那么公式為:
等價(jià)于(可自行推導(dǎo))
提示
一定要區(qū)分本文中v, u和row, col的對(duì)應(yīng)關(guān)系,v代表行數(shù)偏差,u代表列數(shù)偏差(如果混淆了,會(huì)造成最終的圖像偏差很大)
如何理解卷積?
這是大學(xué)數(shù)學(xué)內(nèi)容,推薦看看這個(gè)答案如何通俗易懂的解釋卷積-知乎
采樣公式
在卷積公式中有一個(gè)S(x),它就是關(guān)鍵的卷積插值公式
不同的公式,插值效果會(huì)有所差異(會(huì)導(dǎo)致加權(quán)值不一樣)
本文中采用WIKI-Bicubic interpolation中給出的插值公式:
公式中的特點(diǎn)是:
-
S(0) = 1
-
S(n) = 0(當(dāng)n為整數(shù)時(shí))
-
當(dāng)x超出范圍時(shí),S(x)為0
-
當(dāng)a取不同值時(shí)可以用來逼近不同的樣條函數(shù)(常用值-0.5, -0.75)
當(dāng)a取值為-1
公式如下:
此時(shí),逼近的函數(shù)是y = sin(x*PI)/(x*PI),如圖
當(dāng)a取值為-0.5
公式如下:
此時(shí)對(duì)應(yīng)三次Hermite樣條
不同a的簡(jiǎn)單對(duì)比
推導(dǎo)
可參考:
-
圖像處理(一)bicubic解釋推導(dǎo)
-
WIKI-Bicubic interpolation
關(guān)于網(wǎng)上的一些推導(dǎo)公式奇怪實(shí)現(xiàn)
在網(wǎng)上查找了不少相關(guān)資料,發(fā)現(xiàn)有不少文章中都用到了以下這個(gè)奇怪的公式(譬如百度搜索雙立方插值)
一般這些文章中都聲稱這個(gè)公式是用來近似y = sin(x*PI)/(x)
但事實(shí)上,進(jìn)過驗(yàn)證,它與y = sin(x*PI)/(x)相差甚遠(yuǎn)(如上圖中是將sin函數(shù)縮放到合理系數(shù)后比對(duì))
由于類似的文章較多,年代都比較久遠(yuǎn),無從得知最初的來源
可能是某文中漏掉了分母的PI,亦或是這個(gè)公式只是某文自己實(shí)現(xiàn)的一個(gè)采樣公式,與sin無關(guān),然后被誤傳了。
這里都無從考據(jù),僅此記錄,避免疑惑。
另一種基于系數(shù)的實(shí)現(xiàn)
可以參考:圖像處理(一)bicubic解釋推導(dǎo)
像這類的實(shí)現(xiàn)就是直接計(jì)算最原始的系數(shù),然后通過16個(gè)像素點(diǎn)計(jì)算不同系數(shù)值,最終計(jì)算出目標(biāo)像素
本質(zhì)是一樣的,只不過是沒有基于最終的卷積方程計(jì)算而已(也就是說在原始理論階段沒有推成插值公式,而是直接解出系數(shù)并計(jì)算)。
代碼實(shí)現(xiàn)在github項(xiàng)目中可看到,參考最后的開源項(xiàng)目
代碼實(shí)現(xiàn)
以下是JavaScript代碼實(shí)現(xiàn)的插值核心方程
/*** 采樣公式的常數(shù)A取值,調(diào)整銳化與模糊* -0.5 三次Hermite樣條* -0.75 常用值之一* -1 逼近y = sin(x*PI)/(x*PI)* -2 常用值之一*/ const A = -0.5;function interpolationCalculate(x) {const absX = x > 0 ? x : -x;const x2 = x * x;const x3 = absX * x2;if (absX <= 1) {return 1 - (A + 3) * x2 + (A + 2) * x3;} else if (absX <= 2) {return -4 * A + 8 * A * absX - 5 * A * x2 + A * x3;}return 0; }以上是卷積方程的核心實(shí)現(xiàn)。下面則是一套完整的實(shí)現(xiàn)
/*** 采樣公式的常數(shù)A取值,調(diào)整銳化與模糊* -0.5 三次Hermite樣條* -0.75 常用值之一* -1 逼近y = sin(x*PI)/(x*PI)* -2 常用值之一*/ const A = -1;function interpolationCalculate(x) {const absX = x >= 0 ? x : -x;const x2 = x * x;const x3 = absX * x2;if (absX <= 1) {return 1 - (A + 3) * x2 + (A + 2) * x3;} else if (absX <= 2) {return -4 * A + 8 * A * absX - 5 * A * x2 + A * x3;}return 0; }function getPixelValue(pixelValue) {let newPixelValue = pixelValue;newPixelValue = Math.min(255, newPixelValue);newPixelValue = Math.max(0, newPixelValue);return newPixelValue; }/*** 獲取某行某列的像素對(duì)于的rgba值* @param {Object} data 圖像數(shù)據(jù)* @param {Number} srcWidth 寬度* @param {Number} srcHeight 高度* @param {Number} row 目標(biāo)像素的行* @param {Number} col 目標(biāo)像素的列*/ function getRGBAValue(data, srcWidth, srcHeight, row, col) {let newRow = row;let newCol = col;if (newRow >= srcHeight) {newRow = srcHeight - 1;} else if (newRow < 0) {newRow = 0;}if (newCol >= srcWidth) {newCol = srcWidth - 1;} else if (newCol < 0) {newCol = 0;}let newIndex = (newRow * srcWidth) + newCol;newIndex *= 4;return [data[newIndex + 0],data[newIndex + 1],data[newIndex + 2],data[newIndex + 3],]; }function scale(data, width, height, newData, newWidth, newHeight) {const dstData = newData;// 計(jì)算壓縮后的縮放比const scaleW = newWidth / width;const scaleH = newHeight / height;const filter = (dstCol, dstRow) => {// 源圖像中的坐標(biāo)(可能是一個(gè)浮點(diǎn))const srcCol = Math.min(width - 1, dstCol / scaleW);const srcRow = Math.min(height - 1, dstRow / scaleH);const intCol = Math.floor(srcCol);const intRow = Math.floor(srcRow);// 計(jì)算u和vconst u = srcCol - intCol;const v = srcRow - intRow;// 真實(shí)的index,因?yàn)閿?shù)組是一維的let dstI = (dstRow * newWidth) + dstCol;dstI *= 4;// 存儲(chǔ)灰度值的權(quán)重卷積和const rgbaData = [0, 0, 0, 0];// 根據(jù)數(shù)學(xué)推導(dǎo),16個(gè)點(diǎn)的f1*f2加起來是趨近于1的(可能會(huì)有浮點(diǎn)誤差)// 因此就不再單獨(dú)先加權(quán)值,再除了// 16個(gè)鄰近點(diǎn)for (let m = -1; m <= 2; m += 1) {for (let n = -1; n <= 2; n += 1) {const rgba = getRGBAValue(data,width,height,intRow + m,intCol + n,);// 一定要正確區(qū)分 m,n和u,v對(duì)應(yīng)的關(guān)系,否則會(huì)造成圖像嚴(yán)重偏差(譬如出現(xiàn)噪點(diǎn)等)// F(row + m, col + n)S(m - v)S(n - u)const f1 = interpolationCalculate(m - v);const f2 = interpolationCalculate(n - u);const weight = f1 * f2;rgbaData[0] += rgba[0] * weight;rgbaData[1] += rgba[1] * weight;rgbaData[2] += rgba[2] * weight;rgbaData[3] += rgba[3] * weight;}}dstData[dstI + 0] = getPixelValue(rgbaData[0]);dstData[dstI + 1] = getPixelValue(rgbaData[1]);dstData[dstI + 2] = getPixelValue(rgbaData[2]);dstData[dstI + 3] = getPixelValue(rgbaData[3]);};// 區(qū)塊for (let col = 0; col < newWidth; col += 1) {for (let row = 0; row < newHeight; row += 1) {filter(col, row);}} }export default function bicubicInterpolation(imgData, newImgData) {scale(imgData.data,imgData.width,imgData.height,newImgData.data,newImgData.width,newImgData.height);return newImgData; }運(yùn)行效果
分別用三種算法對(duì)一個(gè)圖進(jìn)行放大,可以明顯的看出雙立方插值效果最好
總結(jié)
以上是生活随笔為你收集整理的最邻近插值、双线性插值、三次卷积插值最通俗入门理论解析,论文材料的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在CDH5.16.2中部署海豚调度器
- 下一篇: Django代码编写规范