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

article/2025/10/5 7:49:47

目录

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

输出:

  • map1
  • map2

我们通常把参数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]

那么,ijuv的含义是:

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

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

即如下图所示:

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


注:

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

此时map1保存m1typeCV_16SC2map10通道,map2保存m1typeCV_16SC2map11通道。

即,此时令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、cyMatx33d camMat = K.getMat(); //把内参矩阵K复制给camMat变量f = Vec2d(camMat(0, 0), camMat(1, 1)); //二维向量f存储fx、fyc = Vec2d(camMat(0, 2), camMat(1, 2)); //二维向量f存储cx、cyVec4d k = Vec4d::all(0); //创建4维向量k,用于存储畸变系数k1、k2、k3、k4k = (Vec4d)*D.getMat().ptr<Vec4f>(); //把畸变系数k1、k2、k3、k4复制给向量kcv::Matx33d RR  = cv::Matx33d::eye(); //在单目鱼眼相机的矫正中,RR设为单位向量即可cv::Matx33d PP = cv::Matx33d::eye(); //创建矩阵PP,用于存储参数PP.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 < size.height; ++i){float* m1f = map1.getMat().ptr<float>(i);float* m2f = map2.getMat().ptr<float>(i);short*  m1 = (short*)m1f; //令m1为指向map1每个元素的指针,初始m1为&map1(0,0)[0],加1后为&map1(0,0)[1],再加1后为&map1(0,1)[0],再加1后为&map1(0,1)[1]……ushort* m2 = (ushort*)m2f; //令m2为指向map2每个元素的指针,map2为一维矩阵//计算相机坐标系下的坐标double _x = i*iR(0, 1) + iR(0, 2),_y = i*iR(1, 1) + iR(1, 2),_w = i*iR(2, 1) + iR(2, 2);for( int j = 0; j < size.width; ++j){//(i, j)是无畸变图上的像素点坐标
//===========================================矫正过程=============================================double x = _x/_w, y = _y/_w;//计算无畸变情况下的r、thetadouble r = sqrt(x*x + y*y);double theta = atan(r);double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta4*theta4;//计算畸变情况下的theta_ddouble theta_d = theta * (1 + k[0]*theta2 + k[1]*theta4 + k[2]*theta6 + k[3]*theta8);//计算畸变因子scaledouble 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)转化为整数,并存入map1m1[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 < size.height; ++i){double _x = i*iR(0, 1) + iR(0, 2),_y = i*iR(1, 1) + iR(1, 2),_w = i*iR(2, 1) + iR(2, 2);for( int j = 0; j < size.width; ++j){doSomeThingsForAectify();_x += iR(0, 0);_y += iR(1, 0);_w += iR(2, 0);}}

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

	double _x, _y, _w;for( int i = 0; i < size.height; ++i)    for( int j = 0; j < size.width; ++j){_x = i*iR(0, 1) + iR(0, 2) + j * iR(0, 0);_y = i*iR(1, 1) + iR(1, 2) + j * iR(1, 0);_w = i*iR(2, 1) + iR(2, 2) + j * iR(2, 0);doSomeThingsForAectify();}

观察上面的程序,不难看出,_x、_y、_w其实就是矩阵iR齐次坐标(j, i, 1)的乘积,即如下所示:

而在程序注释中我们已说明,矩阵iR相机内参的逆。而相机内参的作用是:缩放因子 x 像素坐标 = 相机内参 x 相机坐标系坐标,如下(注: 根据下图可以看出,缩放因子其实就是相机坐标系下的Z坐标):

