图像校正-仿射图像的畸变校正
仿射變換
原始平面經(jīng)過仿射變換后,兩直線夾角會(huì)發(fā)生變化,產(chǎn)生畸變,如下圖
仿射變換的變換矩陣為:
可以簡(jiǎn)寫為:
對(duì)偶于圓點(diǎn)(circular point)的圓錐曲線為:
如果直線 l 和 m 在原平面上垂直,那么有:
對(duì)于仿射變換的平面,可以推導(dǎo)出如下等式:
其中(l1’, l2’, l3’)、(m1’, m2’, m3’)分別為直線 l 和 m 的齊次坐標(biāo),令:
進(jìn)而可以得到等式:
再令:
可以得到:
向量 s 具有兩個(gè)自由度,因此只需要兩個(gè)方程就能求解,進(jìn)而需要找到兩對(duì)在原平面互相垂直的直線。求解出向量 s 后,矩陣 S 、 K 也都能對(duì)應(yīng)求解,然后代入仿射變換矩陣中,對(duì)圖像平面進(jìn)行逆變換,就能夠消除仿射畸變。
校正步驟
仿射畸變校正是在投影畸變校正的基礎(chǔ)上進(jìn)行的,我的上一篇筆記記錄了投影畸變校正的一些方法(https://blog.csdn.net/qq_44226964/article/details/121549545?spm=1001.2014.3001.5501)。
首先在圖像上選取兩組垂直線,其中 a ⊥ b ,c ⊥ d ,每條直線上至少找兩個(gè)點(diǎn)以確定直線方程,如下圖所示:
在求出兩組垂直線的齊次坐標(biāo)后,代入
中,建立二元一次方程組(可以令S22 = 1),解出向量 s 以及矩陣 S ,矩陣 K 使用 cholesky分解 的方法求解,最后得到仿射變換矩陣 Ha,求出 Ha 的逆矩陣后,就能對(duì)原圖像進(jìn)行逆變換了。
最終結(jié)果如下圖,從上到下依次為 原圖、投影校正、仿射校正:
代碼
代碼中使用到的部分函數(shù)是投影校正里面的函數(shù),在前面的文章中有,下面就沒有給出來了。
void RectifyAffine() {Mat src = imread("rectfing_projection.jpg", IMREAD_GRAYSCALE);IplImage* src2 = cvLoadImage("rectfing_projection.jpg");//使用鼠標(biāo)獲取點(diǎn)的坐標(biāo)//GetMouse(src2);//手動(dòng)輸入上面獲取的8個(gè)點(diǎn),每四個(gè)點(diǎn)確定一組垂直線Point3d points_3d_8[8] = { Point3d(23, 71, 1), Point3d(101, 126, 1) ,Point3d(101, 126, 1) ,Point3d(238, 76, 1),Point3d(50, 91, 1), Point3d(196, 91, 1), Point3d(141, 59, 1), Point3d(101, 126, 1) };//傳入點(diǎn)和圖像進(jìn)行校正RectifyByOrthogonal( points_3d_8, src); }void RectifyByOrthogonal(Point3d points[8], Mat src) {//通過輸入的8個(gè)點(diǎn)得到4條連線vector<vector<float>> lines;int num_lines = 4;for (int i = 0; i < num_lines; i++){//獲取兩點(diǎn)連線GetLineFromPoints(points[2 * i], points[2 * i + 1], lines);}//使用兩組正交直線求解仿射變換矩陣(l1'm1', l1'm2'+l2'm1', l2'm2')s = 0//先求s=(s11,s12,s22)vector<vector<float>> coefficients;for (int i = 0; i < 2; i++){vector<float> coefficient;coefficient.push_back(lines[i * 2][0] * lines[i * 2 + 1][0]);coefficient.push_back(lines[i * 2][0] * lines[i * 2 + 1][1] + lines[i * 2][1] * lines[i * 2 + 1][0]);coefficient.push_back(lines[i * 2][1] * lines[i * 2 + 1][1]);coefficients.push_back(coefficient);}vector<float> s;ComputeEquationSet(coefficients, s);//計(jì)算K矩陣 S = KKT,使用Cholesky分解的方法vector<float> K;ComputeCholesky(s, K);//仿射變換矩陣Hafloat Ha[3][3] = { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} };Ha[0][0] = 1 / K[0];Ha[0][1] = 0;Ha[1][0] = - K[2] / K[0];Ha[1][1] = 1;Mat image = Mat::zeros(src.rows, src.cols, CV_8UC1);GetRectifingImage1(Ha, src, image); }void ComputeEquationSet(vector<vector<float>> coefficients, vector<float> &result) {float a0 = coefficients[0][0];float b0 = coefficients[0][1];float c0 = coefficients[0][2];float a1 = coefficients[1][0];float b1 = coefficients[1][1];float c1 = coefficients[1][2];float s12 = (a0 * c1 - a1 * c0) / (a1 * b0 - a0 * b1);float s11 = -(b0 * s12 + c0) / a0;float s22 = 1;result.push_back(s11);result.push_back(s12);result.push_back(s22); }void ComputeCholesky(vector<float> s, vector<float> &K) {float s11 = s[0];float s12 = s[1];float s22 = s[2];float x11 = sqrt(s11);float x21 = s12 / s11 * x11;float x22 = sqrt(1 - x21 * x21);//歸一化x11 = x11 / x22;x21 = x21 / x22;x22 = 1;K.push_back(x11);K.push_back(0);K.push_back(x21);K.push_back(x22); }void GetRectifingImage1( float H[3][3], Mat& src, Mat dst) {Size size_src = src.size();for (int i = 0; i < size_src.height; i++){for (int j = 0; j < size_src.width; j++){float x3 = 1;int x1 = Round(j * H[0][0] / x3);int x2 = Round((j * H[1][0] + i) / x3);if (x1 < size_src.width && x1 >= 0 && x2 < size_src.height && x2 >= 0){dst.at<uint8_t>(x2, x1) = src.at<uint8_t>(i, j);}}}imshow("src1", src);imshow("dst", dst);waitKey(0);src = dst.clone(); }總結(jié)
以上是生活随笔為你收集整理的图像校正-仿射图像的畸变校正的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python成功将PDF文件转为图片,一
- 下一篇: 百度实时热点爬取