《Learning OpenCV3》ch19:双目标定与立体匹配-程序员宅基地

技术标签: 人工智能  数据结构与算法  

上一章讲到一些射影几何和单摄像机参数标定的内容,主要还是为了这一章的内容来服务。我们会用到18章的一些概念,比如说内参数矩阵M,畸变参数,旋转矩阵等内容。

目前来说,还没有可靠的方法能够在单幅图像的情况下进行标定和提取三维信息。在立体视觉中,通过把从不同摄像机同时获取的两个或更多的图像的特征,与其他图像中的相关特征进行匹配,分析视差,产生深度信息。


img_25907b501cc21be34cbbfe43acba84fe.png
双目摄像头标定

立体成像

使用双目摄像机进行立体成像包括四个步骤:

  1. 使用数学的方法消除透镜畸变,上一章已经提到。

  2. 调整摄像机之间的位置和距离,并根据标定图像来进行计算。这一步称为“标定”。

  3. 在两个摄像头得到的图像中寻找相同的特征,得到视差d,这一步称为“匹配”。

  4. 通过三角测量的方法将视差图转化为深度图,这一步称为“重投影”。

我们从最后一步开始,然后分析前三个步骤。

三角测量

想象一个理想的立体装置,图像没有畸变,图像平面共面,光轴平行,焦距相同,主点已经被校准到该有的位置(没有接触过这些概念的可以看我的另一篇读书笔记)。

此外,假设摄像机前向平行排列,即每一个像素行和另一个摄像机中的像素行精确地对齐,并能够在物理世界中找到一点P,在左右图像中分别有成像点。那么,通过相似三角形可以得到深度Z。

img_893023e4a73a69872afb46cefbf430ab.png
理想设备

img_91a24278ffc7a934865160cbb2ed22e4.png
相似三角形

Obviously,由公式可知,视差与深度成反比,并且存在非线性关系。也就是说,当视差趋近于零时,微小的变化不会对深度有太大的影响,反之亦然。可以得出结论,立体视觉系统只有在屋里与摄像机距离比较近时才有比较高的深度分辨率。

img_a3760bd0e9379a560ba2008035aa84d4.png
深度与视差成反比

用于立体视觉中OpenCV的二维和三维坐标系如下。


img_bffe6da797046133ea16b010e2d1f643.png
坐标系如上

了解简化模型之后,就要讨论丑陋的现实模型了。现实世界中摄像机不会严格地前向平行对准,虽然可以通过数学的方法矫正图像,但还是要尽量对准,这样使数学变换更容易处理。

并且为了得到更好的结果,需要同步摄像机,即在同一时刻捕获图像,那么物体如果移动,就会出现问题。我们的目标是从数学上将两个摄像机对准到一个观察平面,使得摄像机之间的像素行使彼此精确对准的。


img_a6adef4a4df8ade2d023b18f45198c67.png
真实情况

对极几何

之前搞全景图像拼接的时候用到过,可以看看这篇文章。图中的连接投影中心的直线与投影平面(projective plane)的交点称为极点(epipole),连接投影点和极点的虚线称为极线(epipolar line)。两条极线显然是共面的,称为极面

img_a0eabd8828ecc131bcb9536000ebfb63.png
对极几何

根据推导和几何领域的公理,我们得出以下结论:

  • 摄像机视图中的每一个三维点(图中的点P)都被包含在与每个图像相交的极面中。两者产生的直线是极线。

  • 给定一幅图像上的特征点,在另一个图像中相匹配的点一定位于对应的极线上,这被称为“极线约束”

  • 极线约束意味着,一旦我们知道了立体实验设备之间的对极几何,在两幅图像之间匹配特征的二维搜索可以转变为沿着极线的一维搜索(在应用三角测量原理的时候)。这不仅会极大地节省计算成本,同时会帮助我们排除许多可能会产生虚假匹配的点。

本征矩阵和基本矩阵

