PCL点云按指定方向进行聚类(指定类的宽度)

news2025/5/12 16:18:35

 

需指定方向和类的宽度。测试代码如下:

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/common/centroid.h>  // 新增质心计算头文件
#include <pcl/common/common.h> 

 //类型定义简化
typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> PointCloudT;

/**
 * @brief 沿Z轴方向聚类点云,限制每类在Z轴的长度不超过thresh
 * @param cloud 输入点云
 * @param thresh Z轴方向的最大允许长度
 * @return 聚类结果的索引集合
 */
std::vector<pcl::PointIndices> axisAlignedClustering(
	Eigen::Vector3f direction,
    const PointCloudT::Ptr& cloud, float thresh)
{
    std::vector<pcl::PointIndices> clusters;
    if (cloud->empty()) return clusters;

	auto p0 = cloud->points[0];
	direction.normalize();
auto project_dist = [&direction, &p0](PointT& p)
	{
		return direction.dot(Eigen::Vector3f(p.x - p0.x, p.y - p0.y, p.z - p0.z));
	};

    std::vector<int> indices(cloud->size());
    for (int i = 0; i < indices.size(); ++i) indices[i] = i;
    std::sort(indices.begin(), indices.end(), [&](int a, int b) {
        return project_dist(cloud->points[a]) < project_dist(cloud->points[b]);
        });

     
     //2. 滑动窗口分割
    pcl::PointIndices current_cluster;
	float start_z = project_dist(cloud->points[indices[0]]);//cloud->points[indices[0]].z;
    current_cluster.indices.push_back(indices[0]);

    for (size_t i = 1; i < indices.size(); ++i) {
        float current_z = project_dist(cloud->points[indices[i]]);
        if (current_z - start_z <= thresh) {
            current_cluster.indices.push_back(indices[i]);
        }
        else {
            clusters.push_back(current_cluster);
            current_cluster.indices.clear();
            current_cluster.indices.push_back(indices[i]);
            start_z = current_z;
        }
    }

    if (!current_cluster.indices.empty()) {
        clusters.push_back(current_cluster);
    }

    return clusters;
}

/**
 * @brief 可视化聚类结果
 * @param cloud 原始点云
 * @param clusters 聚类索引
 */
void visualizeClusters(
    const PointCloudT::Ptr& cloud,
    const std::vector<pcl::PointIndices>& clusters, float cloud_size, Eigen::Vector4f centroid)
{
    pcl::visualization::PCLVisualizer viewer("Cluster Visualization");
    viewer.setBackgroundColor(0, 0, 0);	

     //为每个聚类生成随机颜色
    std::vector<pcl::visualization::PointCloudColorHandlerCustom<PointT>> color_handlers;
    for (size_t i = 0; i < clusters.size(); ++i) {
        uint8_t r = rand() % 256;
        uint8_t g = rand() % 256;
        uint8_t b = rand() % 256;
        color_handlers.emplace_back(
            pcl::visualization::PointCloudColorHandlerCustom<PointT>(
                cloud, r, g, b));
    }

    
     //添加每个聚类的点云到可视化
    for (size_t i = 0; i < clusters.size(); ++i) {
        PointCloudT::Ptr cluster_cloud(new PointCloudT);
        pcl::ExtractIndices<PointT> extract;
        pcl::PointIndices::Ptr indices(new pcl::PointIndices(clusters[i]));
        extract.setInputCloud(cloud);
        extract.setIndices(indices);
        extract.filter(*cluster_cloud);

        std::string cluster_id = "cluster_" + std::to_string(i);
		viewer.addPointCloud<PointT>(
			cluster_cloud, color_handlers[i], cluster_id);
		viewer.setPointCloudRenderingProperties(
			pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, cluster_id);
    }

	// 关键参数设置
	const float camera_distance = 3.0 * cloud_size;  // 观察距离为点云尺寸的3倍
	const Eigen::Vector3f camera_pos = {
		centroid[0],
		centroid[1],
		centroid[2] + camera_distance
	};
viewer.initCameraParameters();
viewer.setCameraPosition(
	camera_pos[0], camera_pos[1], camera_pos[2],  // 相机位置
	centroid[0], centroid[1], centroid[2],         // 焦点位置
	0, -1, 0                                       // 上方向向量(Y轴负方向)
);

    viewer.addCoordinateSystem(1.0);
    while (!viewer.wasStopped()) {
        viewer.spinOnce(100);
    }
}

int main(int argc, char** argv) {

 //1. 创建点云对象
pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);

 //2. 打开并读取TXT文件
std::ifstream file("E:\\Data\\PathPlanning\\data.txt");
if (!file.is_open()) {
	std::cerr << "Error opening file: " << argv[1] << std::endl;
	return -1;
}

