(2)特征点匹配,并求旋转矩阵R和位移向量t
include头文件中有slamBase.h
- # pragma once
- // 各种头文件
- // C++标准库
- #include <fstream>
- #include <vector>
- using namespace std;
- // OpenCV
- #include <opencv2/core/core.hpp>
- #include <opencv2/highgui/highgui.hpp>
- //PCL
- #include <pcl/io/pcd_io.h>
- #include <pcl/point_types.h>
- // 类型定义
- typedef pcl::PointXYZRGBA PointT;
- typedef pcl::PointCloud<PointT> PointCloud;
- // 相机内参结构
- struct CAMERA_INTRINSIC_PARAMETERS
- {
- double cx, cy, fx, fy, scale;
- };
- // 函数接口
- // image2PonitCloud 将rgb图和深度图转换为点云
- PointCloud::Ptr image2PointCloud( cv::Mat& rgb, cv::Mat& depth, CAMERA_INTRINSIC_PARAMETERS& camera );
- // point2dTo3d 将单个点从图像坐标转换为空间坐标
- // input: 3维点Point3f (u,v,d)
- cv::Point3f point2dTo3d( cv::Point3f& point, CAMERA_INTRINSIC_PARAMETERS& camera );
其中有三个部分,相机内参结构,rgb图和深度图转点云,2维像素点转3维空间点坐标(头文件中函数原型)。
src中源程序slamBase.cpp
- #include "slamBase.h"
- //传入rgb, depth, 和相机内参
- PointCloud::Ptr image2PointCloud( cv::Mat& rgb, cv::Mat& depth, CAMERA_INTRINSIC_PARAMETERS& camera )
- {
- PointCloud::Ptr cloud ( new PointCloud );
- for (int m = ; m < depth.rows; m++)
- for (int n=; n < depth.cols; n++)
- {
- // 获取深度图中(m,n)处的值
- ushort d = depth.ptr<ushort>(m)[n];
- // d 可能没有值,若如此,跳过此点
- if (d == )
- continue;
- // d 存在值,则向点云增加一个点
- PointT p;
- // 计算这个点的空间坐标
- p.z = double(d) / camera.scale;
- p.x = (n - camera.cx) * p.z / camera.fx;
- p.y = (m - camera.cy) * p.z / camera.fy;
- // 从rgb图像中获取它的颜色
- // rgb是三通道的BGR格式图,所以按下面的顺序获取颜色
- p.b = rgb.ptr<uchar>(m)[n*];
- p.g = rgb.ptr<uchar>(m)[n*+];
- p.r = rgb.ptr<uchar>(m)[n*+];
- // 把p加入到点云中
- cloud->points.push_back( p );
- }
- // 设置并保存点云
- cloud->height = ;
- cloud->width = cloud->points.size();
- cloud->is_dense = false;
- return cloud;
- }
- //像素坐标转为3维点
- cv::Point3f point2dTo3d( cv::Point3f& point, CAMERA_INTRINSIC_PARAMETERS& camera )
- {
- cv::Point3f p; // 3D 点
- p.z = double( point.z ) / camera.scale; //point.z d
- p.x = ( point.x - camera.cx) * p.z / camera.fx; //point.x u
- p.y = ( point.y - camera.cy) * p.z / camera.fy; //point.y v
- return p;
- }
和实现程序slamBase.cpp在同一文件夹下的CMakeLists.txt
- # 增加PCL库的依赖
- FIND_PACKAGE( PCL REQUIRED COMPONENTS common io )
- list(REMOVE_ITEM PCL_LIBRARIES "vtkproj4") # use this in Ubuntu 16.04
- # 增加opencv的依赖
- FIND_PACKAGE( OpenCV REQUIRED )
- INCLUDE_DIRECTORIES( ${OpenCV_INCLUDE_DIRS} )
- # 添加头文件和库文件
- ADD_DEFINITIONS( ${PCL_DEFINITIONS} )
- INCLUDE_DIRECTORIES( ${PCL_INCLUDE_DIRS} )
- LINK_LIBRARIES( ${PCL_LIBRARY_DIRS} )
- #把slamBase.cpp编译成 slamBase 库,并把该库里调到的OpenCV和PCL的部分,和相应的库链接起来
- ADD_LIBRARY( slambase slamBase.cpp ) # 实现文件 slamBase.cpp
- TARGET_LINK_LIBRARIES( slambase
- ${OpenCV_LIBS}
- ${PCL_LIBRARIES} )
- ADD_EXECUTABLE( detectFeatures detectFeatures.cpp ) # 可执行程序 detectFeatures.cpp
- TARGET_LINK_LIBRARIES( detectFeatures
- slambase
- ${OpenCV_LIBS}
- ${PCL_LIBRARIES} )
库函数:ADD_LIBRARY( slambase slamBase.cpp ) TARGET_LINK_LIBRARIES( slambase ${OpenCV_LIBS} ${PCL_LIBRARIES} )
可执行程序:ADD_EXECUTABLE( detectFeatures detectFeatures.cpp ) TARGET_LINK_LIBRARIES( detectFeatures slambase ${OpenCV_LIBS} ${PCL_LIBRARIES} ) 使用到上诉的库
- #include<iostream>
- #include "slamBase.h" //
- using namespace std;
- // OpenCV 特征检测模块
- #include <opencv2/features2d/features2d.hpp>
- // #include <opencv2/nonfree/nonfree.hpp> // use this if you want to use SIFT or SURF
- #include <opencv2/calib3d/calib3d.hpp>
- int main( int argc, char** argv )
- {
- // 声明并从data文件夹里读取两个rgb与深度图
- cv::Mat rgb1 = cv::imread( "./data/rgb20.png");
- cv::Mat rgb2 = cv::imread( "./data/rgb21.png");
- cv::Mat depth1 = cv::imread( "./data/depth20.png", -);
- cv::Mat depth2 = cv::imread( "./data/depth21.png", -);
- // 声明特征提取器与描述子提取器
- cv::Ptr<cv::FeatureDetector> detector;
- cv::Ptr<cv::DescriptorExtractor> descriptor;
- // 构建提取器,默认两者都为 ORB
- // 如果使用 sift, surf ,之前要初始化nonfree模块
- // cv::initModule_nonfree();
- // _detector = cv::FeatureDetector::create( "SIFT" );
- // _descriptor = cv::DescriptorExtractor::create( "SIFT" );
- detector = cv::ORB::create();
- descriptor = cv::ORB::create();
- vector< cv::KeyPoint > kp1, kp2; //关键点
- detector->detect( rgb1, kp1 ); //提取关键点
- detector->detect( rgb2, kp2 );
- cout<<"Key points of two images: "<<kp1.size()<<", "<<kp2.size()<<endl;
- // 可视化, 显示关键点
- cv::Mat imgShow;
- cv::drawKeypoints( rgb1, kp1, imgShow, cv::Scalar::all(-), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS );
- cv::namedWindow("Keypoints",);
- cv::imshow( "keypoints", imgShow );
- cv::imwrite( "./data/keypoints.png", imgShow );
- cv::waitKey(); //暂停等待一个按键
- // 计算描述子
- cv::Mat desp1, desp2;
- descriptor->compute( rgb1, kp1, desp1 );
- descriptor->compute( rgb2, kp2, desp2 );
- // 匹配描述子
- vector< cv::DMatch > matches;
- cv::BFMatcher matcher;
- matcher.match( desp1, desp2, matches );
- cout<<"Find total "<<matches.size()<<" matches."<<endl;
- // 可视化:显示匹配的特征
- cv::Mat imgMatches;
- cv::drawMatches( rgb1, kp1, rgb2, kp2, matches, imgMatches );
- cv::namedWindow("matches",);
- cv::imshow( "matches", imgMatches );
- cv::imwrite( "./data/matches.png", imgMatches );
- cv::waitKey( );
- // 筛选匹配,把距离太大的去掉
- // 这里使用的准则是去掉大于四倍最小距离的匹配
- vector< cv::DMatch > goodMatches;
- double minDis = ;
- for ( size_t i=; i<matches.size(); i++ )
- {
- if ( matches[i].distance < minDis )
- minDis = matches[i].distance;
- }
- cout<<"min dis = "<<minDis<<endl;
- for ( size_t i=; i<matches.size(); i++ )
- {
- if (matches[i].distance < *minDis)
- goodMatches.push_back( matches[i] );
- }
- // 显示 good matches
- cout<<"good matches="<<goodMatches.size()<<endl;
- cv::drawMatches( rgb1, kp1, rgb2, kp2, goodMatches, imgMatches );
- cv::namedWindow("good matches",);
- cv::imshow( "good matches", imgMatches );
- cv::imwrite( "./data/good_matches.png", imgMatches );
- cv::waitKey();
- // 计算图像间的运动关系
- // 关键函数:cv::solvePnPRansac()
- // 为调用此函数准备必要的参数
- // 第一个帧的三维点
- vector<cv::Point3f> pts_obj;
- // 第二个帧的图像点
- vector< cv::Point2f > pts_img;
- // 相机内参,使用到结构
- CAMERA_INTRINSIC_PARAMETERS C;
- C.cx = 682.3;
- C.cy = 254.9;
- C.fx = 979.8;
- C.fy = 942.8;
- C.scale = 1000.0;
- for (size_t i=; i<goodMatches.size(); i++)
- {
- // query 是第一个, train 是第二个
- cv::Point2f p = kp1[goodMatches[i].queryIdx].pt;
- // 获取d是要小心!x是向右的,y是向下的,所以y才是行,x是列!
- ushort d = depth1.ptr<ushort>( int(p.y) )[ int(p.x) ];
- if (d == )
- continue;
- pts_img.push_back( cv::Point2f( kp2[goodMatches[i].trainIdx].pt ) );
- // 将(u,v,d)转成(x,y,z)
- cv::Point3f pt ( p.x, p.y, d ); // 深度值/scale = z
- cv::Point3f pd = point2dTo3d( pt, C );
- pts_obj.push_back( pd );
- }
- //使用到结构
- double camera_matrix_data[][] = {
- {C.fx, , C.cx},
- {, C.fy, C.cy},
- {, , }
- };
- // 构建相机矩阵
- cv::Mat cameraMatrix( , , CV_64F, camera_matrix_data );
- cv::Mat rvec, tvec, inliers;
- // 求解pnp
- cv::solvePnPRansac( pts_obj, pts_img, cameraMatrix, cv::Mat(), rvec, tvec, false, , 1.0, 0.95, inliers ,cv::SOLVEPNP_ITERATIVE);
- // 求旋转向量和平移向量,旋转矩阵
- cout<<"inliers: "<<inliers.rows<<endl;
- cout<<"R="<<rvec<<endl;
- cout<<"t="<<tvec<<endl;
//旋转矩阵
cv::Mat R;
cv::Rodrigues(rvec,R);
cout<<"R_matrix="<<R<<endl;
- // 画出inliers匹配
- vector< cv::DMatch > matchesShow;
- for (size_t i=; i<inliers.rows; i++)
- {
- matchesShow.push_back( goodMatches[inliers.ptr<int>(i)[]] );
- }
- cv::drawMatches( rgb1, kp1, rgb2, kp2, matchesShow, imgMatches );
- cv::namedWindow("inlier matches",);
- cv::imshow( "inlier matches", imgMatches );
- cv::imwrite( "./data/inliers.png", imgMatches );
- cv::waitKey( );
- return ;
- }
使用到结构,和将旋转向量转为旋转矩阵的函数cv::Rodrigues()
OpenCV会利用一种“随机采样一致性”(Random Sample Consensus)的思路(见https://en.wikipedia.org/wiki/RANSAC)
cv::solvePnPRansac()函数 https://blog.csdn.net/jay463261929/article/details/53818611
和src,include文件同一文件夹下的CMakeLists.txt
- CMAKE_MINIMUM_REQUIRED( VERSION 2.8 )
- PROJECT( slam )
- SET(CMAKE_CXX_COMPILER "g++")
- SET( CMAKE_BUILD_TYPE Debug )
- SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) #可执行文件输出的文件夹
- SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) #库函数编译输出位置
- INCLUDE_DIRECTORIES( ${PROJECT_SOURCE_DIR}/include ) #头文件
- LINK_DIRECTORIES( ${PROJECT_SOURCE_DIR}/lib) #使用编译好的库文件libslamBase.a
- ADD_SUBDIRECTORY( ${PROJECT_SOURCE_DIR}/src ) #可执行程序
注意:当lib 文件下已经有编译好的库库文件libslamBase.a,可以将第一个CMakeLists.txt中ADD_LIBRARY( slambase slamBase.cpp ) TARGET_LINK_LIBRARIES( slambase ${OpenCV_LIBS} ${PCL_LIBRARIES} )去掉,因为slamBase.cpp已经被编译。
将第二个CMakeLists.txt中SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) 去掉。
(2)特征点匹配,并求旋转矩阵R和位移向量t的更多相关文章
- c++ 知道旋转前后矩阵向量值 求旋转矩阵c++/c#代码 知道两个向量求他们的旋转矩阵
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/12115244.html 知道旋转前后矩阵向量值 如何去求旋转矩阵R 的c++/c#代码??? ...
- 第二篇 特征点匹配以及openvslam中的相关实现详解
配置文件 在进入正题之前先做一些铺垫,在openvslam中,配置文件是必须要正确的以.yaml格式提供,通常需要指明使用的相机模型,ORB特征检测参数,跟踪参数等. #==============# ...
- 容斥原理应用(求1~r中有多少个数与n互素)
问题:求1~r中有多少个数与n互素. 对于这个问题由容斥原理,我们有3种写法,其实效率差不多.分别是:dfs,队列数组,位运算. 先说说位运算吧: 用二进制1,0来表示第几个素因子是否被用到,如m=3 ...
- OpenCV使用FLANN进行特征点匹配
使用FLANN进行特征点匹配 目标 在本教程中我们将涉及以下内容: 使用 FlannBasedMatcher 接口以及函数 FLANN 实现快速高效匹配( 快速最近邻逼近搜索函数库(Fast Appr ...
- sift、surf、orb 特征提取及最优特征点匹配
目录 sift sift特征简介 sift特征提取步骤 surf surf特征简介 surf特征提取步骤 orb orb特征简介 orb特征提取算法 代码实现 特征提取 特征匹配 附录 sift si ...
- OpenCvSharp 通过特征点匹配图片
现在的手游基本都是重复操作,一个动作要等好久,结束之后继续另一个动作.很麻烦,所以动起了自己写一个游戏辅助的心思. 这个辅助本身没什么难度,就是通过不断的截图,然后从这个截图中找出预先截好的能代表相应 ...
- opencv 增强现实(二):特征点匹配
import cv2 as cv import numpy as np # def draw_keypoints(img, keypoints): # for kp in keypoints: # x ...
- 机器学习入门-数值特征-数据四分位特征 1.quantile(用于求给定分数位的数值) 2.plt.axvline(用于画出竖线) 3.pd.pcut(对特征进行分位数切分,生成新的特征)
函数说明: 1. .quantile(cut_list) 对DataFrame类型直接使用,用于求出给定列表中分数的数值,这里用来求出4分位出的数值 2. plt.axvline() # 用于画 ...
- (分解质因数模板)求 1~r 内与 n 互素的元素个数
void Solve(LL n){ ///分解质因数保存结果于p p.clear(); ; i*i<=n; i++) ){ p.push_back(i); ) n/=i; } ) p.push_ ...
随机推荐
- 如何用Mockplus快速做一个手风琴菜单?
手风琴菜单是一种比较常用的菜单形式,利用原型工具来做这种菜单通常要用到中继器.即使是功能强大的Axure,想实现该效果也比较麻烦.但如果你对Mockplus有所了解,你一定知道,利用Mockplus的 ...
- app怎么测试性能
性能测试一般来说 都是代码能力相对薄弱的测试人员 进阶的一个方向:但是当你成为一个真正的全栈人才的时候你就不得不学习代码: APP 或者安卓手机 或者iOS 一本测试他的性能的话都是采用:手机安装一 ...
- Kendo UI中TreeView 放入tabstrip中,大数据量时超过边框的解决方案。
参考http://www.kendoui.com/forums/ui/tabstrip/tabstip-with-treeview-treeview-breaking-out-of-tabstrip. ...
- 使用delphi 开发多层应用(二十四)KbmMW 的消息方式和创建WIB节点
KbmMW 中支持基于UDP的消息广播,也支持TCP/IP hub/spoke 方式,还有 基于UDP或者TCP/IP 的点对点的消息传输. 1.基于UDP的消息广播
- 2018.08.27 rollcall(非旋treap)
描述 初始有一个空集,依次插入N个数Ai.有M次询问Bj,表示询问第Bj个数加入集合后的排名为j的数是多少 输入 第一行是两个整数N,M 接下来一行有N个整数,Ai 接下来一行有M个整数Bj,保证数据 ...
- Spring Cloud基础教程视频教程
视频课程包含: Spring Cloud基础视频教程24G 目录 获取方式: 关注公众微信号:博涵大数据 或者扫描下面的二维码关注获取. 关注后在公众平台上回复"SpringCloud基础& ...
- IntelliJ IDEA 2017版 编译器使用学习笔记(一) (图文详尽版);IDE快捷键使用;IDE多行代码同时编写
IntellJ是一款强大的编译器,那么它有很多实用的功能 一.鼠标点击减少效率,快捷键实现各种跳转 (1)项目之间的跳转 快捷键位置: 操作:首先要有两个项目,然后,在不同窗口打开:如图: 然后使用快 ...
- LA 3708 && POJ 3154 Graveyard (思维)
题意:在周长为10000的圆上等距分布着n个雕塑,现在又加入m个,现在让m+n个等距分布,那就得移动一些原有的雕塑,问你移动的最少总距离是多少. 析:首先我们可以知道,至少有一个雕塑是可以不用移动的, ...
- wordpaster更新说明
官方网站:http://www.ncmem.com/ 产品首页:http://www.ncmem.com/webapp/wordpaster/index.aspx 在线演示:FCKEditor2x示例 ...
- struts2和JSON的数据交互
一.实验环境 1.struts2基本包 2.json-plugin 在struts2的lib下可以找到. 3.web.xml 加入struts2 <filter> <filter-n ...