圖像幾何變換:旋轉,縮放,斜切

幾何變換

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

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

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

空間變換

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


所以在程序中我們可以使用一個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角度。
  1. //逆時針旋轉圖像degree角度(原尺寸)  
  2. void rotateImage(IplImage* img, IplImage *img_rotate,int degree)  
  3. {  
  4.     //旋轉中心爲圖像中心  
  5.     CvPoint2D32f center;    
  6.     center.x=float (img->width/2.0+0.5);  
  7.     center.y=float (img->height/2.0+0.5);  
  8.     //計算二維旋轉的仿射變換矩陣  
  9.     float m[6];              
  10.     CvMat M = cvMat( 2, 3, CV_32F, m );  
  11.     cv2DRotationMatrix( center, degree,1, &M);  
  12.     //變換圖像,並用黑色填充其餘值  
  13.     cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) );  
  14. }  
逆時針旋轉30度結果:
這裏我們將新的圖像還保留原來的圖像尺寸。這樣的效果顯然不太好,我們通過計算相應放大圖像尺寸。

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

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

所以新圖size爲(width*cos(a)+height+sin(a), height*cos(a)+width*sin(a))
  1. //旋轉圖像內容不變,尺寸相應變大  
  2. IplImage* rotateImage1(IplImage* img,int degree){  
  3.     double angle = degree  * CV_PI / 180.; // 弧度    
  4.     double a = sin(angle), b = cos(angle);   
  5.     int width = img->width;    
  6.     int height = img->height;    
  7.     int width_rotate= int(height * fabs(a) + width * fabs(b));    
  8.     int height_rotate=int(width * fabs(a) + height * fabs(b));    
  9.     //旋轉數組map  
  10.     // [ m0  m1  m2 ] ===>  [ A11  A12   b1 ]  
  11.     // [ m3  m4  m5 ] ===>  [ A21  A22   b2 ]  
  12.     float map[6];  
  13.     CvMat map_matrix = cvMat(2, 3, CV_32F, map);    
  14.     // 旋轉中心  
  15.     CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2);    
  16.     cv2DRotationMatrix(center, degree, 1.0, &map_matrix);    
  17.     map[2] += (width_rotate - width) / 2;    
  18.     map[5] += (height_rotate - height) / 2;    
  19.     IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3);   
  20.     //對圖像做仿射變換  
  21.     //CV_WARP_FILL_OUTLIERS - 填充所有輸出圖像的象素。  
  22.     //如果部分象素落在輸入圖像的邊界外,那麼它們的值設定爲 fillval.  
  23.     //CV_WARP_INVERSE_MAP - 指定 map_matrix 是輸出圖像到輸入圖像的反變換,  
  24.     cvWarpAffine( img,img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0));    
  25.     return img_rotate;  
  26. }  


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

試一下用cvGetQuadrangleSubPix函數:
  1. //旋轉圖像內容不變,尺寸相應變大  
  2. IplImage* rotateImage2(IplImage* img, int degree)    
  3. {    
  4.     double angle = degree  * CV_PI / 180.;   
  5.     double a = sin(angle), b = cos(angle);   
  6.     int width=img->width, height=img->height;  
  7.     //旋轉後的新圖尺寸  
  8.     int width_rotate= int(height * fabs(a) + width * fabs(b));    
  9.     int height_rotate=int(width * fabs(a) + height * fabs(b));    
  10.     IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels);    
  11.     cvZero(img_rotate);    
  12.     //保證原圖可以任意角度旋轉的最小尺寸  
  13.     int tempLength = sqrt((double)width * width + (double)height *height) + 10;    
  14.     int tempX = (tempLength + 1) / 2 - width / 2;    
  15.     int tempY = (tempLength + 1) / 2 - height / 2;    
  16.     IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels);    
  17.     cvZero(temp);    
  18.     //將原圖複製到臨時圖像tmp中心  
  19.     cvSetImageROI(temp, cvRect(tempX, tempY, width, height));    
  20.     cvCopy(img, temp, NULL);    
  21.     cvResetImageROI(temp);    
  22.     //旋轉數組map  
  23.     // [ m0  m1  m2 ] ===>  [ A11  A12   b1 ]  
  24.     // [ m3  m4  m5 ] ===>  [ A21  A22   b2 ]  
  25.     float m[6];    
  26.     int w = temp->width;    
  27.     int h = temp->height;    
  28.     m[0] = b;    
  29.     m[1] = a;    
  30.     m[3] = -m[1];    
  31.     m[4] = m[0];    
  32.     // 將旋轉中心移至圖像中間    
  33.     m[2] = w * 0.5f;    
  34.     m[5] = h * 0.5f;    
  35.     CvMat M = cvMat(2, 3, CV_32F, m);    
  36.     cvGetQuadrangleSubPix(temp, img_rotate, &M);    
  37.     cvReleaseImage(&temp);    
  38.     return img_rotate;  
  39. }    


實踐:圖像放射變換(通過三點確定變換矩陣)

在OpenCV 2.3的參考手冊中《opencv_tutorials》介紹了另一種確定變換矩陣的方法,通過三個點變換的幾何關係映射實現變換。
變換示意圖如下:

即通過三個點就可以確定一個變換矩陣。(矩形變換後一定爲平行四邊形)
以下是基於OpenCV 2.3的代碼(需至少2.0以上版本的支持)
  1. int main( )  
  2. {  
  3.     Point2f srcTri[3];  
  4.     Point2f dstTri[3];  
  5.     Mat rot_mat( 2, 3, CV_32FC1 );  
  6.     Mat warp_mat( 2, 3, CV_32FC1 );  
  7.     Mat src, warp_dst, warp_rotate_dst;  
  8.     //讀入圖像  
  9.     src = imread( "baboon.jpg", 1 );  
  10.     warp_dst = Mat::zeros( src.rows, src.cols, src.type() );  
  11.     // 用3個點確定A仿射變換  
  12.     srcTri[0] = Point2f( 0,0 );  
  13.     srcTri[1] = Point2f( src.cols - 1, 0 );  
  14.     srcTri[2] = Point2f( 0, src.rows - 1 );  
  15.     dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );  
  16.     dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );  
  17.     dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 );  
  18.     warp_mat = getAffineTransform( srcTri, dstTri );  
  19.     warpAffine( src, warp_dst, warp_mat, warp_dst.size() );  
  20.     /// 旋轉矩陣  
  21.     Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );  
  22.     double angle = -50.0;  
  23.     double scale = 0.6;  
  24.     rot_mat = getRotationMatrix2D( center, angle, scale );  
  25.     warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );  
  26.     ////OpenCV 1.0的形式  
  27.     //IplImage * img=cvLoadImage("baboon.jpg");  
  28.     //IplImage *img_rotate=cvCloneImage(img);  
  29.     //CvMat M =warp_mat;  
  30.     //cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) );  
  31.     //cvShowImage("Wrap2",img_rotate);  
  32.   
  33.     namedWindow( "Source", CV_WINDOW_AUTOSIZE );  
  34.     imshow( "Source", src );  
  35.     namedWindow( "Wrap", CV_WINDOW_AUTOSIZE );  
  36.     imshow( "Wrap", warp_dst );  
  37.     namedWindow("Wrap+Rotate", CV_WINDOW_AUTOSIZE );  
  38.     imshow( "Wrap+Rotate", warp_rotate_dst );  
  39.     waitKey(0);  
  40.     return 0;  
  41. }  
變換結果:

轉載請註明出處:http://blog.csdn.net/xiaowei_cqu/article/details/7616044
實驗代碼下載:http://download.csdn.net/detail/xiaowei_cqu/4339856

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章