OpenCV中initUndistortRectifyMap ()函数与十四讲中去畸变公式的区别探究
文章目录
- 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_16SC2OutputArray 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);elseAr = 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);//映射矩阵map2float* 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 ()函数与十四讲中去畸变公式的区别探究
文章目录 1.十四讲中的去畸变公式2. OpenCV中的去畸变公式3. 4个参数和8个参数之间的区别4.initUndistortRectifyMap()函数源码 最近在使用OpenCV对鱼眼相机图像去畸变时发现一个问题,基于针孔模型去畸变时所使用的参数和之前十四讲以及视觉SLAM中的畸变系数有一点不…...

【C++】C++11——智能指针、内存泄漏、智能指针的使用和原理、RAII、auto_ptr、unique_ptr、shared_ptr、weak_ptr
文章目录 C117.智能指针7.1内存泄漏7.2智能指针的概念7.3智能指针的使用7.3.1 auto_ptr7.3.2 unique_ptr7.3.3 shared_ptr7.3.4 weak_ptr C11 7.智能指针 7.1内存泄漏 什么是内存泄漏: 内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏…...

EDUSRC-记某擎未授权与sql注入
目录 360天擎 - 未授权与sql注入 信息收集 FOFA语法 鹰图搜索 360天擎未授权访问 - 数据库信息泄露 漏洞复现 修复方案 360天擎终端安全管理系统ccid处SQL注入 漏洞复现 手动测试方法 修复方案 360天擎 - 未授权与sql注入 通常访问的页面如下,存在登录框…...

1688拍立淘API接口分享
拍立淘接口,顾名思义,就是通过图片搜索到相关商品列表。通过此接口,可以实现图片搜索爆款商品等功能。 接口地址:1688.item_search_img 公共参数 名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中&…...

昇腾910使用记录
一. 压缩文件和解压文件 1. 压缩文件 tar -czvf UNITE-main.tar.gz ./UNITE-main/2. 解压文件 tar -xvf ./UNITE-main/二. CUDA更改为NPU data[label] data[label].cuda() data[instance] data[instance].cuda() data[image] data[image].cuda()更改为 data[label] da…...

从一部iPhone手机看芯片的分类
目录 问题 iPhone X 手机处理器:A11 iPhone X 的两大存储芯片 数字 IC CPU:计算设备的运算核心和控制核心 GPU:图形处理器 ASIC:为解决特定应用问题而定制设计的集成电路 存储芯片:DRAM 和 NAND Flash iPhone…...

arm day 7
完成字符串收发函数的封装并且验证现象,一个字符串发送接受后会有‘\n’ \r src/uart.c #include"uart.h"void uart4_init() {//设置UART4的RCc时钟使能//RCC_MP_APB1ENSETR[16]->1RCC->MP_APB1ENSETR | (0x1<<16);//设置GPIOB和GPIOG的时钟…...

Java基础面试-面向对象
什么是面向对象? 对比面向过程,是两种不同的处理问题角度 面向过程更注重事情的每一个步骤及顺序,面向对象更注重事情有哪些参与者(对象),及各自需要做什么 比如洗衣机洗衣服 面向过程会将任务拆解成一系…...

