天天看點

OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射

resize函數

OpenCV提供了resize函數來改變圖像的大小,函數原型如下:

CV_EXPORTS_W void resize( InputArray src, OutputArray dst,
                          Size dsize, double fx=0, double fy=0,
                          int interpolation=INTER_LINEAR );
           

src:輸入原圖像;

dst:改變大小之後的圖像,這個圖像和原圖像具有相同的内容,隻是大小和原圖像不一樣而已;

dsize:輸出圖像的大小。如果這個參數不為0,那麼就代表将原圖像縮放到這個Size(width,height)指定的大小;如果這個參數為0,那麼原圖像縮放之後的大小就要通過下面的公式來計算:

                               dsize = Size(round(fx*src.cols), round(fy*src.rows))

其中,fx和fy就是下面要說的兩個參數,是圖像width方向和height方向的縮放比例。

fx:width方向的縮放比例,如果它是0,那麼它就會按照(double)dsize.width/src.cols來計算;

fy:height方向的縮放比例,如果它是0,那麼它就會按照(double)dsize.height/src.rows來計算;

interpolation:這個是指定插值的方式,圖像縮放之後,肯定像素要進行重新計算的,就靠這個參數來指定重新計算像素的方式,有以下幾種:

INTER_NEAREST - 最鄰近插值

INTER_LINEAR - 雙線性插值,如果最後一個參數你不指定,預設使用這種方法

INTER_AREA - 使用像素區域關系重新采樣。 它可能是圖像抽取的首選方法,因為它可以提供無莫爾條紋的結果。 但是當圖像被縮放時,它類似于INTER_NEAREST方法。

INTER_CUBIC - 4x4像素鄰域内的雙立方插值

INTER_LANCZOS4 - 8x8像素鄰域内的Lanczos插值

INTER_MAX=7,

WARP_INVERSE_MAP

要縮小圖像,一般推薦使用CV_INETR_AREA來插值;若要放大圖像,推薦使用CV_INTER_LINEAR。

注意事項:

1. dsize和fx/fy不能同時為0,要麼你就指定好dsize的值,讓fx和fy空置直接使用預設值,就像 resize(img, imgDst, Size(30,30));

要麼讓dsize為0,指定好fx和fy的值,比如fx=fy=0.5,那麼就相當于把原圖兩個方向縮小一倍!

2. 至于最後的插值方法,正常情況下使用預設的雙線性插值就夠用了。

幾種常用方法的效率是:最鄰近插值>雙線性插值>雙立方插值>Lanczos插值;但是效率和效果成反比,是以根據自己的情況酌情使用。

接下來說說圖像金字塔

    圖像金字塔就是用來進行圖像縮放的,幹的事情跟resize函數沒兩樣,金字塔的底部是待處理圖像的高分辨率表示,而頂部是低分辨率的近似我們将一層一層的圖像比喻成金字塔,層級越高,則圖像越小,分辨率越低。

OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射

其實非常好了解,如上圖所示,我們将一層層的圖像比喻為金字塔,層級越高,則圖像尺寸越小,分辨率越低。

兩種類型的金字塔:

  • 高斯金字塔:用于下采樣,主要的圖像金字塔;
  • 拉普拉斯金字塔:用于重建圖像,用來從金字塔低層圖像重建上層未采樣圖像,在數字圖像進行中也即是預測殘差,可以對圖像進行最大程度的還原,配合高斯金字塔一起使用。(因為小圖像放大,必須插入一些像素值,那這些像素值是什麼才合适呢,那就得進行根據周圍像素進行預測),對圖像進行最大程度的還原。

兩者的簡要差別:高斯金字塔用來向下降采樣圖像,而拉普拉斯金字塔則用來從金字塔底層圖像中向上采樣重建一個圖像。

圖像金字塔有兩個高頻出現的名詞:上采樣和下采樣。現在說說他們倆。

上采樣:就是圖檔放大(所謂上嘛,就是變大),使用PyrUp函數