本征矩阵E包含关于物理空间中两个摄像机的平移和旋转的信息。基本矩阵(基础矩阵)F除了包含相同的信息外,矩阵F还包含两个摄像机的内参数,他可以在像素坐标系上将两台摄像机关联。

img_fb55b277c09ca919f2a07bab3b88661f.png
平移和旋转

接下来就要讨论一下公式推导的问题了。

具体的思路是用我们已知的极面来把所有这些关联起来。设法向量n,x为极面上的任意一点,a为极面上的固定点,则有


img_738640d54a35eab0e61633cb73f1e4b0.png
法向量垂直于相应平面

可以用叉乘的方式构造法向量,如下

img_0fd8fe4b2e970683ae90ba93d5af4db4.png
叉乘得到一个垂直的向量

我们可以将已经知道的等式变换一下


img_6ab0ec59d2586e449efadb830721e9bb.png
回想一下上面的图
img_db252e25dca634d38d7a11cd5315f3ed.png
简单变换

上一章讲到,旋转矩阵为对称阵,则有

img_f8c7c8c93c547a46628097c736057543.png
对称矩阵

跟据上述条件,则得到


img_15dd83418a6fd5a3d594db07723edb25.png
带入上上上上式

根据线代知识,我们可以讲叉乘写成一个矩阵乘法的形式


img_f4d5df14771abdacc0e8351d24bd345f.png
叉乘转换成矩阵乘法

印象中线代老师好像没讲这个。可以看看这篇博客,其实推导挺简单的

将这个条件回代,得到一个重要结论

img_0df435b9c5354d18c3657ca87716e767.png
重要结论

点乘R·S就是我们对本征矩阵E的定义


img_3ffd48d10dd032659f9fccbfd56d6349.png
最终结论

实际使用中,我们需要的是投影平面上的观察到的点。我们可以利用投影公式


img_32cbff625f555e741e4dd45c54b873d3.png
回忆一下相机模型

则最终结论的左边可以变成


img_5f6997d9bbff1ae39075c8a03ba14e02.png
实际使用

看似我们可以通过基础矩阵来将左边的成像点映射到另一边,但很遗憾的是,E是一个秩亏矩阵(一个秩为2的3x3矩阵),所以只能将一个点映射到一条直线上。

矩阵E包含两个摄像机相关的所有几何信息,但不包括相机的内部参数。上面推导中的向量p只是几何意义上的点,通过下式跟像素点关联起来

img_98385b14be5a0f04d3f1914687eecb09.png
M为内参矩阵

则根据前面的推导结论,可以得到

img_60fdcb903aba724304016a3ed1e64413.png
有点丑

那么中间的一坨就是基本矩阵的定义


img_41b2a700bdd8c16bec6a0f53c1ce6b48.png
基本矩阵

实际使用中就可以这样用


img_25c2e0d92b61901c8ac477ef68d6b45e.png
基本矩阵的最终推导

简而言之,基本矩阵F和本征矩阵E差别在于,基础矩阵在图像像素坐标中操作,本征矩阵E在物理坐标系中操作。相类似的,矩阵F的秩也为2

对于OpenCV中这两个玩意怎么计算,还是看我之前写的这篇文章

立体标定(Stereo Calibration)

讲到这里,就该使用棋盘进行立体标定了。

书的中文版中将 Stereo Calibration 翻译成立体校正,这个很奇怪,按道理说应该是立体标定。后面讲到 Stereo Rectification 的时候又翻译成立体校正,估计前面是错的,后面才是正确的。

具体实现跟单目标定差不多。除了计算出矩阵E和F,还需要计算出两个摄像机之间的旋转矩阵R和平移向量T。方法是分别计算出两个摄像机的R和T,再由以下公式计算


img_8e10509bd817419e13c5de565de42c75.png
旋转

img_f9c7c6671e6cd717c8db94dbd7af6a80.png
平移

立体矫正(Stereo Rectification)

正如之前说到,不存在理想的前线平行对准。那么立体矫正的目的就在于:将两个摄像机的像平面重新映射,使两者位于完全相同的平面上,图像行完全对准到前向平行对准。

