雙線性插值

2021-07-08 14:52:44 字數 3499 閱讀 1673

雙線性插值作為opencv中預設使用的影象縮放演算法,其效果和速度都是不錯的。並且效果也比較穩定,計算複雜度並不算太高。我看了很多網上的演算法,自己也沒看太懂,下面是從網上找的雙線性插值 演算法的講解。

「影象的雙線性插值放大演算法中,目標影象中新創造的象素值,是由源影象位置在它附近的2*2區域4個鄰近象素的值通過加權平均計算得出的。雙線性內插值演算法放大後的影象質量較高,不會出現畫素值不連續的的情況。然而次演算法具有低通濾波器的性質,使高頻分量受損,所以可能會使影象輪廓在一定程度上變得模糊。」

下面還是根據我自己的理解來繼續講述吧,相信讀者中有很多高手,希望讀者能給予我指點一下,讓我也能更明白一些。

雙線性插值 演算法和最近鄰插值演算法比較類似。在最近鄰插值演算法中,目標影象中的某個點(x,y)是去源影象中找最鄰近的乙個點(x0, y0)即可。目標影象中的點(x, y)對應於源影象中的點(x0',y0'),x0'、y0'很可能不是整數,而是小數,而最近鄰插值演算法是找其鄰近整型值(int(x0'+0.5f),int(y0'+0.5f))(上篇文章中沒有進行四捨五入)。我們現在找x0', y0'所在位置旁邊的四個點,根據這四個點與(x0',y0')距離的關係計算目標影象中(x,y)一點的畫素值。演算法描述如下:

(1)計算源影象與目標影象寬與高的比例

w0 : 表示源影象的寬度

h0 : 表示源影象的高度

w1 : 表示目標影象的寬度

h1 : 表示目標影象的高度

float fw = float(w0-1)/(w1-1);

float fh = float(h0-1)/(h1-1);

(2)針對目標影象的乙個點(x, y),計算在源影象中的對應座標,結果為浮點數。

float x0 = x * fw;

float y0 = y * fh;

int x1 = int(x0);

int x2 = x1 + 1;

int y1 = int(y0);

int y2 = y1+1;

所求的源影象中的四個點座標為(x1, y1) (x1, y2) (x2, y1) (x2,y2)

(3)求周圍四個點所佔的權重比值

如上圖,

fx1 = x0 - x1;

fx2 = 1.0f - fx1;

fy1 = y0 - y1; 

fy2 = 1.0f - fy1;

float s1 = fx1*fy1;

float s2 = fx2*fy1;

float s3 = fx2*fy2;

float s4 = fx1*fy2;

我們以value(座標)來代表取得此點的座標值,則:

value(x0,y0) = value(x2,y2)*s1+value(x1,y2)*s2+value(x1,y1)*s3+value(x2,y1)*s4;

如果 對上述運算不夠明白 的話,可以這樣來求。

我們先要求得(x0, y1) 和(x0,y2)的畫素值。

則float value(x0,y1) = value(x1,y1)*fx2 + value(x2,y1)*fx1;

float value(x0,y2) = value(x1,y2)*fx2 + value(x2,y2)*fx1;

注釋:離某點越近,離權重越大,故取其與1的差值。

float value(x0,y0) = value(x0,y1)*fy2 + value(x0,y2)*fy1;

驗證後與上邊公式一樣。

(4)求得值後填充到目標影象上就可以了。

為了能讓人更容易理解,咱還是使用getpixel和setpixel進行取值和賦值。

void resizelinear01(cimage& src, cimage& dst)}}

測試程式仍是將670*503尺寸的縮放為200*160。經過測試,上邊的演算法執行一次就需要0.5秒左右,可以說是非常的慢。如果將其縮放成2000*1600大小的,一次就需要50秒。這是非常可怕的,假如現在我們做乙個類似photoshop的軟體,使用者想將其擴大若干倍,卻要等50秒,估計使用者已經沒有耐心使用您的軟體了。

我們來分析一下怎樣可以優化程式。在每一步優化演算法後,我們都再用上邊同樣的程式和步驟進行測試,觀察執行所用時間。

(1)改函式呼叫 為指標操作如第1節。(進行完此步後,程式只需要0.2秒左右,速度提高了250倍,哈哈!)

(2)將x0,y0座標的計算提取到 迴圈外,因為第二層迴圈裡的x座標每次迴圈都要重複一次,並且是重複的。(仍然是需要0.2秒左右。我們讓其迴圈一百次,再比較下所用時間。使用resizelinear02方法用時19.6秒,使用resizelinear02方法都是用時17.4秒,看來還是有作用的。)

下面是最終的**。

void resizelinear04(cimage& src, cimage& dst)

for(int y=0; yint(y0);

y2 = int(y0+0.5f);

fy1 = y0-y1;

fy2 = 1.0f - fy1;

//trace(l"y=%6d; y0=%6.3f; y1=%6d; y2=%6d; fy1=%6.3f;\n", y, y0, y1, y2, fy1);

for(int x=0; x1.0f-fx1;

float s1 = fx2*fy2;

float s2 = fx1*fy2;

float s3 = fx1*fy1;

float s4 = fx2*fy1;

//trace(l"s1=%6.3f; s2=%6.3f; s3=%6.3f; s4=%6.3f; sum=%6.3f\n", s1,s2,s3,s4, s1+s2+s3+s4);

byte* p11 = psrc + pitch0*y1 + 3*x1;

byte* p12 = psrc + pitch0*y1 + 3*x2;

byte* p21 = psrc + pitch0*y2 + 3*x1;

byte* p22 = psrc + pitch0*y2 + 3*x2;

*p1 = byte((*p11)*s1 + (*p12)*s2 + (*p21)*s4 + (*p22)*s3); p1++; p11++; p12++; p21++; p22++;

*p1 = byte((*p11)*s1 + (*p12)*s2 + (*p21)*s4 + (*p22)*s3); p1++; p11++; p12++; p21++; p22++;

*p1 = byte((*p11)*s1 + (*p12)*s2 + (*p21)*s4 + (*p22)*s3); p1++;

}p1 = pdst + y*pitch1;

}delete arr_x1;

delete arr_x2;

delete arr_fx1;

}

雙線性插值

轉至 雙線性插值,這個名字咋一聽很高大上的樣紙,再在維基百科上一查 見文末,我去,一堆的公式嚇死人 像俺這種半文盲,看到公式腦子就懵的型別,真心給跪。雖然看著好複雜,但仔細一看道理再簡單不過了,所以還是自己梳理一下好。雙線性插值,顧名思義就是兩個方向的線性插值加起來 這解釋過於簡單粗暴,哈哈 所以只...

雙線性插值

雙線性插值就是在x軸和y軸兩個方向上進行插入操作。假設a b兩個點,要在ab中間插入乙個點c c座標在ab連線上 就直接讓c的值落在ab的連線上即可。例如a點座標 0,0 值為3,b點座標 0,2 值為5,要對座標 0,1 的點c進行插值,就讓c落在ab上,值就為4。如果c點不在ab線上,如圖所示 ...

線性插值和雙線性插值

線性插值 如果你只處理分離的資料 想知道分離點之間的某些值,需要用到某種型別的插值。這種情況如圖5 17座標所示。對某些分離的 整數 x值,你知道y值。當x 2,你知道y 10,x 3時y 30。但你不知道x 2.7時的y值。使用線性插值,你通過連線兩點的線段找到x 2.7對應的y值,如圖1所示。使...