使用 PCL 和 Qt 实现点云可视化与交互

news2025/4/22 8:41:08

下面我将介绍如何结合点云库(PCL)和Qt框架(特别是QML)来实现点云的可视化与交互功能,包括高亮选择等效果。

1. 基本架构设计

首先需要建立一个结合PCL和Qt的基本架构:

// PCLQtViewer.h
#pragma once

#include <QObject>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>

class PCLQtViewer : public QObject
{
    Q_OBJECT
    
public:
    explicit PCLQtViewer(QObject* parent = nullptr);
    
    // 加载点云
    Q_INVOKABLE void loadPointCloud(const QString& filePath);
    
    // 获取点云数据供QML使用
    Q_INVOKABLE QVariantList getPointCloudData() const;
    
    // 高亮选择的点
    Q_INVOKABLE void highlightPoints(const QVariantList& indices);
    
signals:
    void pointCloudLoaded();
    void selectionChanged();
    
private:
    pcl::PointCloud<pcl::PointXYZRGB>::Ptr m_cloud;
    std::vector<int> m_selectedIndices;
};

2. PCL与Qt的集成实现

// PCLQtViewer.cpp
#include "PCLQtViewer.h"
#include <pcl/io/pcd_io.h>
#include <pcl/common/colors.h>

PCLQtViewer::PCLQtViewer(QObject* parent) 
    : QObject(parent), m_cloud(new pcl::PointCloud<pcl::PointXYZRGB>())
{
}

void PCLQtViewer::loadPointCloud(const QString& filePath)
{
    if (pcl::io::loadPCDFile<pcl::PointXYZRGB>(filePath.toStdString(), *m_cloud) == -1) {
        qWarning() << "Failed to load PCD file";
        return;
    }
    emit pointCloudLoaded();
}

QVariantList PCLQtViewer::getPointCloudData() const
{
    QVariantList points;
    for (const auto& point : *m_cloud) {
        QVariantMap pt;
        pt["x"] = point.x;
        pt["y"] = point.y;
        pt["z"] = point.z;
        pt["r"] = point.r;
        pt["g"] = point.g;
        pt["b"] = point.b;
        pt["selected"] = std::find(m_selectedIndices.begin(), 
                                 m_selectedIndices.end(), 
                                 &point - &m_cloud->points[0]) != m_selectedIndices.end();
        points.append(pt);
    }
    return points;
}

void PCLQtViewer::highlightPoints(const QVariantList& indices)
{
    m_selectedIndices.clear();
    for (const auto& idx : indices) {
        m_selectedIndices.push_back(idx.toInt());
    }
    emit selectionChanged();
}

3. QML点云可视化界面

qml

// main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import Qt3D.Core 2.15
import Qt3D.Render 2.15
import Qt3D.Input 2.15
import Qt3D.Extras 2.15

