Affine transformation 仿射變換

本來想介紹projective transformation(投影變換)的,想了想還是應該先介紹一下比較簡單的affine transformation(仿射變換),以便給自己一個更清晰的思路。其實仿射變換是投影變換的一種特殊形式,在圖像處理中,大部分圖像變換演算法用仿射變換就可以得到不錯的效果,並且數學意義比較明確易懂,因此仿射變換相對來說更常用一些。

首先從數學的角度,所謂仿射變換,就是向量經過一次線性變換加一次平移變換,用公式可以表示為:

其中,p為變換前原始向量,q為變換後目標向量,A為線性變換矩陣,b為平移變換向量。公式可統一寫成矩陣形式以方便計算:

在圖像處理中,我們所謂的向量是二維坐標(x,y),所以向量p、q可以如下表示:

因此我們又能將上述矩陣改寫成:

對於一副圖像,如果我們知道矩陣T,便可以對圖像中任何一點進行仿射變換。由於T是3*3矩陣,要求得該矩陣需要三組點的值。所以通常在一幅圖像里,我們進行仿射變換的原型是將一個三角形變成另外一個形狀不相同的三角形。有了原圖的三組頂點坐標和目標圖像的三組頂點坐標,便可求得仿射變換矩陣T,再用T便可求出所有點的仿射變換位置值。

假設原始三角形三個頂點分別為A0 B0nC0,目標三角形三個頂點分別為A1 B1 C1,先通過這6個點坐標求轉換矩陣T:

P1為三角形A1 B1 C1所在平面內任意點,P1可對應到三角形A0 B0nC0所在平面的唯一確定點。假設P1在三角形A1 B1 C1內,那麼P0一定在三角形A0 B0 C0內。之後通過轉換矩陣可求出P1對應點P0的坐標,注意P0和P1的先後順序會影響轉換矩陣的形勢(T或者T的逆):

針對三角形A1 B1 C1所在的全圖,只需遍歷圖中每一點,對每一點進行仿射變換,便可求出針對整幅圖像的仿射變換。

代碼如下:

#include <opencv.hpp>n#include <iostream>nusing namespace std;nusing namespace cv;nnclass Process{npublic:nt///<函數>仿射變換nt///<參數 "I">輸入原始圖像nt///<參數 "A0B0C0">原圖仿射變換基準點nt///<參數 "A1B1C1">仿射變換目標點nt///<結果>返回Mat型仿射變換之後圖像ntMat affine(Mat I, Point2d A0, Point2d B0, Point2d C0, Point2d A1, Point2d B1, Point2d C1)nt{nttMat result(I.rows, I.cols, CV_8UC3);nttdouble dst_num[9] =ntt{ A0.x, B0.x, C0.x,nttA0.y, B0.y, C0.y,ntt1, 1, 1 };nttdouble src_num[9] =ntt{ A1.x, B1.x, C1.x,nttA1.y, B1.y, C1.y,ntt1, 1, 1 };nttMat src_mat(3, 3, CV_64F, src_num);nttMat dst_mat(3, 3, CV_64F, dst_num);ntt//affine transformation matrix 仿射變換矩陣nttMat T = src_mat * dst_mat.inv();nttfor (int i = 0; i < I.cols; i++)ntt{ntttfor (int j = 0; j < I.rows; j++)nttt{nttttdouble P1_num[3] = { i, j, 1 };nttttMat P1_mat(3, 1, CV_64F, P1_num);ntttt//cout << P1_mat << endl;nttttMat P0_mat = T*P1_mat;ntttt//cout << P0_mat << endl;nnttttif (P0_mat.at<double>(0, 0) < 0) P0_mat.at<double>(0, 0) = 0;nttttif (P0_mat.at<double>(0, 0) > I.cols - 1) P0_mat.at<double>(0, 0) = I.cols - 1;nttttif (P0_mat.at<double>(1, 0) < 0) P0_mat.at<double>(1, 0) = 0;nttttif (P0_mat.at<double>(1, 0) > I.rows - 1) P0_mat.at<double>(1, 0) = I.rows - 1;nnttttPoint2d P0(P0_mat.at<double>(0, 0), P0_mat.at<double>(1, 0));nttttresult.at<Vec3b>(P0) = I.at<Vec3b>(j, i);nttt}ntt}ntt//標記原圖仿射變換基準點nttcircle(I, A0, 2, Scalar(0, 0, 255));nttcircle(I, B0, 2, Scalar(0, 0, 255));nttcircle(I, C0, 2, Scalar(0, 0, 255));ntt//標記目標圖像仿射變換目標點nttcircle(result, A1, 2, Scalar(0, 0, 255));nttcircle(result, B1, 2, Scalar(0, 0, 255));nttcircle(result, C1, 2, Scalar(0, 0, 255));nttreturn result;nt}n};nnvoid main()n{ntMat I = imread("..//chicago.jpg");ntProcess a;nntPoint2d A0(300, 250);ntPoint2d B0(200, 100);ntPoint2d C0(500, 200);nntPoint2d A1(400, 300);ntPoint2d B1(300, 200);ntPoint2d C1(500, 250);nntMat result = a.affine(I, A0, B0, C0, A1, B1, C1);nntimshow("ori", I);ntimshow("res", result);ntwaitKey();n}n

運行結果:

兩幅圖中已用紅色圓點分別表示出選定的三角形點,可以看出,雖然圖像整體出現變化,但對應的點像素值是相同的。

推薦閱讀:

OpenCV機器學習——樸素貝葉斯NBC
1.26【OpenCV圖像處理】模板匹配

TAG:图形图像 | 计算机技术 | OpenCV |