PCL、ハルコン クラスタリング: ユークリッド クラスタリング、ユークリッド + 法線ベクトル領域成長、K 平均法

序章

halcon の connection_object_model_3d は、さまざまな属性を使用して点群を複数の点群に分解できます。PCL では、次のようにクラスタリングによって実行できます。

PCL を使用したクラスタリング:

ヨーロッパのクラスタリング、

ユークリッド + 法線ベクトル

地域の成長、

ガウス分類 (1.81 には含まれていません)

K 平均法

1.ハルコン

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)

 

2.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.法線曲率と曲率曲率を計算し、曲率の昇順に並べ替えます。

2. 曲率が最も低い初期シード点を選択し、シードの周囲の隣接点をシー​​ド点群と比較します。

法線の方向が十分に近いかどうか(法線角度が十分な rpy であるかどうか)、法線角度のしきい値。

曲率が十分に小さいかどうか(表面の曲率が同じであるかどうか)、面積差の閾値。

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平均法

最も単純な分類方法は、クラスターの数を決定し、現在の点から各クラスターの中心までの距離を計算し、これを連続的に繰り返すことです。さまざまなクラスターを分類できますが、制限も大きくなります。点群内の距離が比較的近い場合、または点群に大きな穴がある場合は、間違いが発生しやすくなります。

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