基於opencv的仿射變換


本文內容參考了http://blog.csdn.net/xiaowei_cqu/article/details/7616044

幾何變換

幾何變換可以看成圖像中物體(或像素)空間位置改變,或者說是像素的移動。

幾何運算需要空間變換和灰度級差值兩個步驟的算法,像素通過變換映射到新的座標位置,新的位置可能是在幾個像素之間,即不一定爲整數座標。這時就需要灰度級差值將映射的新座標匹配到輸出像素之間。最簡單的插值方法是最近鄰插值,就是令輸出像素的灰度值等於映射最近的位置像素,該方法可能會產生鋸齒。這種方法也叫零階插值,相應比較複雜的還有一階和高階插值。

插值算法感覺只要瞭解就可以了,圖像處理中比較需要理解的還是空間變換。

空間變換

空間變換對應矩陣的仿射變換。一個座標通過函數變換的新的座標位置:


所以在程序中我們可以使用一個2*3的數組結構來存儲變換矩陣:


以最簡單的平移變換爲例,平移(b1,b2)座標可以表示爲:


因此,平移變換的變換矩陣及逆矩陣記爲:


縮放變換:將圖像橫座標放大(或縮小)sx倍,縱座標放大(或縮小)sy倍,變換矩陣及逆矩陣爲:


選擇變換:圖像繞原點逆時針旋轉a角,其變換矩陣及逆矩陣(順時針選擇)爲:


OpenCV中的圖像變換函數

基本的放射變換函數:

  1. void cvWarpAffine(   
  2.     const CvArr* src,//輸入圖像  
  3.     CvArr* dst, //輸出圖像  
  4.     const CvMat* map_matrix,   //2*3的變換矩陣  
  5.     int flags=CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,   //插值方法的組合  
  6.     CvScalar fillval=cvScalarAll(0)   //用來填充邊界外的值  
  7. );  
另外一個比較類似的函數是cvGetQuadrangleSubPix:

  1. void cvGetQuadrangleSubPix(   
  2.        const CvArr* src,  //輸入圖像   
  3.        CvArr* dst,   // 提取的四邊形  
  4.        const CvMat* map_matrix //2*3的變換矩陣  
  5. );  
這個函數用以提取輸入圖像中的四邊形,並通過map_matrix變換存儲到dst中,與WarpAffine變換意義相同,

即對應每個點的變換:


WarpAffine與 GetQuadrangleSubPix 不同的在於cvWarpAffine 要求輸入和輸出圖像具有同樣的數據類型,有更大的資源開銷(因此對小圖像不太合適)而且輸出圖像的部分可以保留不變。而 cvGetQuadrangleSubPix 可以精確地從8位圖像中提取四邊形到浮點數緩存區中,具有比較小的系統開銷,而且總是全部改變輸出圖像的內容。

用cvWarpAffine實驗將圖像逆時針旋轉degree角度:

#include<cv.h>
#include<cxcore.h>
#include<highgui.h>
#include<iostream>


int main ( int argc ,char* argv)
{
	IplImage* src = cvLoadImage("F:\\bb1.jpg");
	IplImage* dst = cvCloneImage( src);
	int degree = 30;//逆時針旋轉30度
	CvPoint2D32f center ;//創建一箇中心點
	center.x = src->width/2.0+0.5;
	center.y = src->height/2.0+0.5;
	float m[6];
	CvMat M = cvMat( 2 , 3 , CV_32F , m);//構建一個2*3的矩陣
	cv2DRotationMatrix( center , degree , 1 , &M);//計算圍繞中心點旋轉的映射矩陣
	cvWarpAffine( src , dst , &M , CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, cvScalarAll(0));//仿射變換
	cvNamedWindow("src");
	cvShowImage("src" ,src);
	cvNamedWindow("dst");
	cvShowImage("dst" ,dst);
	cvWaitKey(0);

}
結果爲



實踐:圖像旋轉變換(保留原圖內容,放大尺寸)

需要計算新圖的尺寸,示意圖如下:

所以新圖size爲(width*cos(a)+height*sin(a), height*cos(a)+width*sin(a))

int main ( int argc ,char* argv)
{
	IplImage* src = cvLoadImage("F:\\bb1.jpg");
	int degree = 30;
	double angle = degree  * CV_PI / 180.; // 弧度 
	double a = sin(angle), b = cos(angle);   
    int width = src ->width;    
    int height = src->height;    
    int width_rotate= int(height * fabs(a) + width * fabs(b));    
    int height_rotate=int(width * fabs(a) + height * fabs(b));  

	float map[6];  
    CvMat map_matrix = cvMat(2, 3, CV_32F, map);    
    // 旋轉中心  
    CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2);    
    cv2DRotationMatrix(center, degree, 1.0, &map_matrix);    
    map[2] += (width_rotate - width) / 2;  //對圖像進行平移  
    map[5] += (height_rotate - height) / 2;     //對圖像進行平移  
    IplImage* dst = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3);   
    //對圖像做仿射變換  
    //CV_WARP_FILL_OUTLIERS - 填充所有輸出圖像的象素。  
    //如果部分象素落在輸入圖像的邊界外,那麼它們的值設定爲 fillval.  
    //CV_WARP_INVERSE_MAP - 指定 map_matrix 是輸出圖像到輸入圖像的反變換,  
    cvWarpAffine( src,dst, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0));    
	cvNamedWindow("src");
	cvShowImage("src" ,src);
	cvNamedWindow("dst");
	cvShowImage("dst" ,dst);
	cvWaitKey(0);
    return 0;  
}


結果爲






發佈了8 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章