经过标定和矫正,我们希望两个摄像机之间的图像行是对准的,以使立体对应(在两个不同摄像机视图中找到同一个点可靠和计算容易。

OpenCV中实现了两个立体矫正算法:1)Hartley算法,只使用基本矩阵产生非标定立体视觉;2)Bouguet算法,使用两个标定摄像机中的旋转和平移参数。一般使用Bouguet算法,这里只讨论这个。算法的主要思想如下:

  • 计算出来的旋转矩阵R切成两半,左图像旋转一半,右图像旋转一半,这样重投影畸变会比较小。

  • 为了使极线平行对准,我们需要将极点变换到无穷远并使极线平行对准的矩阵Rrect。


    img_c9b0364433703dcea4349c0a6f7ae2cd.png
    把旋转矩阵砍一半

    img_52d1181a03dd474a9d71e7f826c6f078.png
    把旋转矩阵砍一半

设一个可以将极点变换到无穷远的矩阵如下


img_a321961567e36e9a53160d0e44f1c00c.png
校正矩阵

左右相机的投影中心之间的平移向量就是左极点方向,则构造e1为


img_14946e77c064a4f22533e7298c24d9d4.png
x映射方向

下一个矢量应该和e1正交,选择沿着图像平面且正交于光轴的方向比较好

img_c54ba9288daee969588fb2d791508fbf.png
y映射方向

最后的就很简单了,叉乘就完事了


img_ea62872a374d6fa88f5c87e4faef7ce4.png
z映射方向

校正矩阵就构造完毕了。最后使用这个下面这个矩阵对图像平面进行变换就可以实现行对准了。


img_c7175f2573bd0265758f09b33d35337f.png
完毕

重投影矩阵可以将图像平面中的二维点映射回物理世界中的三维坐标。这里不加证明地给出重投影矩阵的定义


img_8acbf6cbbbe25104db9fffe170ca328d.png
重投影矩阵

参数来自左侧视图,如果光轴相交于无穷远,则右下角的项应该为0。再给定视差d,则可以投影到物理世界了。


img_8be4c97a32bd66d7749bcb145277dbdf.png
使用重投影矩阵

最后结果
img_02c299a78d5c0c6b05aa7a59048e4a8c.png
三维坐标

通过以上矫正方法,就能得到理想的立体成像装置了,就像最开始那幅图一样。

立体匹配

立体校正过后就是立体匹配这个步骤了。OpenCV实现了两个算法,一个是块匹配算法(block matching, BM),一个是半全局块匹配算法(semi-global block matching, SGBM)。这两种方法是互补的,BM比较快,但精确度有限;SGBM则比较慢,但效果比较好。

这两个算法比较复杂,感觉书上并没有讲清楚。笔者参阅了一些论文和博客,但仍尚未完全理解。尤其是SGBM算法,使用到了信息熵,以及动态规划的思想。之前学过数据结构,但没学到动态规划。这里先留个坑,等我把基础补上去再回过头来总结一下。

有能力的读者可以参考一下最后贴出的论文,我个人是看的有点晕,还是需要继续学习啊- -!

来自三位重投影的深度映射

假设得到了视差d,那得到深度映射就是轻而易举了,用上上面提到的重投影矩阵


img_8be4c97a32bd66d7749bcb145277dbdf.png
使用重投影矩阵

就可以从视差图转换到深度映射

img_e7ef4fb7b23eb66f8313c6ea180f470c.png
视差图,灰度越深越视差越小,变换一下就可以得到深度了

暂时就到这里吧。感觉给自己开了好多坑,幸好总体课不多,还是很有信心把坑都填上的 -.-

Reference:
Small Vision Systems: Hardware and Implementation —— Kurt Konolige
(opencv中 BM算法实现 )
Stereo Processing by SemiglobalMatching and Mutual Information —— Heiko Hirschmu¨ller(SGBM算法)
学习opencv3(中文版) —— Adrian Kaehler & Gary Bradski
向量叉乘与叉乘矩阵

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33752045/article/details/89595923

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签