下采樣:就是圖檔縮小(所謂下嘛,就是變小),使用PryDown函數

函數原型:

void pyrDown( InputArray src, OutputArray dst,
                           const Size& dstsize=Size(), int borderType=BORDER_DEFAULT );
void pyrUp( InputArray src, OutputArray dst,
                         const Size& dstsize=Size(), int borderType=BORDER_DEFAULT );
           
  • 第一個參數,InputArray類型的src,輸入圖像,即源圖像,填Mat類的對象即可。
  • 第二個參數,OutputArray類型的dst,輸出圖像,和源圖檔有一樣的尺寸和類型。
  • (pyrUp)第三個參數,const Size&類型的dstsize,輸出圖像的大小;有預設值Size(),即預設情況下,由Size(src.cols*2,src.rows*2)來進行計算,且一直需要滿足下列條件:
OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射
  • (pyrDown)第三個參數,const Size&類型的dstsize,輸出圖像的大小;有預設值Size(),即預設情況下,由Size Size((src.cols+1)/2, (src.rows+1)/2)來進行計算,且一直需要滿足下列條件:
OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射
  • 第四個參數,int類型的borderType,邊界模式,一般不用去管它   

           pyrUp函數執行高斯金字塔的采樣操作,其實它也可以用于拉普拉斯金字塔的。首先,它通過插入可為零的行與列,對源圖像進行向上取樣操作,然後将結果與pyrDown()乘以4的核心做卷積。

           pyrDown函數執行了高斯金字塔建造的向下采樣的步驟。首先,它将源圖像與如下核心做卷積運算:

OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射

    接着,它便通過對圖像的偶數行和列做插值來進行向下采樣操作。

pyrUp、pyrDown其實和專門用作放大縮小圖像尺寸的resize在功能上差不多,披着圖像金字塔的皮,說白了還是在對圖像進行放大和縮小操作

下采樣就是圖像壓縮,會丢失圖像資訊。下采樣步驟:

  1. 對圖像進行高斯核心卷積
  2. 将所有偶數行和列去除

上采樣步驟:

  1. 将圖像在每個方向放大為原來的兩倍,新增的行和列用0填充;
  2. 使用先前同樣的核心(乘以4)與放大後的圖像卷積,獲得新增像素的近似值。

     這裡的向下與向上采樣,是對圖像的尺寸而言(和金字塔的方向相反),向上就是圖像尺寸加倍,向下就是圖像尺寸減半。而如果我們按上圖中示範的金字塔方向來了解,金字塔向上圖像其實在縮小,這樣剛剛是反過來了。  需要注意的是,PryUp和PryDown不是互逆的,即PryUp不是降采樣的逆操作。這種情況下,圖像首先在每個次元上擴大為原來的兩倍,新增的行(偶數行)以0填充。然後給指定的濾波器進行卷積(實際上是一個在每個次元都擴大為原來兩倍的過濾器)去估計“丢失”像素的近似值。

     PryDown( )是一個會丢失資訊的函數。為了恢複原來更高的分辨率的圖像,我們要獲得由降采樣操作丢失的資訊,這些資料就和拉普拉斯金字塔有關系了。

    通過上述上采樣得到的圖像即為放大後的圖像,但是與原來的圖像相比會發覺比較模糊,因為在縮放的過程中已經丢失了一些資訊,如果想在縮小和放大整個過程中減少資訊的丢失,這些資料形成了拉普拉斯金字塔。

拉普拉斯金字塔第i層的數學定義:

OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射

     式中的

OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射

表示第i層的圖像。而UP操作是将源圖像中位置為(x,y)的像素映射到目标圖像的(2x+1,2y+1)位置,即在進行向上取樣。符号

OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射

表示卷積,

OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射

為5x5的高斯核心。 下文的pryUp,就是在進行上面這個式子的運算。

 是以,我們可以直接用OpenCV進行拉普拉斯運算:

OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射

   也就是說,拉普拉斯金字塔是通過源圖像減去先縮小後再放大的圖像的一系列圖像構成的。整個拉普拉斯金字塔運算過程可以通過下圖來概括:

OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射

 是以,我們可以将拉普拉斯金字塔了解為高斯金字塔的逆形式。

         另外再提一點,關于圖像金字塔非常重要的一個應用就是實作圖像分割。圖像分割的話,先要建立一個圖像金字塔,然後在G_i和G_i+1的像素直接依照對應的關系,建立起”父與子“關系。而快速初始分割可以先在金字塔高層的低分辨率圖像上完成,然後逐層對分割加以優化。

from:https://www.cnblogs.com/wyuzl/p/6294275.html

        在平面圖像進行中,因為鏡頭角度等原因,容易導緻圖像出現傾斜、變形等情況,為了友善後續處理我們常常需要進行圖像矯正,其中主要技術原理是兩種變換類型--仿射變換(Affine Transformation)和透視變換(Perspective Transformation)。

仿射變換

      仿射變換是二維坐标間的線性變換, 故而變換後的圖像仍然具有原圖的一些性質,包括“平直性”以及“平行性”,常用于圖像翻轉(Flip)、旋轉(Rotations)、平移(Translations)、縮放(Scale operations)等,函數原型:

CV_EXPORTS_W void warpAffine( InputArray src, OutputArray dst,
                              InputArray M, Size dsize,
                              int flags=INTER_LINEAR,
                              int borderMode=BORDER_CONSTANT,
                              const Scalar& borderValue=Scalar());
           

     src是輸入圖像;dst是輸出圖像,M是3x3變換矩陣,dsize是輸出圖像的大小,flags指定像素插補方法以及矩陣倒置标志cv::WARP_INVERSE_MAP;

     borderMode指定邊沿像素的推算模式,其中BORDER_CONSTANT訓示邊沿像素用borderValue替換,因為預設是0,是以我們變換後的圖像邊界可能會出現黑邊。

上述參數中M是變換矩陣,它可以通過自定義構造,或者通過getAffineTransform函數得到,其函數原型:

Mat getAffineTransform( const Point2f src[], const Point2f dst[] );
           

 src[]:輸入圖像的三角形頂點坐标的數組。數組元素是三個像素點坐标,Point2f類型

dst[]:輸出圖像的相對應的三角形頂點坐标的數組。具體可參考下文的執行個體。

這裡要注意:坐标Point(x,y)對應的是第y行第x列的像素點的位置。

透射變換

       仿射變換不能矯正一些變形,如矩形區域的部分發生變化最終變成梯形,這時候矯正就需要用到透視變換。透視變換(Perspective Transformation),又稱投影映射(Projective Mapping)、投射變換等,是三維空間上的非線性變換,可看作是仿射變換的更一般形式,簡單講即通過一個3x3的變換矩陣将原圖投影到一個新的視平面(Viewing Plane),在視覺上的直覺表現就是産生或消除了遠近感。落實到OpenCV,圖像的透視變換由函數perspectiveTransform進行向量坐标的變換):

CV_EXPORTS_W void warpPerspective( InputArray src, OutputArray dst,
                                   InputArray M, Size dsize,
                                   int flags=INTER_LINEAR,
                                   int borderMode=BORDER_CONSTANT,
                                   const Scalar& borderValue=Scalar());
           

src是輸入圖像;dst是輸出圖像,M是3x3變換矩陣,dsize是輸出圖像的大小,flags指定像素插補方法以及矩陣倒置标志cv::WARP_INVERSE_MAP;

borderMode指定邊沿像素的推算模式,其中BORDER_CONSTANT訓示邊沿像素用borderValue替換,因為預設是0,是以我們變換後的圖像邊界可能會出現黑邊。

同上,上述參數中M是變換矩陣,它可以通過自定義構造,或者通過getAffineTransform函數得到,其函數原型:

Mat getPerspectiveTransform( const Point2f src[], const Point2f dst[] );
           

 src[]:輸入圖像的三角形頂點坐标的數組。數組元素是三個像素點坐标,Point2f類型

dst[]:輸出圖像的相對應的三角形頂點坐标的數組。具體可參考下文的執行個體。

執行個體:

#include<opencv2\imgproc.hpp>
#include<opencv2\opencv.hpp>
#include<iostream>
#include<opencv2\core\core.hpp>
#include<cstdlib>
using namespace std;
using namespace cv;

int main() {
	Mat src = imread("111.jpg", 1);
	if (src.empty()) {
		cout << "open failed"<<endl;
		return -1;
	}
	
	/*圖檔縮放及顯示*/
	//設定縮放後的圖檔的尺寸
	Size ResImgSiz = Size(src.cols*0.5, src.rows*0.5);
	Mat ResImg = Mat(ResImgSiz, src.type());
	Mat dst3, dst4;
	resize(src, ResImg, ResImgSiz, CV_INTER_CUBIC);
	pyrUp(src, dst3, Size(src.cols * 2, src.rows * 2));
	pyrDown(src, dst4, Size(src.cols / 2, src.rows / 2));
	//透射變換
	Point2f srcTri2[] = {Point2f(0,0),Point2f(src.cols -1,0),Point2f(src.cols - 1,src.rows - 1),Point2f(0,src.rows-1)};//x對應col,y對應row
	Point2f dstTri2[] = { Point2f(0,src.rows*0.5f),Point2f(src.cols *0.7f,0),Point2f(src.cols *0.8f,src.rows *0.9f),Point2f(0,src.rows - 1) };
	//仿射變換
	Point2f srcTri[] = { Point2f(0,0),Point2f(src.cols - 1,0),Point2f(0,src.rows - 1) };//x對應col,y對應row
	Point2f dstTri[] = { Point2f(0,src.rows*0.5f),Point2f(src.cols -1,0),Point2f(0,src.rows - 6) };
	Mat warp_mat2 = getPerspectiveTransform(srcTri2, dstTri2);
	Mat warp_mat = getAffineTransform(srcTri, dstTri);
	Mat dst,dst2;
	warpAffine(src, dst, warp_mat, src.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar());
	warpPerspective(src, dst2, warp_mat2, src.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar());
	for (int i = 0; i < 3; i++) {
		circle(src, srcTri[i], 5, Scalar(0, 0, 255), -1);
		circle(src, srcTri2[i], 5, Scalar(0, 255,0), 1);
		circle(dst, dstTri[i], 5, Scalar(0, 0, 255), -1);
		circle(dst2, dstTri2[i], 5, Scalar(0, 255, 0), -1);
	}
	namedWindow("src", CV_WINDOW_NORMAL);//可以用滑鼠調整視窗大小
	namedWindow("仿射", CV_WINDOW_NORMAL);
	namedWindow("dst3", CV_WINDOW_NORMAL);
	imshow("src", src);
	imshow("ResImg", ResImg);
	imshow("仿射", dst);
	imshow("透射", dst2);
	imshow("dst3", dst3);
	imshow("dst4", dst4);
           

結果:

OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射
OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射
OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射
OpenCV3學習(5.1)——圖像變換之縮放、金字塔、仿射、透射

從上圖看出:圖像與原圖相比,變模糊了,資訊損失了。

補充一個函數circle,用于畫圓,函數原型: 

CV_EXPORTS_W void circle(CV_IN_OUT Mat& img, Point center, int radius,
                       const Scalar& color, int thickness=1,
                       int lineType=8, int shift=0);
           

img為源圖像指針;center為畫圓的圓心坐标;radius為圓的半徑;color為設定圓的顔色,用Scale類型,順序是BGR。

thickness 如果是正數,表示組成圓的線條的粗細程度。否則,表示圓被填充;

line_type 線條的類型。預設是8;shift 圓心坐标點和半徑值的小數點位數。