【图像】【OpenCV鱼眼矫正】二、fisheye::initUndistortRectifyMap()源码分析

您所在的位置:网站首页 内参标定算法 【图像】【OpenCV鱼眼矫正】二、fisheye::initUndistortRectifyMap()源码分析

【图像】【OpenCV鱼眼矫正】二、fisheye::initUndistortRectifyMap()源码分析

2023-03-20 23:06| 来源: 网络整理| 查看: 265

目录 一、fisheye::initUndistortRectifyMap() 之 功能介绍二、fisheye::initUndistortRectifyMap() 之 源码分析1. 源码分析2. 更进一步3. 如何由 (j, i) 算出 (u, v) ?

一、fisheye::initUndistortRectifyMap() 之 功能介绍

在上一篇文章的第 2. 部分中,我们已对fisheye::initUndistortRectifyMap()的功能做过详细介绍,这里再重复一下:

fisheye::initUndistortRectifyMap()的作用是根据无畸变图的像素位置(i, j),推出它对应的畸变图中的像素位置(u, v),然后把畸变图中的(u, v)复制到新图中的(i, j),就得到了矫正图像。

fisheye::initUndistortRectifyMap()函数在OpenCV中的声明如下:

void cv::fisheye::initUndistortRectifyMap ( InputArray K, InputArray D, InputArray R, InputArray P, const cv::Size & size, int m1type, OutputArray map1, OutputArray map2 )

其中各形参的作用在网上有很多文章介绍,本文不再赘述。

这里只简单介绍其输入输出:

输入:

内参矩阵K畸变系数DR并非旋转向量/矩阵,矫正单目鱼眼相机时,R默认为单位矩阵若想使矫正后图片对应的内参与原相机内参不同,则把P设为新内参;否则,P与原内参相同输出矩阵map1的类型m1type其他参数

输出:

map1map2

我们通常把参数m1type设为CV_16SC2时,此时最重要的输出是map1:

根据CV_16SC2,map1此时是一个2通道的矩阵,每个点(i, j)都是一个2维向量,包含:

map1(i, j) [0]map2(i, j) [1]

现在,我们令u = map1(i, j)[0], v= map1(i, j)[1]。

那么,i和j、u和v的含义是:

畸变图中坐标为(u, v)的像素点,在无畸变图中应该处于(i, j)位置。

这样,把畸变图(u, v)处的像素,复制到(i, j)处,就得到了矫正后的图像。

即如下图所示:

而此时的map2是为了做插值、让矫正后图像更清晰而用,本文不做介绍。

注:

若把参数m1type设为CV_32FC1,则此时map1不再是二通道矩阵,而是一通道矩阵。

此时map1保存m1type为CV_16SC2时map1的0通道,map2保存m1type为CV_16SC2时map1的1通道。

即,此时令u = map1(i, j),v = map2(i, j),那么才有:

畸变图中坐标为(u, v)的像素点,在无畸变图中应该处于(i, j)位置。

二、fisheye::initUndistortRectifyMap() 之 源码分析 1. 源码分析

把参数m1type设为CV_16SC2,再**抛去*断言、类型判断等非必要的部分,我们把fisheye::initUndistortRectifyMap简化如下:

//本文对程序做了详细注释 //转发请注明出处https://editor.csdn.net/md?not_checkout=1&articleId=112751432。 void cv::fisheye::initUndistortRectifyMap( InputArray K, InputArray D, InputArray R, InputArray P, const cv::Size& size, int m1type, OutputArray map1, OutputArray map2 ) { map1.create( size, CV_16SC2); //创建2通道的map1矩阵 map2.create( size, CV_16UC1); //创建1通道的map2矩阵 cv::Vec2d f, c; //创建f、c两个2维向量,用于存储内参fx、fy及cx、cy Matx33d camMat = K.getMat(); //把内参矩阵K复制给camMat变量 f = Vec2d(camMat(0, 0), camMat(1, 1)); //二维向量f存储fx、fy c = Vec2d(camMat(0, 2), camMat(1, 2)); //二维向量f存储cx、cy Vec4d k = Vec4d::all(0); //创建4维向量k,用于存储畸变系数k1、k2、k3、k4 k = (Vec4d)*D.getMat().ptr(); //把畸变系数k1、k2、k3、k4复制给向量k cv::Matx33d RR = cv::Matx33d::eye(); //在单目鱼眼相机的矫正中,RR设为单位向量即可 cv::Matx33d PP = cv::Matx33d::eye(); //创建矩阵PP,用于存储参数P P.getMat().colRange(0, 3).convertTo(PP, CV_64F); //把P复制给PP //其中,P是我们想使矫正后图片看起来像内参为怎样的相机得到的,就把P设为这个内参。 //一般令P等于原鱼眼相机的内参 cv::Matx33d iR = (PP * RR).inv(cv::DECOMP_SVD); //令iR为P的逆,也就是内参矩阵的逆 //==================================两层for循环更新 i 和 j ======================================= /************************************************************************************************ * i和j的实际意义是: * (j, i)是矫正后图像(输出图像)的像素点坐标,根据(j, i)计算出它对应的畸变图中的像素点坐标(u, v) ************************************************************************************************/ for( int i = 0; i //(i, j)是无畸变图上的像素点坐标 //===========================================矫正过程============================================= double x = _x/_w, y = _y/_w; //计算无畸变情况下的r、theta double r = sqrt(x*x + y*y); double theta = atan(r); double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta4*theta4; //计算畸变情况下的theta_d double theta_d = theta * (1 + k[0]*theta2 + k[1]*theta4 + k[2]*theta6 + k[3]*theta8); //计算畸变因子scale double scale = (r == 0) ? 1.0 : theta_d / r; //计算无畸变图上的点(i, j)对应的畸变图上的像素点(u, v) double u = f[0]*x*scale + c[0]; double v = f[1]*y*scale + c[1]; //将(u, v)转化为整数,并存入map1 m1[j*2+0] = (int)(u); m1[j*2+1] = (int)(v); //将插值所用的数据存入map2 //插值只是为了让图像更清晰一些,作用不大,不作讲解 m2[j] = (ushort)((iv & (cv::INTER_TAB_SIZE-1))*cv::INTER_TAB_SIZE + (iu & (cv::INTER_TAB_SIZE-1))); //计算相机坐标系下的坐标 _x += iR(0, 0); _y += iR(1, 0); _w += iR(2, 0); } } }

注: 在上述程序中,由于i、j意义的不同,像素坐标的表示方法不是(i, j),而应该是(j, i),即(width, height),符合像素坐标系,不需过分在意。

因此,上述程序中由(j, i )计算得到的(u, v)的意义应该是:

畸变图中坐标为(u, v)的像素点,在无畸变图中应该处于(j, i)位置。

也就是说,上述程序的作用是,从无畸变图的像素坐标(j, i),反推出了它在畸变图中对应的像素坐标(u, v)。

其中,比较难理解的是两个for循环,以及_x、_y、_w的计算,我们把这一部分代码单独摘出来:

for( int i = 0; i doSomeThingsForAectify(); _x += iR(0, 0); _y += iR(1, 0); _w += iR(2, 0); } }

上面的程序,可如下书写:

double _x, _y, _w; for( int i = 0; i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3