ApplicationWindow {
    id: window
    width: 1280
    height: 720
    visible: true
    
    PCLQtViewer {
        id: pclViewer
        onPointCloudLoaded: pointCloudModel.reload()
        onSelectionChanged: pointCloudModel.reload()
    }
    
    // 3D视图
    Qt3DWindow {
        id: view3d
        anchors.fill: parent
        
        Camera {
            id: camera
            projectionType: CameraLens.PerspectiveProjection
            fieldOfView: 45
            aspectRatio: view3d.width / view3d.height
            nearPlane: 0.1
            farPlane: 1000.0
            position: Qt.vector3d(0.0, 0.0, 10.0)
            upVector: Qt.vector3d(0.0, 1.0, 0.0)
            viewCenter: Qt.vector3d(0.0, 0.0, 0.0)
        }
        
        // 点云实体
        Entity {
            id: pointCloudEntity
            
            components: [
                Transform {
                    id: cloudTransform
                    scale: 1.0
                },
                GeometryRenderer {
                    id: pointGeometry
                    primitiveType: GeometryRenderer.Points
                    geometry: Geometry {
                        boundingVolumePositionAttribute: position
                        
                        Attribute {
                            id: position
                            attributeType: Attribute.VertexAttribute
                            vertexBaseType: Attribute.Float
                            vertexSize: 3
                            byteOffset: 0
                            byteStride: 6 * 4
                            count: pointCloudModel.count
                            buffer: pointBuffer
                        }
                        
                        Attribute {
                            id: color
                            attributeType: Attribute.ColorAttribute
                            vertexBaseType: Attribute.Float
                            vertexSize: 3
                            byteOffset: 3 * 4
                            byteStride: 6 * 4
                            count: pointCloudModel.count
                            buffer: pointBuffer
                        }
                        
                        Buffer {
                            id: pointBuffer
                            data: pointCloudModel.geometryData
                        }
                    }
                },
                Material {
                    effect: Effect {
                        techniques: Technique {
                            renderPasses: RenderPass {
                                shaderProgram: ShaderProgram {
                                    vertexShaderCode: "
                                        #version 330
                                        in vec3 vertexPosition;
                                        in vec3 vertexColor;
                                        out vec3 color;
                                        uniform mat4 mvp;
                                        void main() {
                                            color = vertexColor;
                                            gl_Position = mvp * vec4(vertexPosition, 1.0);
                                            gl_PointSize = 3.0;
                                        }"
                                    fragmentShaderCode: "
                                        #version 330
                                        in vec3 color;
                                        out vec4 fragColor;
                                        void main() {
                                            fragColor = vec4(color, 1.0);
                                        }"
                                }
                            }
                        }
                    }
                }
            ]
        }
        
        OrbitCameraController {
            camera: camera
        }
    }
    
    // 点云数据模型
    ListModel {
        id: pointCloudModel
        
        function reload() {
            clear();
            var points = pclViewer.getPointCloudData();
            for (var i = 0; i < points.length; i++) {
                append(points[i]);
            }
        }
        
        property var geometryData: {
            var data = new Float32Array(count * 6);
            for (var i = 0; i < count; i++) {
                var pt = get(i);
                data[i * 6] = pt.x;
                data[i * 6 + 1] = pt.y;
                data[i * 6 + 2] = pt.z;
                
                // 选中的点显示为红色,否则使用原始颜色
                if (pt.selected) {
                    data[i * 6 + 3] = 1.0;
                    data[i * 6 + 4] = 0.0;
                    data[i * 6 + 5] = 0.0;
                } else {
                    data[i * 6 + 3] = pt.r / 255.0;
                    data[i * 6 + 4] = pt.g / 255.0;
                    data[i * 6 + 5] = pt.b / 255.0;
                }
            }
            return data;
        }
    }
    
    // 控制面板
    Rectangle {
        width: 300
        height: parent.height
        color: "#eee"
        
        Column {
            spacing: 10
            padding: 10
            
            Button {
                text: "加载点云"
                onClicked: fileDialog.open()
            }
            
            ListView {
                id: selectionListView
                width: parent.width
                height: 200
                model: ListModel {}
                
                delegate: ItemDelegate {
                    width: parent.width
                    text: "点 " + index
                    highlighted: pointCloudModel.get(index).selected
                    
                    onClicked: {
                        pclViewer.highlightPoints([index]);
                    }
                }
            }
        }
    }
    
    FileDialog {
        id: fileDialog
        title: "选择点云文件"
        folder: shortcuts.home
        nameFilters: ["PCD文件 (*.pcd)"]
        onAccepted: pclViewer.loadPointCloud(fileDialog.fileUrl)
    }
}

4. 主程序集成

cpp

// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "PCLQtViewer.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);
    
    qmlRegisterType<PCLQtViewer>("PCL", 1, 0, "PCLQtViewer");
    
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
    
    return app.exec();
}

5. 高级功能扩展

点云聚类高亮

cpp

// 在PCLQtViewer类中添加
Q_INVOKABLE void highlightCluster(int clusterId)
{
    // 假设已经执行过聚类算法,结果存储在m_clusterIndices中
    m_selectedIndices = m_clusterIndices[clusterId];
    emit selectionChanged();
}

3D框选功能

qml

// 在Qt3DWindow中添加
Entity {
    components: [
        ObjectPicker {
            id: picker
            dragEnabled: true
            onClicked: {
                var worldPos = pick.worldPosition;
                // 转换坐标并选择附近的点
                pclViewer.selectPointsInSphere(worldPos.x, worldPos.y, worldPos.z, 0.5);
            }
            onPressed: {
                // 开始框选
                selectionRect.visible = true;
                selectionRect.x = pick.x;
                selectionRect.y = pick.y;
            }
            onReleased: {
                // 结束框选
                selectionRect.visible = false;
                var selected = selectPointsInRectangle(selectionRect);
                pclViewer.highlightPoints(selected);
            }
        }
    ]
}

Rectangle {
    id: selectionRect
    visible: false
    color: "#8033B5E5"
    border.color: "#0033B5E5"
}

点云着色模式切换

qml