GCC vs. G++:C 与 C++ 编译器的差异和比较
本文将介绍 GCC(GNU Compiler Collection)和 G 编译器的区别,并对它们在 C 和 C 程序开发中的特性和用法进行比较和总结。 引言 在 C 和 C 程序开发中,选择合适的编译器是至关重要的。GCC(GNU Compiler Collection&a…...

MAC m系列docker login报错
错误:ERROR: failed to solve: XXX error getting credentials - err: exit status 1, out: 解决: vi ~/.docker/config.jsonzsxzsx [15时55分55秒] [~] { {"auths": {"harbor-g42c.corp.matrx.team": {"auth": "…...

Redis通用指令和五大基本数据类型常用指令总结
通用指令 keys parttern 查询key (parttern即通配符,不是正则表达式,例如 keys a? 匹配以a开头的长度为2的key) del key 删除key exists key 获取key是否存在 type key 获取key的类型 expire key seconds 为指定key设置有效期,单位秒 …...

uCharts常用图表组件demo
带渐变阴影的曲线图 <view class"charts-box"><qiun-data-charts type"area" :opts"opts" :chartData"chartData" :ontouch"true":background"rgba(256,256,256,0)" /> </view>data(){return{…...

VNC:Timed out waiting for a response from the computer
VNC的服务端使用的是TigerVNC,客户端使用的是RealVNC TigerVNC按其他博客配好后,防火墙ip什么的都配了,vnc客户端怎么连都是超时。 这里建议大家可以尝试一下重启服务器。我的是CentOS的 shutdown -r now 配了2天,最后服务器重启…...

Kotlin 协程 知识点
Android 上的 Kotlin 协程 | Android Developers (google.cn) 官方网址 1.什么是协程? 我觉得协程就是kotlin中一种优雅的实现异步请求 协程(Coroutines)是一种轻量级的并发编程概念,旨在简化异步编程和并发任务的处理。它是…...

简单大方的自我介绍 PPT 格式
自我介绍是展示自己的机会,同时也是展现自信和魅力的重要时刻。通过简单大方的PPT格式,可以更好地展示自己的个性和才华。下面是一些建议,帮助你在自我介绍中展现自信和魅力。 1. 打造简洁而有吸引力的PPT布局: - 选择简洁大方的背…...

panads操作excel
panads简介 pandas是基于Numpy创建的Python包,内置了大量标准函数,能够高效地解决数据分析数据处理和分析任务,pandas支持多种文件的操作,比如Excel,csv,json,txt 文件等,读取文件之…...

【MySQL】联合查询、子查询、合并查询
这里提供了三个表: 表1: mysql> select * from class; -------------- | id | name | -------------- | 1 | 一班 | | 2 | 二班 | | 3 | 三班 | -------------- 3 rows in set (0.01 sec) 表2: mysql> select * fro…...

小程序中如何设置所服务地区的时区
在全球化的背景下,小程序除了在中国使用外,还为海外的华人地区提供服务。例如我们采云小程序为泰国、阿根廷、缅甸等国家的商家就提供过微信小程序。这些商家开通小程序,为本地的华人提供服务。但通常小程序的开发者/服务商位于中国ÿ…...

Linux环境安装mysql8.0
1个人习惯我喜欢给软件安装在/use/local下,我使用的finalshell软件,直接手动新建一个文件夹名字为mysql 2下载mysql wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.xz 3解压文件 tar -xvf mysql-8.0.2…...

STM32_DMA_多通道采集ADC出现错位现象
STM32_DMA_多通道采集ADC出现错位现象 问题描述: adcSensorValue[0],adcSensorValue[3],adcSensorValue[6]… //存储通道1数据 adcSensorValue[1],adcSensorValue[4],adcSensorValue[7]… //存储通道2数据 adcSensorValue[2],adcSensorValue[5],adcSensorValue[8]……...

Linux内存管理 (2):memblock 子系统的建立
前一篇:Linux内存管理 (1):内核镜像映射临时页表的建立 文章目录 1. 前言2. 分析背景3. memblock 简介3.1 memblock 数据结构3.2 memblock 接口 4. memblock 的构建过程 1. 前言 限于作者能力水平,本文可能存在谬误,因此而给读者…...

创新学习方式,电大搜题助您迈向成功之路
近年来,随着信息技术的发展,互联网在教育领域发挥的作用越来越显著。贵州开放大学作为国内首家电视大学,一直致力于创新教学模式,帮助学生更好地获取知识。在学习过程中,学生常常遇到疑难问题,而解决这些问…...

Mybatis整理
Mybatis 定义 Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,加载驱动、创建连接、创建statement等繁杂的过程,开发者开发时只需要关注如何编写SQL语句,可以严格控制sql执行性能,灵…...

pytorch定义datase多次重复采样
有的时候训练需要对样本重复抽样为一个batch,可以按如下格式定义: class TrainLoader(Dataset):def __init__(self, fns, repeat1):super(TrainLoader, self).__init__()self.length len(fns) # 数据数量self.repeat repeat # 数据重复次数def __getitem__(self,…...

自动化测试 —— Pytest fixture及conftest详解!
前言 fixture是在测试函数运行前后,由pytest执行的外壳函数。fixture中的代码可以定制,满足多变的测试需求,包括定义传入测试中的数据集、配置测试前系统的初始状态、为批量测试提供数据源等等。fixture是pytest的精髓所在,类似u…...

Nginx解析漏洞
常见的解析漏洞: IIS 5.x/6.0解析漏洞 IIS 7.0/IIS 7.5/ Nginx <0.8.3畸形解析漏洞 Nginx <8.03 空字节代码执行漏洞 Apache解析漏洞 Nginx文件解析漏洞 对于任意文件名,例如:cd.jpg在后面添加/x.php后,即可将文件作为php解析。 原理…...

【机器学习】决策树原理及scikit-learn使用
文章目录 决策树详解ID3 算法C4.5算法CART 算法 scikit-learn使用分类树剪枝参数重要属性和接口 回归树重要参数,属性及接口交叉验证代码示例 一维回归的图像绘制 决策树详解 决策树(Decision Tree)是一种非参数的有监督学习方法,…...

#基于一个小车项目的FREERTOS分析(一)系统时钟
系统时钟 //初始化延迟函数 //SYSTICK的时钟固定为AHB时钟,基础例程里面SYSTICK时钟频率为AHB/8 //这里为了兼容FreeRTOS,所以将SYSTICK的时钟频率改为AHB的频率! //SYSCLK:系统时钟频率 /* 系统定时器是一个 24bit 的向下递减的计数器&…...

ubuntu mmdetection配置
mmdetection配置最重要的是版本匹配,特别是cuda,torch与mmcv-full 本项目以mmdetection v2.28.2为例介绍 1.查看显卡算力 因为gpu的算力需要与Pytorch依赖的CUDA算力匹配,低版本GPU可在相对高的CUDA版本下运行,相反则不行 算力…...

嵌入式面试常见问题(一)
目录 1.什么情况下会出现段错误? 2.swap() 函数为什么不能交换两个变量的值 3.一个函数有六个参数 分别放在哪个区? 4.定义一个变量,赋初值和不赋初值分别保存在哪个区? 5.linux查看端口状态的命令 6.结构体中->和.的区…...