1. 仿射變換
1) 用途
旋轉 (線性變換),平移(向量加).縮放(線性變換),錯切,反轉
2) 方法
仿射變換是一種二維座標到二維座標之間的線性變換,它保持了二維圖形的“平直性”(直線經過變換之後依然是直線)和“平行性”(二維圖形之間的相對位置關係保持不變,平行線依然是平行線,且直線上點的位置順序不變)。任意的仿射變換都能表示爲乘以一個矩陣(線性變換),再加上一個向量 (平移) 的形式.
原理
由於用齊次座標表示,三維幾何變換的矩陣是一個4階方陣,其形式如下:
其中,產生按軸縮放、旋轉、錯切等變換。產生平移變換,產生投影變換,產生整體的縮放變換。
什麼是仿射變換?
-
一個任意的仿射變換都能表示爲 乘以一個矩陣 (線性變換) 接着再 加上一個向量 (平移).
-
綜上所述, 我們能夠用仿射變換來表示:
- 旋轉 (線性變換)
- 平移 (向量加)
- 縮放操作 (線性變換)
你現在可以知道, 事實上, 仿射變換代表的是兩幅圖之間的 關係 .
-
我們通常使用 矩陣來表示仿射變換.
考慮到我們要使用矩陣 和 對二維向量 做變換, 所以也能表示爲下列形式:
or
怎樣才能求得一個仿射變換?
-
好問題. 我們在上文有提到過仿射變換基本表示的就是兩幅圖片之間的 聯繫 . 關於這種聯繫的信息大致可從以下兩種場景獲得:
- 我們已知 和 T 而且我們知道他們是有聯繫的. 接下來我們的工作就是求出矩陣
- 我們已知 and . 要想求得 . 我們只要應用算式 即可. 對於這種聯繫的信息可以用矩陣 清晰的表達 (即給出明確的2×3矩陣) 或者也可以用兩幅圖片點之間幾何關係來表達.
-
讓我們形象地說明一下. 因爲矩陣 聯繫着兩幅圖片, 我們以其表示兩圖中各三點直接的聯繫爲例. 見下圖:
-
點1, 2 和 3 (在圖一中形成一個三角形) 與圖二中三個點一一映射, 仍然形成三角形, 但形狀已經大大改變. 如果我們能通過這樣兩組三點求出仿射變換 (你能選擇自己喜歡的點), 接下來我們就能把仿射變換應用到圖像中所有的點.
#if 1 #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> #include <stdio.h> using namespace cv; using namespace std; /// 全局變量 char* source_window = "Source image"; char* warp_window = "Warp"; char* warp_rotate_window = "Warp + Rotate"; /** @function main */ int main(int argc, char** argv) { Point2f srcTri[3]; Point2f dstTri[3]; Mat rot_mat(2, 3, CV_32FC1); Mat warp_mat(2, 3, CV_32FC1); Mat src, warp_dst, warp_rotate_dst; /// 加載源圖像 src = imread("f.bmp", 1); /// 設置目標圖像的大小和類型與源圖像一致 warp_dst = Mat::zeros(src.rows, src.cols, src.type()); /// 設置源圖像和目標圖像上的三組點以計算仿射變換 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()); /** 對圖像扭曲後再旋轉 */ /// 計算繞圖像中點順時針旋轉50度縮放因子爲0.6的旋轉矩陣 Point center = Point(warp_dst.cols / 2, warp_dst.rows / 2); double angle = -50.0; double scale = 0.6; /// 通過上面的旋轉細節信息求得旋轉矩陣 rot_mat = getRotationMatrix2D(center, angle, scale); /// 旋轉已扭曲圖像 warpAffine(warp_dst, warp_rotate_dst, rot_mat, warp_dst.size()); /// 顯示結果 namedWindow(source_window, CV_WINDOW_AUTOSIZE); imshow(source_window, src); namedWindow(warp_window, CV_WINDOW_AUTOSIZE); imshow(warp_window, warp_dst); namedWindow(warp_rotate_window, CV_WINDOW_AUTOSIZE); imshow(warp_rotate_window, warp_rotate_dst); /// 等待用戶按任意按鍵退出程序 waitKey(0); return 0; } #endif // warp_affine <image> #include <cv.h> #include <highgui.h> //#define affine #ifdef affine int main(int argc, char** argv) { CvPoint2D32f srcTri[3], dstTri[3]; CvMat* rot_mat = cvCreateMat(2,3,CV_32FC1); CvMat* warp_mat = cvCreateMat(2,3,CV_32FC1); IplImage *src, *dst; if( argc == 2 && ((src=cvLoadImage(argv[1],1)) != 0 )) { dst = cvCloneImage(src); dst->origin = src->origin; cvZero(dst);//將圖像中的每個像素都置爲0,那麼顯示的frame自然就是全黑了 //COMPUTE WARP MATRIX srcTri[0].x = 0; //src Top left srcTri[0].y = 0; srcTri[1].x = src->width - 1; //src Top right srcTri[1].y = 0; srcTri[2].x = 0; //src Bottom left srcTri[2].y = src->height - 1; //- - - - - - - - - - - - - - -// dstTri[0].x = src->width*0.0; //dst Top left dstTri[0].y = src->height*0.33; dstTri[1].x = src->width*0.85; //dst Top right dstTri[1].y = src->height*0.25; dstTri[2].x = src->width*0.15; //dst Bottom left dstTri[2].y = src->height*0.7; cvGetAffineTransform(srcTri,dstTri,warp_mat);// 求得仿射變換warp_mat cvWarpAffine(src,dst,warp_mat);// 對源圖像應用上面求得的仿射變換 cvCopy(dst,src); /** 對圖像扭曲後再旋轉 */ //COMPUTE ROTATION MATRIX /// 計算繞圖像中點順時針旋轉50度縮放因子爲0.6的旋轉矩陣 CvPoint2D32f center = cvPoint2D32f(src->width/2, src->height/2); double angle = -50.0; double scale = 0.6; /// 通過上面的旋轉細節信息求得旋轉矩陣 cv2DRotationMatrix(center,angle,scale,rot_mat); /// 旋轉已扭曲圖像 cvWarpAffine(src,dst,rot_mat); //DO THE TRANSFORM: cvNamedWindow( "Affine_Transform", 1 ); cvShowImage( "Affine_Transform", dst ); cvWaitKey(); } cvReleaseImage(&dst); cvReleaseMat(&rot_mat); cvReleaseMat(&warp_mat); return 0; } #endif
效果: