http://blog.sina.com.cn/s/blog_4298002e01013yb8.html

很多时候我们不知道摄像机的内参数矩阵,并且我们也不太关注内参数到底是多少,因为我们仅仅关心如何得到两幅图像的稠密匹配,或者两幅图像的差别——例如我们只想计算两幅图像的视差图,或者说得到两幅立体图像对的深度图就足够了。既然不知道摄像机的内参数,那么就只能借助对极约束来达到目的了。通过计算两幅图像的基础矩阵F,然后利用对极约束矫正极线为平行线的方法,可以很好的实现这个目标,该方法也被称为Hartly方法,在OpenCV中由cv::stereoRectifyUncalibrated函数实现。

立体图像的极线矫正需要三个步骤:

(1)提取特征点并匹配,参考http://blog.sina.com.cn/s/blog_4298002e01013w4z.html

(2)计算基本矩阵F,参考http://blog.sina.com.cn/s/blog_4298002e01013w9a.html

(3)极线矫正。

Hartly方法的函数原型如下:

//! computes the rectification transformation for an uncalibrated stereo camera (zero distortion is assumed)
CV_EXPORTS_W bool stereoRectifyUncalibrated( const Mat& points1, const Mat& points2,
                                             const Mat& F, Size imgSize,
                                             CV_OUT Mat& H1, CV_OUT Mat& H2,
                                             double threshold=5 );

该函数输入参数为两幅图像的匹配特征点,基本矩阵F以及图像的尺寸,返回的参数是两幅图像各自对应的单应变换矩阵H1和H2。只需要对两幅图像按照H1和H2做单应变换,即可得到矫正后图像。假设I为图像,变换如下:

I_recty = H*I

需要说明一点,该函数的前两个参数Mat& points1, Mat& points2与cv::findFundamentalMat的前两个参数并不是相同的数据结构。它们虽然可以是同一个匹配点集,但是他们的数据结构是完全不同的!cv::findFundamentalMat中传入的匹配点集要求是2xN或者Nx2的矩阵,但是cv::stereoRectifyUncalibrated中要求传入的匹配点集必须是1x2N或者2Nx1的矩阵!在很多文档中都说他们的参数是一样的,这其实是一个天大的错误,如果用计算F的匹配点集直接传给图像矫正函数,程序将直接崩溃。正确的做法是利用cv::Mat的构造函数,直接从vector<Point2f>构造一个cv::Mat传入。

cv::stereoRectifyUncalibrated函数默认原始图像是没有径向畸变的,因此在矫正图像之前,最好先对原始图像做径向矫正。

另外需要注意的一点,函数返回的单应变换矩阵H1和H2都是double类型,也即CV_64F类型,若不是该类型的矩阵,与之相乘会报错。下面是示例代码:

// 假设前面我们已经得到两幅图像的匹配特征点,并计算出了基本矩阵F,同时得到了匹配特征点的inlier

// Mat m_matLeftImage;
// Mat m_matRightImage;

// vector<Point2f> m_LeftInlier;
// vector<Point2f> m_RightInlier;

// Mat m_Fundamental;

// 计算图像矫正的单应变换矩阵

Mat m_LeftH;
Mat m_RightH;

stereoRectifyUncalibrated(Mat(m_LeftInlier), Mat(m_RightInlier), m_Fundamental,
                          Size(m_matLeftImage.cols, m_matLeftImage.rows),
                          m_LeftH, m_RightH);

// 任意指定一个内参数矩阵K,不会影响计算结果,此处设为单位阵。

Mat K = Mat::eye(3, 3, CV_64F); // 注意一定是double类型
Mat invK = K.inv(DECOMP_SVD);
Mat LeftR = invK*m_LeftH*K;     // 根据单应变换矩阵计算左图摄像机在空间中的变换矩阵R1
Mat RightR = invK*m_RightH*K;   // 计算右图摄像机在空间中的变换矩阵R2
Mat LeftMap1, LeftMap2;
Mat RightMap1, RightMap2;

Mat Distort;                    // 径向畸变为0,设为空矩阵
Size UndistSize(m_matLeftImage.cols, m_matLeftImage.rows);

// 计算左右两幅图像的映射矩阵

initUndistortRectifyMap(K, Distort, LeftR, K, UndistSize, CV_32FC1, LeftMap1, LeftMap2);
initUndistortRectifyMap(K, Distort, RightR, K, UndistSize, CV_32FC1, RightMap1, RightMap2);

// 把原始图像投影到新图像上,得到矫正图像

Mat m_LeftRectyImage;
Mat m_RightRectyImage;

remap(m_matLeftImage, m_LeftRectyImage, LeftMap1, LeftMap2, INTER_LINEAR);
remap(m_matRightImage, m_RightRectyImage, RightMap1, RightMap2, INTER_LINEAR);

// 显示结果

cvNamedWindow( "left image", 1);
cvShowImage("left image", &(IplImage(m_LeftRectyImage)));
cvNamedWindow( "right image", 1);
cvShowImage("right image", &(IplImage(m_RightRectyImage)));
cvWaitKey( 0 );
cvDestroyWindow( "left image" );
cvDestroyWindow( "right image" );

程序计算的结果如下图所示:

原始图像对:

极线矫正后的图像:

