PCL特征点与配准(1)
关于输入一个具体的物体的点云,从场景中找出与该物体点云相匹配的,这种方法可以用来抓取指定的物体等等,具体的代码的解释如下,需要用到的一些基础的知识,在之前的博客中都有提及,其中用到的一些方法可以翻阅前面的博客,当然有问题可以关注公众号,与众多爱好者一起交流
具体的代码实现
#include <pcl/io/pcd_io.h>
#include <pcl/point_cloud.h> //点云类型头文件
#include <pcl/correspondence.h> //对应表示两个实体之间的匹配(例如,点,描述符等)。
#include <pcl/features/normal_3d_omp.h> //法线
#include <pcl/features/shot_omp.h> //描述子
#include <pcl/features/board.h>
#include <pcl/filters/uniform_sampling.h> //均匀采样
#include <pcl/recognition/cg/hough_3d.h> //hough算子
#include <pcl/recognition/cg/geometric_consistency.h> //几何一致性
#include <pcl/visualization/pcl_visualizer.h> //可视化
#include <pcl/kdtree/kdtree_flann.h> //配准方法
#include <pcl/kdtree/impl/kdtree_flann.hpp> //
#include <pcl/common/transforms.h> //转换矩阵
#include <pcl/console/parse.h> typedef pcl::PointXYZRGBA PointType; //PointXYZRGBA数据结构
typedef pcl::Normal NormalType; //法线结构
typedef pcl::ReferenceFrame RFType; //参考帧
typedef pcl::SHOT352 DescriptorType; //SHOT特征的数据结构 std::string model_filename_; //模型的文件名
std::string scene_filename_; //Algorithm params
bool show_keypoints_ (false);
bool show_correspondences_ (false);
bool use_cloud_resolution_ (false);
bool use_hough_ (true);
float model_ss_ (0.01f);
float scene_ss_ (0.03f);
float rf_rad_ (0.015f);
float descr_rad_ (0.02f);
float cg_size_ (0.01f);
float cg_thresh_ (5.0f); void
showHelp (char *filename)
{
std::cout << std::endl;
std::cout << "***************************************************************************" << std::endl;
std::cout << "* *" << std::endl;
std::cout << "* Correspondence Grouping Tutorial - Usage Guide *" << std::endl;
std::cout << "* *" << std::endl;
std::cout << "***************************************************************************" << std::endl << std::endl;
std::cout << "Usage: " << filename << " model_filename.pcd scene_filename.pcd [Options]" << std::endl << std::endl;
std::cout << "Options:" << std::endl;
std::cout << " -h: Show this help." << std::endl;
std::cout << " -k: Show used keypoints." << std::endl;
std::cout << " -c: Show used correspondences." << std::endl;
std::cout << " -r: Compute the model cloud resolution and multiply" << std::endl;
std::cout << " each radius given by that value." << std::endl;
std::cout << " --algorithm (Hough|GC): Clustering algorithm used (default Hough)." << std::endl;
std::cout << " --model_ss val: Model uniform sampling radius (default 0.01)" << std::endl;
std::cout << " --scene_ss val: Scene uniform sampling radius (default 0.03)" << std::endl;
std::cout << " --rf_rad val: Reference frame radius (default 0.015)" << std::endl;
std::cout << " --descr_rad val: Descriptor radius (default 0.02)" << std::endl;
std::cout << " --cg_size val: Cluster size (default 0.01)" << std::endl;
std::cout << " --cg_thresh val: Clustering threshold (default 5)" << std::endl << std::endl;
} void
parseCommandLine (int argc, char *argv[])
{
//Show help
if (pcl::console::find_switch (argc, argv, "-h"))
{
showHelp (argv[]);
exit ();
} //Model & scene filenames
std::vector<int> filenames;
filenames = pcl::console::parse_file_extension_argument (argc, argv, ".pcd");
if (filenames.size () != )
{
std::cout << "Filenames missing.\n";
showHelp (argv[]);
exit (-);
} model_filename_ = argv[filenames[]];
scene_filename_ = argv[filenames[]]; //Program behavior
if (pcl::console::find_switch (argc, argv, "-k"))
{
show_keypoints_ = true;
}
if (pcl::console::find_switch (argc, argv, "-c"))
{
show_correspondences_ = true;
}
if (pcl::console::find_switch (argc, argv, "-r")) //计算点云的分辨率和多样性
{
use_cloud_resolution_ = true;
} std::string used_algorithm;
if (pcl::console::parse_argument (argc, argv, "--algorithm", used_algorithm) != -)
{
if (used_algorithm.compare ("Hough") == )
{
use_hough_ = true;
}else if (used_algorithm.compare ("GC") == )
{
use_hough_ = false;
}
else
{
std::cout << "Wrong algorithm name.\n";
showHelp (argv[]);
exit (-);
}
} //General parameters
pcl::console::parse_argument (argc, argv, "--model_ss", model_ss_);
pcl::console::parse_argument (argc, argv, "--scene_ss", scene_ss_);
pcl::console::parse_argument (argc, argv, "--rf_rad", rf_rad_);
pcl::console::parse_argument (argc, argv, "--descr_rad", descr_rad_);
pcl::console::parse_argument (argc, argv, "--cg_size", cg_size_);
pcl::console::parse_argument (argc, argv, "--cg_thresh", cg_thresh_);
} double
computeCloudResolution (const pcl::PointCloud<PointType>::ConstPtr &cloud)//计算点云分辨率
{
double res = 0.0;
int n_points = ;
int nres;
std::vector<int> indices ();
std::vector<float> sqr_distances ();
pcl::search::KdTree<PointType> tree;
tree.setInputCloud (cloud); //设置输入点云 for (size_t i = ; i < cloud->size (); ++i)
{
if (! pcl_isfinite ((*cloud)[i].x))
{
continue;
}
//Considering the second neighbor since the first is the point itself.
//运算第二个临近点,因为第一个点是它本身
nres = tree.nearestKSearch (i, , indices, sqr_distances);//return :number of neighbors found
if (nres == )
{
res += sqrt (sqr_distances[]);
++n_points;
}
}
if (n_points != )
{
res /= n_points;
}
return res;
} int
main (int argc, char *argv[])
{
parseCommandLine (argc, argv); pcl::PointCloud<PointType>::Ptr model (new pcl::PointCloud<PointType> ()); //模型点云
pcl::PointCloud<PointType>::Ptr model_keypoints (new pcl::PointCloud<PointType> ());//模型角点
pcl::PointCloud<PointType>::Ptr scene (new pcl::PointCloud<PointType> ()); //目标点云
pcl::PointCloud<PointType>::Ptr scene_keypoints (new pcl::PointCloud<PointType> ());//目标角点
pcl::PointCloud<NormalType>::Ptr model_normals (new pcl::PointCloud<NormalType> ()); //法线
pcl::PointCloud<NormalType>::Ptr scene_normals (new pcl::PointCloud<NormalType> ());
pcl::PointCloud<DescriptorType>::Ptr model_descriptors (new pcl::PointCloud<DescriptorType> ()); //描述子
pcl::PointCloud<DescriptorType>::Ptr scene_descriptors (new pcl::PointCloud<DescriptorType> ()); //载入文件
if (pcl::io::loadPCDFile (model_filename_, *model) < )
{
std::cout << "Error loading model cloud." << std::endl;
showHelp (argv[]);
return (-);
}
if (pcl::io::loadPCDFile (scene_filename_, *scene) < )
{
std::cout << "Error loading scene cloud." << std::endl;
showHelp (argv[]);
return (-);
} // 设置分辨率
if (use_cloud_resolution_)
{
float resolution = static_cast<float> (computeCloudResolution (model));
if (resolution != 0.0f)
{
model_ss_ *= resolution;
scene_ss_ *= resolution;
rf_rad_ *= resolution;
descr_rad_ *= resolution;
cg_size_ *= resolution;
} std::cout << "Model resolution: " << resolution << std::endl;
std::cout << "Model sampling size: " << model_ss_ << std::endl;
std::cout << "Scene sampling size: " << scene_ss_ << std::endl;
std::cout << "LRF support radius: " << rf_rad_ << std::endl;
std::cout << "SHOT descriptor radius: " << descr_rad_ << std::endl;
std::cout << "Clustering bin size: " << cg_size_ << std::endl << std::endl;
} //计算法线 normalestimationomp估计局部表面特性在每个三个特点,分别表面的法向量和曲率,平行,使用OpenMP标准。//初始化调度程序并设置要使用的线程数(默认为0)。
pcl::NormalEstimationOMP<PointType, NormalType> norm_est;
norm_est.setKSearch (); //设置K邻域搜索阀值为10个点
norm_est.setInputCloud (model); //设置输入点云
norm_est.compute (*model_normals); //计算点云法线 norm_est.setInputCloud (scene);
norm_est.compute (*scene_normals); //均匀采样点云并提取关键点
pcl::UniformSampling<PointType> uniform_sampling;
uniform_sampling.setInputCloud (model); //输入点云
uniform_sampling.setRadiusSearch (model_ss_); //设置半径
uniform_sampling.filter (*model_keypoints); //滤波
std::cout << "Model total points: " << model->size () << "; Selected Keypoints: " << model_keypoints->size () << std::endl; uniform_sampling.setInputCloud (scene);
uniform_sampling.setRadiusSearch (scene_ss_);
uniform_sampling.filter (*scene_keypoints);
std::cout << "Scene total points: " << scene->size () << "; Selected Keypoints: " << scene_keypoints->size () << std::endl; //为关键点计算描述子
pcl::SHOTEstimationOMP<PointType, NormalType, DescriptorType> descr_est;
descr_est.setRadiusSearch (descr_rad_); //设置搜索半径 descr_est.setInputCloud (model_keypoints); //输入模型的关键点
descr_est.setInputNormals (model_normals); //输入模型的法线
descr_est.setSearchSurface (model); //输入的点云
descr_est.compute (*model_descriptors); //计算描述子 descr_est.setInputCloud (scene_keypoints); //同理
descr_est.setInputNormals (scene_normals);
descr_est.setSearchSurface (scene);
descr_est.compute (*scene_descriptors); // 使用Kdtree找出 Model-Scene 匹配点
pcl::CorrespondencesPtr model_scene_corrs (new pcl::Correspondences ()); pcl::KdTreeFLANN<DescriptorType> match_search; //设置配准的方法
match_search.setInputCloud (model_descriptors); //输入模板点云的描述子 //每一个场景的关键点描述子都要找到模板中匹配的关键点描述子并将其添加到对应的匹配向量中。
for (size_t i = ; i < scene_descriptors->size (); ++i)
{
std::vector<int> neigh_indices (); //设置最近邻点的索引
std::vector<float> neigh_sqr_dists (); //申明最近邻平方距离值
if (!pcl_isfinite (scene_descriptors->at (i).descriptor[])) //忽略 NaNs点
{
continue;
}
int found_neighs = match_search.nearestKSearch (scene_descriptors->at (i), , neigh_indices, neigh_sqr_dists);
//scene_descriptors->at (i)是给定点云 1是临近点个数 ,neigh_indices临近点的索引 neigh_sqr_dists是与临近点的索引 if(found_neighs == && neigh_sqr_dists[] < 0.25f) // 仅当描述子与临近点的平方距离小于0.25(描述子与临近的距离在一般在0到1之间)才添加匹配
{
//neigh_indices[0]给定点, i 是配准数 neigh_sqr_dists[0]与临近点的平方距离
pcl::Correspondence corr (neigh_indices[], static_cast<int> (i), neigh_sqr_dists[]);
model_scene_corrs->push_back (corr); //把配准的点存储在容器中
}
}
std::cout << "Correspondences found: " << model_scene_corrs->size () << std::endl; // 实际的配准方法的实现
std::vector<Eigen::Matrix4f, Eigen::aligned_allocator<Eigen::Matrix4f> > rototranslations;
std::vector<pcl::Correspondences> clustered_corrs; // 使用 Hough3D算法寻找匹配点
if (use_hough_)
{
//
// Compute (Keypoints) Reference Frames only for Hough
//计算参考帧的Hough(也就是关键点)
pcl::PointCloud<RFType>::Ptr model_rf (new pcl::PointCloud<RFType> ());
pcl::PointCloud<RFType>::Ptr scene_rf (new pcl::PointCloud<RFType> ());
//特征估计的方法(点云,法线,参考帧)
pcl::BOARDLocalReferenceFrameEstimation<PointType, NormalType, RFType> rf_est;
rf_est.setFindHoles (true);
rf_est.setRadiusSearch (rf_rad_); //设置搜索半径 rf_est.setInputCloud (model_keypoints); //模型关键点
rf_est.setInputNormals (model_normals); //模型法线
rf_est.setSearchSurface (model); //模型
rf_est.compute (*model_rf); //模型的参考帧 rf_est.setInputCloud (scene_keypoints); //同理
rf_est.setInputNormals (scene_normals);
rf_est.setSearchSurface (scene);
rf_est.compute (*scene_rf); // Clustering聚类的方法 //对输入点与的聚类,以区分不同的实例的场景中的模型
pcl::Hough3DGrouping<PointType, PointType, RFType, RFType> clusterer;
clusterer.setHoughBinSize (cg_size_);//霍夫空间设置每个bin的大小
clusterer.setHoughThreshold (cg_thresh_);//阀值
clusterer.setUseInterpolation (true);
clusterer.setUseDistanceWeight (false); clusterer.setInputCloud (model_keypoints);
clusterer.setInputRf (model_rf); //设置输入的参考帧
clusterer.setSceneCloud (scene_keypoints);
clusterer.setSceneRf (scene_rf);
clusterer.setModelSceneCorrespondences (model_scene_corrs);//model_scene_corrs存储配准的点 //clusterer.cluster (clustered_corrs);辨认出聚类的对象
clusterer.recognize (rototranslations, clustered_corrs);
}
else // Using GeometricConsistency 或者使用几何一致性性质
{
pcl::GeometricConsistencyGrouping<PointType, PointType> gc_clusterer;
gc_clusterer.setGCSize (cg_size_); //设置几何一致性的大小
gc_clusterer.setGCThreshold (cg_thresh_); //阀值 gc_clusterer.setInputCloud (model_keypoints);
gc_clusterer.setSceneCloud (scene_keypoints);
gc_clusterer.setModelSceneCorrespondences (model_scene_corrs); //gc_clusterer.cluster (clustered_corrs);
gc_clusterer.recognize (rototranslations, clustered_corrs);
} //输出的结果 找出输入模型是否在场景中出现
std::cout << "Model instances found: " << rototranslations.size () << std::endl;
for (size_t i = ; i < rototranslations.size (); ++i)
{
std::cout << "\n Instance " << i + << ":" << std::endl;
std::cout << " Correspondences belonging to this instance: " << clustered_corrs[i].size () << std::endl; // 打印处相对于输入模型的旋转矩阵与平移矩阵
Eigen::Matrix3f rotation = rototranslations[i].block<,>(, );
Eigen::Vector3f translation = rototranslations[i].block<,>(, ); printf ("\n");
printf (" | %6.3f %6.3f %6.3f | \n", rotation (,), rotation (,), rotation (,));
printf (" R = | %6.3f %6.3f %6.3f | \n", rotation (,), rotation (,), rotation (,));
printf (" | %6.3f %6.3f %6.3f | \n", rotation (,), rotation (,), rotation (,));
printf ("\n");
printf (" t = < %0.3f, %0.3f, %0.3f >\n", translation (), translation (), translation ());
} //可视化
pcl::visualization::PCLVisualizer viewer ("Correspondence Grouping");
viewer.addPointCloud (scene, "scene_cloud");//可视化场景点云 pcl::PointCloud<PointType>::Ptr off_scene_model (new pcl::PointCloud<PointType> ());
pcl::PointCloud<PointType>::Ptr off_scene_model_keypoints (new pcl::PointCloud<PointType> ()); if (show_correspondences_ || show_keypoints_) //可视化配准点
{
// We are translating the model so that it doesn't end in the middle of the scene representation
//就是要对输入的模型进行旋转与平移,使其在可视化界面的中间位置
pcl::transformPointCloud (*model, *off_scene_model, Eigen::Vector3f (-,,), Eigen::Quaternionf (, , , ));
pcl::transformPointCloud (*model_keypoints, *off_scene_model_keypoints, Eigen::Vector3f (-,,), Eigen::Quaternionf (, , , )); //因为模型的位置变化了,所以要对特征点进行变化 pcl::visualization::PointCloudColorHandlerCustom<PointType> off_scene_model_color_handler (off_scene_model, , , ); //对模型点云上颜色
viewer.addPointCloud (off_scene_model, off_scene_model_color_handler, "off_scene_model");
} if (show_keypoints_) //可视化关键点
{
pcl::visualization::PointCloudColorHandlerCustom<PointType> scene_keypoints_color_handler (scene_keypoints, , , ); //对场景中的点云上为绿色
viewer.addPointCloud (scene_keypoints, scene_keypoints_color_handler, "scene_keypoints");
viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, , "scene_keypoints"); pcl::visualization::PointCloudColorHandlerCustom<PointType> off_scene_model_keypoints_color_handler (off_scene_model_keypoints, , , );
viewer.addPointCloud (off_scene_model_keypoints, off_scene_model_keypoints_color_handler, "off_scene_model_keypoints");
viewer.setPointCloudRenderingProperties (pcl::visualization::PCL_VISUALIZER_POINT_SIZE, , "off_scene_model_keypoints");
} for (size_t i = ; i < rototranslations.size (); ++i)
{
pcl::PointCloud<PointType>::Ptr rotated_model (new pcl::PointCloud<PointType> ());
pcl::transformPointCloud (*model, *rotated_model, rototranslations[i]);//把model转化为rotated_model
// <Eigen::Matrix4f, Eigen::aligned_allocator<Eigen::Matrix4f> > rototranslations是射影变换矩阵
std::stringstream ss_cloud;
ss_cloud << "instance" << i; pcl::visualization::PointCloudColorHandlerCustom<PointType> rotated_model_color_handler (rotated_model, , , );
viewer.addPointCloud (rotated_model, rotated_model_color_handler, ss_cloud.str ()); if (show_correspondences_) //显示配准连接
{
for (size_t j = ; j < clustered_corrs[i].size (); ++j)
{
std::stringstream ss_line;
ss_line << "correspondence_line" << i << "_" << j;
PointType& model_point = off_scene_model_keypoints->at (clustered_corrs[i][j].index_query);
PointType& scene_point = scene_keypoints->at (clustered_corrs[i][j].index_match); // We are drawing a line for each pair of clustered correspondences found between the model and the scene
viewer.addLine<PointType, PointType> (model_point, scene_point, , , , ss_line.str ());
}
}
} while (!viewer.wasStopped ())
{
viewer.spinOnce ();
} return ();
}
可视化特征角点
使用Hough配准的角点的方法
有兴趣者可以扫描二维码关注公众号,加QQ交流群 一起分享你的学习方法,
如果有好多想法和工程可以一起学习,并且欢迎您一起投稿分享给更多的人,大家一起进步!!
PCL特征点与配准(1)的更多相关文章
- Opencv中使用Surf特征实现图像配准及对透视变换矩阵H的平移修正
图像配准需要将一张测试图片按照第二张基准图片的尺寸.角度等形态信息进行透视(仿射)变换匹配,本例通过Surf特征的定位和匹配实现图像配准. 配准流程: 1. 提取两幅图像的Surf特征 2. 对Sur ...
- PCL点云配准(1)
在逆向工程,计算机视觉,文物数字化等领域中,由于点云的不完整,旋转错位,平移错位等,使得要得到的完整的点云就需要对局部点云进行配准,为了得到被测物体的完整数据模型,需要确定一个合适的坐标系,将从各个视 ...
- PCL系列——怎样逐渐地配准一对点云
欢迎訪问 博客新址 PCL系列 PCL系列--读入PCD格式文件操作 PCL系列--将点云数据写入PCD格式文件 PCL系列--拼接两个点云 PCL系列--从深度图像(RangeImage)中提取NA ...
- Opencv探索之路(二十):制作一个简易手动图像配准工具
近日在做基于sift特征点的图像配准时遇到匹配失败的情况,失败的原因在于两幅图像分辨率相差有点大,而且这两幅图是不同时间段的同一场景的图片,所以基于sift点的匹配已经找不到匹配点了.然后老师叫我尝试 ...
- CV 两幅图像配准
http://www.cnblogs.com/Lemon-Li/p/3504717.html 图像配准算法一般可分为: 一.基于图像灰度统计特性配准算法:二.基于图像特征配准算法:三.基于图像理解的配 ...
- Halcon一日一练:图像拼接技术
图像拼接技术就是针对同一场景的一系列图片,根据图片的特征,比如位置,重叠部分等,拼接成一张大幅的宽视角的图像. 图像拼接要求拼接后图像最大程度的与原图一致,失真尽可能的小,并且要尽量做到天衣无缝即没有 ...
- SVO+PL-SVO+PL-StVO
PL-SVO是基于点.线特征的半直接法单目视觉里程计,我们先来介绍一下基于点特征的SVO,因为是在这个基础上提出的. [1]References: SVO: Fast Semi-Direct ...
- 【CV现状-3.3】特征提取与描述
#磨染的初心--计算机视觉的现状 [这一系列文章是关于计算机视觉的反思,希望能引起一些人的共鸣.可以随意传播,随意喷.所涉及的内容过多,将按如下内容划分章节.已经完成的会逐渐加上链接.] 缘起 三维感 ...
- Computer Vision_33_SIFT:An efficient SIFT-based mode-seeking algorithm for sub-pixel registration of remotely sensed images——2015
此部分是计算机视觉部分,主要侧重在底层特征提取,视频分析,跟踪,目标检测和识别方面等方面.对于自己不太熟悉的领域比如摄像机标定和立体视觉,仅仅列出上google上引用次数比较多的文献.有一些刚刚出版的 ...
随机推荐
- SG仿真常用模块
workspace交互 配合gateway in/out,实现信号仿真与workspace的互联. 滤波器 可与FDATool同时使用,直接关联FDATool的参数,而不必输入FDATool的滤波器系 ...
- 幽灵漏洞(Ghost gethost)
幽灵漏斗简介: 编号CVE-2015-0235的GHOST(幽灵)漏洞是Qualys研究员发现的一个Linux服务上非常严重的安全漏洞,可以被利用来远程代码执行及本地权限提升. 漏洞简要描述 该漏洞存 ...
- 分布式系统的那些事儿(四) - MQ时代的通信
之前在讲RPC通信的各种好处,特别好用,但是RPC并不是万能的,也并不是适用于各种场景的,因为他是同步的:现如今很多场景下的调用都是异步的,系统A调用B后,并不需要知道B的结果,而且对B的结果无所谓, ...
- ios中解析json对象基类
这个是对上面一篇写的一个解析json对象的基类 @interface BaseObjectFromJson : NSObject + (id) objectWithDict:(NSDictionary ...
- 银联在线支付B2C UnionPay.NET
新春即将来临,首先给大家拜个早年,祝攻城狮们新年快乐.万事如意.合家欢乐.团团圆圆.幸福健康.来年更能大展宏图 实现各自的梦想! 同时预祝各大科技公司大佬们事业蒸蒸日上.公司转型突破创新.冲出突围带领 ...
- (原创)c++11改进我们的模式之改进代理模式,实现通用的AOP框架
c++11 boost技术交流群:296561497,欢迎大家来交流技术. 本次要讲的时候如何改进代理模式,具体来说是动态代理模式,动态代理模式一般实现AOP框架,不懂AOP的童鞋看这里.我前面的博文 ...
- CSS中Zen Coding
值别名 有几个常用的别名: p → % e → em x → ex 可以用这些别名来代替完整的单位: w100p → width: 100% m10p30e5x → margin: 10% 30em ...
- android开发图片分辨率
一直受到android开发图片分辨率问题困扰.drawable-(xdpi,hdpi,mdpi,ldpi,nodpi)这几个文件夹到底怎么放图片呢? dpi是什么呢? dpi是“dot per inc ...
- 【delphi】多线程与多线程同步
在 Delphi 中使用多线程有两种方法: 调用 API.使用 TThread 类; 使用 API 的代码更简单. CreateThread function CreateThread( lpThre ...
- 生产者消费者问题Java三种实现
生产者-消费者Java实现 2017-07-27 1 概述 生产者消费者问题是多线程的一个经典问题,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品. 解决生产者/ ...