PCL 、Halcon 聚类:欧式聚类、欧式+法向量区域生长、K-Means

简介

halcon 中的connection_object_model_3d  可以使用不同的属性来实现将一块点云分解成多块点云,而在PCL 中可以使用聚类的方式来完成,具体如下:

pcl 的聚类:

欧式聚类、

欧式+法向量

区域生长、

高斯分类(1.81 中没有)

K-Means

一、Halcon

read_object_model_3d ('C:/Users/Albert/Desktop/Halcon2PCL/Connect3D_test.ply', 'm', [], [], connect3d, Status1)
connection_object_model_3d (connect3d, 'distance_3d', 0.01, ObjectModel3DConnected_222)

 

二、PCL

1、欧式聚类


int  Connection_3d_models(pcl::PointCloud<pcl::PointXYZ>::Ptr  cloud, Connection_3d_Param &p, Connection_3d_Value &v, vector<pcl::PointCloud<pcl::PointXYZ>>& clouds)
{
	if (cloud->size()<1)
	{
		return -1;
	}
	pcl::PointIndices::Ptr inliner(new pcl::PointIndices);
	vector<pcl::PointIndices>ece_inlier;
	pcl::search::KdTree<pcl::PointXYZ>::Ptr  tree(new pcl::search::KdTree<pcl::PointXYZ>);
	pcl::EuclideanClusterExtraction<pcl::PointXYZ> ece;
	ece.setInputCloud(cloud);
	ece.setClusterTolerance(v.distance_3d_value);
	ece.setMinClusterSize(v.minClusterSize);
	ece.setMaxClusterSize(v.maxClusterSize);
	ece.setSearchMethod(tree);
	ece.extract(ece_inlier);

	for (int i = 0; i < ece_inlier.size(); i++)
	{
		pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_copy(new pcl::PointCloud<pcl::PointXYZ>);
		vector<int> ece_inlier_ext = ece_inlier[i].indices;
		pcl::copyPointCloud(*cloud, ece_inlier_ext, *cloud_copy);//按照索引提取点云数据
		clouds.push_back(*cloud_copy);
		std::string filename_result224 = "C:\\Users\\Albert\\Desktop\\Halcon2PCL\\classfiy\\"+to_string(i)+".pcd";
		pcl::io::savePCDFileBinaryCompressed(filename_result224, *cloud_copy);
	}
}

2、欧式+法向量

在欧式分类的基础上加入了法向量角度的维度

int Euclidean_Normal_Clusters(pcl::PointCloud<pcl::PointXYZ>::Ptr  cloud, Connection_3d_Value &v, vector<pcl::PointIndices>&ece_inlier)
{

	if (cloud->size()<1)
	{
		return -1;
	}
	pcl::PointCloud<pcl::Normal>::Ptr  normal(new  pcl::PointCloud<pcl::Normal>);
	// 计算法向量
	pcl::search::KdTree<pcl::PointXYZ> ::Ptr  tree(new pcl::search::KdTree<pcl::PointXYZ>);
	pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> normalomp;
	normalomp.setInputCloud(cloud);
	normalomp.setNumberOfThreads(10);  //	设置加速线程书
	normalomp.setSearchMethod(tree);
	normalomp.setRadiusSearch(v.normal_Radius);   // 计算法向量的 半径大小
	normalomp.compute(*normal);
	// KDtree用于欧式聚类
	pcl::KdTree<pcl::PointXYZ>::Ptr tree_cluster(new pcl::KdTreeFLANN<pcl::PointXYZ>());
	tree_cluster->setInputCloud(cloud);

	// -------------------------------基于法向量的欧式聚类--------------------------------
	pcl::extractEuclideanClusters(*cloud, *normal, v.Euclidean_distance_3d_value, tree_cluster, ece_inlier, v.normal_Esp_Angle, v.minClusterSize);
}

3、区域生长

算法流程:

1. 计算法线normal和曲率curvatures,依据曲率升序排序;

2. 选择曲率最低的为初始种子点,种子周围的临近点和种子点云相比较;

3. 法线的方向是否足够相近(法线夹角足够rpy),法线夹角阈值;

4. 曲率是否足够小(表面处在同一个弯曲程度),区域差值阈值;

5. 如果满足2,3则该点可用做种子点;

6. 如果只满足2,则归类而不做种子。
https://blog.csdn.net/weixin_53660567/article/details/120675139

int  Regional_Growth_Clustering(pcl::PointCloud<pcl::PointXYZ>::Ptr  cloud, Connection_3d_Value &v, vector<pcl::PointIndices>&ece_inlier)
{
	if (cloud->size()<1)
	{
		return -1;
	}

	//  计算法向量
	pcl::PointCloud<pcl::Normal>::Ptr  normal(new  pcl::PointCloud<pcl::Normal>);
	pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new  pcl::search::KdTree<pcl::PointXYZ>);
	pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> nor_Omp;
	nor_Omp.setInputCloud(cloud);
	nor_Omp.setRadiusSearch(v.normal_Radius); 
	nor_Omp.setNumberOfThreads(10);
	nor_Omp.setSearchMethod(tree);
	nor_Omp.compute(*normal);

	// 法向量和曲率的  区域生长分类
	pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal>rg;
	rg.setInputCloud(cloud);
	rg.setInputNormals(normal);
	rg.setSearchMethod(tree);// 搜索方法 搜索树 
	rg.setNumberOfNeighbours(v.rg_Neighbours_Number);  // 领域搜索点的个数
	rg.setMinClusterSize(v.minClusterSize);
	rg.setMaxClusterSize(v.maxClusterSize);
	rg.setCurvatureThreshold(v.rg_Curvature_Threshold);  // 设置曲率的阈值
	rg.setSmoothnessThreshold(v.rg_SmoothnessThreshold);  // 设置平滑度 法线插值阈值
	rg.extract(ece_inlier);
}

 

4、K-Means

最简单的分类方法,就是确定蔟的个数,然后计算当前点到各个蔟中心的距离,然后不断的迭代。可以分类出不同的蔟,但是局限性也很大。当点云中的距离比较接近,或者点云中有大的崆峒的时候容易出错。

1、设置初始蔟中心

2、计算当前点到各个蔟中心的欧式距离v

3、v 中选距离最小的,保存到那个蔟中

4、跟新几何中心(点云中我计算的是质心,这个和分类的结果有很大的关系)

5、迭代,重复2 3 4 


// k-means  分类算法
int  K_Means_Clusters(pcl::PointCloud<pcl::PointXYZ>::Ptr  cloud, Connection_3d_Value &v, vector<pcl::PointIndices>&ece_inlier)
{
	if (cloud->size()<1)
	{
		return -1;
	}

	//1、  初始质心的设置  尽可能的将质心分开点
	pcl::IndicesPtr  index(new vector<int>(cloud->size()));
	//================// 提取所有点云中的索引===========================
	//pcl::ExtractIndices<pcl::PointXYZ> extract;
	//extract.setInputCloud(cloud);
	//extract.setIndices(index);
	//extract.setNegative(true);	
	//extract.filter(*index);  
	//===========================================
	index->resize(v.k_Means_Clusters_Number);
	std::iota(std::begin(*index), std::end(*index), (int)0);
	std::random_device rd;
	std::mt19937 prng(rd());

	// 将数据打乱 并开始重新排列
	std::shuffle(index->begin(), index->end(), prng);
	pcl::PointCloud<pcl::PointXYZ>::Ptr cluster_Centroids(new pcl::PointCloud<pcl::PointXYZ>);
	pcl::copyPointCloud(*cloud, *index, *cluster_Centroids);


	ece_inlier.clear();
	//  k_Means_Clusters_Number 个点云蔟
	ece_inlier.resize(v.k_Means_Clusters_Number);


	//  2、迭代   质心  点云蔟
	while (v.k_Means_Iterator_Number)
	{ 
		float  acc_Tolerance = 0;  // 点云蔟之间的累计误差

		// 定义一个临时索引 vector
		vector<pcl::PointIndices> ece_Inlier;
		 // 定义当前迭代1次的 点云蔟 中各种的点云索引
		ece_Inlier.clear();
		//  k_Means_Clusters_Number 个点云蔟
		ece_Inlier.resize(v.k_Means_Clusters_Number);

		// 2.1  计算点云到中每个点到cluster 质心的欧式距离
		for (size_t i = 0; i < cloud->size(); i++)  
		{
			   vector<float> v;
				for (size_t j = 0; j< cluster_Centroids->size(); j++)
				{
					// 计算当前点到各个蔟的欧式距离 并保存起来
				 v.emplace_back(pcl::euclideanDistance(cloud->points[i], cluster_Centroids->points[j]));
				}
				// 2.2 找到那个里面最小的值 和索引   那个索引就是当前点所要假如的点云cluster 
				std::vector<float>::const_iterator min_v = std::min_element(v.cbegin(),v.cend());
				int  index = std::distance(v.cbegin(), min_v);  // 欧式距离最小的那个位置index
				//2.3 将该点点云的索引放到各自的蔟中
				ece_Inlier.at(index).indices.push_back(i);
		}

		// 2.4 开始迭代, 重新计算点云的质心
		pcl::PointCloud<pcl::PointXYZ>::Ptr update_Centroid(new pcl::PointCloud<pcl::PointXYZ>);

		for (size_t i = 0; i < v.k_Means_Clusters_Number; ++i)
		{
			Eigen::Vector4f centroid;
			pcl::compute3DCentroid(*cloud, ece_Inlier.at(i), centroid);
			cout <<i<< "      点云的个数  :" << ece_Inlier.at(i).indices.size() << endl;
			pcl::PointXYZ center{ centroid[0] ,centroid[1] ,centroid[2] };
			update_Centroid->points.push_back(center);
		}

		// 2.5 判断推出的误差 :迭代档次  和 上次 点云的质心的欧氏距离
		for (size_t i = 0; i < v.k_Means_Clusters_Number; ++i)
		{
			acc_Tolerance+= pcl::euclideanDistance(update_Centroid->points[i], cluster_Centroids->points[i]);
		}

		if (v.k_Means_Clusters_Tolerance>= acc_Tolerance)
		{
			ece_inlier = ece_Inlier;
			break;
		}
		cluster_Centroids->clear();
		cluster_Centroids = update_Centroid;
		cout <<"迭代次数  : "<< v.k_Means_Iterator_Number << endl;
		ece_inlier = ece_Inlier;
		v.k_Means_Iterator_Number--;

	}


}

 不适合的点云:

这种就不适合采用k-means 方法,前面我做测试点云用k-meas方法,不管则么调参数都是有问题的,和我的更新的质心有关,我这里应该更新的是点云的中心,不是质心。

猜你喜欢

转载自blog.csdn.net/weixin_39354845/article/details/131212203
今日推荐