这篇文章阐述的相机的较正原理,不涉及相机的标定问题,即假设我们已经对相机进行了标定,得到了相机的投影矩阵和畸变参数。
较正可分为三个过程。
(1)首先,是计算新的投影矩阵(我把它命名为扩展投影矩阵),此矩阵用于恢复未较正坐标的相机坐标(即三维点在相机坐标系中坐标)。其实,这里没有真正计算出三维坐标,只是一个相对关系,但可以用此三维坐标重投影得到较正坐标。我们设扩展投影矩阵为camNewMat。计算camNewMat的输入参数包括投影矩阵、畸变参数及图像尺寸,还可以设置主点是否为未较正图像的中心。得到camNewMat的同时,我们还可以得到较正后的图像的有效区域。这里,有效区域是这些区域,在未较正图像中可以找到对应区域的区域。较正图像中,还可能存在一些无区域,即在未较正图像中找不到对应区域的区域。
(2)其次,是根据扩展投影矩阵计算未较正坐标的相机坐标。这个容易进行。若设未较正坐标为Y=(u,v,1)T,相机坐标为X=(x,y,z)T。由成像模型可知,Y=camNewMat.X。所以X=inverse(camNewMat)Y。
(3)最后,就是将相机坐标重投影到像素坐标,即较正坐标。设投影矩阵为camMat,由成像模型可知,较正坐标Z=camMat.X。
过程(2)(3)在OpenCV中的源码如下。我对代码进行了相应的精简和调整,以更好理解。函数输出的是两个映射矩阵,用这两个矩阵再结合remap函数就能将未较正图像映射为较正图像。
1 void initUndistortRectifyMap1(InputArray _camMat, InputArray _distoration,InputArray _rotation, InputArray _newCamMat,Size size, int type, OutputArray _map1, OutputArray _map2 ) 2 3 { 4 Mat_<double> camMat = _camMat.getMat(); 5 Mat_<double> distoration = _distoration.getMat(); 6 Mat_<double> rotation = _rotation.getMat(); 7 Mat_<double> newCamMat = _newCamMat.getMat(); 8 _map1.create(size, type);//暂不考虑CV_32FC情况 9 _map2.create(size, type == CV_16SC2 ? CV_16UC1 : CV_32FC1 ); 10 Mat map1 = _map1.getMat(); 11 Mat map2 = _map2.getMat(); 12 13 //计算扩展投影矩阵的逆 14 if(newCamMat.empty()) newCamMat = getDefaultNewCameraMatrix(camMat, size, true );//若未计算扩展投影矩阵则用投影矩阵代替 15 if(rotation.empty()) rotation = Mat_<double>::eye(3, 3);//单目的情况 16 Mat_<double> iR = (newCamMat.colRange(0,3)*rotation).inv(DECOMP_LU); 17 18 //获取相机参数 19 double cx = camMat(0, 2); 20 double cy = camMat(1, 2); 21 double fx = camMat(0, 0); 22 double fy = camMat(1, 1); 23 24 //获取畸变参数 25 double k1 = distoration(0); 26 double k2 = distoration(1); 27 double p1 = distoration(2); 28 double p2 = distoration(3); 29 double k3 = distoration.total() >= 5 ? distoration(4) : 0.; 30 double k4 = distoration.total() >= 8 ? distoration(5) : 0.; 31 double k5 = distoration.total() >= 8 ? distoration(6) : 0.; 32 double k6 = distoration.total() >= 8 ? distoration(7) : 0.; 33 double s1 = distoration.total() >= 12 ? distoration(8) : 0.; 34 double s2 = distoration.total() >= 12 ? distoration(9) : 0.; 35 double s3 = distoration.total() >= 12 ? distoration(10) : 0.; 36 double s4 = distoration.total() ? distoration(11) : 0.; 37 38 //计算映射关系 39 for( int i = 0; i < size.height; i++ ) 40 { 41 double x = i*iR(0,1) + iR(0,2); 42 double y = i*iR(1,1) + iR(1,2); 43 double z = i*iR(2,1) + iR(2,2); 44 45 for( int j = 0; j < size.width; j++, x += iR(0,0), y += iR(1,0), z += iR(2,0) ) 46 { 47 double wz = 1./z;if(z>1.0000001){cout<<z;getchar();} 48 ouble wx = x/wz; 49 double wy = y/wz; 50 double x2 = wx*wx; 51 double y2 = wy*wy; 52 double r2 = x2 + y2; 53 double _2xy = 2*wx*wy; 54 double kr = (1 + ((k3*r2 + k2)*r2 + k1)*r2)/(1 + ((k6*r2 + k5)*r2 + k4)*r2); 55 double u = fx*(x*kr + p1*_2xy + p2*(r2 + 2*x2) + s1*r2+s2*r2*r2) + cx; 56 double v = fy*(y*kr + p1*(r2 + 2*y2) + p2*_2xy + s3*r2+s4*r2*r2) + cy; 57 map1.at<float>(i,j)=(float)u; 58 map2.at<float>(i,j)=(float)v; 59 } 60 } 61 }
关于过程(1)的原理还没弄明白。明白了再写吧。