雙線性插值算法


1、介紹雙線性插值算法前先講下線性插值(Linear Interpolate):
在數學中,線性插值是一種曲線擬合方法,利用線性多項式在已知數據點的離散集合范圍內構造新的數據點。
兩個已知點之間的線性插值:
已知兩點由坐標(x0,y0)和(x1,y1)給出,線性插值就是兩點之間的直線。對於區間(x0,x1)中的x值,由方程給出沿直線的y值
(1)
 
已知2點(x0,  y0)、(x1, y1),
設線性方程:f(x) = ax + b
則有:
    y0 = f(x0)  = a * x0 + b;
    y1 = f(x1)  = a * x1 + b;
=>
y1 - y0 = a (x1 - x0)
得:a = (y1 - y0) / (x1 - x0),a是斜率
y0 = a * x0 + b = (y1 - y0) / (x1 - x0)  * x0 + b
b = (x1 * y0 - x0 * y1) / (x1 - x0)
=>
y = f(x) = (y1 - y0) / (x1- x0) * x  +(x1 * y0 - x0 * y1) / (x1 - x0)
 
應用舉例:D3.js的d3.interpolateRgb顏色插值原理應該用的是線性插值算法,根據RGB3個分量值的范圍(0-255)分別進行線性插值。
 
2、雙線性插值(Bilinear Interpolate)
在數學上,雙線性插值是線性插值的拓展,是針對2維直線網格上的2個自變量函數z = f(x, y)的插值,涉及到2元函數,要復雜點。
主要思想是先在一個方向上執行線性插值,再在另一個方向上操作一次。雖然樣本值和樣本位置中的每一步插值是線性的,但是整體上是非線性的。
 

 

 注:紅色點是原有的數據點,綠色點是我們想要插值的結果。

 
算法思路:
令我們要找的未知值的函數關系 f 和相應坐標(x, y), 假設已知的4個點是Q11 = (x1, y1),Q12=(x2, y2),Q21=(x2, y1), Q22=(x2, y2)。
首先在x軸上做線性插值,得到如下式子:
(2)
分析:
點R1是在Q11與Q21之間在x軸上進行一次線性插值的結果,y值都是y1不變。
f(x, y1) ≈ a * f(Q11) + b * f(Q21)
a = (x2 - x) / (x2 - x1),  b = (x - x1) / (x2 - x1),這2個系數是如何確定的?
我認為和點的(x, y)位置相關,Q11、Q21對點R1的值f(x, y1)的影響程度取決於它們和R1點的距離值,也就是|x - x1|和|x2 - x|,誰與點R1距離近,影響程度越大,也就是相應的系數越大。
總的距離為| x2 - x1|,即點Q11與Q21之間的距離D, R1距離Q11為d1 = |x - x1|,   R1距離Q21為d2 = |x2 - x|,且R1位於Q11與Q21之間,即 x1 < x < x2,  當d1減小時,d2會變大,而此時Q11的相關系數a應該變大,Q21的相關系數b應變小,觀察發現d1/D會隨着d1變小而變小,d2 = D-d1,  d2/D會隨着d1變小而變大,因此a = d2/D = (x2 - x) / (x2 - x1),b = (x - x1) / (x2 - x1),這樣a * f(Q11)在隨R1靠近點Q11而變大,b * f(Q21)隨R11遠離Q21而變小,滿足條件。
 
 
我們繼續在y軸上插值得到估算式子:
(3)
注:先在x軸還是y軸上插值的最終結果是一致的,順序不影響插值結果。
 
上面的代數式比較繁,我們可以用矩陣來求解,以達到化繁為簡。
設二元函數如下:
代入四個已知點,系數a0,a1,a2,a3可以通過解線性系統得到:
若一個解用f(Q)表示,我們可以寫成:
  (4)
推導:
       a0 + a1 * x + a2 * y + a3 * xy = b11 * f(Q11) + b12 * f(Q12) + b21 * f(Q21) + b22 * f(Q22)
=>  
 

 =>

 =>

 

=>

   =>

=>

  得出最終式子:

(5)

系數可以通過解線性系統獲得。

