序章
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 法を使用しました。パラメータをどのように調整しても問題があります。それは更新の重心に関連しています。ここで更新する必要があるのは点群であり、重心ではなく の中心です。