需要将骨架图中的节点分为叶尖、内部节点和交叉节点。可以使用PCL库中的pcl::GreedyProjectionTriangulation类进行点云分割然后根据骨架图中节点的坐标和连接关系将节点分类。接下来找到最高的交叉节点并将其标记为植物轮廓。可以使用PCL库中的pcl::getMaxDistance函数找到点云中离点最远的点然后根据该点和骨架图中的节点坐标进行匹配找到最高的交叉节点。然后需要提
以下是使用C++编写PCL 1.8.1的代码示例,实现将点云分割成叶尖、内部节点和交叉节点,并对骨架图进行分割和标记。
#include <pcl/io/ply_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>
#include <pcl/features/normal_3d_omp.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/surface/gp3.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/segmentation/region_growing.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/common/centroid.h>
#include <pcl/common/common.h>
#include <pcl/common/transforms.h>
#include <pcl/common/eigen.h>
int main(int argc, char** argv)
{
// 读取点云数据
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPLYFile<pcl::PointXYZ>("D:/DIANYUNWENJIA/test5_ply.ply", *cloud);
// 进行点云分割,得到叶尖、内部节点和交叉节点
pcl::GreedyProjectionTriangulation<pcl::PointXYZ> gp3;
pcl::PolygonMesh triangles;
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZ>);
tree->setInputCloud(cloud);
gp3.setSearchRadius(0.025);
gp3.setMu(2.5);
gp3.setMaximumNearestNeighbors(100);
gp3.setMaximumSurfaceAngle(M_PI / 4);
gp3.setMinimumAngle(M_PI / 18);
gp3.setMaximumAngle(2 * M_PI / 3);
gp3.setNormalConsistency(false);
gp3.setInputCloud(cloud);
gp3.setSearchMethod(tree);
gp3.reconstruct(triangles);
// 找到最高的交叉节点,将其标记为“植物轮廓”
pcl::PointXYZ max_point;
pcl::getMaxDistance(*cloud, pcl::PointXYZ(0, 0, 0), max_point);
std::vector<pcl::Vertices> vertices = triangles.polygons;
pcl::PointXYZ contour_point;
float max_z = -std::numeric_limits<float>::infinity();
for (auto& v : vertices) {
pcl::PointXYZ p1 = cloud->points[v.vertices[0]];
pcl::PointXYZ p2 = cloud->points[v.vertices[1]];
pcl::PointXYZ p3 = cloud->points[v.vertices[2]];
float z = (p1.z + p2.z + p3.z) / 3;
if (z > max_z) {
max_z = z;
contour_point.x = (p1.x + p2.x + p3.x) / 3;
contour_point.y = (p1.y + p2.y + p3.y) / 3;
contour_point.z = z;
}
}
// 提取出低于轮廓的点作为茎的一部分
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud(cloud);
pass.setFilterFieldName("z");
pass.setFilterLimits(-std::numeric_limits<float>::infinity(), contour_point.z);
pcl::PointCloud<pcl::PointXYZ>::Ptr stem_cloud(new pcl::PointCloud<pcl::PointXYZ>);
pass.filter(*stem_cloud);
// 从数据中提取出茎的内点,并将骨架图中的一度节点重新连接
pcl::PointCloud<pcl::PointXYZ>::Ptr stem_cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>);
pcl::RegionGrowing<pcl::PointXYZ, pcl::Normal> reg;
pcl::search::Search<pcl::PointXYZ>::Ptr stem_tree = boost::shared_ptr<pcl::search::Search<pcl::PointXYZ>>(new pcl::search::KdTree<pcl::PointXYZ>);
stem_tree->setInputCloud(stem_cloud);
pcl::PointCloud<pcl::Normal>::Ptr stem_normals(new pcl::PointCloud<pcl::Normal>);
pcl::NormalEstimationOMP<pcl::PointXYZ, pcl::Normal> stem_ne;
stem_ne.setInputCloud(stem_cloud);
stem_ne.setSearchMethod(stem_tree);
stem_ne.setKSearch(20);
stem_ne.compute(*stem_normals);
reg.setInputCloud(stem_cloud);
reg.setInputNormals(stem_normals);
reg.setMinClusterSize(50);
reg.setMaxClusterSize(1000000);
reg.setSearchMethod(stem_tree);
reg.setNumberOfNeighbours(30);
reg.setSmoothnessThreshold(5.0 / 180.0 * M_PI);
reg.setCurvatureThreshold(5.0);
std::vector<pcl::PointIndices> stem_clusters;
reg.extract(stem_clusters);
std::vector<pcl::PointXYZ> stem_points;
for (auto& i : stem_clusters[0].indices) {
stem_cloud_filtered->points.push_back(stem_cloud->points[i]);
stem_points.push_back(stem_cloud->points[i]);
}
pcl::PointCloud<pcl::PointXYZ>::Ptr stem_skeleton(new pcl::PointCloud<pcl::PointXYZ>);
std::vector<int> stem_neighbors;
std::vector<float> stem_sqr_distances;
pcl::KdTreeFLANN<pcl::PointXYZ> stem_kdtree;
stem_kdtree.setInputCloud(stem_cloud_filtered);
for (auto& p : stem_points) {
stem_kdtree.nearestKSearch(p, 2, stem_neighbors, stem_sqr_distances);
if (stem_sqr_distances[1] < 0.01) { // 连接距离小于0.1cm的节点
stem_skeleton->points.push_back(stem_cloud_filtered->points[stem_neighbors[0]]);
stem_skeleton->points.push_back(stem_cloud_filtered->points[stem_neighbors[1]]);
}
}
// 将骨架图分割成多个部分,每个部分都以叶尖为起点,以交叉节点为终点
pcl::EuclideanClusterExtraction<pcl::PointXYZ> ec;
ec.setClusterTolerance(0.05);
ec.setMinClusterSize(50);
ec.setMaxClusterSize(1000000);
ec.setSearchMethod(tree);
ec.setInputCloud(cloud);
std::vector<pcl::PointIndices> cluster_indices;
ec.extract(cluster_indices);
for (int i = 0; i < cluster_indices.size(); i++) {
pcl::PointCloud<pcl::PointXYZ>::Ptr leaf_cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZ>::Ptr branch_cloud(new pcl::PointCloud<pcl::PointXYZ>);
for (auto& j : cluster_indices[i].indices) {
if (cloud->points[j].z > contour_point.z) { // 如果点高于轮廓,则标记为叶子
leaf_cloud->points.push_back(cloud->points[j]);
}
else {
branch_cloud->points.push_back(cloud->points[j]);
}
}
// 标记叶尖和交叉节点
pcl::PointXYZ leaf_point, branch_point;
pcl::getMinMax3D(*leaf_cloud, leaf_point, branch_point);
float leaf_distance = std::numeric_limits<float>::infinity();
float branch_distance = std::numeric_limits<float>::infinity();
pcl::PointXYZ leaf_tip, branch_tip;
for (auto& p : leaf_cloud->points) {
float distance = pcl::euclideanDistance(leaf_point, p);
if (distance < leaf_distance) {
leaf_distance = distance;
leaf_tip = p;
}
}
for (auto& p : branch_cloud->points) {
float distance = pcl::euclideanDistance(branch_point, p);
if (distance < branch_distance) {
branch_distance = distance;
branch_tip = p;
}
}
}
// 保存点云数据
pcl::io::savePLYFile<pcl::PointXYZ>("D:/DIANYUNWENJIA/test6_ply.ply", *cloud);
return 0;
}
``
原文地址: http://www.cveoy.top/t/topic/g1np 著作权归作者所有。请勿转载和采集!