幾何變換
幾何變換可以看成圖像中物體(或像素)空間位置改變,或者說是像素的移動。
幾何運算需要空間變換和灰度級差值兩個步驟的算法,像素通過變換映射到新的坐標位置,新的位置可能是在幾個像素之間,即不一定為整數坐標。這時就需要灰度級差值將映射的新坐標匹配到輸出像素之間。最簡單的插值方法是最近鄰插值,就是令輸出像素的灰度值等於映射最近的位置像素,該方法可能會產生鋸齒。這種方法也叫零階插值,相應比較復雜的還有一階和高階插值。
插值算法感覺只要了解就可以了,圖像處理中比較需要理解的還是空間變換。
空間變換
空間變換對應矩陣的仿射變換。一個坐標通過函數變換的新的坐標位置:
所以在程序中我們可以使用一個2*3的數組結構來存儲變換矩陣:
以最簡單的平移變換為例,平移(b1,b2)坐標可以表示為:
因此,平移變換的變換矩陣及逆矩陣記為:
縮放變換:將圖像橫坐標放大(或縮小)sx倍,縱坐標放大(或縮小)sy倍,變換矩陣及逆矩陣為:
選擇變換:圖像繞原點逆時針旋轉a角,其變換矩陣及逆矩陣(順時針選擇)為:
OpenCV中的圖像變換函數
基本的放射變換函數:

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

