影象縮放--插值法(opencv,原理)

總是加班的狗發表於2017-06-20

第一部分:  

   在做數字影象處理時,經常會碰到小數象素座標的取值問題,這時就需要依據鄰近象素的值來對該座標進行插值。比如:做地圖投影轉換,對目標影象的一個象素進行座標變換到源影象上對應的點時,變換出來的對應的座標是一個小數,再比如做影象的幾何校正,也會碰到同樣的問題。以下是對常用的三種數字影象插值方法進行介紹。

1、最鄰近元法

  這是最簡單的一種插值方法,不需要計算,在待求象素的四鄰象素中,將距離待求象素最近的鄰象素灰度賦給待求象素。設i+u,j+v(i, j為正整數, u, v為大於零小於1的小數,下同)為待求象素座標,則待求象素灰度的值 f(i+u,j+v) 如下圖所示:

 

 

 

如果(i+u, j+v)落在A區,即u<0.5,v<0.5,則將左上角象素的灰度值賦給待求象素,同理,落在B區則賦予右上角的象素灰度值,落在C區則賦予左下角象素的灰度值,落在D區則賦予右下角象素的灰度值。

最鄰近元法計算量較小,但可能會造成插值生成的影象灰度上的不連續,在灰度變化的地方可能出現明顯的鋸齒狀。

 

2、雙線性內插法

雙線性內插法是利用待求象素四個鄰象素的灰度在兩個方向上作線性內插,如下圖所示:

 

 

 

對於 (i, j+v),f(i, j) 到 f(i, j+1)的灰度變化為線性關係,則有:

   f(i, j+v) = [f(i, j+1) - f(i, j)] *v + f(i, j)

同理對於 (i+1, j+v) 則有:

      f(i+1,j+v) = [f(i+1, j+1) - f(i+1, j)] * v + f(i+1, j)

從f(i, j+v) 到 f(i+1, j+v)的灰度變化也為線性關係,由此可推匯出待求象素灰度的計算式如下:

     f(i+u, j+v) = (1-u) * (1-v) * f(i, j) + (1-u) * v * f(i, j+1) + u *(1-v) * f(i+1, j) + u * v * f(i+1, j+1)

雙線性內插法的計算比最鄰近點法複雜,計算量較大,但沒有灰度不連續的缺點,結果基本令人滿意。它具有低通濾波性質,使高頻分量受損,影象輪廓可能會有一點模糊。

 

3、三次內插法

該方法利用三次多項式S(x)求逼近理論上最佳插值函式sin(x)/x,其數學表示式為:

 

待求畫素(x, y)的灰度值由其周圍16個灰度值加權內插得到,如下圖:

 

 

 

待求畫素的灰度計算式如下:

 

f(x, y) = f(i+u, j+v) =ABC

 

其中: 

 

三次曲線插值方法計算量較大,但插值後的影象效果最好。

 

第二部分:影象縮放--OpenCVcvResize函式--最近鄰插值---雙線性插值--基本原理

影象大小變換

void cvResize( const CvArr* src,CvArr* dst, int interpolation=CV_INTER_LINEAR ); src輸入影象.dst輸出影象.interpolation插值方法:

  • CV_INTER_NN - 最近鄰插值,
  • CV_INTER_LINEAR - 雙線性插值 (預設使用)
  • CV_INTER_AREA - 使用象素關係重取樣。當影象縮小時候,該方法可以避免波紋出現。當影象放大時,類似於CV_INTER_NN 方法..
  • CV_INTER_CUBIC - 立方插值.

函式 cvResize 將影象 src 改變尺寸得到與dst 同樣大小。若設定 ROI,函式將按常規支援 ROI.

最近鄰插值:效果(放大4倍)有馬賽克現象

影象縮放--插值法

雙線性插值:效果(放大4倍)比最近鄰插值效果好

影象縮放--插值法

