OpenCV中initUndistortRectifyMap ()函数与十四讲中去畸变公式的区别探究

这篇具有很好参考价值的文章主要介绍了OpenCV中initUndistortRectifyMap ()函数与十四讲中去畸变公式的区别探究。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


最近在使用OpenCV对鱼眼相机图像去畸变时发现一个问题,基于针孔模型去畸变时所使用的参数和之前十四讲以及视觉SLAM中的畸变系数有一点不一样。

1.十四讲中的去畸变公式

首先是十四讲或者视觉SLAM中的方法,针孔模型的畸变系数为[k1, k2, p1, p2],使用以下去畸变公式计算:

cv::initundistortrectifymap,SLAM,opencv,人工智能,计算机视觉

2. OpenCV中的去畸变公式

在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()函数中的畸变公式,如下:
cv::initundistortrectifymap,SLAM,opencv,人工智能,计算机视觉

推导过程的核心是:
cv::initundistortrectifymap,SLAM,opencv,人工智能,计算机视觉
k3, k4, k5, k6以及s1, s2, s3, s4均为0的时候该去畸变公式和十四讲中的公式就一样了,即十四讲中的去畸变公式是该公式的一个简略版。

3. 4个参数和8个参数之间的区别

已经说过,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会得到失败的结果。文章来源地址https://www.toymoban.com/news/detail-751861.html

4.initUndistortRectifyMap()函数源码

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;
            }
        }
    }
}

到了这里,关于OpenCV中initUndistortRectifyMap ()函数与十四讲中去畸变公式的区别探究的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 视觉SLAM十四讲——ch10实践(后端2)

    视觉SLAM(Simultaneous Localization and Mapping)后端是一种用于处理视觉SLAM问题的算法。视觉SLAM是指机器在未知环境中同时实现自身的定位和地图构建的技术。 视觉SLAM后端的任务是在视觉SLAM中负责维护一个优化后的地图和机器人的轨迹。常见的视觉SLAM后端算法包括基于图优化的

    2024年02月09日
    浏览(49)
  • 视觉SLAM十四讲——ch9实践(后端1)

    Ceres BA使用的是BAL数据集。在本例中,使用problem-16-22106-pre.txt文件。 BAL的数据集自身存在的特殊 BAL的相机内参模型由焦距f和畸变参数k1,k2给出。 因为BAL数据在投影时假设投影平面在相机光心之后,所以按照我们之前用的模型计算,需要在投影之后乘以系数-1。 安装meshlab,

    2024年02月09日
    浏览(49)
  • 《视觉SLAM十四讲》报错信息和解决方案

    ch4-Sophus 编译报错 报错信息: 解决方法:修改Sophus下的so2.cpp文件 将下面这个修改一下: 修改为: ch5/imageBasics 安装opencv4.x报错 环境:Unbuntu22.04,安装opencv4.x 报错信息: 解决方案:参照https://blog.csdn.net/qq_51022848/article/details/128095476 ch5/joinMap/CMakeLists.txt 编译报错 报错信息:

    2024年02月14日
    浏览(38)
  • 【视觉SLAM十四讲学习笔记】第五讲——相机模型

    专栏系列文章如下: 【视觉SLAM十四讲学习笔记】第一讲——SLAM介绍 【视觉SLAM十四讲学习笔记】第二讲——初识SLAM 【视觉SLAM十四讲学习笔记】第三讲——旋转矩阵 【视觉SLAM十四讲学习笔记】第三讲——旋转向量和欧拉角 【视觉SLAM十四讲学习笔记】第三讲——四元数 【视

    2024年01月17日
    浏览(42)
  • 《视觉 SLAM 十四讲》V2 第 5 讲 相机与图像

    空间点 投影到 相机成像平面 前面内容总结: 1、机器人如何表示自身位姿 视觉SLAM: 观测 主要是指 相机成像 的过程 。 投影过程描述: 针孔 + 畸变 相机 内参 外参 像素坐标系 与 成像平面之间,相差了一个缩放 和一个原点的平移。 像素坐标系: 原点 o ′ o^{prime} o ′ 位

    2024年02月07日
    浏览(45)
  • 视觉SLAM十四讲|【五】相机与IMU时间戳同步

    Z [ u v 1 ] = [ f x 0 c x 0 f y c y 0 0 1 ] [ X Y Z ] = K P Z begin{bmatrix} u \\\\ v \\\\ 1 end{bmatrix}= begin{bmatrix} f_x 0 c_x \\\\ 0 f_y c_y \\\\ 0 0 1 end{bmatrix} begin{bmatrix} X \\\\ Y \\\\ Z end{bmatrix}= KP Z ​ u v 1 ​ ​ = ​ f x ​ 0 0 ​ 0 f y ​ 0 ​ c x ​ c y ​ 1 ​ ​ ​ X Y Z ​ ​ = K P 其中, K = [ f x 0 c x 0 f y c y 0 0

    2024年01月20日
    浏览(49)
  • 考研数二第十四讲 牛顿-莱布尼茨公式与用定义法求解定积分

    牛顿-莱布尼茨公式在微分与积分以及不定积分与定积分之间架起了一座桥梁,因此,这个公式又被称为微积分基本公式。 微积分基本公式的简单推导 在看微积分基本公式之前,我们先来看一个有点特殊的函数,积分上限函数 ψ ( x ) = ∫ x a f ( t ) d t psi (x) =int_{x}^{a}f

    2024年02月08日
    浏览(40)
  • 踩坑 Sophus 模板库安装及编译(视觉SLAM 十四讲第二版 ch4 )

    在《视觉slam十四讲》第二版中,第4、7、8、9、10讲都需要Sophus库,因此我们需要安装Sophus库,并且需要的是Sophus模板库,因此很多人因为安装了非模板版本导致报错,下面提供Sophus模板版本安装方式,以及对应不报错版本。 只要是 3.3以上的版本即可 官网进入,然后下载T

    2024年01月22日
    浏览(63)
  • 【数学建模】《实战数学建模:例题与讲解》第十四讲-模拟退火、遗传算法(含Matlab代码)

    本系列侧重于例题实战与讲解,希望能够在例题中理解相应技巧。文章开头相关基础知识只是进行简单回顾,读者可以搭配课本或其他博客了解相应章节,然后进入本文正文例题实战,效果更佳。 如果这篇文章对你有帮助,欢迎点赞与收藏~ 现代优化算法,自20世纪80年代初开

    2024年02月04日
    浏览(55)
  • 《视觉 SLAM 十四讲》V2 第 4 讲 李群与李代数 【什么样的相机位姿 最符合 当前观测数据】

    P71 什么样的相机位姿 最符合 当前观测数据。 求解最优的 R , t bm{R, t} R , t , 使得误差最小化。 群 : 只有一个(良好的)运算的集合。 封结幺逆 、 丰俭由你 李群 : 具有连续(光滑)性质的群。 在 t = 0 附近,旋转矩阵可以由 e x p ( ϕ 0 ∧ t ) exp(phi_0^{land}t) e x p ( ϕ 0 ∧ ​ t

    2024年02月07日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包