双线性插值理论与代码实例
1. 雙線性插值
? ? ? 假設源圖像大小為mxn,目標圖像為axb。那么兩幅圖像的邊長比分別為:m/a和n/b。注意,通常這個比例不是整數,編程存儲的時候要用浮點型。目標圖像的第(i,j)個像素點(i行j列)可以通過邊長比對應回源圖像。其對應坐標為(i*m/a,j*n/b)。顯然,這個對應坐標一般來說不是整數,而非整數的坐標是無法在圖像這種離散數據上使用的。雙線性插值通過尋找距離這個對應坐標最近的四個像素點,來計算該點的值(灰度值或者RGB值)。
? 若圖像為灰度圖像,那么(i,j)點的灰度值的數學計算模型是:
f(x,y)=b1+b2x+b3y+b4xy
其中b1,b2,b3,b4是相關的系數。關于其的計算過程如下如下:
? ? ? 如圖,已知Q12,Q22,Q11,Q21,但是要插值的點為P點,這就要用雙線性插值了,首先在x軸方向上,對R1和R2兩個點進行插值,這個很簡單,然后根據R1和R2對P點進行插值,這就是所謂的雙線性插值。
?
? ? ? 雙線性插值,又稱為雙線性內插。在數學上,雙線性插值是有兩個變量的插值函數的線性插值擴展,其核心思想是在兩個方向分別進行一次線性插值。
假如我們想得到未知函數??在點??的值,假設我們已知函數??在?,?,?, 及??四個點的值。
首先在?x?方向進行線性插值,得到
然后在?y?方向進行線性插值,得到
這樣就得到所要的結果?,
如果選擇一個坐標系統使得??的四個已知點坐標分別為 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那么插值公式就可以化簡為
或者用矩陣運算表示為
與這種插值方法名稱不同的是,這種插值方法的結果通常不是線性的,它的形式是
常數的數目都對應于給定的?f?的數據點數目
線性插值的結果與插值的順序無關。首先進行?y?方向的插值,然后進行?x?方向的插值,所得到的結果是一樣的。
2.存在的問題
這部分的前提是,你已經明白什么是雙線性插值并且在給定源圖像和目標圖像尺寸的情況下,可以用筆計算出目標圖像某個像素點的值。當然,最好的情況是你已經用某種語言實現了網上一大堆博客上原創或轉載的雙線性插值算法,然后發現計算出來的結果和matlab、openCV對應的resize()函數得到的結果完全不一樣。
那這個究竟是怎么回事呢?
其實答案很簡單,就是坐標系的選擇問題,或者說源圖像和目標圖像之間的對應問題。
按照網上一些博客上寫的,源圖像和目標圖像的原點(0,0)均選擇左上角,然后根據插值公式計算目標圖像每點像素,假設你需要將一幅5x5的圖像縮小成3x3,那么源圖像和目標圖像各個像素之間的對應關系如下:
只畫了一行,用做示意,從圖中可以很明顯的看到,如果選擇右上角為原點(0,0),那么最右邊和最下邊的像素實際上并沒有參與計算,而且目標圖像的每個像素點計算出的灰度值也相對于源圖像偏左偏上。
那么,讓坐標加1或者選擇右下角為原點怎么樣呢?很不幸,還是一樣的效果,不過這次得到的圖像將偏右偏下。
最好的方法就是,兩個圖像的幾何中心重合,并且目標圖像的每個像素之間都是等間隔的,并且都和兩邊有一定的邊距,這也是matlab和openCV的做法。如下圖:
如果你不懂我上面說的什么,沒關系,只要在計算對應坐標的時候改為以下公式即可,?
int x=(i+0.5)*m/a-0.5
int y=(j+0.5)*n/b-0.5?
instead of??
int x=i*m/a
int y=j*n/b?
利用上述公式,將得到正確的雙線性插值結果
總結:
總結一下,我得到的教訓有這么幾條。
1.網上的一些資料有的時候并不靠譜,自己還是要多做實驗。
2.不要小瞧一些簡單的、基本的算法,讓你寫你未必會寫,而且其中可能還藏著一些玄妙。
3.要多動手編程,多體會算法,多看大牛寫的源碼(雖然有的時候很吃力,但是要堅持看)。
源碼:
void scale(Mat &srcmat, Mat &desmat, double sx, double sy){int nc = x, nl = y, srccol = 0, srcrow = 0;double alph = 0.0, beta = 0.0;for (int i = 0; i < nc; i++){uchar* desdata = desmat.ptr<uchar>(i);for (int j = 0; j < nl; j++){srcrow = int(i / sx);//下面的的幾個if是判斷放大后圖像對應到原圖像的坐標是否越界的if (srcrow >= srcmat.rows - 1){srcrow = srcmat.rows - 2;}alph = i / sx - srcrow;if (alph >= 1)alph = 1;srccol = int(j / sy);if (srccol >= srcmat.cols - 1)srccol = srcmat.cols - 2;beta = j / sy - srccol;if (beta >= 1)beta = 1;for (int k = 0; k < 3; k++){double kk = srcmat.at<Vec3b>(srcrow, srccol)[k] +beta*(srcmat.at<Vec3b>(srcrow, srccol + 1)[k] - srcmat.at<Vec3b>(srcrow, srccol)[k]);double jj = srcmat.at<Vec3b>(srcrow + 1, srccol)[k] +beta*(srcmat.at<Vec3b>(srcrow + 1, srccol + 1)[k] - srcmat.at<Vec3b>(srcrow + 1, srccol)[k]);desdata[j * 3 + k] = kk + alph*(jj - kk);}}} }//sx=1.2,sy=1.6
//圖像基本信息輸出int image_height = image.size().height;int image_width = image.size().width;cout << "Image Info: height:" << image_height << " width:" << image_width << endl;//===============================================================================================================================//圖像處理-----雙線性插值cout << "Please input the Sx and Sy:" << endl;float Sx, Sy;cin >> Sx >> Sy;cout << "Sx = " << Sx << "; Sy = " << Sy << ";" << endl;Mat final_img;int final_img_height, final_img_width;final_img_height = image.size().height * Sx;final_img_width = image.size().width * Sy;final_img.create(final_img_height, final_img_width, CV_8UC1);int y, x;int x1, x2, y1, y2;float temp1, temp2;for (y = 0; y < final_img_height; y++){for (x = 0; x < final_img_width; x++){x1 = (int)(x / Sx);x2 = x1 + 1;y1 = (int)(y / Sy);y2 = y1 + 1;//判斷邊界if (y2 >= image_height || x2 >= image_width){final_img.at<uchar>(y, x) = image.at<uchar>(y1, x1);continue;}temp1 = (x2 - x / Sx) * image.at<uchar>(y1, x1) + (x / Sx - x1) * image.at<uchar>(y1, x2);temp2 = (x2 - x / Sx) * image.at<uchar>(y2, x1) + (x / Sx - x1) * image.at<uchar>(y2, x2);final_img.at<uchar>(y, x) = (uchar)((y2 - y / Sy) * temp1 + (y / Sy - y1) * temp2);}}//===============================================================================================================================//顯示處理前后的圖像namedWindow("original_image");imshow("original_image", image);namedWindow("final_image");cout << "Final image Info: height:" << final_img.size().height << " width:" << final_img.size().width << endl;imshow("final_image", final_img); //sx=1.2,sy=1.2
總結
以上是生活随笔為你收集整理的双线性插值理论与代码实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 热水器的开关在哪(厨房想用热水)
- 下一篇: 360免费wifi创建失败怎么办?创建失