#include <iostream> #include <pcl/point_types.h> #include <pcl/io/ply_io.h> #include <pcl/visualization/pcl_visualizer.h> #include <pcl/common/centroid.h> #include <pcl/features/normal_3d.h> #include <pcl/visualization/cloud_viewer.h> #include <pcl/segmentation/extract_clusters.h> #include <vector> #include <unordered_map> 'vtkAutoInit.h' #include <pcl/visualization/point_cloud_color_handlers.h> VTK_MODULE_INIT(vtkRenderingOpenGL); // VTK was built with vtkRenderingOpenGL2

using namespace pcl; typedef pcl::PointXYZ PointT; typedef pcl::PointCloud<PointT> PointCloudT;

// 表示一条边的结构体 struct Edge { int src, tgt; float weight; };

// 表示并查集的子集的结构体 struct Subset { int parent, rank; };

// 表示一个连通的、无向的、带权重的图的类 class Graph { public: int V, E; std::vector<Edge> edges;

Graph(int v, int e)
{
	V = v;
	E = e;
}

&#x2F;&#x2F; 添加一条边到图中
void addEdge(int src, int tgt, float weight)
{
	Edge edge;
	edge.src = src;
	edge.tgt = tgt;
	edge.weight = weight;
	edges.push_back(edge);
}

&#x2F;&#x2F; 查找某个元素所在的集合
int find(Subset subsets[], int i)
{
	if (subsets[i].parent != i)
		subsets[i].parent = find(subsets, subsets[i].parent);
	return subsets[i].parent;
}