最近鄰插值和雙線性插值的基本原理

 

影象的縮放很好理解,就是影象的放大和縮小。傳統的繪畫工具中,有一種叫做“放大尺”的繪畫工具,畫家常用它來放大圖畫。當然,在計算機上,我們不再需要用放大尺去放大或縮小影象了,把這個工作交給程式來完成就可以了。下面就來講講計算機怎麼來放大縮小圖象;在本文中,我們所說的影象都是指點陣圖,也就是用一個畫素矩陣來描述影象的方法,對於另一種影象:用函式來描述影象的向量圖,不在本文討論之列。
越是簡單的模型越適合用來舉例子,我們就舉個簡單的影象:3X3的256級灰度圖,也就是高為3個象素,寬也是3個象素的影象,每個象素的取值可以是0-255,代表該畫素的亮度,255代表最亮,也就是白色,0代表最暗,即黑色。假如影象的象素矩陣如下圖所示(這個原始圖把它叫做源圖,Source):
234  38    22
67    44    12
89    65    63

這個矩陣中,元素座標(x,y)是這樣確定的,x從左到右,從0開始,y從上到下,也是從零開始,這是圖象處理中最常用的座標系,就是這樣一個座標:

  ---------------------->X
  |
  |
  |
  |
  |
∨Y

如果想把這副圖放大為4X4大小的影象,那麼該怎麼做呢?那麼第一步肯定想到的是先把4X4的矩陣先畫出來再說,好了矩陣畫出來了,如下所示,當然,矩陣的每個畫素都是未知數,等待著我們去填充(這個將要被填充的圖的叫做目標圖,Destination):
?       ?       ?      ?
?       ?       ?      ?
?       ?       ?      ?
?       ?       ?      ? 
               
       然後要往這個空的矩陣裡面填值了,要填的值從哪裡來來呢?是從源圖中來,好,先填寫目標圖最左上角的象素,座標為(0,0),那麼該座標對應源圖中的座標可以由如下公式得出:                                      
srcX=dstX* (srcWidth/dstWidth) , srcY = dstY *(srcHeight/dstHeight)
好了,套用公式,就可以找到對應的原圖的座標了(0*(3/4),0*(3/4))=>(0*0.75,0*0.75)=>(0,0)
,找到了源圖的對應座標,就可以把源圖中座標為(0,0)處的234象素值填進去目標圖的(0,0)這個位置了。

接下來,如法炮製,尋找目標圖中座標為(1,0)的象素對應源圖中的座標,套用公式:
(1*0.75,0*0.75)=>(0.75,0)
結果發現,得到的座標裡面竟然有小數,這可怎麼辦?計算機裡的影象可是數字影象,象素就是最小單位了,象素的座標都是整數,從來沒有小數座標。這時候採用的一種策略就是採用四捨五入的方法(也可以採用直接舍掉小數位的方法),把非整數座標轉換成整數,好,那麼按照四捨五入的方法就得到座標(1,0),完整的運算過程就是這樣的:
(1*0.75,0*0.75)=>(0.75,0)=>(1,0)
那麼就可以再填一個象素到目標矩陣中了,同樣是把源圖中座標為(1,0)處的畫素值38填入目標圖中的座標。
         
依次填完每個象素,一幅放大後的影象就誕生了,畫素矩陣如下所示:
234   38    22    22  
67     44    12    12  
89     65    63    63  
89     65    63    63  

