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

news2024/9/21 0:32:12

文章目录

      • 1.十四讲中的去畸变公式
      • 2. OpenCV中的去畸变公式
      • 3. 4个参数和8个参数之间的区别
      • 4.initUndistortRectifyMap()函数源码

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

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

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

在这里插入图片描述

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()函数中的畸变公式,如下:
在这里插入图片描述

推导过程的核心是:
在这里插入图片描述
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会得到失败的结果。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1080743.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Blender:对模型着色

Blender&#xff1a;使用立方体制作动漫头像-CSDN博客 上一步已经做了一个头像模型&#xff0c;我做的太丑了&#xff0c;就以这个外星人头像为例 首先切换到着色器编辑器 依次搜索&#xff1a;纹理坐标、映射、分离xyz和颜色渐变 这里的功能也是非常丰富和强大&#xff0c…

期权账户怎么开通的?佣金最低多少?

场内期权的合约由交易所统一标准化定制&#xff0c;大家面对的同一个合约对应的价格都是一致的&#xff0c;比较公开透明。期权开户当天不能交易的&#xff0c;期权开户需要满足20日日均50万及半年交易经验即可操作。 个人投资者想要交易期权首先就得先开户&#xff0c;根据规…

【LeetCode刷题笔记】哈希查找

771. 宝石与石头 解题思路&#xff1a; 1. HashSet &#xff0c;把所有 宝石 加入 set , 然后遍历检查 每一块石头是否包含在set中 &#xff0c;若包含就是宝石。 2. 计数数组map, 把所有 宝石 进行 count 数组 计数 &#xff0c;, 然后遍历检查 每一块石头是否 count[stone] …

了解交互设计:深入研究五个重要维度

交互设计是用户体验&#xff08;UX&#xff09;设计的重要组成部分。本文将解释什么是交互设计&#xff0c;并分享一些有用的交互设计模型&#xff0c;并简要描述交互设计师通常做什么。 如何解释交互设计 交互式设计可以用一个简单的术语来理解&#xff1a;它是用户和产品之…

在字节跳动和滴滴干了5年测试,月薪25K,熬夜总结出来的划水经验.....

先简单交代一下背景吧&#xff0c;某不知名 985 的本硕&#xff0c;17 年毕业加入字节&#xff0c;之后跳槽到了滴滴&#xff0c;一直从事软件测试的工作。之前没有实习经历&#xff0c;算是5年的工作经验吧。 这5年之间完成了一次晋升&#xff0c;换了一家公司&#xff0c;有…

【数据结构】算法的时间复杂度

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 一.算法时间复杂度定义 二.大O阶渐近表示法 &#x1f38f;大O阶渐近表示法的定义 &#x1f38f;推导大O阶方法 三.常见的时间复杂度 &#x1f4cc;常数阶 &#x…

5款好用的工具软件推荐给你

​ 人类与99%的动物之间最大差别在于是否会运用工具&#xff0c;借助好的工具&#xff0c;能提升几倍的工作效率。 1.三维建模——Rhinoceros ​ Rhinoceros是一款专业的三维建模和渲染软件&#xff0c;它可以创建、编辑、分析和转换NURBS曲面和实体。NURBS是一种数学描述曲面…

安卓-apk系统签名篇

时至今日,我在与一些安卓程序员的接触来看,有些人连系统签名是什么都不知道,这是我写这篇文章的理由. 1.什么是系统签名 apk的签名&#xff0c;简单说开发者可以通过签名 对应用进行标识和更新。包名在一个设备上是唯一的&#xff0c;这样可以避免被相同包名应用随意覆盖安装。…

计算机视觉(Computer Vision, CV)是什么?

什么是计算机视觉 近年来&#xff0c;计算机视觉 (Computer Vision&#xff0c;简称CV) 不断普及&#xff0c;已成为人工智能 (AI) 增长最快的领域之一。计算机视觉致力于使计算机能够识别和理解图像和视频中的物体和人。 计算机视觉应用程序使用来自传感设备、人工智能、机器…

