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