在標定時常用到仿射變換,但Opencv中放射變換僅支持三對點作爲參數。遇到需要多點標定的情況則需要最小二乘法的仿射變換了。
原opencv中 獲取仿射變換的函數getAffineTransform() 的用法如下
vector<Point2f> sro={Point2f(0,0),Point2f(1,0),Point2f(0,1)};
vector<Point2f> dst{Point2f(0,0),Point2f(10,0),Point2f(0,10)};
Mat AffineTransform=getAffineTransform(sro, dst);
其中得到的AffineTransform就是2x3的仿射變換矩陣。
接下來介紹如何獲得要最小二乘法的仿射變換矩陣。
若要解 A x = b 中的x。
使用常規超定矩陣解法 具體如下:
1.兩邊同時加乘 A^t (A^t表示A的轉置)
有:A^t A x =A^t b
2.使x側(A^t A)消去,再增乘(At A)^-1
有 (At A)^-1 (A^t A) x=(At A)^-1 A^t b
左側 (At A)^-1 (A^t A)消去後
即有:x=(At A)^-1 A^t b
由此得出仿射矩陣的最小二乘法解 x。
用C++和OpenCV代碼實現如下:
cv::Mat text233(const vector<cv::Point2f> src, const vector<cv::Point2f> dst)
{
int n_Points = MIN(src.size(), dst.size());
cv::Mat_<float> Mat_x;//數量不足時,返回單位矩陣
Mat_x = (cv::Mat_<float>(2, 3) << 1, 0, 0, 0, 1, 0);
if (n_Points >= 3)
{
//超定方程組求解 A x=b =====> (At A)-1 (At A) x =(At A)-1 At b =====> I x =(At A)-1 At b;
cv::Mat_<float> Mat_A = cv::Mat(n_Points, 3, CV_32FC1, cv::Scalar(0));
cv::Mat_<float> Mat_b = cv::Mat(n_Points, 2, CV_32FC1, cv::Scalar(0));
for (int i = 0; i < n_Points; i++)
{
Mat_A(i, 0) = src[i].x;
Mat_A(i, 1) = src[i].y;
Mat_A(i, 2) = 1;
Mat_b(i, 0) = dst[i].x;
Mat_b(i, 1) = dst[i].y;
}
Mat_x = (Mat_A.t() * Mat_A).inv() * Mat_A.t() * Mat_b;//最小二乘法求解過程
Mat_x = Mat_x.t();//按照Opencv中按列計算習慣,需要轉置
}
return Mat_x;
}