标签:features ase 轻松 解决方案 附加 并且 normal 欧几里得 解决
本教程描述如何使用pcl::ConditionalEuclideanClustering该类:一种分割算法,该算法基于欧几里得距离和需要保持的用户可自定义条件对点进行聚类。
此类使用与欧几里得聚类提取,区域增长分割和基于颜色的区域增长分割相同的类似贪婪/区域增长/洪水填充方法。与其他类相比,使用该类的优点是,聚类的约束(纯欧几里得,平滑度,RGB)现在可以由用户自定义。一些缺点包括:没有初始的种子系统,没有过度分割和不足分割控制,以及从主计算循环内部调用条件函数的时间效率较低的事实。
理论入门
该欧几里德集群提取和区域生长分割的教程已经解释的区域非常准确地生长算法。这些说明的唯一补充是,现在可以完全定制将邻居合并到当前集群中所需要满足的条件。
随着聚类的增长,它将评估聚类内已经存在的点与附近候选点之间的用户定义条件。候选点(最近的相邻点)是使用聚类中每个点周围的欧式半径搜索来找到的。对于结果集群中的每个点,条件必须与它的至少一个邻居保持,而不是与所有邻居保持。
条件欧几里得聚类也可以基于大小约束自动过滤聚类。分类过小或过大的群集仍可以在以后检索。
首先,下载数据集Statues_4.pcd并从档案中提取PCD文件。这是一个非常大的室外环境数据集,我们希望将单独的对象聚类,并且即使建筑物以欧几里得意义连接,也希望将建筑物与地面分开。现在,conditional_euclidean_clustering.cpp在您喜欢的编辑器中创建一个文件,并将以下内容放入其中:
#include <pcl/point_types.h>#include <pcl/io/pcd_io.h>#include <pcl/console/time.h>
#include <pcl/filters/voxel_grid.h>#include <pcl/features/normal_3d.h>#include <pcl/segmentation/conditional_euclidean_clustering.h>
typedef pcl::PointXYZI PointTypeIO;typedef pcl::PointXYZINormal PointTypeFull;
boolenforceIntensitySimilarity (const PointTypeFull& point_a, const PointTypeFull& point_b, float squared_distance){
if (fabs (point_a.intensity - point_b.intensity) < 5.0f)
return (true);
else
return (false);}
boolenforceCurvatureOrIntensitySimilarity (const PointTypeFull& point_a, const PointTypeFull& point_b, float squared_distance){
Eigen::Map<const Eigen::Vector3f> point_a_normal = point_a.getNormalVector3fMap (), point_b_normal = point_b.getNormalVector3fMap ();
if (fabs (point_a.intensity - point_b.intensity) < 5.0f)
return (true);
if (fabs (point_a_normal.dot (point_b_normal)) < 0.05)
return (true);
return (false);}
boolcustomRegionGrowing (const PointTypeFull& point_a, const PointTypeFull& point_b, float squared_distance){
Eigen::Map<const Eigen::Vector3f> point_a_normal = point_a.getNormalVector3fMap (), point_b_normal = point_b.getNormalVector3fMap ();
if (squared_distance < 10000)
{
if (fabs (point_a.intensity - point_b.intensity) < 8.0f)
return (true);
if (fabs (point_a_normal.dot (point_b_normal)) < 0.06)
return (true);
}
else
{
if (fabs (point_a.intensity - point_b.intensity) < 3.0f)
return (true);
}
return (false);}
intmain (int argc, char** argv){
// Data containers used
pcl::PointCloud<PointTypeIO>::Ptr cloud_in (new pcl::PointCloud<PointTypeIO>), cloud_out (new pcl::PointCloud<PointTypeIO>);
pcl::PointCloud<PointTypeFull>::Ptr cloud_with_normals (new pcl::PointCloud<PointTypeFull>);
pcl::IndicesClustersPtr clusters (new pcl::IndicesClusters), small_clusters (new pcl::IndicesClusters), large_clusters (new pcl::IndicesClusters);
pcl::search::KdTree<PointTypeIO>::Ptr search_tree (new pcl::search::KdTree<PointTypeIO>);
pcl::console::TicToc tt;
// Load the input point cloud
std::cerr << "Loading...\n", tt.tic ();
pcl::io::loadPCDFile ("Statues_4.pcd", *cloud_in);
std::cerr << ">> Done: " << tt.toc () << " ms, " << cloud_in->points.size () << " points\n";
// Downsample the cloud using a Voxel Grid class
std::cerr << "Downsampling...\n", tt.tic ();
pcl::VoxelGrid<PointTypeIO> vg;
vg.setInputCloud (cloud_in);
vg.setLeafSize (80.0, 80.0, 80.0);
vg.setDownsampleAllData (true);
vg.filter (*cloud_out);
std::cerr << ">> Done: " << tt.toc () << " ms, " << cloud_out->points.size () << " points\n";
// Set up a Normal Estimation class and merge data in cloud_with_normals
std::cerr << "Computing normals...\n", tt.tic ();
pcl::copyPointCloud (*cloud_out, *cloud_with_normals);
pcl::NormalEstimation<PointTypeIO, PointTypeFull> ne;
ne.setInputCloud (cloud_out);
ne.setSearchMethod (search_tree);
ne.setRadiusSearch (300.0);
ne.compute (*cloud_with_normals);
std::cerr << ">> Done: " << tt.toc () << " ms\n";
// Set up a Conditional Euclidean Clustering class
std::cerr << "Segmenting to clusters...\n", tt.tic ();
pcl::ConditionalEuclideanClustering<PointTypeFull> cec (true);
cec.setInputCloud (cloud_with_normals);
cec.setConditionFunction (&customRegionGrowing);
cec.setClusterTolerance (500.0);
cec.setMinClusterSize (cloud_with_normals->points.size () / 1000);
cec.setMaxClusterSize (cloud_with_normals->points.size () / 5);
cec.segment (*clusters);
cec.getRemovedClusters (small_clusters, large_clusters);
std::cerr << ">> Done: " << tt.toc () << " ms\n";
// Using the intensity channel for lazy visualization of the output
for (int i = 0; i < small_clusters->size (); ++i)
for (int j = 0; j < (*small_clusters)[i].indices.size (); ++j)
cloud_out->points[(*small_clusters)[i].indices[j]].intensity = -2.0;
for (int i = 0; i < large_clusters->size (); ++i)
for (int j = 0; j < (*large_clusters)[i].indices.size (); ++j)
cloud_out->points[(*large_clusters)[i].indices[j]].intensity = +10.0;
for (int i = 0; i < clusters->size (); ++i)
{
int label = rand () % 8;
for (int j = 0; j < (*clusters)[i].indices.size (); ++j)
cloud_out->points[(*clusters)[i].indices[j]].intensity = label;
}
// Save the output point cloud
std::cerr << "Saving...\n", tt.tic ();
pcl::io::savePCDFile ("output.pcd", *cloud_out);
std::cerr << ">> Done: " << tt.toc () << " ms\n";
return (0);}
由于条件欧几里得群集类是针对更高级的用户的,因此,我将跳过对代码更明显部分的解释:
第85-95行建立了条件欧几里得聚类类以供使用:
// Set up a Conditional Euclidean Clustering class
std::cerr << "Segmenting to clusters...\n", tt.tic ();
pcl::ConditionalEuclideanClustering<PointTypeFull> cec (true);
cec.setInputCloud (cloud_with_normals);
cec.setConditionFunction (&customRegionGrowing);
cec.setClusterTolerance (500.0);
cec.setMinClusterSize (cloud_with_normals->points.size () / 1000);
cec.setMaxClusterSize (cloud_with_normals->points.size () / 5);
cec.segment (*clusters);
cec.getRemovedClusters (small_clusters, large_clusters);
std::cerr << ">> Done: " << tt.toc () << " ms\n";
对不同代码行的更详细描述:
第12-49行显示了条件函数的一些示例:
条件函数的格式是固定的:前两个输入参数的类型必须与条件欧几里德聚类中使用的模板化类型相同。这些参数将传递当前种子点(第一个参数)和当前候选点(第二个参数)的点信息。第三个输入参数必须为浮点数。该参数将传递种子和候选点之间的平方距离。尽管此信息也可以使用前两个自变量进行计算,但是它已经由底层的最近邻居搜索提供,并且可以用于轻松制作距离相关的条件函数。输出参数必须为布尔值。返回TRUE将把候选点合并到种子点的簇中。返回FALSE不会通过该特定点对合并候选点,但是,仍然有可能两个点将通过不同的点对关系最终位于同一群集中。
这些示例条件函数仅用于指示如何使用它们。例如,只要第二条件函数在表面法线方向上相似或强度值相似,它们就会增长。希望这应该将纹理与一个群集相似的建筑物群集在一起,但不要将它们合并为与相邻对象相同的群集。如果强度与附近的物体足够不同并且附近的物体没有以相同的法线共享附近的表面,则可以解决此问题。第三条件函数与第二条件函数相似,但根据点之间的距离具有不同的约束。
第97-109行包含一段代码,该代码是一种快速而肮脏的解决方案,用于可视化结果:
/
// Using the intensity channel for lazy visualization of the output
for (int i = 0; i < small_clusters->size (); ++i)
for (int j = 0; j < (*small_clusters)[i].indices.size (); ++j)
cloud_out->points[(*small_clusters)[i].indices[j]].intensity = -2.0;
for (int i = 0; i < large_clusters->size (); ++i)
for (int j = 0; j < (*large_clusters)[i].indices.size (); ++j)
cloud_out->points[(*large_clusters)[i].indices[j]].intensity = +10.0;
for (int i = 0; i < clusters->size (); ++i)
{
int label = rand () % 8;
for (int j = 0; j < (*clusters)[i].indices.size (); ++j)
cloud_out->points[(*clusters)[i].indices[j]].intensity = label;
}
当使用PCL的标准PCD查看器打开输出点云时,按“ 5”将切换到强度通道可视化。太小的簇将被标记为红色,太大的簇将被标记为蓝色,并且实际的感兴趣的簇/对象将在黄色和青色之间随机着色。
条件欧几里得聚类 pcl::ConditionalEuclideanClustering
标签:features ase 轻松 解决方案 附加 并且 normal 欧几里得 解决
原文地址:https://www.cnblogs.com/codeAndlearn/p/12348784.html