[原始碼] C / C++ 線性插補 bilinear 與注意事項
linear 意思就是依照比例去補數值,可以參考維基百科也說得很詳細
https://www.wikiwand.com/en/Bilinear_interpolation
https://www.wikiwand.com/en/Bilinear_interpolation
現在 A 的座標是 0,B的座標1,那個我要取出AB這個位置的點該怎麼辦呢
就是依照比例取因為比較靠近A所以分到的A比較多,離B比較遠分到的就比較少
就是依照比例取因為比較靠近A所以分到的A比較多,離B比較遠分到的就比較少
現在讓我們來計算算法如下
AB = A*dx2 + B*dx1
有ABCD四個點,把他當成2條一維就是AB跟CD,分別先算出他們中間的點
然後再把AB跟CD這兩個點在做一次運算就可以算出中間那個點了
然後再把AB跟CD這兩個點在做一次運算就可以算出中間那個點了
float AB = A*dx2 + B*dx1;
float CD = C*dx2 + D*dx1;
float X = AB*dy2 + CD*dy1;
linear是一維的意思,bilinear就是二維的意思,在上去還有三維都是差不多意思推倒
實作代碼
實作的時候要注意幾點
獲取鄰點
獲取鄰近點的時候不能用+1或-1,假設現在的點的 0 和 1,需要獲取的點是 1,先使用 floor 無條件捨去獲得1在加1就變成2了;正確的來說因為正好就落在點上,就直接把AB都帶那個點就可以了算出來是正確的
使用 ceil() 這樣就可以正確獲得了
獲取比例
這時候也是一樣,不過情況倒是反過來了,一定要用1去減,否則如果出現兩個一樣的點,比例兩邊都是0。
/*****************************************************************
Name :
Date : 2017/11/16
By : CharlotteHonG
Final: 2017/11/16
*****************************************************************/
#include <iostream>
#include <cmath>
using namespace std;
float linear(int* arr, float pos){
// 獲取鄰點(不能用 1+)
size_t c0 = floor(pos);
size_t c1 = ceil(pos);
// 獲取比例(只能用 1-)
float dx1 = pos - c0;
float dx2 = 1 - dx1;
// 乘出比例(要交叉)
float X = arr[c0]*dx2 + arr[c1]*dx1;
return X;
}
float bilinear(int* arr, size_t w, float y, float x){
// 獲取鄰點(不能用 1+, 選中結尾時 x0=x1=W-1 才是對的)
size_t x0 = floor(x);
size_t x1 = ceil(x);
size_t y0 = floor(y);
size_t y1 = ceil(y);
// 獲取比例(只能用 1-, 選中結尾時 1:0 才是對的)
float dx1 = x - x0;
float dx2 = 1 - dx1;
float dy1 = y - y0;
float dy2 = 1 - dy1;
// 獲取點
const float& A = arr[y0*w + x0];
const float& B = arr[y0*w + x1];
const float& C = arr[y1*w + x0];
const float& D = arr[y1*w + x1];
// 乘出比例(要交叉)
float AB = A*dx2 + B*dx1;
float CD = C*dx2 + D*dx1;
float X = AB*dy2 + CD*dy1;
return X;
}
//================================================================
int main(int argc, char const *argv[]){
int arr[] = {0, 10};
cout << linear(arr, 0.1f) << endl;
int arr2[] = {0, 0, 10, 10};
cout << bilinear(arr2, 2, 0.1f, 0.1f) << endl;
return 0;
}
//================================================================
縮放比例
以下 dst 指的是處理過後的圖,src 指的是原圖。
縮小比例的時候,要注意新圖的點要向中間對齊,不然最右邊那一整排跟最下面一種排會沒有被計算到。
// 縮小向中間對齊
r=srcW/dstW;
double srcX = (dstX+r) * ((double)srcW/dstW) - r;
double srcY = (dstY+r) * ((double)srcH/dstH) - r;
原理其實就是原本會向左上角也就是原點對齊,然後再加上偏差值讓他向中間對齊。
不過這樣向中間對齊有個缺陷,邊緣的向素值會丟失,數值丟失還算小問題,比較大的問題是連帶圖片會稍微放大,假設你做個10次放大縮小,你會發現可能原圖邊緣一圈整個不見了。
// 縮小的倍率
double r1W = ((double)src.width )/(dst.width );
double r1H = ((double)src.height)/(dst.height);
// 縮小時候的誤差
double deviW = ((src.width-1.0) - (dst.width -1.0)*(r1W)) /dst.width;
double deviH = ((src.height-1.0) - (dst.height-1.0)*(r1H)) /dst.height;
// 縮小保持邊緣對齊
srcX = i*(r1W+deviW);
srcY = j*(r1H+deviH);
公式從原本的向原點對齊,會產生一定誤差,把這個誤差分攤給每個位置。
放大比例的時候則是要注意最外圍的一圈正好要疊在點上面
// 放大保持邊緣對齊
double srcX = (dstX)* ((width-1.0) / (newW-1.0));
double srcY = (dstY)* ((height-1.0) / (newH-1.0));
另外上述算是為了表達沒有加上(double)轉型修飾,是因為有 +1.0 會自動隱式轉型,所以不用補。但是如果是 (int)/(double) 就要補,這個是後面的會隱式轉型成 (int)。
沒有留言:
張貼留言