opencv已知相机投影及内参求外参_一步步用c++实现相机标定(张氏标定法)
前言
最近在做相机标定方面的工作,虽然以前多次进行相机标定,但是多数时候是调用opencv的函数,过程相对简单。虽然对标定过程有一定的了解,但是掌握的不是很扎实,所以趁这次机会,对相机标定过程做了一下深入的了解,并且用c++重新实现了下整个标定过程(opencv 中有源码,但是有点乱我没太看懂,有可能我比较渣渣),所以这是篇学习笔记。其中有些自己的理解,所以难免有错误的地方,还望指正,抱拳。
小学时候大家都做过小孔成像实验,当孔足够小的时候,在小孔的另一端我们能看到物体的倒立成像,但是当小孔变大的时候图像的不同部分发出的光线会在孔另一端的屏幕上重叠成像就不清晰了。现代相机正是依据小孔成像原理制作的,由于亮度等其他原因,使用镜头代替了小孔。同时镜头又给成像带来了其他问题,比如说畸变。
标定的目的
相机的作用是把看到的3D世界转换成2D图像,相当于我的输入是三维数据经过一个函数把他变成二维数据。我们想找到这个理想的函数,那么从数学角度先对这个问题进行建模,也就是相机的成像模型+畸变模型,而标定的目的就是通过观察到的数据,去无线逼近这个函数,也就是逼近这个理想模型的参数。
相机模型(透视投影模型,成像过程)
在看相机模型前先明确几个坐标系
世界坐标系: 单位m,三维世界的坐标系,用户在描述真实世界位置而引入的坐标系,后面我们把棋盘板坐标系看作世界坐标系。
相机坐标系: 单位m,通常原点在光心,z轴与光轴平行,为了描述物体和相机的相对位置而引入。
图像坐标系: 单位m,原点位于sensor中心,比如ccd相机的sensor中心,xy轴分别平行于sensor的两个边。
像素坐标系: 单位像素,这个是我们最常见的用于描述图片的,比如从相机读取出来的图片,原点在图片左上角。
图1 source:mathworks
上图是各坐标系间的关系,
世界坐标系 -> 相机坐标系
世界坐标系到相机坐标系是一个刚体变换过程,刚体变换可以用一个旋转矩阵(也可以为旋转向量、欧拉角、四元数,程序中对外参进行优化时就是利用旋转向量这种紧凑的表示方法进行优化,后文会提到)和平移向量来表示。
本文中以标定版坐标系为世界坐标系,所以$z_w=0$,所以$W$的第三列可以省略,用更紧凑的形式表示。
相机坐标系 -> 理想无畸变的图像坐标系
图2 source:opencv
图中蓝色的坐标轴(xy)是图像坐标系,橘黄色的坐标轴(uv)为像素坐标系,两个坐标系重合并且和相机坐标系(
根据相似三角形原理,图像坐标系下点P,焦距f,可得到对应的相机坐标系下的点为:
向量话形式为:
式2,3是在笛卡尔坐标系下对透视变换的描述(非线性变换),我们也可以在齐次坐标系下对这个过程进行线性描述。
其中:
图像坐标系 -> 像素坐标系
上面我们得到了点在相机坐标系下的坐标,要想得到实际图片的坐标的话,还要进行进一步的变换,可以理解为仿射变换。那么我们需要知道,xy方向上两个坐标系的尺度变换系数;由于像素坐标系的原点在图像的左上角,而图像坐标系的原点在图像的中心
相机坐标系->实际的图像坐标系
相机的畸变过程就发生在相机坐标系到图像坐标系这个变换过程,所以我们也是在这个过程中引入畸变模型。相机畸变主要分为两类,径向畸变和切向畸变。径向畸变是由于透镜形状的制造工艺导致。且越向透镜边缘移动径向畸变越严重。
切向畸变是由于透镜和CMOS或者CCD的安装位置误差导致。切向畸变需要两个额外的畸变参数来描述,矫正前后的坐标关系为:
所以,现在我们需要五个参数来描述相机的畸变,我们可以用下式来表示:
投影过程总结
其中:
上式中不是严格的矩阵乘法,其中涉及极坐标化等。其中
标定过程
我们已经了解了相机投影模型,下面我们来看下怎么对未知参数进行标定。首先我们用相机采集不同姿态标定板的图像,标定板上带有明显视觉特征点。其中我们以标定板左上角角点为原点,垂直于标定板平面方向为z轴,建立世界坐标系,这样我们就能得到特征点在世界坐标系下的坐标和其在像素坐标系下的坐标。整个过程可分为五部分。
1. 求单映性矩阵(homography)
单映性矩阵用来描述两个平面上的点的映射关系,结合前文提到的图像坐标系到像素坐标系的映射关系,我们有:
设
2.计算内参矩阵
H已知,H可以分解为下式:
由于
用h的展开式替换约束条件中的h:
令
根据上文得到的$A$,展开$B$可知$B$为对称矩阵,且两个约束条件可变为:
下面我们用一个六维的向量b对B进行表示:
对约束条件完全展开可以得到:
其中:
所以,我们的约束条件变为:
因为$B$为内参矩阵$A$计算得来,通过cholesky分解,可得到$A$的闭式解:
其中,
3.计算外参矩阵
上面我们计算出了内参矩阵$A$,和单映性矩阵$H$,所以我们可以得到:
其中,$r$为旋转矩阵的向量,所以列向量正交且模为1,所以:
4.计算畸变参数
本文对畸变参数的计算只考虑二元径向畸变的情况来介绍畸变参数的求解过程。畸变模型可以表示为:
其中,
所以,我们可以对观察到的点进行建模:
化简得到:
其中
可使用SVD,或者QR分解,对参数进行求解。
5.全局微调
前面的步骤我们已经得到所有参数,包括内参,外参和畸变参数,但是这些值都是初值,我们需要利用所有数据对参数进行进一步优化。有话的方法是最小化投影误差,即计算到的点和观察到的点的偏差。因为这个过程中涉及到投影变换,畸变模型等,所以这是非线性优化问题,本文的实现使用ceres库对各参数进行优化。其中,将外参中的旋转矩阵转化为更为紧凑的旋转向量进行表示,降低优化难度。
结果
相同样本图片的前提下,本文实现的相机标定结果为:
opencv calibrateCamera 结果:
参考文献
[1] Burger, Wilhelm. "Zhang’s camera calibration algorithm: in-depth tutorial and implementation." Hagenberg, Austria (2016).
[2] Hartley, Richard, and Andrew Zisserman. Multiple view geometry in computer vision. Cambridge university press, 2003.
[3] https://zhuanlan.zhihu.com/p/24651968
[4] http://ceres-solver.org/index.htm
[5] Zhang, Zhengyou. "A flexible new technique for camera calibration." IEEE Transactions on pattern analysis and machine intelligence 22.11 (2000): 1330-1334.
opencv已知相机投影及内参求外参_一步步用c++实现相机标定(张氏标定法)相关推荐
- Step1:模型 16个相机参数(内参、外参、畸变参数)
16个相机参数 摘要:本文首先介绍了针孔相机模型,然后推导四个坐标轴变换的关系,引出R.T.K.D中包含相机的5个内参,6个外参,5个畸变参数.相机的标定是从空间点及其对应的像素点,获得相机的位置信息 ...
- 1.模型 16个相机参数(内参、外参、畸变参数)
16个相机参数 摘要:本文首先介绍了针孔相机模型,然后推导四个坐标轴变换的关系,引出R.T.K.D中包含相机的5个内参,6个外参,5个畸变参数.相机的标定是从空间点及其对应的像素点,获得相机的位置信息 ...
- OPENCV已知内参求外参
利用OPENCV,已知内参标定外参 #include <opencv2/calib3d.hpp> #include <iostream> #include <opencv ...
- python已知Camera及Lidar2到Lidar1外参,求Camera到Lidar2外参
公式推导: Camra到Lidar1外参:camera_short_extrinsics.yaml 这里有我自己整理了一套最新的python系统学习教程,包括从基础的python脚本到web开发.爬虫 ...
- 相机校正与相机内参、外参
简 介: 对于相机进行校正,是为之后视觉测量奠定基础.本文在总结了相机校正中的基本概念.使用cv2的函数说明校正的过程. 关键词: 相机校正,内参,外参 #mermaid-svg-0bogxxocrI ...
- 已知随机变量X的协方差矩阵求去X的特征值 特征向量 PCA投影矩阵
已知随机变量X的协方差矩阵求去X的特征值 特征向量 PCA投影矩阵 相关的知识都忘记了,去查的时候没有耐心看别人长篇大论讲解,就只简单记录了一下如果从协方差矩阵来计算特征值和特征向量. 定义:1.特征 ...
- 天文观测理论——已知像素大小、焦距,求像素分辨率
一.已知像素大小.焦距,求像素分辨率 1. 计算 像素分辨率为 Pix, 单位:角秒/像素 像素大小为 d2d^2d2, 单位:平方毫米 焦距为 fff, 单位:毫米 总像素为 rerere, 单位: ...
- (九)相机内参、外参、反透视变换python opencv
背景知识 任务需求:将相机上的一个点投影到真实世界平面上去. 原则上单目相机是不可以的,因为只记录了二维信息,真实世界是三维的,双目相机可以通过视差,或者单目+IMU组合,但是由于特征点在地面上的先验 ...
- 转 已知两点坐标和半径求圆心坐标程序C++
数学思想:利用圆方程和直线方程 已知两点坐标和半径求圆心坐标程序 #include <iostream> #include <fstream> #include <cma ...
最新文章
- Spring MVC和Struts2
- 二进制求和Python解法
- 在2008 server安装vm server时发生的错误error1718、error1335……
- 视觉SLAM笔记(36) 3D-2D: PnP
- 关于SilverLight视频播放器
- 利用Java手写简单的httpserver
- 万能硬盘数据恢复软件注册码真的可以用吗?
- qt开发linux性能测试工具,基于Qt的ARM-Linux系统测试工具
- python爬虫气象数据_python爬虫入门,获取全国气象站24小时整点气象数据(二)...
- Django搭建后台管理系统
- 计算几何 - 你绝对找不到比这更好的计算几何
- Couch-to-5K 跑步计划
- Confluence相关背景知识
- 雷军:做互联网需7字诀
- 为什么BFE可以取代Nginx:十问十答
- 五、HTML5单页框架View.js介绍 - View.js的比较优势
- React项目 antd 修改主题颜色
- <贪心算法>学习及经典实例分析
- 清华三宝:单车、土豆、N字班
- 混凝土搅拌站远程监控解决方案