ComboBox {
    model: ["原始颜色", "高度着色", "曲率着色", "聚类着色"]
    onCurrentIndexChanged: pclViewer.setColorMode(currentIndex)
}

6. 性能优化建议

  1. 点云分块加载:对于大型点云,实现分块加载机制

  2. LOD(细节层次):根据视点距离动态调整显示细节

  3. 后台处理:将PCL处理任务放在后台线程

  4. GPU加速:使用计算着色器处理点云数据

  5. 空间索引:使用八叉树等结构加速空间查询

7. 项目配置(CMake)

cmake_minimum_required(VERSION 3.5)

project(PCL_Qt_Viewer)

find_package(Qt5 COMPONENTS Quick QuickControls2 3DCore 3DRender 3DInput 3DExtras REQUIRED)
find_package(PCL 1.8 REQUIRED)

set(CMAKE_AUTOMOC ON)
set(CMAKE_CXX_STANDARD 14)

add_executable(${PROJECT_NAME}
    main.cpp
    PCLQtViewer.cpp
    PCLQtViewer.h
)

target_link_libraries(${PROJECT_NAME}
    Qt5::Quick
    Qt5::QuickControls2
    Qt5::3DCore
    Qt5::3DRender
    Qt5::3DInput
    Qt5::3DExtras
    ${PCL_LIBRARIES}
)

这种集成方式充分利用了PCL强大的点云处理能力和Qt/QML出色的UI/交互能力,可以构建出功能丰富、交互友好的点云处理应用程序。

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

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

相关文章

静态网页的开发

文章目录 基于 idea 开发静态网页添加web框架前端配置服务器并启动服务资源名字不是 index 静态网页 流转 基于 idea 开发静态网页 添加web框架 方法1 方法2 前端 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8&quo…

【CPU】结合RISC-V CPU架构回答中断系统的7个问题(个人草稿)

结合RISC-V CPU架构对中断系统七个关键问题的详细解析&#xff0c;按照由浅入深的结构进行说明&#xff1a; 一、中断请求机制&#xff08;问题①&#xff09; 硬件基础&#xff1a; RISC-V通过CLINT&#xff08;Core Local Interrupter&#xff09;和PLIC&#xff08;Platfor…

uCOS3实时操作系统(任务切换和任务API函数)

文章目录 任务切换任务API函数 任务切换 C/OS-III 将 PendSV 的中断优先级配置为最低的中断优先级&#xff0c;这么一来&#xff0c; PendSV 异常的中断服务函数就会在其他所有中断处理完成后才被执行。C/OS-III 就是将任务切换的过程放到 PendSV 异常的中断服务函数中处理的。…

科学养生指南:解锁健康生活新方式

在快节奏的现代生活中&#xff0c;健康养生已成为人们关注的焦点。科学合理的养生方式&#xff0c;能帮助我们增强体质、预防疾病&#xff0c;享受更优质的生活。​ 饮食是健康养生的基石。遵循 “均衡饮食” 原则&#xff0c;每日饮食需包含谷类、蔬菜水果、优质蛋白质和健康…

第十四届蓝桥杯 2023 C/C++组 有奖问答

目录 题目&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 思路&#xff1a; 核心思路&#xff1a; 思路详解&#xff1a; 代码&#xff1a; 代码详解&#xff1a; 题目&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 蓝桥云课 有奖问答 思路&…

transformer注意力机制

单头注意力机制 import torch import torch.nn.functional as Fdef scaled_dot_product_attention(Q, K, V):# Q: (batch_size, seq_len, d_k)# K: (batch_size, seq_len, d_k)# V: (batch_size, seq_len, d_v)batch_size: 一次输入的句子数。 seq_len: 每个句子的词数。 d_mo…

QT 5.15 程序打包

说明&#xff1a; windeployqt 是 Qt 提供的一个工具&#xff0c;用于自动收集并复制运行 Qt 应用程序所需的动态链接库&#xff08;.dll 文件&#xff09;及其他资源&#xff08;如插件、QML 模块等&#xff09;到可执行文件所在的目录。这样你就可以将应用程序和这些依赖项一…

【路由交换方向IE认证】BGP选路原则之AS-Path属性

文章目录 一、路由器BGP路由的处理过程控制平面和转发平面选路工具 二、BGP的选路顺序选路的前提选路顺序 三、AS-Path属性选路原则AS-Path属性特性AS-Path管进还是管出呢&#xff1f;使用AS-Path对进本AS的路由进行选路验证AS-Path不接收带本AS号的路由 四、BGP邻居建立配置 一…

