2017年11月16日 星期四

[原始碼] C / C++ 線性插補 bilinear 與注意事項

[原始碼] C / C++ 線性插補 bilinear 與注意事項

linear 意思就是依照比例去補數值,可以參考維基百科也說得很詳細
https://www.wikiwand.com/en/Bilinear_interpolation
簡單來說就是現在有兩個點 A=0 與 B=10
現在 A 的座標是 0,B的座標1,那個我要取出AB這個位置的點該怎麼辦呢
就是依照比例取因為比較靠近A所以分到的A比較多,離B比較遠分到的就比較少
現在讓我們來計算算法如下
AB = A*dx2 + B*dx1
大致上就這樣而已,如果是二維空間那個就要算三次這個公式意思如下
有ABCD四個點,把他當成2條一維就是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+)
    size_t x0 = floor(x);
    size_t x1 = ceil(x);
    size_t y0 = floor(y);
    size_t y1 = ceil(y);
    // 獲取比例(只能用 1-)
    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;
}
//================================================================

縮放比例

縮小比例的時候,要注意新圖的點要向中間對齊,不然最右邊那一整排跟最下面一種排會沒有被計算到。
srcX = (dstX+0.5) * (srcW/dstW) - 0.5
srcY = (dstY+0.5) * (srcH/dstH) - 0.5
放大比例的時候則是要注意最外圍的一圈正好要疊在點上面
srcY = ((j)/ ((newH-1.f) / (height-1.f)) );
srcX = ((i)/ ((newW-1.f) / (width-1.f)) );
另外上述算是為了容易表達沒有加上修飾,要注意除法的時候如果是
(int) / (int);
返回的數值可能會出問題,至少讓其中一個是float或double才可以獲得正確結果。
然後連續除法兩次另一個除法也可以優化成乘法,效率會好一咪咪咪咪。
srcY = j * (height-1.f) / (newH-1.f);
srcX = i * (width-1.f)  / (newW-1.f);
這裡用到一個小技巧是 width-1.f 後面那一個是float所以在呼叫 operator-(float, float) 的時候會呼叫兩者都是float的operator,就幫你隱式轉型了。

沒有留言:

張貼留言