void cvGetQuadrangleSubPix( const CvArr* src, //輸入圖像 CvArr* dst, // 提取的四邊形 const CvMat* map_matrix //2*3的變換矩陣 );
這個函數用以提取輸入圖像中的四邊形,並通過map_matrix變換存儲到dst中,與WarpAffine變換意義相同,
即對應每個點的變換:
WarpAffine與 GetQuadrangleSubPix 不同的在於cvWarpAffine 要求輸入和輸出圖像具有同樣的數據類型,有更大的資源開銷(因此對小圖像不太合適)而且輸出圖像的部分可以保留不變。而 cvGetQuadrangleSubPix 可以精確地從8位圖像中提取四邊形到浮點數緩存區中,具有比較小的系統開銷,而且總是全部改變輸出圖像的內容。
實踐:圖像旋轉變換(原尺寸)

//逆時針旋轉圖像degree角度(原尺寸) void rotateImage(IplImage* img, int degree) { IplImage *img_rotate = cvCloneImage(img); cvZero(img_rotate); //旋轉中心為圖像中心 CvPoint2D32f center; center.x=float (img->width/2.0+0.5); center.y=float (img->height/2.0+0.5); //計算二維旋轉的仿射變換矩陣 float m[6]; CvMat M = cvMat( 2, 3, CV_32F, m ); cv2DRotationMatrix( center, degree,1, &M); //變換圖像,並用黑色填充其余值 cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) ); cvNamedWindow("原圖"); cvNamedWindow("旋轉后的圖像"); cvShowImage("原圖",img); cvShowImage("旋轉后的圖像",img_rotate); }
逆時針旋轉30度結果:

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


//旋轉圖像內容不變,尺寸相應變大 IplImage* rotateImage1(IplImage* img,int degree){ //逆時針旋轉 double angle = degree * CV_PI / 180.; // 弧度 double a = sin(angle), b = cos(angle); int width = img->width; int height = img->height; int width_rotate= int(height * fabs(a) + width * fabs(b)); int height_rotate=int(width * fabs(a) + height * fabs(b)); //旋轉數組map // [ m0 m1 m2 ] ===> [ A11 A12 b1 ] // [ m3 m4 m5 ] ===> [ A21 A22 b2 ] float map[6]; CvMat map_matrix = cvMat(2, 3, CV_32F, map); // 旋轉中心 CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2); //函數返回一個指向2X3矩陣的指針 //PointF center:源圖像的旋轉中心 //double angle:源圖像旋轉的角度,正值表示逆時針旋轉(坐標原點假設在圖像左上角) //double scale:等向比例因子 //IntPer mapMatrix:用於返回的2X3矩陣 cv2DRotationMatrix(center, degree, 1.0, &map_matrix); map[2] += (width_rotate - width) / 2; map[5] += (height_rotate - height) / 2; IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3); //對圖像做仿射變換 //輸入圖像 //輸出圖像 //2*3的變換矩陣 //插值方法的組合 CV_WARP_FILL_OUTLIERS - 填充所有輸出圖像的象素。 //如果部分象素落在輸入圖像的邊界外,那么它們的值設定為 fillval. //CV_WARP_INVERSE_MAP - 指定 map_matrix 是輸出圖像到輸入圖像的反變換, //用來填充邊界外的值 cvWarpAffine( img,img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); cvNamedWindow("原圖"); cvNamedWindow("旋轉后的圖像"); cvShowImage("原圖",img); cvShowImage("旋轉后的圖像",img_rotate); return img_rotate; }
實踐:圖像旋轉變換(保留原圖內容,放大尺寸)-2

//旋轉圖像內容不變,尺寸相應變大 IplImage* rotateImage2(IplImage* img, int degree) //順時針旋轉 { double angle = degree * CV_PI / 180.; double a = sin(angle), b = cos(angle); int width=img->width, height=img->height; //旋轉后的新圖尺寸 int width_rotate= int(height * fabs(a) + width * fabs(b)); int height_rotate=int(width * fabs(a) + height * fabs(b)); IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels); cvZero(img_rotate); //保證原圖可以任意角度旋轉的最小尺寸 int tempLength = sqrt((double)width * width + (double)height *height) + 10; //sqrt()開平方根 int tempX = (tempLength + 1) / 2 - width / 2; int tempY = (tempLength + 1) / 2 - height / 2; IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels); cvZero(temp); //將原圖復制到臨時圖像tmp中心 cvSetImageROI(temp, cvRect(tempX, tempY, width, height)); cvCopy(img, temp, NULL); cvResetImageROI(temp); cvNamedWindow("臨時圖像"); cvShowImage("臨時圖像",temp); //旋轉數組map // [ m0 m1 m2 ] ===> [ A11 A12 b1 ] // [ m3 m4 m5 ] ===> [ A21 A22 b2 ] float m[6]; int w = temp->width; int h = temp->height; m[0] = b; m[1] = a; m[3] = -m[1]; m[4] = m[0]; // 將旋轉中心移至圖像中間 m[2] = w * 0.5f; m[5] = h * 0.5f; CvMat M = cvMat(2, 3, CV_32F, m); cvGetQuadrangleSubPix(temp, img_rotate, &M); cvReleaseImage(&temp); cvNamedWindow("原圖"); cvNamedWindow("旋轉后的圖像"); cvShowImage("原圖",img); cvShowImage("旋轉后的圖像",img_rotate); return img_rotate; }



//以下代碼理論可參考:http://blog.csdn.net/fengbingchun/article/details/17713429 Mat rotateImage3(Mat src){ //仿射變換 Point2f srcTri[3]; Point2f dstTri[3]; Mat rot_mat( 2, 3, CV_32FC1 ); Mat warp_mat( 2, 3, CV_32FC1 ); Mat warp_dst, warp_rotate_dst; warp_dst = Mat::zeros( src.rows, src.cols, src.type() ); // 用3個點確定A仿射變換 srcTri[0] = Point2f( 0,0 ); srcTri[1] = Point2f( src.cols - 1, 0 ); srcTri[2] = Point2f( 0, src.rows - 1 ); dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 ); dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 ); dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 ); warp_mat = getAffineTransform( srcTri, dstTri ); warpAffine( src, warp_dst, warp_mat, warp_dst.size() ); /// 旋轉矩陣 Point center = Point( warp_dst.cols/2, warp_dst.rows/2 ); double angle = -50.0; double scale = 0.6; ////獲取旋轉矩陣 scale為縮放因子(x、y方向保持一致),angle為旋轉角度(弧長),centerx,centery為旋轉中心 // | a b (1-a)*center.x-b*center.y | a=scale*cos(angle) // M= | | // | -b a b*center.x+(1-a)*center.y| b=scale*sin(angle) rot_mat = getRotationMatrix2D( center, angle, scale ); warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() ); //實現坐標系仿射變換 ////OpenCV 1.0的形式 //IplImage * img=cvLoadImage("baboon.jpg"); //IplImage *img_rotate=cvCloneImage(img); //CvMat M =warp_mat; //cvWarpAffine(img,img_rotate, &M,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS,cvScalarAll(0) ); //cvShowImage("Wrap2",img_rotate); namedWindow( "Source", CV_WINDOW_AUTOSIZE ); imshow( "Source", src ); namedWindow( "Wrap", CV_WINDOW_AUTOSIZE ); imshow( "Wrap", warp_dst ); namedWindow("Wrap+Rotate", CV_WINDOW_AUTOSIZE ); imshow( "Wrap+Rotate", warp_rotate_dst ); return warp_dst; }

主函數:

// ImageRotation2.cpp : 定義控制台應用程序的入口點。 // 本文代碼參考:http://www.cnblogs.com/slysky/archive/2012/03/21/2410743.html #include "stdafx.h" #include <opencv2/core/core.hpp> //cvGetSize cvCreateImage #include <opencv2/highgui/highgui.hpp> #include <opencv2/opencv.hpp> //cvResize cvInitMatHeader cvGetMinMaxHistValue cvCvtColor #include <opencv2/imgproc/imgproc.hpp> #ifdef _DEBUG #pragma comment(lib, "opencv_core244d") #pragma comment(lib, "opencv_highgui244d") #pragma comment(lib, "opencv_imgproc244d") //cvResize #else #pragma comment(lib, "opencv_core244") #pragma comment(lib, "opencv_highgui244") #pragma comment(lib, "opencv_imgproc244") //cvResize #endif using namespace std; //隱藏控制台窗口 #pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") int main( ) { //讀入圖像 //Mat src = imread( "./images/baboon.jpg", 1 ); //src = rotateImage3(src); int degree = 30; IplImage *src = cvLoadImage("./images/meng3.jpg",CV_LOAD_IMAGE_UNCHANGED); rotateImage(src, degree); //src = rotateImage1(src, degree); //src = rotateImage2(src, degree); waitKey(0); return 0; }
本文轉自:http://blog.csdn.net/xiaowei_cqu/article/details/7616044