std::string line;
while (std::getline(file, line)) {
	if (line.empty()) continue;

	std::istringstream iss(line);
	PointT point;

	 //读取坐标 (x,y,z) 和法向量
    float nx, ny, nz;
	if (!(iss >> point.x >> point.y >> point.z >> nx >> ny >> nz)) {
		std::cerr << "Error parsing line: " << line << std::endl;
		continue;
	}
	cloud->push_back(point);
}
file.close();

 //3. 设置点云属性
cloud->width = cloud->size();
cloud->height = 1;
cloud->is_dense = false;

// 计算点云质心
Eigen::Vector4f centroid;
if (pcl::compute3DCentroid(*cloud, centroid)){
	std::cout << "点云质心坐标: ("
		<< centroid[0] << ", "
		<< centroid[1] << ", "
		<< centroid[2] << ")" << std::endl;
}
else {
	std::cerr << "无法计算空点云的质心" << std::endl;
	return -1;
}

// 计算点云尺寸范围
PointT min_pt, max_pt;
pcl::getMinMax3D(*cloud, min_pt, max_pt);
float cloud_size = std::max({
	max_pt.x - min_pt.x,
	max_pt.y - min_pt.y,
	max_pt.z - min_pt.z
	});


 //2. 沿Z轴聚类(设置Thresh = 1.0)
float thresh = 100.0f;
std::vector<pcl::PointIndices> clusters = 
	axisAlignedClustering(Eigen::Vector3f(1, 1, 0), cloud, thresh);

 //3. 输出聚类信息
std::cout << "Found " << clusters.size() << " clusters." << std::endl;
for (size_t i = 0; i < clusters.size(); ++i) {
	std::cout << "Cluster " << i << ": " << clusters[i].indices.size()
		<< " points" << std::endl;
}

 //4. 可视化
visualizeClusters(cloud, clusters, cloud_size, centroid);	

return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2374063.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#对SQLServer增删改查