這種放大影象的方法叫做最臨近插值演算法,這是一種最基本、最簡單的影象縮放演算法,效果也是最不好的,放大後的影象有很嚴重的馬賽克,縮小後的影象有很嚴重的失真;效果不好的根源就是其簡單的最臨近插值方法引入了嚴重的影象失真,比如,當由目標圖的座標反推得到的源圖的的座標是一個浮點數的時候,採用了四捨五入的方法,直接採用了和這個浮點數最接近的象素的值,這種方法是很不科學的,當推得座標值為0.75的時候,不應該就簡單的取為1,既然是0.75,比1要小0.25 ,比0要大0.75,那麼目標象素值其實應該根據這個源圖中虛擬的點四周的四個真實的點來按照一定的規律計算出來的,這樣才能達到更好的縮放效果。雙線型內插值演算法就是一種比較好的影象縮放演算法,它充分的利用了源圖中虛擬點四周的四個真實存在的畫素值來共同決定目標圖中的一個畫素值,因此縮放效果比簡單的最鄰近插值要好很多。

雙線性內插值演算法描述如下:
  對於一個目的畫素,設定座標通過反向變換得到的浮點座標為(i+u,j+v)(其中i、j均為浮點座標的整數部分,u、v為浮點座標的小數部分,是取值[0,1)區間的浮點數),則這個畫素得值 f(i+u,j+v)可由原影象中座標為 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所對應的周圍四個畫素的值決定,即:

  f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) +uvf(i+1,j+1)                         公式1

其中f(i,j)表示源影象(i,j)處的的畫素值,以此類推。

比如,象剛才的例子,現在假如目標圖的象素座標為(1,1),那麼反推得到的對應於源圖的座標是(0.75 , 0.75),這其實只是一個概念上的虛擬象素,實際在源圖中並不存在這樣一個象素,那麼目標圖的象素(1,1)的取值不能夠由這個虛擬象素來決定,而只能由源圖的這四個象素共同決定:(0,0)(0,1)(1,0)(1,1),而由於(0.75,0.75)離(1,1)要更近一些,那麼(1,1)所起的決定作用更大一些,這從公式1中的係數uv=0.75×0.75就可以體現出來,而(0.75,0.75)離(0,0)最遠,所以(0,0)所起的決定作用就要小一些,公式中係數為(1-u)(1-v)=0.25×0.25也體現出了這一特點。

原理參考link:http://blog.csdn.net/andrew659/article/details/4818988

OpenCV程式碼:scale是放縮比例


點選(此處)摺疊或開啟

  1. #include "stdafx.h"
  2. #include <</SPAN>cv.h>
  3. #include <</SPAN>cxcore.h>
  4. #include <</SPAN>highgui.h>
  5. #include <</SPAN>cmath>
  6. using namespace std;
  7. using namespace cv;

  8. int main(intargc ,char **argv)
  9. {
  10.     IplImage*scr=0;
  11.     IplImage*dst=0;
  12.     doublescale=4;
  13.     CvSizedst_cvsize;
  14.     if(argc==2&&(scr=cvLoadImage(argv[1],-1))!=0)
  15.     {
  16.         dst_cvsize.width=(int)(scr->width*scale);
  17.         dst_cvsize.height=(int)(scr->height*scale);
  18.         dst=cvCreateImage(dst_cvsize,scr->depth,scr->nChannels);

  19.         cvResize(scr,dst,CV_INTER_NN);//
  20. //            CV_INTER_NN- 最近鄰插值,
  21. //            CV_INTER_LINEAR- 雙線性插值 (預設使用)
  22. //            CV_INTER_AREA-使用象素關係重取樣。當影象縮小時候,該方法可以避免波紋出現。
  23.         /*當影象放大時,類似於 CV_INTER_NN 方法..*/
  24. //            CV_INTER_CUBIC- 立方插值.

  25.         cvNamedWindow("scr",CV_WINDOW_AUTOSIZE);
  26.         cvNamedWindow("dst",CV_WINDOW_AUTOSIZE);
  27.         cvShowImage("scr",scr);
  28.         cvShowImage("dst",dst);
  29.         cvWaitKey();
  30.         cvReleaseImage(&scr);
  31.         cvReleaseImage(&dst);
  32.         cvDestroyWindow("scr");
  33.         cvDestroyWindow("dst");
  34.     }
  35.     return0;
  36. }

相關文章