若用一個坐標系來表示Q11 =(0,0)、Q12 =(0,1)、Q21 =(1, 0)、Q22 =(1, 1)四個已知的點,則插值公式可以簡化為:

(6)

 

 上面的式子可以直接把坐標代入式子(3)得到,也可以解式子(5)矩陣求得系數b11、b12、b21、b22,再代入(4)得到,計算過程如下(復習下線性代數):

復習:矩陣A的逆矩陣A^-1可以用高斯若爾當消元法;A*A^-1 = I(單位矩陣); (A^-1)^T = (A^T)^-1。

 

得:b11 = (1 - x) * (1 - y), b12 = y * (1 - y) ,  b21 = x * (1 - y) , b22 = xy

f(x, y) = b11 * f(Q11) + b12 * f(Q12) + b21 * f(Q21) + b22 * f(Q22) = (1 - x) * (1 - y) * f(0, 0) + y * (1 - x) * f(0, 1) + x * (1 - y) * f(1, 0) + xy * f(1, 1), 整理即為式(6)

 

 3、雙線性插值算法應用:對NCEP氣象數據使用雙線性插值以增加數據的稠密度,改善繪制的氣象圖譜效果。

溫度分布圖繪制:

 /**
     * 直接根據原始數據繪制,不插值
     */
    private void drawGridsWithoutInterpolation(){
        int x = 0, xMax = this.imgWidth, y = 0, yMax = this.imgHeight;
        int nx = this.entity.getNx();
        int ny = this.entity.getNy();
        double lo1 = this.entity.getLo1();
        double lo2 = this.entity.getLo2();
        double la1 = this.entity.getLa1();
        double la2 = this.entity.getLa2();
        double deltLO = lo2 - lo1;
        double deltLA = la2 - la1;
        Double[] data = this.entity.getData();
        double[] color = {0, 0, 0, 0}; //Transparent black
        for(int i=x; i<xMax; i++) {
            for(int j=y; j<yMax; j++){
                Point p = CRSutil.toPoint(i, j);
                p = p.add(this.pixelOrigin);
                LngLat lnglat = this.crs.pixelPointToLngLat(p);
                double lng = lnglat.lng;
                double lat = lnglat.lat;
                int col = (int) Math.floor((lng -lo1)/deltLO * nx);
                int row = (int) Math.floor((lat -la1)/deltLA * ny);
                if(col<0) {
                    col = 0;
                }
                if(col>=xMax){
                    col = xMax-1;
                }
                if(row<0){
                    row = 0;
                }
                if(col>=yMax){
                    row = yMax - 1;
                }
                int index = row * nx + col;
                double value = data[index];
                color = this.scale.getPointColor(value, OVERLAY_ALPHA);
                drawGrid(i, j, color);
                drawGrid(i+1, j, color);
                drawGrid(i, j+1, color);
                drawGrid(i+1, j+1, color);
            }
        }

    }
    

 

 /**
     * 先插值再繪制
     */
    private void interpolateField() {
        int x = 0, xMax = this.imgWidth, y = 0, yMax = this.imgHeight;
        Double scalar = 0d;
        double[] color = {0, 0, 0, 0}; //Transparent black
        for(int i=x; i<xMax; i+=2) {
            for(int j=y; j<=yMax; j+=2) {
               Point p = CRSutil.toPoint(i, j);
               p = p.add(this.pixelOrigin);
               LngLat lnglat = this.crs.pixelPointToLngLat(p);
               if(this.entity.getMeteoType().equals(MeteoTypeEnum.WIND)){
                    //TODO
               }else{
                  scalar = interpolate(lnglat, this.grid);
                  if(scalar==null){
                      continue;
                  }
                  color = this.scale.getPointColor(scalar, OVERLAY_ALPHA);
                //   logger.info(scalar+"--"+color[0]+","+color[1]+","+color[2]+","+color[3]);
                  drawGrid(i, j, color);
                  drawGrid(i+1, j, color);
                  drawGrid(i, j+1, color);
                  drawGrid(i+1, j+1, color);
               }
            }
        }

    }

 

效果圖對比:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM