PCL快速点特征直方图配准算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014801811/article/details/79887876

已知点云P中有n个点,那么它的点特征直方图(PFH)的理论计算复杂度是O(nk^2) , 其中k是点云P中每个点p计算特征向量时考虑的邻域数量。对于实际应用中,密集点云的点特征直方图(PFH)的计算,是十分耗时的。本博文介绍一种快速快速点特征直方图FPFH(Fast Point Feature Histograms)算法。FPFH把算法的计算复杂度降低到了O(nk) ,但是任然保留了PFH大部分的识别特性。
理论基础
直方图特征计算步骤:
Step 1.计算了每个查询点Pq的一系列这里写图片描述值,并把它叫做SPFH(Simplified Point Feature Histgram)
Step 2.重新确定每个点的k邻域,使用邻近的SPFH值来计算 的最终直方图(称为FPFH),如下所示:
这里写图片描述
这里的Wk代表了两点的距离。权重(weight)的组合是非常重要的,下面的图显示了这一点:
这里写图片描述
可以看到越近的权重越大,线越粗。

因此,给定一个点Pq,这个算法第一步评估了SPFH的值,通过创造它和它的近邻的匹配。这个过程将一直重复,通过近邻SPFH的值不停的改变权重,最终生成了Pq的FPFH。

PFH与FPFH之间的差异

1.FPFH没有和它所有的近邻有着联系,因此可能会丢失一些值的匹配。

2.PFH模型可以更精确的描述表面,而FPFH则包括了额外的点的配对在半径为r的圆的外面(最多不会超过2r)

3.因为权重的组合,FPFH结合了SPFH的值并且重新获取了一些点的近邻。

4.FPFH复杂度大大降低,计算更快。

5.最终的直方图是简化了。
这里写图片描述
预估FPFH的特征值

FPFH的执行使用了11个分发的子区间,和一个非相关的组合(33位的数组),把它存在pcl::FPFHSignature33这个点类型里面。
下面看一段代码:

// Feature.cpp : 定义控制台应用程序的入口点。
//
//  用于特征提取学习 

// NARF (Normal Aligned Radial Feature 法向径向特征对齐)

// FPFH (Fast Point Feature histograms 快速点特征直方图)

// SIFT (Scale-invariant Feature Transform 尺寸不变特征变换)

// BRIEF (Binary Robust Independent Elementary Features 二进制健壮的独立的基本特性)

#include "stdafx.h"

#include <iostream>
#include <pcl\io\pcd_io.h>
#include <ctime>
#include <Eigen\Core>
#include <pcl\point_types.h>
#include <pcl\point_cloud.h>
#include <pcl\features\fpfh.h>  //FPFH

#include <pcl\registration\ia_ransac.h>

#include <pcl\features\normal_3d.h>

#include <pcl\kdtree\kdtree_flann.h>
#include <pcl\visualization\pcl_visualizer.h>
#include <boost\thread\thread.hpp>

#include <pcl\features\fpfh.h>
#include <pcl\features\fpfh_omp.h>//omp加速计算
#include <pcl\registration\correspondence_estimation.h>
#include <pcl\registration\correspondence_rejection_features.h>
#include <pcl\registration\correspondence_rejection_sample_consensus.h>

#include <pcl\filters\voxel_grid.h>
#include <pcl\filters\approximate_voxel_grid.h>


using namespace std;
typedef pcl::PointCloud<pcl::PointXYZ> pointcloud;
typedef pcl::PointCloud<pcl::Normal> pointnormal;
typedef pcl::PointCloud<pcl::FPFHSignature33> fpfhFeature;

//为了使用fpfh特征匹配,声明一个计算fpfh特征点的函数
fpfhFeature::Ptr compute_fpfh_feature(pointcloud::Ptr input_cloud, pcl::search::KdTree<pcl::PointXYZ>::Ptr tree);

int main(int argc, char* argv[])
{

	clock_t start, end; //long
	start = clock();//开始时间
	pointcloud::Ptr source(new pointcloud);
	pointcloud::Ptr target(new pointcloud);
	pcl::io::loadPCDFile("bunny_target.pcd", *target);
	pcl::io::loadPCDFile("bunny_source.pcd", *source);
	cout << target->size() << endl;

	//精简
	pcl::PointCloud<pcl::PointXYZ> ::Ptr target_filtered(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::PointCloud<pcl::PointXYZ> ::Ptr source_filtered(new pcl::PointCloud<pcl::PointXYZ>());

	pcl::VoxelGrid<pcl::PointXYZ> sor;
	sor.setInputCloud(target);
	sor.setLeafSize(0.005, 0.005, 0.005);
	sor.filter(*target_filtered);

	pcl::VoxelGrid<pcl::PointXYZ> sor1;
	sor1.setInputCloud(source);
	sor1.setLeafSize(0.005, 0.005, 0.005);
	sor1.filter(*source_filtered);
	cout << source_filtered->size() << "  " << target_filtered->size() << endl;

	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>());//创建搜索树
	fpfhFeature::Ptr source_fpfh = compute_fpfh_feature(source_filtered, tree);//计算点云点特征直方图
	fpfhFeature::Ptr target_fpfh = compute_fpfh_feature(target_filtered, tree);

	//对齐  //输入参数 ①源点云+源点特征直方图 ②目标点云+目标点特征直方图
	pcl::SampleConsensusInitialAlignment<pcl::PointXYZ, pcl::PointXYZ, pcl::FPFHSignature33> sac_ia;
	sac_ia.setInputSource(source_filtered);
	sac_ia.setSourceFeatures(source_fpfh);
	sac_ia.setInputTarget(target_filtered);
	sac_ia.setTargetFeatures(target_fpfh);
	pointcloud::Ptr final(new pointcloud);//
	
	//对齐参数设置
	sac_ia.setNumberOfSamples(20);
	sac_ia.setCorrespondenceRandomness(6);//设置计算协方差时选择多少近邻点,该值越大,协防差越精确,但是计算效率越低.(可省)
	//sac_ia.setMaximumIterations(100);
	sac_ia.setEuclideanFitnessEpsilon(0.001);
	sac_ia.setTransformationEpsilon(1e-10);
	sac_ia.setRANSACIterations(30);
	sac_ia.align(*final);
	cout <<"has converged:"<< sac_ia.hasConverged() <<"score"<<sac_ia.getFitnessScore()<< endl;
	
	end = clock();
	cout << "calculate time is " << float(end - start) / CLOCKS_PER_SEC << endl;

	//可视化
	boost::shared_ptr<pcl::visualization::PCLVisualizer> view(new
		pcl::visualization::PCLVisualizer("fpfh test"));
	int v1 = 0;
	int v2 = 1;
	view->createViewPort(0, 0, 0.5, 1, v1);
	view->createViewPort(0.5, 0, 1, 1, v2);
	view->setBackgroundColor(0, 0, 0, v1);
	view->setBackgroundColor(0.05, 0, 0, v2);

	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> source_cloud_color(source_filtered, 255, 0, 0);
	view->addPointCloud(source_filtered, source_cloud_color, "sources_cloud_v1", v1);

	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> target_cloud_color(target_filtered, 0, 255, 0);
	view->addPointCloud(target_filtered, target_cloud_color, "target_cloud_v1", v1);
	view->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sources_cloud_v1", v1);

	//
	pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> aligend_cloud_color(final, 255, 0, 0);
	view->addPointCloud(final, aligend_cloud_color, "aligend_cloud_v2", v2);

	view->addPointCloud(target_filtered, target_cloud_color, "target_cloud_v2", v2);
	view->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "aligend_cloud_v2");
	view->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "target_cloud_v2");

	//对应关系可视化
	pcl::registration::CorrespondenceEstimation<pcl::FPFHSignature33, pcl::FPFHSignature33> crude_cor_est;
	boost::shared_ptr<pcl::Correspondences> cru_correspondences(new pcl::Correspondences);
	crude_cor_est.setInputSource(source_fpfh);
	crude_cor_est.setInputTarget(target_fpfh);
	//crude_cor_est.determineCorrespondences(*cru_correspondences);
	crude_cor_est.determineReciprocalCorrespondences(*cru_correspondences);
	cout << "crude size is " << cru_correspondences->size() << endl;
	view->addCorrespondences<pcl::PointXYZ>(source, target, *cru_correspondences,"correspondence", v1);
	view->initCameraParameters();
	while (!view->wasStopped())
	{
		view->spinOnce(100);
		boost::this_thread::sleep(boost::posix_time::microseconds(100000));
	}

	system("pause");
	return 0;
}

fpfhFeature::Ptr compute_fpfh_feature(pointcloud::Ptr input_cloud, pcl::search::KdTree<pcl::PointXYZ>::Ptr tree)
{
	//法向量
	pointnormal::Ptr point_normal(new pointnormal);
	pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> est_normal;
	est_normal.setInputCloud(input_cloud);
	est_normal.setSearchMethod(tree);
	est_normal.setKSearch(10);
	//est_normal.setSearchSurface();
	est_normal.compute(*point_normal);

	//fpfh估计
	fpfhFeature::Ptr fpfh(new fpfhFeature);
	//pcl::FPFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> est_target_fpfh;
	pcl::FPFHEstimationOMP<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> est_fpfh;
	est_fpfh.setNumberOfThreads(4);//指定4核计算

	est_fpfh.setInputCloud(input_cloud);
	est_fpfh.setInputNormals(point_normal);
	est_fpfh.setSearchMethod(tree);
	est_fpfh.setKSearch(10);
	est_fpfh.compute(*fpfh);

	return fpfh;

}

结果显示
结果显示

References:
[1].https://blog.csdn.net/qq_25491201/article/details/51105826
[2].https://segmentfault.com/a/1190000007703156
[3].http://www.pclcn.org/study/shownews.php?lang=cn&id=96
[4].http://pointclouds.org/documentation/tutorials/fpfh_estimation.php#fpfh-estimation

猜你喜欢

转载自blog.csdn.net/u014801811/article/details/79887876
今日推荐