(下面把相机坐标系下的坐标简称为相机坐标

那么,把内参矩阵求逆后得到iR,再与(j, i)相乘,得到的其实就是归一化后的相机坐标

结论:

[_x, _y, _w] 实际上就是 [Xc / Zc, Yc / Zc, 1] !

而由于我们假设(j, i)是矫正后的、无畸变图像中的像素点坐标,那么根据相机内参的逆反推回去的归一化后的相机坐标,实际上就是无畸变情况下的归一化后的相机坐标!

也就是说,求**[_x, _y, _w],实际上就是在求无畸变情况下的归一化相机坐标**。

2. 更进一步

更进一步来说,鱼眼畸变发生在相机坐标 -> 像素坐标的过程,而现实世界中的一个点,无论怎样,它的相机坐标是不会发生畸变的,只有像素坐标会发生畸变。

因此,在上面我们说 [_x, _y, _w]无畸变情况下的归一化相机坐标,是不太准确的,更准确的说法应该是:

归一化相机坐标 [_x, _y, _w] 处的点,在无畸变镜头的投影作用下,对应的像素坐标应该是程序中的 (j, i) ;而由于鱼眼镜头有畸变,本应该在 (j, i) 处的点,现在跑到了 (u, v) 处!

3. 如何由 (j, i) 算出 (u, v) ?

那么程序是怎样由(j, i)计算得到(u, v)的呢?OpenCV官方给出了公式:

其中,第一行的x、y、z即程序中的_x、_y、_w

公式摆在这里,但原理如何,OpenCV却没有详细给出。博主大致研究了其原理,不知正确与否,将在下一篇博文给出。


http://chatgpt.dhexx.cn/article/0AqRqMSo.shtml

相关文章

Monocular Fisheye Camera Depth Estimation Using Sparse LiDAR Supervision

Paper name Monocular Fisheye Camera Depth Estimation Using Sparse LiDAR Supervision Paper Reading Note URL: https://arxiv.org/pdf/1803.06192.pdf TL;DR 2018 年 ITSC 文章&#xff0c;出自于德国 Valeo 自动驾驶公司&#xff0c;提出了单目鱼眼深度估计的有监督…

Linux环境fisheye+crucible安装与破解

由于最新的版本中fisheye和crucible已经集成在一起了&#xff0c;故只需要下载一个包就可以了。 需要的软件及版本情况如下&#xff1a; fisheye4.8.7&#xff1b;&#xff08;安装包&#xff09; atlassian-agent-v1.2.3&#xff1b;&#xff08;破解工具&#xff09; mysql-c…

相机模型-鱼眼模型(fisheye camera model)

鱼眼相机模型 &#xff08;fisheye camera model&#xff09; 模型介绍等距投影等立体角投影正交投影体视投影线性投影 Kannala-Brandt 模型去畸变过程投影过程反投影过程 雅可比计算 之前总结了一下针孔相机的模型&#xff0c;然后得到了比较积极的回复&#xff08;其实是我到…

opencv fisheye calibration(鱼眼相机校正)

文章目录 fisheye_calibration小孔成像模型&#xff08;理想相机成像模型&#xff09;fisheye model 相机模型内参校正的原理calibration过程对这个过程简单分析参考文章 fisheye_calibration 小孔成像模型&#xff08;理想相机成像模型&#xff09; 请参考下面的文章 理想相…

【论文】RAPiD: Rotation-Aware People Detection in Overhead Fisheye Images

RAPiD: Rotation-Aware People Detection in Overhead Fisheye Images 在这项工作中&#xff0c;我们开发了一种端到端的旋转感知的人检测方法&#xff0c;称为RAPID&#xff0c;它使用任意方向的包围盒来检测人。我们的完全卷积神经网络使用周期损失函数直接回归每个包围盒的…

fisheye calib视野调整

1. 畸变模型采用 K np.array([[6.2597563231075685e02, 0., 1.1601088601848592e03],[0., 6.2525998102575511e02, 1.1634786618991664e03],[0., 0., 1.]]) 2. 直接使用 initUndistortRectifyMap和remap时注意调节视野的大小&#xff0c;为了方便得到想要的大小需要借助 es…

初识FishEye

一、简介 这几个月一直都在整白盒测试&#xff0c;虽然断断续续&#xff0c;但是也确实学习了不少东西&#xff0c;长了不少见识。今天就要给大家隆重介绍一款非常棒的工具——FishEye&#xff01; FishEye能够为你打开代码库&#xff0c;并帮助你分析代码&#xff0c;一句话概…

FishEye简介

前言 在项目开发过程中&#xff0c;随着开发的进行&#xff0c;将有大量的代码编写提交到代码仓库&#xff0c;如何能全面准确的了解源代码的变化&#xff0c;提交的频率&#xff0c;代码量的趋势&#xff0c;发现代码的缺陷&#xff0c;将是控制源代码质量的重要指标&#xff…

linux服务器开机提示f1,Dell的Linux服务器开机按F1解决方法

最近买了些二手服务器&#xff0c;dell品牌&#xff0c;性价比还不错&#xff0c;但是一台dell 服务器开机总是提示按F1才能进系统,到底应该怎么解决呢&#xff1f; 请问我的电脑开机老是提示“strike the f1 key to continue &#xff0c;f2 to run the setup utility”这些&a…

服务器开机显示f1 f2,开机提示按F1/F2才能进入系统的解决方法

电脑开机出现了一个怪现象&#xff0c;那就是每次开机的时候都会有“F1”或者是“F2”的提示&#xff0c;并且要按一下才能正常进入系统&#xff0c;该怎么解决呢&#xff1f;下面秋叶系统小编教大家解决办法&#xff01; 下图为开机按F1键才能正常进入系统的画面&#xff01; …

联想计算机启机按F1,电脑开机提示按f1不能正常启动怎么办

电脑开机一般都是自动启动,不过有时出现提示要按f1,否则不能正常启动,怎么回事呢?不知道的朋友可以看看以下关于开机提示F1的解决方法,希望对你有帮助! 电脑开机提示按f1的解决方法 启动电脑需要按F1才能进入系统解决方法:开机时要按F1这是BIOS设置错误引起的,所以可能是…

DELL服务器重启以后需要按F1才能进去系统

1、DELL服务器断电以后启动会一直卡在这个页面&#xff0c;需要手动按F1才能进入系统&#xff0c;解决方法如下&#xff1a; 2、按F2进入到"for System Setup (BIOS)" 3、进入到&#xff1a;system BIOS 4、进入到&#xff1a;Miscellaneous Settings 5、设置 F1…

计算机开机按f1f2,电脑开机总是提示按f1 f2问题的解决办法

一、问题描述 开机后出现如下图界面,无法进入系统。 我们看最后两条: 尝试按F1,可以进入了BIOS, 而按F2却一直卡着,无法进入系统,一会之后又自动关机。 其实这是某品牌主板的通病,特别是一些低端的主板。在我们开机后,一直会出现CMOS Checksum Bad 提示按F1 F2的问题。…

技嘉服务器主板按f1才能进系统,电脑开机要按F1或F2才能进入系统的解决方法

有些电脑有段时间没用&#xff0c;开机后发现进不到系统&#xff0c;显示如下图&#xff0c;必须按F1或者F2才能进入系统&#xff0c;虽然每次也能进入系统&#xff0c;但感觉很烦&#xff0c;这是什么原因呢&#xff1f;下面分享两个电脑开机要按F1或F2才能进入系统的解决方法…

服务器启动显示按f1f2f10,电脑开机提示按f1f2f5 电脑开机要按F1F2F5

电脑开机要求按F1&#xff0c;F2&#xff0c;F3或F5 有朋友跟我反应说他的XP系统&#xff0c;开机的时候要手动按F1才可以进WIN程序。那怎么改成默认的呢&#xff1f;可以尝试下以下方法。方法一、开启计算机或重新启动计算机后&#xff0c;及时按下“Del”键进入BIOS的设置界面…

电脑开机,总需要按F1,是什么原因造成的?

开机需要按F1键,原因有好几个,最常见的是BIOS电池没有电了,更换一块电池即可。 一、什么是BIOS电池 BIOS电池也叫主板电池,位置在主板上面,打开机箱盖子就可以看到啦,样子就是圆圆的一个银币一样的,很好看。 BIOS电池其实和电子秤、电视遥控器里的电池是一样的,一般…

华硕主板开机提示按F1怎么办?华硕主板开机提示按F1的解决方法

最近有很多网友问我的电脑采用的是华硕主板&#xff0c;不过最近在进入黑苹果后再开机出现了F1键&#xff0c;想要在BIOS中关闭F1报错检测&#xff0c;不知道如何操作。开机需要按下F1键&#xff0c;通常这种问题并非是致命性故障&#xff0c;可以直接在主板中关闭。那么华硕主…

按F1才能进系统怎么办?电脑开机需要按f1才能进入系统的解决办法

经常有网友问到一个问题&#xff0c;我电脑开机后提示按f1怎么解决&#xff1f;不管理是台式电脑&#xff0c;还是笔记本&#xff0c;都有可能会遇到开机需要按F1&#xff0c;才能进入系统的问题&#xff0c;引起这个问题的原因比较多&#xff0c;今天小编在这里给大家列举了比…

计算机开机按f1,开机要按F1怎么解决?电脑开机不按F1进去系统的方法

相信很多用户在开启电脑的时候都会这种情况&#xff0c;开机的时候电脑要按F1才能进入系统&#xff0c;如果不按F1就进不了系统桌面。时间长了&#xff0c;会让人非常厌烦。那么要如何设置不按F1就直接进入系统桌面呢&#xff1f;下面就和大家介绍一下电脑开机不按F1直接进去系…