得到上面的矫正图像之后,就可以计算视差或者进行稠密匹配了。下面以视差计算的应用为例,分别用GC和SGBM算法计算视差,结果如下图:

SGBM算法:

GC算法:

OpenCV在未知相机内参数情况下的立体图像矫正方法及注意事项的更多相关文章

  1. 【原创】Linux系统不能上互联网情况下软件更新安装方法

    实际工作中,遇到了Linux 服务器在一台被隔离互联网的PC服务器上运行,使用Virtual Box,在解决此Linux更新的问题上,自己尝试了如下几种策略: 1.将DVD安装盘作为Linux更新源, ...

  2. oracle11g在没DNS情况下failover的配置方法

    1.Oracle 11g引入scan ip ,可在没有DNS的情况下,客户端连接RAC配置failover时,很多情况还是使用oracle10g方式.即在客户端tnsname.ora中配置failov ...

  3. php 502 无错误行和报错文件的情况下使用gdb调试方法

    lnmp环境 gdb  /usr/local/php5.2/bin/php-cgi  /tmp/coredump-php-cgi.20503 source /home/tmp/lnmp1.4-full ...

  4. css3种不知道宽高的情况下水平垂直居中的方法

    第一种:display:table-cell 组合使用display:table-cell和vertical-align.text-align,使父元素内的所有行内元素水平垂直居中(内部div设置di ...

  5. Java中只有按值传递,没有按引用传递!(两种参数情况下都是值传递)

    今天,我在一本面试书上看到了关于java的一个参数传递的问题: 写道 java中对象作为参数传递给一个方法,到底是值传递,还是引用传递? 我毫无疑问的回答:“引用传递!”,并且还觉得自己对java的这 ...

  6. Ubuntu 在未知root密码的情况下修改root密码

    一, 开机按 F12 (或长按Shift), 进入GRUB界面. 二, 在 recovery mode 按e Ubuntu, Linux 3.5.0-17-generic (恢复模式) (或recov ...

  7. @FeignClient 情况下header的传递方法,RestTemplate情况下Header传递方法

    今天因为要调用另一个服务,因为我们用的是SpringCloud框架,所以通过Fegin调用,正好另一方服务有权限校验,需要传递token和设备ID,这两个参数都需要放到Header中, 用 @Requ ...

  8. linux在不关机情况下新增硬盘的方法

    开机状态插入硬盘 不关机执行命令 echo "- - -" > /sys/class/scsi_host/host0/scan 注意 echo "- - -&quo ...

  9. 在有主分支和个人分支情况下的TFS使用方法

    从事.NET开发的资深童鞋一定都知道VS有自带的代码管理工具TFS(Team Foundation Server ),但是开发萌新可能就不太了解了,下面我就介绍一下这个工具以及它的一些常用操作. TF ...

随机推荐

  1. 简要说说NUC972和linux的那些大坑

    刚开始装虚拟机,按照步骤,一步一步,装完,发现虚拟机连不上网,后来在网上得知得需要启动虚拟机设置,可是观察我的虚拟机并没有该选项,起初我认为是版本的问题,可是后来才发现,一时贪便宜,图省事,就没有注册 ...

  2. C语言之顺序结构

    该章内容:这章我们学习三大结构之一:顺序结构,它是程序从上往下顺序执行,是程序运行最简单的方式.printf和scanf函数使用和特例是必考知识.本章是考试的重点章节. 学习方法:从简单的顺序结构题目 ...

  3. 基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个立方体(5)

    在上篇文章中,我们介绍了VBO索引的使用,使用VBO索引可以有效地减少顶点个数,优化内存,提高程序效率. 本教程将带领大家一起走进3D--绘制一个立方体.其实画立方体本质上和画三角形没什么区别,所有的 ...

  4. 使用Editplus和Dev C++配置C++的编译运行环 境

    或许大家会有疑问,为何不直接使用VC;VS;或Dev这些IDE呢?何必舍近求远.主要是因为写程序这么多年来已经习惯了Editplus,包括他的快捷键,语法自动完成,语法提示等等,Editplus用了这 ...

  5. poj_3283 trie树

    题目大意 将一副牌进行编号,四种花色分别标记为'C'.'D'.'H'.'S',数值标记为'A'.'1'.'2'.'3'.'4'.'5'.'6'.'7'.'8'.'9'.'10'.'J'.'Q'.'K' ...

  6. activemq 实战三 了解连接器的URI-Understanding connector URIs

    Before discussing the details of connectors and their role in the overall ActiveMQ architecture, it’ ...

  7. nohop以及后台运行的相关集合

    本文参考:https://blog.csdn.net/u011095110/article/details/78666833 1. 后台运行一个命令: & tar -czvf /mnt/aa. ...

  8. .net asp 在1.asp页面嵌入另一个页面2.asp

    <iframe src="http://www.baidu.com" width="100%" height="100%" onloa ...

  9. c# winform窗体边框风格的设计

    1.首先,窗体的FormBorderStyle设置成None,不要控制边框. 2.然后,TransparencyKey和BackColor颜色设置成相同的,这样,窗体就透明了. 3.最后,窗体的拖动 ...

  10. FNV hash算法

    原文:https://blog.csdn.net/u013137970/article/details/79020095 FNV算法简介FNV算法属于非密码学哈希函数,它最初由Glenn Fowler ...