這篇文章主要介紹如何實(shí)現(xiàn)js的雙線性插值和雙三次插值法,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比洮北網(wǎng)站開(kāi)發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式洮北網(wǎng)站制作公司更省心,省錢(qián),快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋洮北地區(qū)。費(fèi)用合理售后完善,10年實(shí)體公司更值得信賴。
在網(wǎng)頁(yè)中利用canvas進(jìn)行繪圖時(shí),遇到一個(gè)問(wèn)題,原始的數(shù)據(jù)分辨率很小,而圖片要放大到整個(gè)網(wǎng)頁(yè),所以需要把數(shù)據(jù)進(jìn)行插值放大。學(xué)習(xí)了雙線性插值和三次內(nèi)插法插值,兩種方式實(shí)現(xiàn)效果不同,都用js代碼實(shí)現(xiàn)了一下。
雙線性插值
原理
雙線性插值即在x和y兩個(gè)方向上,對(duì)數(shù)據(jù)各進(jìn)行一次線性插值。
原始數(shù)據(jù)的矩陣,即一個(gè)二維數(shù)組,大小為a*b,目標(biāo)矩陣大小為m*n,m、n比a、b可以大(放大),也可以?。s小),當(dāng)然比例也可以不一樣, 取決于你插值后的數(shù)據(jù)需要多大。
基本思想為,遍歷目標(biāo)矩陣的坐標(biāo),如x*y這個(gè)點(diǎn),找到這個(gè)點(diǎn)在原始矩陣中對(duì)應(yīng)的位置,稱為映射點(diǎn),然后找到這個(gè)映射點(diǎn)P在原始矩陣中周?chē)乃膫€(gè)點(diǎn),然后根據(jù)映射點(diǎn)P到這個(gè)四個(gè)點(diǎn)的x和y方向上的坐標(biāo)的距離,進(jìn)行兩次線性插值,得到映射點(diǎn)的值即可。
如上圖所示,p點(diǎn)為目標(biāo)矩陣中x*y點(diǎn)在原始矩陣中映射的位置,它周?chē)罱挠蠶12,Q11,Q21,Q22四個(gè)點(diǎn),現(xiàn)在x方向進(jìn)行線性插值,得到R1和R2兩個(gè)點(diǎn)的值,再在y方向進(jìn)行一次線性插值,得到P點(diǎn)的值。
注意:用雙線性插值放大數(shù)據(jù)后,如果放大倍數(shù)過(guò)大,生成圖片后發(fā)現(xiàn)有著明顯的馬賽克現(xiàn)象
實(shí)現(xiàn)代碼參考后面js代碼
雙三次插值法
原理
雙三次插值又稱立方卷積插值。三次卷積插值是一種更加復(fù)雜的插值方式。該算法利用待采樣點(diǎn)周?chē)?6個(gè)點(diǎn)的灰度值作三次插值,不僅考慮到4 個(gè)直接相鄰點(diǎn)的灰度影響,而且考慮到各鄰點(diǎn)間灰度值變化率的影響。具體的原理可參考下面博客:
參考這里的博客
基本原理就是,先找到目標(biāo)矩陣中點(diǎn)在源數(shù)據(jù)矩陣中的映射點(diǎn)P,然后找到P點(diǎn)周?chē)?6個(gè)點(diǎn),然后根據(jù)P點(diǎn)坐標(biāo)距離16個(gè)點(diǎn)的x和y方向的距離,利用BiCubic函數(shù)算出每個(gè)點(diǎn)的權(quán)重,最后每個(gè)點(diǎn)乘以權(quán)重后,加起來(lái)即可得到P的值。
BiCubic函數(shù):
其中,a取-0.5時(shí),BiCubic函數(shù)具有如下形狀:
取a=-0.5時(shí),放大的數(shù)據(jù)挺好,生成的圖片非常平滑,也保留了很多細(xì)節(jié)。
具體為什么要用這個(gè)函數(shù),我也沒(méi)有深入研究,不過(guò)利用該方法放大數(shù)據(jù)后,生成圖片效果很好,沒(méi)有馬賽克現(xiàn)象
js實(shí)現(xiàn)
/** * 數(shù)據(jù)處理工具類(也可以自己直接定義方法,不用class) */class DataUtil { constructor() {}}/** * 數(shù)據(jù)插值 * @param w 目標(biāo)矩陣寬度 * @param h 目標(biāo)矩陣高度 * @param data 源數(shù)據(jù)矩陣(二維數(shù)組) * @param type 插值方式,1:雙線性插值,2:雙三次插值法 */DataUtil.scaleData = function(w, h, data, type = 2) { let t1 = new Date().getTime(); let dw = data[0].length; let dh = data.length; let resData = new Array(h); for (let j = 0; j < h; j++) { let line = new Array(w); for (let i = 0; i < w; i++) { let v; if (type === 2) { // 雙三次插值法 v = DataUtil.cubicInterpolation(w, h, i, j, data); } else if (type === 1) { // 雙線性插值 v = DataUtil.interpolation(w, h, i, j, data); } else { throw new Error('scale data, type not supported(type must be 1 or 2)'); } line[i] = Math.round(v); } resData[j] = line; } let t2 = new Date().getTime(); console.log("數(shù)據(jù)插值耗時(shí):", (t2 - t1)); return resData;}/** * 雙線性插值 * @param sw 目標(biāo)矩陣的寬度 * @param sh 目標(biāo)矩陣的高度 * @param x_ 目標(biāo)矩陣中的x坐標(biāo) * @param y_ 目標(biāo)矩陣中的y坐標(biāo) * @param data 源數(shù)據(jù)矩陣(二維數(shù)組) */DataUtil.interpolation = function(sw, sh, x_, y_, data) { let t1 = new Date().getTime(); let w = data[0].length; let h = data.length; let x = (x_ + 0.5) * w / sw - 0.5; let y = (y_ + 0.5) * h / sh - 0.5; let x1 = Math.floor(x); let x2 = Math.floor(x + 0.5); let y1 = Math.floor(y); let y2 = Math.floor(y + 0.5); x1 = x1 < 0 ? 0 : x1; y1 = y1 < 0 ? 0 : y1; x1 = x1 < w - 1 ? x1 : w - 1; y1 = y1 < h - 1 ? y1 : h - 1; x2 = x2 < w - 1 ? x2 : w - 1; y2 = y2 < h - 1 ? y2 : h - 1; // 取出原矩陣中對(duì)應(yīng)四個(gè)點(diǎn)的值 let f11 = data[y1][x1]; let f21 = data[y1][x2]; let f12 = data[y2][x1]; let f22 = data[y2][x2]; // 計(jì)算該點(diǎn)的值 let xm = x - x1; let ym = y - y1; let r1 = (1 - xm) * f11 + xm * f21; let r2 = (1 - xm) * f12 + xm * f22; let value = (1-ym) * r1 + ym * r2; return value;}/** * 雙三次插值法 * @param sw 目標(biāo)矩陣的寬度 * @param sh 目標(biāo)矩陣的高度 * @param x_ 目標(biāo)矩陣中的x坐標(biāo) * @param y_ 目標(biāo)矩陣中的y坐標(biāo) * @param data 源數(shù)據(jù)矩陣(二維數(shù)組) */DataUtil.cubicInterpolation = function (sw, sh, x_, y_, data) { let w = data[0].length; let h = data.length; // 計(jì)算縮放后坐標(biāo)對(duì)應(yīng)源數(shù)據(jù)上的坐標(biāo) let x = x_ * w / sw; let y = y_ * h / sh; // 計(jì)算x和y方向的最近的4*4的坐標(biāo)和權(quán)重 let wcx = DataUtil.getCubicWeight(x); let wcy = DataUtil.getCubicWeight(y); // 權(quán)重 let wx = wcx.weight; let wy = wcy.weight; // 坐標(biāo) let xs = wcx.coordinate; let ys = wcy.coordinate; let val = 0; // 遍歷周?chē)?*4的點(diǎn),根據(jù)權(quán)重相加 for (let j = 0; j < 4; j++) { let py = ys[j]; py = py < 0 ? 0 : py; py = py > h - 1 ? h - 1 : py; for (let i = 0; i < 4; i++) { let px = xs[i]; px = px < 0 ? 0 : px; px = px > w - 1 ? w - 1 : px; // 該點(diǎn)的值 let dv = data[py][px]; // 該點(diǎn)的權(quán)重 let w_x = wx[i]; let w_y = wy[j]; // 根據(jù)加權(quán)加起來(lái) val += (dv * w_x * w_y); } } return val;}/** * 雙三次插值法中,基于BiCubic基函數(shù),計(jì)算源坐標(biāo)v,最近的4*4的坐標(biāo)和坐標(biāo)對(duì)應(yīng)的權(quán)重 * @param v 目標(biāo)矩陣中坐標(biāo)對(duì)應(yīng)在源矩陣中坐標(biāo)值 */DataUtil.getCubicWeight = function (v){ let a = -0.5; // 取整 let nv = Math.floor(v); // 坐標(biāo)差值集合 let xList = new Array(4); // 坐標(biāo)集合 let xs = new Array(4); // 最近的4個(gè)坐標(biāo)差值 xList[0] = nv - v - 1; xList[1] = nv - v xList[2] = nv - v + 1; xList[3] = nv - v + 2; // xs[0] = nv - 1; xs[1] = nv; xs[2] = nv + 1; xs[3] = nv + 2; // 計(jì)算權(quán)重 let ws = new Array(4); for (let i = 0; i < 4; i++) { let val = Math.abs(xList[i]); let w = 0; // 基于BiCubic基函數(shù)的雙三次插值 if (val <= 1) { w = (a + 2) * val * val * val - (a + 3) * val * val + 1; } else if (val < 2) { w = a * val * val * val - 5 * a * val * val + 8 * a * val - 4 * a; } ws[i] = w; } return { weight: ws, coordinate: xs };}
以上是“如何實(shí)現(xiàn)js的雙線性插值和雙三次插值法”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
名稱欄目:如何實(shí)現(xiàn)js的雙線性插值和雙三次插值法
鏈接URL:http://muchs.cn/article28/pdghjp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供動(dòng)態(tài)網(wǎng)站、網(wǎng)站導(dǎo)航、云服務(wù)器、服務(wù)器托管、品牌網(wǎng)站建設(shè)、App設(shè)計(jì)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)