Linux系统下docker 安装 redis

docker安装最新版的redis 一、docker拉取最新版redis镜像 拉取镜像若没有指定版本&#xff0c;代表拉取最新版本 二、查询redis镜像 三、挂载配置文件 在docker容器内修改redis配置文件不方便&#xff0c;所以挂载配置文件&#xff0c;这样可以在外边修改redis配置 3.1 创建…

深度学习框架PyTorch——从入门到精通(3.3)YouTube系列——自动求导基础

这部分是 PyTorch介绍——YouTube系列的内容&#xff0c;每一节都对应一个youtube视频。&#xff08;可能跟之前的有一定的重复&#xff09; 我们需要Autograd做什么&#xff1f;一个简单示例训练中的自动求导开启和关闭自动求导自动求导与原地操作 自动求导分析器高级主题&…

【基础算法】二分算法详解

🎯 前言:二分不是找某个数,而是找一个满足条件的位置/值 所以最关键的是:找到单调性,写好 check() 函数,剩下交给模板! 什么是二分算法 二分算法是一种在有序区间中查找答案的方法,时间复杂度:O(log n)。核心思想是: 每次把搜索区间分成两半,只保留可能存在答案的…

mysql——基础知识

关键字大小写不敏感 查看表结构中的 desc describe 描述 降序中的 desc descend 1. 数据库的操作 1. 创建数据库 create database 数据库名;为防止创建的数据库重复 CREATE DATABASE IF NOT EXISTS 数据库名;手动设置数据库采用的字符集 character set 字符集名;chars…

OOA-CNN-LSTM-Attention、CNN-LSTM-Attention、OOA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比

OOA-CNN-LSTM-Attention、CNN-LSTM-Attention、OOA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比 目录 OOA-CNN-LSTM-Attention、CNN-LSTM-Attention、OOA-CNN-LSTM、CNN-LSTM四模型多变量时序预测一键对比预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于OOA-CN…

PCIE Spec ---Base Address Registers

7.5.1.2.1 Base Address Registers (Offset 10h - 24h) 在 boot 到操作系统之前&#xff0c;系统软件需要生产一个内存映射的 address map &#xff0c;用于告诉系统有多少内存资源&#xff0c;以及相应功能需要的内存空间&#xff0c;所以在设备的 PCI 内存空间中就有了这个 …

Spring如何通过XML注册Bean

在上一篇当中我们完成了对三种资源文件的读写 上篇内容&#xff1a;Spring是如何实现资源文件的加载 Test public void testClassPathResource() throws IOException { DefaultResourceLoader defaultResourceLoader new DefaultResourceLoader(); Resource resource …

基于Java的不固定长度字符集在指定宽度和自适应模型下图片绘制生成实战

目录 前言 一、需求介绍 1、指定宽度生成 2、指定列自适应生成 二、Java生成实现 1、公共方法 2、指定宽度生成 3、指定列自适应生成 三、总结 前言 在当今数字化与信息化飞速发展的时代&#xff0c;图像的生成与处理技术正日益成为众多领域关注的焦点。从创意设计到数…

【版本控制】idea中使用git

大家好&#xff0c;我是jstart千语。接下来继续对git的内容进行讲解。也是在开发中最常使用&#xff0c;最重要的部分&#xff0c;在idea中操作git。目录在右侧哦。 如果需要git命令的详解&#xff1a; 【版本控制】git命令使用大全-CSDN博客 一、配置git 要先关闭项目&#xf…

Linux——入门常用基础指令

文章目录 Linux入门常用基础指令使用工具介绍基础指令clear指令pwd指令ls指令cd指令Linux系统下的文件路径及文件存储结构文件结构家目录绝对路径和相对路径tree工具 stat指令which指令alias指令touch指令mkdir指令cat指令rm指令man指令cp指令通配符 * Linux入门常用基础指令 …

【技术追踪】Differential Transformer(ICLR-2025)

Differential Transformer&#xff1a;大语言模型新架构&#xff0c; 提出了 differential attention mechanism&#xff0c;Transformer 又多了一个小 trick~ 论文&#xff1a;Differential Transformer 代码&#xff1a;https://github.com/microsoft/unilm/tree/master/Diff…

【Linux网络】应用层自定义协议与序列化

&#x1f308;个人主页&#xff1a;秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343 &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/qinjh_/category_12891150.html 目录 应用层 再谈 "协议" 网络版计算器 序列化 和 反序列化 重新理解…