电脑图片jpeg怎么转jpg格式?jpeg和jpg的转换方法

很多平台对上传的图片格式都有严格的要求&#xff0c;当我们遇到图片格式不对的时候&#xff0c;就需要改图片格式了&#xff0c;下面以jpeg转jpg&#xff08;在线图片格式转换器&#xff08;jpg、png、gif、webp、bmp、tiff&#xff09;-压缩图&#xff09;为例子&#xff0c;…

项目创建 Vue3 + Ts + vite + pinia

vite官网 项目初始化 准备安装工作(按步骤创建) npm init vuelatest创建完成后再次安装对应插件 然后百度配置main.ts里面引入 npm i pinia --save //安装pinia npm i vue-router --save //安装router npm i axios --save //安装axios //安装sass或less npm add -D scss npm…

顺序表的应用——(通讯录)

目录 前提须知&#xff1a; 通讯录的结构&#xff1a; 通讯录的建立&#xff1a; 顺序表的重命名&#xff1a; 顺序表数据类型的更改&#xff1a; 使用通讯录结构体新名字&#xff0c;进行类型重命名的问题&#xff1a; 头文件的添加&#xff1a; 通讯录的初始化和销毁&a…

【Linux】NTP时间服务器Chrony配置详解

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的帮助&#x1f338;文…

CV计算机视觉每日开源代码Paper with code速览-2023.10.11

点击CV51&#xff0c;关注更多CV干货 论文已打包&#xff0c;点击进入—>下载界面 点击加入—>CV计算机视觉交流群 1.【基础网络架构&#xff1a;Transformer】EViT: An Eagle Vision Transformer with Bi-Fovea Self-Attention 论文地址&#xff1a;https://arxiv.or…

Mysql分组查询每组最新的一条数据,查询用户的最新的一条记录

有订单表和用户表&#xff0c;如何获取用户的最新的一条订单记录&#xff1f; 订单表 CREATE TABLE orders (id int(11) NOT NULL AUTO_INCREMENT,uid int(10) NOT NULL DEFAULT 0,created_at datetime(0) NOT NULL,PRIMARY KEY (id) USING BTREE ) ENGINE InnoDB ;INSERT …

Python大数据之Python进阶(七)线程的注意点

线程的注意点 学习目标 能够说出线程的注意点 1. 线程的注意点介绍 线程之间执行是无序的主线程会等待所有的子线程执行结束再结束线程之间共享全局变量线程之间共享全局变量数据出现错误问题 2. 线程之间执行是无序的 import threading import timedef task():time.sleep…

17.(开发工具篇Gitlab)如何在Gitlab配置ssh key

前言: Git是分布式的代码管理工具,远程的代码管理是基于SSH的,所以要使用远程的Git则需要SSH的配置 一、git 配置 (1)打开 git 命令窗口 (2)配置用户名(填自己的姓名) git config --global user.name “chenbc” (3)配置用户邮箱(填自己的邮箱) git config …

有没有普通人也能赚钱的副业?挖漏洞兼职!

如何开展副业&#xff1f;网络安全月入过万&#xff01; ​ 随着大数据和人工智能的兴起。各行业对信息安全和网络安全服务的需求量呈指数级的暴增。 这不最近&#xff0c;一个做运维的朋友在学网络安全。他告诉我&#xff0c;他靠挖漏洞赚钱上个月月入过万了&#xff0c;好香啊…

Unity 设置Inspect上问号的跳转链接

设置Inspect上问号的跳转链接 只需要在Class上添加特性&#xff1a;HelpURL即可&#xff01;

《进化优化》第1章 绪论

文章目录 1.1 术语1.2 又一本关于进化算法的书1.3 先修课程1.4 家庭作业1.5 符号1.6 本书的大纲 1.1 术语 一些作者称进化算法为进化计算。另一些人称进化算法为基于种群的优化。一些作者称进化算法为计算机智能或计算智能。专家系统模仿演绎推理&#xff0c;进化算法则模仿归…