熱線電話:0755-23712116
郵箱:contact@shuangyi-tech.com
地址:深圳市寶安區(qū)沙井街道后亭茅洲山工業(yè)園工業(yè)大廈全至科技創(chuàng)新園科創(chuàng)大廈2層2A
講插值之前,首先講像素重采樣的概念。假設有圖像A和圖像B,其中A為源圖像,B為目標圖像,A與B的坐標具有對應關(guān)系f:
(xa, ya)=f(xb, yb)
通過關(guān)系f,把A的像素值賦值給B中對應像素點的過程,叫做圖像A的像素重采樣,圖像B為重采樣之后的圖像。比如對于B的任意像素點(x, y),其對應的A的像素點為(x', y'),那么則把A中點(x', y')的像素值A(chǔ)(x', y')賦值給B中點(x, y)的像素值B(x, y)。
(x‘, y’)=f(x, y)
B(x, y)=A(x', y')
像素重采樣的常見應用場景為圖像縮放和圖像配準。在實際應用過程中,源圖像A的對應坐標往往不是整數(shù),而是浮點型數(shù)據(jù)(如下圖所示),因此不能直接取其像素值賦值給目標圖像上的對應點。此時要獲取點A(x', y')的像素值,插值算法就派上用場了。
所謂插值,就是使用浮點型坐標點的周圍整型點的像素值來計算該浮點型坐標點的像素值。比如上圖中,點(x', y')為浮點型坐標點,使用其周圍整型點p0、p1、p2、p3的像素值來計算其像素值A(chǔ)(x', y'),這個過程就是插值。
常見的插值算法有最鄰近插值、雙線性插值、雙三次插值,不管什么插值算法,其本質(zhì)都是取浮點型坐標點周圍的n*n個整型坐標點的像素值進行加權(quán)和,從而得到該浮點型坐標點的像素值,如下式:
不同插值算法的區(qū)別在于權(quán)重W的計算不一樣,下文將分別詳細講解最鄰近插值、雙線性插值、雙三次插值的計算原理與實現(xiàn)。
1. 最鄰近插值
最鄰近插值取離浮點型坐標點的最近點的像素值作為其像素值,也可以看成使用浮點型坐標點周圍2*2個整型點的像素值來計算其像素值,不過只有最靠近的那個點權(quán)重為1,其余3個點權(quán)重系數(shù)都為0。
插值的計算如下式:
其權(quán)重計算如下式:
最鄰近插值的代碼實現(xiàn)最簡單,直接對浮點型坐標進行四舍五入取整即可,假設浮點坐標為(x_float, y_float),那么使用最鄰近插值計算A(x_float, y_float)的代碼實現(xiàn)如下:
int x = (int)(x_float + 0.5); //加0.5再截斷取整,與四舍五入取整等效
int y = (int)(y_float + 0.5);
uchar inner_value = A.ptr(y)[x]; //A(x',y')=A(x,y)
2. 雙線性插值
雙線性插值與最鄰近插值類似,同樣使用浮點型坐標點周圍2*2個整型點的像素值來計算其像素值,不過其周圍每個整型點的權(quán)重都不為0,也就是說,其權(quán)重計算與最鄰近插值不一樣:
浮點坐標點(x_float, y_float)雙線性插值的代碼實現(xiàn)如下:
int x0 = floor(x_float);
int x1 = x0 + 1;
int y0 = floor(y_float);
int y1 = y0 + 1;
float fracRow = y_float - y0; //求浮點坐標的小數(shù)部分
float fracCol = x_float - x0;
float k0 = 1.0 - fracRow;
float k1 = 1.0 - fracCol;
float w0 = k0*k1;
float w1 = fracRow*k1;
float w2 = k0*fracCol;
float w3 = fracRow*fracCol;
uchar inner_value = (uchar)(w0 * A.ptr(y0)[x0] + w1 * A.ptr(y1)[x0] + w2 * A.ptr(y0)[x1] + w3 * A.ptr(y1)[x1]);
3. 雙三次插值
雙三次插值使用浮點型坐標點周圍4*4個整型點的像素值來計算其像素值,如下圖所示:
浮點型坐標點的插值為其周圍4*4整型坐標點像素值的加權(quán)和:
其中權(quán)重W(i,j)的計算如下式,其中a取值范圍-1~0之間,一般取固定值-0.5。
雙三次插值的實現(xiàn)代碼如下。
首先是權(quán)重函數(shù)的實現(xiàn):
float cubic_coeff(float x, float a)
{
if(x <= 1)
{
return 1-(a+3)*x*x+(a+2)*x*x*x;
}
else if(x < 2)
{
return -4*a+8*a*x-5*a*x*x+a*x*x*x;
}
return 0.0;
}
接著是權(quán)重系數(shù)的計算實現(xiàn):
void cal_cubic_coeff(float x, float y, float *coeff)
{
/*calc the coeff*/
float u = x - floor(x);
float v = y - floor(y);
u += 1;
v += 1;
float a = -0.15;
float a_mul_4 = (a + a) + (a + a);
float a_mul_5 = a_mul_4 + a;
float a_mul_8 = a_mul_4 + a_mul_4;
float a_add_3 = a + 3;
float a_add_2 = a + 2;
float A[4];
A[0] = cubic_coeff(abs(u), a);
A[1] = cubic_coeff(abs(u-1), a);
A[2] = cubic_coeff(abs(u-2), a);
A[3] = cubic_coeff(abs(u-3), a);
for (int s = 0; s < 4; s++)
{
float C = cubic_coeff(abs(v-s), a);
coeff[s*4] = A[0]*C;
coeff[s*4+1] = A[1]*C;
coeff[s*4+2] = A[2]*C;
coeff[s*4+3] = A[3]*C;
}
}
最后,是雙三次插值代碼:
uchar cubic_inner(Mat A, float x_float, float y_float, float a)
{
float coeff[16];
cal_cubic_coeff(x_float, y_float, coeff); //計算權(quán)重系數(shù)
float sum = 0.0;
int x0 = floor(x_float) - 1;
int y0 = floor(y_float) - 1;
for(int i = 0; i < 4; i++)
{
for(int j = 0; j < 4; j++)
{
sum += coeff[i*4+j]*A.ptr(y0+i)[x0+j];
}
}
uchar inner_value = (uchar)sum;
return inner_value;
}
從插值效果來說:雙三次插值>雙線性插值>最鄰近插值,從計算復雜度來說,同樣是:雙三次插值>雙線性插值>最鄰近插值。所以實際使用時,根據(jù)自己的需要選擇合適的插值算法。
如涉及侵權(quán),請相關(guān)權(quán)利人與我司聯(lián)系刪除