&#x2F;&#x2F; 合并两个集合
void Union(Subset subsets[], int x, int y)
{
	int xroot = find(subsets, x);
	int yroot = find(subsets, y);
	if (subsets[xroot].rank &#x3C; subsets[yroot].rank)
		subsets[xroot].parent = yroot;
	else if (subsets[xroot].rank &#x3E; subsets[yroot].rank)
		subsets[yroot].parent = xroot;
	else {
		subsets[yroot].parent = xroot;
		subsets[xroot].rank++;
	}
}

&#x2F;&#x2F; Kruskal算法找到最小生成树
void KruskalMST(PointCloudT::Ptr cloud, std::vector&#x3C;Edge&#x3E;&#x26; result, float threshold)
{
	&#x2F;&#x2F; 将边按照权重从小到大排序
	std::sort(edges.begin(), edges.end(), [](const Edge&#x26; a, const Edge&#x26; b)
	{
		return a.weight &#x3C; b.weight;
	});

	&#x2F;&#x2F; 为V个元素创建并查集的子集
	Subset* subsets = new Subset[V];
	for (int v = 0; v &#x3C; V; ++v)
	{
		subsets[v].parent = v;
		subsets[v].rank = 0;
	}

	int i = 0;  &#x2F;&#x2F; 用于选择下一条边的索引
	int e = 0;  &#x2F;&#x2F; 用于选择下一条边加入最小生成树的索引

	&#x2F;&#x2F; 需要选择V-1条边
	while (e &#x3C; V - 1 &#x26;&#x26; i &#x3C; E)
	{
		Edge next_edge = edges[i++];

		int x = find(subsets, next_edge.src);
		int y = find(subsets, next_edge.tgt);

		&#x2F;&#x2F; 如果加入这条边不会形成环,并且权重大于阈值,则加入到结果中,并且增加已选择边的计数
		if (next_edge.weight &#x3E;= threshold &#x26;&#x26; x != y)
		{
			result.push_back(next_edge);
			Union(subsets, x, y);
			++e;
		}
	}

	&#x2F;&#x2F; 可视化最小生成树的结果
	pcl::visualization::PCLVisualizer viewer('Minimum Spanning Tree');
	viewer.setBackgroundColor(0, 0, 0);

	&#x2F;&#x2F; 将原始点云添加到可视化窗口中
	pcl::visualization::PointCloudColorHandlerCustom&#x3C;pcl::PointXYZ&#x3E; single_color(cloud, 255, 255, 255);
	viewer.addPointCloud&#x3C;pcl::PointXYZ&#x3E;(cloud, single_color, 'original_cloud');

	&#x2F;&#x2F; 将最小生成树的边添加到可视化窗口中
	for (const auto&#x26; edge : result)
	{
		const auto&#x26; src_point = cloud->points[edge.src];
		const auto&#x26; tgt_point = cloud->points[edge.tgt];
		std::stringstream ss;
		ss &#x3C;&#x3C; 'edge_' &#x3C;&#x3C; edge.src &#x3C;&#x3C; '_' &#x3C;&#x3C; edge.tgt;
		viewer.addLine&#x3C;pcl::PointXYZ&#x3E;(src_point, tgt_point, ss.str());
	}

	&#x2F;&#x2F; Count the number of points in the minimum spanning tree
	int numPoints = 0;
	for (const auto&#x26; edge : result)
	{
		const auto&#x26; src_point = cloud->points[edge.src];
		const auto&#x26; tgt_point = cloud->points[edge.tgt];
		numPoints += 2;
	}

	std::cout &#x3C;&#x3C; 'Number of points in the minimum spanning tree: ' &#x3C;&#x3C; numPoints &#x3C;&#x3C; std::endl;

	while (!viewer.wasStopped())
	{
		viewer.spinOnce();
	}
}

};

// 计算两个点之间的欧式距离 double euclideanDistance(PointXYZ p1, PointXYZ p2) { double dx = p2.x - p1.x; double dy = p2.y - p1.y; double dz = p2.z - p1.z; return std::sqrt(dx * dx + dy * dy + dz * dz); }

int main() { // 从PLY文件加载输入点云 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); pcl::io::loadPLYFile<pcl::PointXYZ>('D:\DIANYUNWENJIANJIA\newOUSHIJULEI_ply.ply', *cloud);

&#x2F;&#x2F; 计算点云的质心
Eigen::Vector4f centroid;
pcl::compute3DCentroid(*cloud, centroid);

&#x2F;&#x2F; 计算点云的法线
pcl::NormalEstimation&#x3C;pcl::PointXYZ, pcl::Normal&#x3E; ne;
pcl::PointCloud&#x3C;pcl::Normal&#x3E;::Ptr cloud_normals(new pcl::PointCloud&#x3C;pcl::Normal&#x3E;);
pcl::search::KdTree&#x3C;pcl::PointXYZ&#x3E;::Ptr tree(new pcl::search::KdTree&#x3C;pcl::PointXYZ&#x3E;);
ne.setInputCloud(cloud);
ne.setSearchMethod(tree);
ne.setKSearch(40);
ne.compute(*cloud_normals);

&#x2F;&#x2F; 创建一个有V个顶点和E个边的图
int V = cloud->size();
int E = V * (V - 1) &#x2F; 2;
Graph graph(V, E);

&#x2F;&#x2F; 基于点之间的欧式距离计算边的权重
for (int i = 0; i &#x3C; V - 1; ++i)
{
	const auto&#x26; src_point = cloud->points[i];
	for (int j = i + 1; j &#x3C; V; ++j)
	{
		const auto&#x26; tgt_point = cloud->points[j];
		float distance = euclideanDistance(src_point, tgt_point);
		graph.addEdge(i, j, distance);
	}
}
&#x2F;&#x2F; 设置修剪的阈值
float threshold = 0.00080;
&#x2F;&#x2F; 执行Kruskal算法找到最小生成树
std::vector&#x3C;Edge&#x3E; result;
graph.KruskalMST(cloud, result, threshold);

// 创建一个新的点云对象保存修剪后的最小生成树结果 pcl::PointCloud<pcl::PointXYZ>::Ptr new_cloud(new pcl::PointCloud<pcl::PointXYZ>); new_cloud->width = cloud->width; new_cloud->height = cloud->height; new_cloud->points.resize(cloud->points.size());

&#x2F;&#x2F; 找到在最小生成树中出现三次的节点
std::unordered_map&#x3C;int, int&#x3E; node_count;
for (const auto&#x26; edge : result)
{
	node_count[edge.src]++;
	node_count[edge.tgt]++;
}

std::vector&#x3C;int&#x3E; threejiedian;
for (const auto&#x26; pair : node_count)
{
	if (pair.second == 3)
	{
		threejiedian.push_back(pair.first);
	}
}
&#x2F;&#x2F; 如果与threejiedian相连的边的权重小于0.0008,则从最小生成树中移除该边
std::vector&#x3C;Edge&#x3E; pruned_result;
for (const auto&#x26; edge : result)
{
	if (std::find(threejiedian.begin(), threejiedian.end(), edge.src) != threejiedian.end() ||
		std::find(threejiedian.begin(), threejiedian.end(), edge.tgt) != threejiedian.end())
	{
		if (edge.weight &#x3C;= 0.008)
		{
			pruned_result.push_back(edge);
		}
	}
	else
	{
		pruned_result.push_back(edge);
	}
}
&#x2F;&#x2F; 将修剪后的最小生成树的顶点添加到新的点云对象中
for (const auto&#x26; edge : pruned_result)
{
	const auto&#x26; src_point = cloud->points[edge.src];
	const auto&#x26; tgt_point = cloud->points[edge.tgt];
	new_cloud->points[edge.src] = src_point;
	new_cloud->points[edge.tgt] = tgt_point;
}
&#x2F;&#x2F; 将与threejiedian相连的边的顶点设置为绿色
pcl::PointCloud&#x3C;pcl::PointXYZRGB&#x3E;::Ptr colored_cloud(new pcl::PointCloud&#x3C;pcl::PointXYZRGB&#x3E;);
colored_cloud->points.resize(cloud->points.size());
for (const auto&#x26; edge : result)
{
	if (std::find(threejiedian.begin(), threejiedian.end(), edge.src) != threejiedian.end() ||
		std::find(threejiedian.begin(), threejiedian.end(), edge.tgt) != threejiedian.end())
	{
		colored_cloud->points[edge.src].r = 0;
		colored_cloud->points[edge.src].g = 255;
		colored_cloud->points[edge.src].b = 0;
		colored_cloud->points[edge.tgt].r = 0;
		colored_cloud->points[edge.tgt].g = 255;
		colored_cloud->points[edge.tgt].b = 0;
	}
}

&#x2F;&#x2F; 将y值最大的threejiedian节点用绿色小球显示
int max_y_index = -1;
float max_y = std::numeric_limits&#x3C;float&#x3E;::min();
for (const auto&#x26; index : threejiedian)
{
	const auto&#x26; point = cloud->points[index];
	if (point.y &#x3E; max_y)
	{
		max_y = point.y;
		max_y_index = index;
	}
}
colored_cloud->points[max_y_index].r = 0;
colored_cloud->points[max_y_index].g = 255;
colored_cloud->points[max_y_index].b = 0;

&#x2F;&#x2F; 将最小生成树中出现一次的节点设置为红色
std::unordered_map&#x3C;int, int&#x3E; node_count;
for (const auto&#x26; edge : result)
{
	node_count[edge.src]++;
	node_count[edge.tgt]++;
}
for (const auto&#x26; pair : node_count)
{
	if (pair.second == 1)
	{
		colored_cloud->points[pair.first].r = 255;
		colored_cloud->points[pair.first].g = 0;
		colored_cloud->points[pair.first].b = 0;
	}
}
&#x2F;&#x2F; 可视化修剪后的最小生成树结果
pcl::visualization::PCLVisualizer viewer2('Pruned Minimum Spanning Tree');
viewer2.setBackgroundColor(0, 0, 0);

&#x2F;&#x2F; 将修剪后的最小生成树的点云添加到可视化窗口中
pcl::visualization::PointCloudColorHandlerCustom&#x3C;pcl::PointXYZ&#x3E; single_color2(new_cloud, 255, 255, 255);
viewer2.addPointCloud&#x3C;pcl::PointXYZ&#x3E;(new_cloud, single_color2, 'pruned_cloud');

&#x2F;&#x2F; 将修剪后的最小生成树的边添加到可视化窗口中
for (const auto&#x26; edge : pruned_result)
{
	const auto&#x26; src_point = cloud->points[edge.src];
	const auto&#x26; tgt_point = cloud->points[edge.tgt];
	std::stringstream ss;
	ss &#x3C;&#x3C; 'edge_' &#x3C;&#x3C; edge.src &#x3C;&#x3C; '_' &#x3C;&#x3C; edge.tgt;
	viewer2.addLine&#x3C;pcl::PointXYZ&#x3E;(src_point, tgt_point, ss.str());
}

&#x2F;&#x2F; 将彩色点云添加到可视化窗口中
pcl::visualization::PointCloudColorHandlerRGBField&#x3C;pcl::PointXYZRGB&#x3E; rgb(colored_cloud);
viewer2.addPointCloud&#x3C;pcl::PointXYZRGB&#x3E;(colored_cloud, rgb, 'colored_cloud');

while (!viewer2.wasStopped())
{
	viewer2.spinOnce();
}

return 0;

}'

替换说明:

  • main 函数中添加新的可视化代码: 将以下代码添加到 main 函数中,在第一次可视化代码后面:c++// 可视化修剪后的最小生成树结果pcl::visualization::PCLVisualizer viewer2('Pruned Minimum Spanning Tree');viewer2.setBackgroundColor(0, 0, 0);

// 将修剪后的最小生成树的点云添加到可视化窗口中pcl::visualization::PointCloudColorHandlerCustompcl::PointXYZ single_color2(new_cloud, 255, 255, 255);viewer2.addPointCloudpcl::PointXYZ(new_cloud, single_color2, 'pruned_cloud');

// 将修剪后的最小生成树的边添加到可视化窗口中for (const auto& edge : pruned_result){ const auto& src_point = cloud->points[edge.src]; const auto& tgt_point = cloud->points[edge.tgt]; std::stringstream ss; ss << 'edge_' << edge.src << '_' << edge.tgt; viewer2.addLinepcl::PointXYZ(src_point, tgt_point, ss.str());}

// 将彩色点云添加到可视化窗口中pcl::visualization::PointCloudColorHandlerRGBFieldpcl::PointXYZRGB rgb(colored_cloud);viewer2.addPointCloudpcl::PointXYZRGB(colored_cloud, rgb, 'colored_cloud');

while (!viewer2.wasStopped()){ viewer2.spinOnce()

PCL点云最小生成树可视化:Kruskal算法实现及修剪

原文地址: http://www.cveoy.top/t/topic/pVWM 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录