赞
踩
首先是十四讲或者视觉SLAM中的方法,针孔模型的畸变系数为[k1, k2, p1, p2]
,使用以下去畸变公式计算:
在OpenCV中可以通过initUndistortRectifyMap()
函数获得原始图像和矫正图像之间的映射表,然后remap()
函数根据映射表对整个图像进行映射处理实现去畸变。
cv::fisheye::initUndistortRectifyMap(K, D, cv::Mat(), K, imageSize, CV_16SC2, map1, map2);
cv::remap(raw_image, undistortImg, map1, map2, cv::INTER_LINEAR, cv::BORDER_CONSTANT);
具体实现可以见文章《对鱼眼相机图像进行去畸变处理》
initUndistortRectifyMap()
函数的声明如下:
void cv::initUndistortRectifyMap
( InputArray cameraMatrix, // 原相机内参矩阵
InputArray distCoeffs, // 原相机畸变参数
InputArray R, // 可选的修正变换矩阵
InputArray newCameraMatrix, // 新相机内参矩阵
Size size, // 去畸变后图像的尺寸
int m1type, // 第一个输出的映射(map1)的类型,CV_32FC1 or CV_16SC2
OutputArray map1, // 第一个输出映射
OutputArray map2 // 第二个输出映射
)
有意思的是,这里的相机畸变参数是可选的,可以是4个参数k1, k2, p1, p2
,可以是5个参数k1, k2, p1, p2, k3
,也可以是8个参数k1, k2, p1, p2, k3, k4, k5, k6
。
后来检索了一下initUndistortRectifyMap()
函数中的畸变公式,如下:
推导过程的核心是:
当k3, k4, k5, k6
以及s1, s2, s3, s4
均为0的时候该去畸变公式和十四讲中的公式就一样了,即十四讲中的去畸变公式是该公式的一个简略版。
已经说过,initUndistortRectifyMap()
函数中的去畸变参数可以是4个参数k1, k2, p1, p2
,可以是5个参数k1, k2, p1, p2, k3
,也可以是8个参数k1, k2, p1, p2, k3, k4, k5, k6
。
对于普通的广角相机图像,径向畸变和切向畸变一般都比较小,所以仅使用k1, k2, p1, p2
就可以完成去畸变过程,对应十四讲中的去畸变公式。
对于鱼眼相机,一般会存在比较大的径向畸变,所以需要更高阶的径向畸变系数k3, k4, k5, k6
,至于为什么是
1
+
k
1
r
2
+
k
2
r
4
+
k
3
r
6
1
+
k
4
r
2
+
k
5
r
4
+
k
6
r
6
\frac{1+k_1r^2+k_2r^4+k_3r^6}{1+k_4r^2+k_5r^4+k_6r^6}
1+k4r2+k5r4+k6r61+k1r2+k2r4+k3r6这种比值形式,暂时为找到公式的设计原理,应该是基于对径向畸变的某种考量进行的设计。
根据标定工具和相机模型的不同,获取的鱼眼相机畸变系数可能有多种形式,需要知道的是都可以在OpenCV去畸变函数中使用。而且有时通过标定得到完整的8个去畸变参数k1, k2, p1, p2, k3, k4, k5, k6
,这就使得在调用OpenCV函数去畸变事需要使用完整的参数,只使用k1, k2, p1, p2
会得到失败的结果。
void cv::initUndistortRectifyMap( InputArray _cameraMatrix, InputArray _distCoeffs, InputArray _matR, InputArray _newCameraMatrix, Size size, int m1type, OutputArray _map1, OutputArray _map2 ) { //相机内参、畸变矩阵 Mat cameraMatrix = _cameraMatrix.getMat(), distCoeffs = _distCoeffs.getMat(); //旋转矩阵、摄像机参数矩阵 Mat matR = _matR.getMat(), newCameraMatrix = _newCameraMatrix.getMat(); if( m1type <= 0 ) m1type = CV_16SC2; CV_Assert( m1type == CV_16SC2 || m1type == CV_32FC1 || m1type == CV_32FC2 ); _map1.create( size, m1type ); Mat map1 = _map1.getMat(), map2; if( m1type != CV_32FC2 ) { _map2.create( size, m1type == CV_16SC2 ? CV_16UC1 : CV_32FC1 ); map2 = _map2.getMat(); } else _map2.release(); Mat_<double> R = Mat_<double>::eye(3, 3); //A为相机内参 Mat_<double> A = Mat_<double>(cameraMatrix), Ar; //Ar 为摄像机坐标参数 if( newCameraMatrix.data ) Ar = Mat_<double>(newCameraMatrix); else Ar = getDefaultNewCameraMatrix( A, size, true ); //R 为旋转矩阵 if( matR.data ) R = Mat_<double>(matR); //distCoeffs为畸变矩阵 if( distCoeffs.data ) distCoeffs = Mat_<double>(distCoeffs); else { distCoeffs.create(8, 1, CV_64F); distCoeffs = 0.; } CV_Assert( A.size() == Size(3,3) && A.size() == R.size() ); CV_Assert( Ar.size() == Size(3,3) || Ar.size() == Size(4, 3)); //摄像机坐标系第四列参数 旋转向量转为旋转矩阵 Mat_<double> iR = (Ar.colRange(0,3)*R).inv(DECOMP_LU); //ir IR矩阵的指针 const double* ir = &iR(0,0); //获取相机的内参 u0 v0 为主坐标点 fx fy 为焦距 double u0 = A(0, 2), v0 = A(1, 2); double fx = A(0, 0), fy = A(1, 1); CV_Assert( distCoeffs.size() == Size(1, 4) || distCoeffs.size() == Size(4, 1) || distCoeffs.size() == Size(1, 5) || distCoeffs.size() == Size(5, 1) || distCoeffs.size() == Size(1, 8) || distCoeffs.size() == Size(8, 1)); if( distCoeffs.rows != 1 && !distCoeffs.isContinuous() ) distCoeffs = distCoeffs.t(); //畸变参数计算 double k1 = ((double*)distCoeffs.data)[0]; double k2 = ((double*)distCoeffs.data)[1]; double p1 = ((double*)distCoeffs.data)[2]; double p2 = ((double*)distCoeffs.data)[3]; double k3 = distCoeffs.cols + distCoeffs.rows - 1 >= 5 ? ((double*)distCoeffs.data)[4] : 0.; double k4 = distCoeffs.cols + distCoeffs.rows - 1 >= 8 ? ((double*)distCoeffs.data)[5] : 0.; double k5 = distCoeffs.cols + distCoeffs.rows - 1 >= 8 ? ((double*)distCoeffs.data)[6] : 0.; double k6 = distCoeffs.cols + distCoeffs.rows - 1 >= 8 ? ((double*)distCoeffs.data)[7] : 0.; //图像高度 for( int i = 0; i < size.height; i++ ) { //映射矩阵map1 float* m1f = (float*)(map1.data + map1.step*i); //映射矩阵map2 float* m2f = (float*)(map2.data + map2.step*i); short* m1 = (short*)m1f; ushort* m2 = (ushort*)m2f; //摄像机参数矩阵最后一列向量转换成的3*3矩阵参数 double _x = i*ir[1] + ir[2]; double _y = i*ir[4] + ir[5]; double _w = i*ir[7] + ir[8]; //图像宽度 for( int j = 0; j < size.width; j++, _x += ir[0], _y += ir[3], _w += ir[6] ) { //获取摄像机坐标系第四列参数 double w = 1./_w, x = _x*w, y = _y*w; double x2 = x*x, y2 = y*y; double r2 = x2 + y2, _2xy = 2*x*y; double kr = (1 + ((k3*r2 + k2)*r2 + k1)*r2)/(1 + ((k6*r2 + k5)*r2 + k4)*r2); double u = fx*(x*kr + p1*_2xy + p2*(r2 + 2*x2)) + u0; double v = fy*(y*kr + p1*(r2 + 2*y2) + p2*_2xy) + v0; if( m1type == CV_16SC2 ) { int iu = saturate_cast<int>(u*INTER_TAB_SIZE); int iv = saturate_cast<int>(v*INTER_TAB_SIZE); m1[j*2] = (short)(iu >> INTER_BITS); m1[j*2+1] = (short)(iv >> INTER_BITS); m2[j] = (ushort)((iv & (INTER_TAB_SIZE-1))*INTER_TAB_SIZE + (iu & (INTER_TAB_SIZE-1))); } else if( m1type == CV_32FC1 ) { m1f[j] = (float)u; m2f[j] = (float)v; } else { m1f[j*2] = (float)u; m1f[j*2+1] = (float)v; } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。