1.创建数据库 2.SqlServerHelper using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks;namespace WindowsFormsApp1 {internal class SqlServerHelper{//…

模拟太阳系(C#编写的maui跨平台项目源码)

源码下载地址&#xff1a;https://download.csdn.net/download/wgxds/90789056 本资源为用C#编写的maui跨平台项目源码&#xff0c;使用Visual Studio 2022开发环境&#xff0c;基于.net8.0框架&#xff0c;生成的程序为“模拟太阳系运行”。经测试&#xff0c;生成的程序可运行…

蓝桥杯14届 数三角

问题描述 小明在二维坐标系中放置了 n 个点&#xff0c;他想在其中选出一个包含三个点的子集&#xff0c;这三个点能组成三角形。然而这样的方案太多了&#xff0c;他决定只选择那些可以组成等腰三角形的方案。请帮他计算出一共有多少种选法可以组成等腰三角形&#xff1f; 输…

HTML12:文本框和单选框

表单元素格式 属性说明type指定元素的类型。text、password、 checkbox、 radio、submit、reset、file、hidden、image 和button&#xff0c;默认为textname指定表单元素的名称value元素的初始值。type为radio时必须指定一个值size指定表单元素的初始宽度。当type为text 或pas…

机器人厨师上岗!AI在餐饮界掀起新风潮!

想要了解人工智能在其他各个领域的应用&#xff0c;可以查看下面一篇文章 《AI在各领域的应用》 餐饮业是与我们日常生活息息相关的行业&#xff0c;而人工智能&#xff08;AI&#xff09;正在迅速改变这个传统行业的面貌。从智能点餐到食材管理&#xff0c;再到个性化推荐&a…

MySQL开篇

文章目录 一、前置知识1. MySQL的安装2. 前置一些概念知识 二、MySQL数据库操作2.1 概念2.2 数据库的操作2.2.1创建数据库命令2.2.2 查看数据库2.2.3 选中数据库2.2.4 删除数据库 三、MySQL数据表操作3.1 概念3.2 数据表的操作3.2.1 创建表 一、前置知识 1. MySQL的安装 MySQ…

Linux电脑本机使用小皮面板集成环境开发调试WEB项目

开发调试WEB项目&#xff0c;有时开发环境配置繁琐&#xff0c;可以使用小皮面板集成环境。 小皮面板官网&#xff1a; https://www.xp.cn/1.可以使用小皮面板安装脚本一键安装。登陆小皮面板管理后台 2.在“软件商店”使用LNMP一键部署集成环境。 3.添加网站&#xff0c;本…

问题及解决01-面板无法随着窗口的放大而放大

在MATLAB的App Designer中&#xff0c;默认情况下&#xff0c;组件的位置是固定的&#xff0c;不会随着父容器的大小变化而改变。问题图如下图所示。 解决&#xff1a; 为了让Panel面板能够随着UIFigure父容器一起缩放&#xff0c;需要使用布局管理器&#xff0c;我利用 MATLA…

操作系统原理实验报告

操作系统原理课程的实验报告汇总 实验三&#xff1a;线程的创建与撤销 实验环境&#xff1a;计算机一台&#xff0c;内装有VC、office等软件 实验日期&#xff1a;2024.4.11 实验要求&#xff1a; 1.理解&#xff1a;Windows系统调用的基本概念&#xff0c;进程与线程的基…

《Linux命令行大全(第2版)》PDF下载

内容简介 本书对Linux命令行进行详细的介绍&#xff0c;全书内容包括4个部分&#xff0c;第一部分由Shell的介绍开启命令行基础知识的学习之旅&#xff1b;第二部分讲述配置文件的编辑&#xff0c;如何通过命令行控制计算机&#xff1b;第三部分探讨常见的任务与必备工具&…

Java高频面试之并发编程-15

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天又来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;as-if-serial 是什么&#xff1f;单线程的程序一定是顺序执行的吗&#xff1f; as-if-serial 规则 定义&#xff1a; …

无人甘蔗小车履带式底盘行走系统的研究

1.1 研究背景与意义 1.1.1 研究背景 甘蔗作为全球最重要的糖料作物之一&#xff0c;在农业经济领域占据着举足轻重的地位。我国是甘蔗的主要种植国家&#xff0c;尤其是广西、广东、云南等地&#xff0c;甘蔗种植面积广泛&#xff0c;是当地农业经济的重要支柱产业。甘蔗不仅…

通俗易懂版知识点:Keepalived + LVS + Web + NFS 高可用集群到底是干什么的?

实验开始前&#xff0c;先搞懂为什么要部署该集群&#xff1f; 这个方案的目标是让网站 永不宕机&#xff0c;即使某台服务器挂了&#xff0c;用户也感觉不到。它主要涉及 负载均衡&#xff08;LVS&#xff09; 高可用&#xff08;Keepalived&#xff09; 共享存储&#xff…

https,http1,http2,http3的一些知识

温故知新&#xff0c;突然有人问我项目中&#x1f914;有使用http3么&#xff0c;一下不知从何说起&#xff0c;就有了这篇文章的出现。 https加密传输&#xff0c;ssltls https 验证身份 提供加密&#xff0c;混合加密 &#xff1a; 对称加密 非对称加密 原理&#xff1a…

go 通过汇编学习atomic原子操作原理

文章目录 概要一、原理1.1、案例1.2、关键汇编 二、LOCK汇编指令2.1、 LOCK2.2、 原理2.2.1、 缓存行2.2.2、 缓存一致性之MESI协议2.2.3、lock原理 三、x86缓存发展四、x86 DMA发展参考 概要 在并发操作下&#xff0c;对一个简单的aa2的操作都会出错&#xff0c;这是因为这样…

WebRTC 源码原生端Demo入门-1

1、概述 我的代码是比较新的&#xff0c;基于webrtc源码仓库的main分支的&#xff0c;在windows下把源码仓库下载好了后&#xff0c;用visual stdio 2022打开进行编译调试src/examples/peerconnection_client测试项目,主要是跑通这个demo来入手和调试&#xff0c;纯看代码很难…

【MySQL】第二弹——MySQL表的增删改查(CRUD)

文章目录 &#x1f393;一. CRUD&#x1f393;二. 新增(Create)&#x1f393;三. 查询(Rertieve)&#x1f4d6;1. 全列查询&#x1f4d6;2. 指定列查询&#x1f4d6;3. 查询带有表达式&#x1f4d6;4. 起别名查询(as )&#x1f4d6; 5. 去重查询(distinct)&#x1f4d6;6. 排序…

基于zernike 拟合生成包裹训练数据-可自定义拟合的项数

可以看到拟合误差其实还是有很多的,但是这个主要是包裹噪声产生的,用到了github 上的zernike 库,直接pip install 一下安装就可以了 import numpy as np import matplotlib.pyplot as plt from matplotlib import cm from mpl_toolkits.mplot3d import Axes3D import matpl…

大模型赋能:2D 写实数字人开启实时交互新时代

在数字化浪潮席卷全球的当下&#xff0c;人工智能技术不断突破创新&#xff0c;其中大模型驱动的 2D 写实数字人正成为实时交互领域的一颗新星&#xff0c;引领着行业变革&#xff0c;为人们带来前所未有的交互体验。 一、2D 写实数字人概述 2D 写实数字人是通过计算机图形学…

5G-A来了!5G信号多个A带来哪些改变?

5G-A来了&#xff01;5G信号多个A带来哪些改变&#xff1f; 随着科技不断进步&#xff0c;通信网络的迭代升级也在加速。自4G、5G的推出以来&#xff0c;我们见证了通信技术的飞跃式发展。最近&#xff0c;越来越多的用户发现自己手机屏幕右上角的5G标识已经变成了“5G-A”。那…