首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >项目简介 VTK-9.5.0 1D2D3DWidget实现灰度图+深度图+3D点云显示(可轻松集成到自己的项目)

项目简介 VTK-9.5.0 1D2D3DWidget实现灰度图+深度图+3D点云显示(可轻松集成到自己的项目)

原创
作者头像
Qt历险记
发布2026-02-05 21:03:30
发布2026-02-05 21:03:30
1410
举报
文章被收录于专栏:Qt6 研发工程师Qt6 研发工程师

项目简介

VTK1D2D3DWidget 是一个基于 Qt 和 VTK (Visualization Toolkit) 开发的 3D 点云可视化工具,用于显示和处理 RGB 点云和深度点云数据。该项目提供了直观的用户界面,支持点云的加载、显示、分析和保存等功能。

展示一下,末尾有源码。

主要功能

点云可视化:支持显示 3D RGB 点云和深度点云

点云保存:支持将点云数据保存为 PLY 格式文件

背景颜色设置:支持白色和黑色两种背景颜色

点间隔设置:可自定义点云的 X 轴和 Y 轴点间隔

坐标轴显示:内置坐标轴标记,方便空间定位

标量条显示:深度点云模式下显示深度值标量条

自定义鼠标交互:提供直观的 3D 视角操作

技术架构

核心组件

MainWindow:主窗口类,负责整体 UI 管理和点云数据处理

VTK1D2D3DWidget:VTK 渲染组件,负责点云的可视化和交互

VTKMouseStyle:自定义鼠标交互样式,提供流畅的 3D 操作体验

技术依赖

Qt 5+:提供 GUI 框架和跨平台支持

core、gui、widgets、concurrent 模块

VTK 9.5.0:提供 3D 可视化核心功能

vtkRenderingOpenGL2、vtkInteractionStyle、vtkRenderingVolumeOpenGL2、vtkRenderingFreeType 等模块

C++11:现代 C++ 标准

项目结构

快速开始

环境要求

操作系统:Windows 10+

编译器:MSVC 2017+

Qt:5.9+

VTK:9.5.0

构建步骤

配置 Qt 环境:确保已安装 Qt 5.9+ 并配置好环境变量

打开项目:使用 Qt Creator 打开 VTK1D2D3DWidget.pro 文件

配置 VTK 路径:确保 VTKWidget/VTK-9.5.0 目录包含正确的 VTK 库文件

构建项目:选择合适的构建配置(Debug/Release)并构建项目

运行程序:构建完成后运行程序

使用指南

加载点云:程序支持加载 RGB 点云和深度点云数据

选择点云类型:在下拉菜单中选择要显示的点云类型(RGB 或深度)

调整背景颜色:在下拉菜单中选择背景颜色(白色或黑色)

显示/隐藏点云:使用复选框控制点云的显示状态

保存点云:点击保存按钮将当前点云保存为 PLY 文件

3D 视角操作

鼠标左键:旋转视角

鼠标中键:平移视角

鼠标滚轮:缩放视角

代码说明

主窗口类 (MainWindow)

主窗口类负责整体 UI 管理和点云数据处理,主要功能包括:

初始化图像数据结构体

加载点云文件

分析点云数据

设置点云类型和背景颜色

保存点云数据到 PLY 文件

VTK 渲染组件 (VTK1D2D3DWidget)

VTK 渲染组件负责点云的可视化和交互,主要功能包括:

初始化 VTK 渲染环境

显示 RGB 点云和深度点云

管理点云的颜色和属性

处理鼠标交互事件

保存点云数据

自定义鼠标样式 (VTKMouseStyle)

自定义鼠标样式类继承自 VTK 的交互样式,提供了更加直观的 3D 视角操作方式。

数据结构

点云数据结构

代码语言:javascript
复制
// 点云数据typedefstruct {     vtkSmartPointer<vtkPoints> points;       // 点云数据点     vtkSmartPointer<vtkPoints> allPoints;    // 所有点云数据点     vtkSmartPointer<vtkLookupTable> lookup;  // 查找表 }In_PointCloudStruct; 

图像测量数据结构

代码语言:javascript
复制
// 图像测量数据typedef struct{    //图片宽度(图片像素、点云X轴点数)    uint32_t                width;    //图片高度(图片像素、点云Y轴点数)    uint32_t                height;    //点云xy轴点间隔,单位mm    float                   point_interval;    /*     * 存放深度数据, 点数等于 width * height,数值单位mm,无信号点数值填充为INVALID_VALUE    */    float*                    z;    //存放灰度图,像素等于 width * height    uint8_t*                  gray;    //存放RGB数据,点数等于width * height*3, 例:rgb[0]、rgb[1]、rgb[2] 对应第一个点的RGB数值    uint8_t*                  rgb;    //返回测量数据统计信息,包含最大值、最小值(深度图Z)    depthMapInfo_t     depthMapInfo;}imageData_t;

配置说明

项目配置

项目使用 Qt 的 .pro 文件进行配置,主要配置项包括:

Qt 模块:core、gui、widgets、concurrent

C++ 标准:C++11

构建模式:支持 Debug 和 Release 模式

平台支持:支持 x86 和 x64 架构

VTK 配置:通过 VTKWidget.pri 包含 VTK 模块

VTK 配置

VTK 库文件位于 VTKWidget/VTK-9.5.0 目录,包含以下配置:

头文件路径:VTK-9.5.0/x64/release/include/vtk-9.5

库文件路径:根据构建模式和架构选择相应目录

常见问题

黑框弹出问题:已通过 vtkOutputWindow::SetGlobalWarningDisplay(0) 解决

QWidget: Must construct a QApplication before a QWidget:确保使用正确的 VTK 库版本(Debug 模式使用 Debug 库,Release 模式使用 Release 库)

编译器堆空间不足:已通过 CONFIG += resources_big 解决

中文乱码:已通过 MSVC 编译器的字符集设置解决


01

界面布局参考,通过将VTK自带的widget提升类,轻松解耦管理模块。

02

main.cpp

代码语言:javascript
复制
#include "mainwindow.h"#include <QApplication>#include <vtkAutoInit.h>            // 包含VTK自动初始化的头文件#include <vtkOutputWindow.h>        // 包含VTK输出窗口的头文件#include <vtkOpenGLRenderWindow.h>  // 包含VTK OpenGL渲染窗口的头文件/* 解决黑框弹出问题 */VTK_MODULE_INIT(vtkRenderingOpenGL2)        // 初始化VTK的OpenGL2渲染模块VTK_MODULE_INIT(vtkInteractionStyle)        // 初始化VTK的交互样式模块VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2)  // 初始化VTK的体积渲染模块VTK_MODULE_INIT(vtkRenderingFreeType)       // 初始化VTK的FreeType字体渲染模块int main(int argc, char *argv[]){    // 去除警告提示框    vtkOutputWindow::SetGlobalWarningDisplay(0); // 设置全局警告显示为0,去除VTK的警告提示框    QApplication a(argc, argv);    MainWindow w;    w.show();    return a.exec();}

03

vtk1d2d3dwidget.cpp

代码语言:javascript
复制
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* #include "vtk1d2d3dwidget.h"#include "ui_vtk1d2d3dwidget.h"#include <QStyle>#include <QDateTime>#include <QMessageBox>#include <QFileDialog>// 定义一个宏,用于创建VTKMouseStyle类的新实例vtkStandardNewMacro(VTKMouseStyle);VTK1D2D3DWidget::VTK1D2D3DWidget(QWidget *parent) :    QWidget(parent),    ui(new Ui::VTK1D2D3DWidget){    ui->setupUi(this);    this->setWindowTitle(tr("VTK教程"));    // 使用Qt自带的保存图标    ui->PB_Save->setIcon(style()->standardIcon(QStyle::SP_DialogSaveButton));    // 初始化VtK    initVtk();}VTK1D2D3DWidget::~VTK1D2D3DWidget(){    delete ui;}// 初始化VtKvoid VTK1D2D3DWidget::initVtk(){    // 创建渲染器    m_Renderer = vtkSmartPointer<vtkRenderer>::New(); // 创建一个新的vtkRenderer实例    m_Renderer->SetBackground(0.0, 0.0, 0.0);         // 设置渲染器的背景颜色为黑色    // 根据VTK版本获取渲染窗口并添加渲染器#if VTK_MAJOR_VERSION <= 7    ui->VTKPointCloud->GetRenderWindow()->AddRenderer(m_render); // VTK 7及以下版本#else    ui->VTKPointCloud->renderWindow()->AddRenderer(m_Renderer); // VTK 8及以上版本👁#endif    // 初始化点云数据及其相关组件    m_pointCloudStruct.m_polyData = vtkSmartPointer<vtkPolyData>::New();    m_pointCloudStruct.m_dataSetMapper = vtkSmartPointer<vtkDataSetMapper>::New();    m_pointCloudStruct.m_actor = vtkSmartPointer<vtkActor>::New();    // m_actor 是一个演员对象,用于在渲染场景中显示点云数据    m_pointCloudStruct.m_actor->SetMapper(m_pointCloudStruct.m_dataSetMapper);    // 调用 m_actor 的 SetMapper 方法,将 m_dataSetMapper 设置为 m_actor 的映射器    // m_dataSetMapper 是一个映射器对象,用于将点云数据转换为图形数据    // 通过设置映射器,m_actor 将能够根据 m_dataSetMapper 中的数据来渲染点云    // 添加参考坐标系    m_pointCloudStruct.m_axesActor = vtkSmartPointer<vtkAxesActor>::New();    m_pointCloudStruct.m_axesActor->SetScale(1); // 设置坐标系的缩放比例    m_pointCloudStruct.m_orientationMarkerWidget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();    // 调用 m_pointCloudStruct 对象的 m_orientationMarkerWidget 成员的 SetOutlineColor 方法    // 该方法用于设置标记框的轮廓颜色    // 参数为 RGB 颜色值,范围从 0 到 1    // 0.9300 表示红色成分,0.5700 表示绿色成分,0.1300 表示蓝色成分    m_pointCloudStruct.m_orientationMarkerWidget->SetOutlineColor(0.9300, 0.5700, 0.1300);                     // 设置标记框的颜色    m_pointCloudStruct.m_orientationMarkerWidget->SetInteractor(m_Renderer->GetRenderWindow()->GetInteractor()); // 设置交互器👁    m_pointCloudStruct.m_orientationMarkerWidget->SetEnabled(1);     // 启用标记框    m_pointCloudStruct.m_orientationMarkerWidget->SetInteractive(0); // 设置标记框为非交互模式    // 创建绘制色卡    m_pointCloudStruct.m_scalarBarActor = vtkSmartPointer<vtkScalarBarActor>::New();    // 将参考坐标系和色卡添加到渲染器上    m_Renderer->AddViewProp(m_pointCloudStruct.m_scalarBarActor); // 添加色卡👁    m_Renderer->AddActor(m_pointCloudStruct.m_actor);             // 添加actor👁    // 设置鼠标交互的渲染器    m_pointCloudStruct.m_mouseStyle = vtkSmartPointer<VTKMouseStyle>::New(); // 创建一个新的VTKMouseStyle实例    m_pointCloudStruct.m_mouseStyle->SetDefaultRenderer(m_Renderer);         // 设置默认渲染器#if VTK_MAJOR_VERSION <= 7    ui->VTKPointCloud->GetRenderWindow()->GetInteractor()->SetInteractorStyle(m_pointCloudStruct.m_camerastyle); // VTK 7及以下版本#else    ui->VTKPointCloud->renderWindow()->GetInteractor()->SetInteractorStyle(m_pointCloudStruct.m_mouseStyle); // VTK 8及以上版本👁#endif    // 初始化VTK鼠标点击获取位置信息    m_pointCloudStruct.m_mouseStyle->initMouseResource(); // 初始化鼠标资源    // 设置灯光    m_Light = vtkSmartPointer<vtkLight>::New();    m_Light->SetFocalPoint(1.875, 0.6125, 0);   // 设置灯光的焦点    m_Light->SetPosition(0.875, 1.6125, 1);     // 设置灯光的位置    m_Light->SetIntensity(1);                   // 设置灯光的强度    m_Renderer->AddLight(m_Light);              // 将灯光添加到渲染器👁    m_pointCloudStruct.m_actor->GetProperty()->SetAmbient(1);     // 设置环境的反射系数    m_pointCloudStruct.m_actor->GetProperty()->SetLighting(true); // 启用光照    m_pointCloudStruct.m_actor->GetProperty()->SetShading(true);  // 启用阴影    m_pointCloudStruct.m_actor->GetProperty()->SetDiffuse(0.5);   // 设置漫反射系数    m_pointCloudStruct.m_actor->GetProperty()->SetSpecular(1);    // 设置镜面反射系数}//////////////////////////////////////// 槽 ///////////////////////////////////////////////// 保存void VTK1D2D3DWidget::on_PB_Save_clicked(){    // 检查点云数据是否为空    if(this->m_pointCloudStruct.m_points != nullptr)    {        // 发射信号获取点云间隔        emit sig_GetPointInterval();        // 发射信号获取点云类型        emit sig_GetPCType();        // 初始化保存操作的结果为false        bool ret = false;        // 根据点云类型选择保存方法        if(m_pointCloudType == PC_RGB)        {            // 如果点云类型为RGB,调用保存RGB点云的函数            ret = this->SaveRGBACSIIPlyFile();        }        else if(m_pointCloudType == PC_DEPTH)        {            // 如果点云类型为DEPTH,调用保存深度点云的函数            ret = this->SaveDepthCSIIPlyFile();        }        // 根据保存结果显示相应的消息框        if(ret)        {            // 如果保存成功,显示信息消息框            QMessageBox::information(this, " ", QObject::tr("保存成功"));        }        else        {            // 如果保存失败,显示警告消息框            QMessageBox::warning(this, "Error", QObject::tr("保存失败"));        }    }    else {        QMessageBox::warning(this, "Warning", QObject::tr("点云为空"));    }}// 自定义右键菜单请求void VTK1D2D3DWidget::on_VTKPointCloud_customContextMenuRequested(const QPoint &pos){    Q_UNUSED(pos)}// 获取当前时间的字符串表示,格式为"yyyy/MM/dd hh:mm"QString VTK1D2D3DWidget::Get_Current_Time(){    // 使用QDateTime获取当前时间,并转换为指定格式的字符串    return QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm");}//////////////////////////////////////// 点云显示 ///////////////////////////////////////////////// 显示点云数据🔴🟢🔵void VTK1D2D3DWidget::showPointCloud(vtkSmartPointer<vtkPoints> vtkpoints, vtkSmartPointer<vtkLookupTable> lookup, PointCloudType type){    // 如果点云数据为空,直接返回    if(vtkpoints->GetNumberOfPoints() <= 0)        return;    // 根据点云类型选择不同的显示方式    if(type == PC_RGB)    {        // 显示RGB点云        this->showRGBPointCloud(vtkpoints, lookup);    }    else if(type == PC_DEPTH)    {        // 显示深度点云        this->showDepthPointCloud(vtkpoints);    }    // 重置点云数据    this->Reset_pointcloud();}// 显示深度点云⚫⚫⚫void VTK1D2D3DWidget::showDepthPointCloud(vtkSmartPointer<vtkPoints> vtkpoints){    // 将传入的点云数据赋值给成员变量    m_pointCloudStruct.m_points = vtkpoints;    // 根据z轴值设置颜色    vtkSmartPointer<vtkUnsignedCharArray> colors_rgb = this->setColorBaseAxis('z', m_pointCloudStruct.m_points);    // 创建颜色查找表    vtkSmartPointer<vtkLookupTable> lookup1 = vtkSmartPointer<vtkLookupTable>::New();    // 将点云数据设置到点云多边形数据polyData中    m_pointCloudStruct.m_polyData->SetPoints(m_pointCloudStruct.m_points);    // 将颜色数据设置到点云多边形数据polyData的点数据中    m_pointCloudStruct.m_polyData->GetPointData()->SetScalars(colors_rgb);    // 创建顶点符号过滤器    vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();    // 将glyphFilter的输入数据设置为m_pointCloudStruct.m_polyData    glyphFilter->SetInputData(m_pointCloudStruct.m_polyData);    // 将颜色模式设置为默认模式    m_pointCloudStruct.m_dataSetMapper->SetColorModeToDefault();    // 启用标量可见性,即根据标量值来显示颜色    m_pointCloudStruct.m_dataSetMapper->SetScalarVisibility(1);    // 设置输入连接为glyphFilter的输出端口,这样数据集映射器就能获取到经过glyphFilter处理后的数据    m_pointCloudStruct.m_dataSetMapper->SetInputConnection(glyphFilter->GetOutputPort());    // 设置颜色刻度表    // 设置查找表的色调范围,从0.620到0    lookup1->SetHueRange(0.620, 0);    // 设置查找表中的颜色数量为255    lookup1->SetNumberOfColors(255);    // 构建查找表    lookup1->Build();    // 将标量条演员(m_scalarBarActor)添加到渲染器(m_Renderer)中    m_Renderer->AddActor(m_pointCloudStruct.m_scalarBarActor);    // 设置标量条演员使用的查找表为lookup1    m_pointCloudStruct.m_scalarBarActor->SetLookupTable(lookup1);    // 设置标量条的高度为0.7    m_pointCloudStruct.m_scalarBarActor->SetHeight(0.7);    // 设置标量条的宽度为0.1    m_pointCloudStruct.m_scalarBarActor->SetWidth(0.1);}// 显示RGB点云🔴🟢🔵void VTK1D2D3DWidget::showRGBPointCloud(vtkSmartPointer<vtkPoints> vtkpoints, vtkSmartPointer<vtkLookupTable> lookup){    // 移除颜色刻度表    m_Renderer->RemoveActor(m_pointCloudStruct.m_scalarBarActor);    // 将传入的点云数据赋值给成员变量    m_pointCloudStruct.m_points = vtkpoints;    // 将传入的颜色查找表赋值给成员变量    m_pointCloudStruct.m_lookupTable = lookup;    // 创建多顶点对象 离散点集    vtkSmartPointer<vtkPolyVertex> polyVertex = vtkSmartPointer<vtkPolyVertex>::New();    // 创建浮点数组用于存储点云的标量值    vtkSmartPointer<vtkFloatArray> pointsScalars = vtkSmartPointer<vtkFloatArray>::New();    // 创建非结构化网格对象    vtkSmartPointer<vtkUnstructuredGrid> aGrid = vtkSmartPointer<vtkUnstructuredGrid>::New();    // 设置多顶点对象的点ID数量    polyVertex->GetPointIds()->SetNumberOfIds(m_pointCloudStruct.m_points->GetNumberOfPoints());    // 设置标量数组的大小    pointsScalars->SetNumberOfTuples(m_pointCloudStruct.m_points->GetNumberOfPoints());    // 遍历点云数据,设置点ID和标量值    for (int i = 0; i < m_pointCloudStruct.m_points->GetNumberOfPoints(); i++)    {        // 设置第i个点的ID为i        polyVertex->GetPointIds()->SetId(i, i);        // 为第i个点插入标量值i        pointsScalars->InsertValue(i, i);    }    // 分配非结构化网格的内存    aGrid->Allocate(1, 1);    // 设置点云数据到非结构化网格    aGrid->SetPoints(m_pointCloudStruct.m_points);    // 设置标量数据到非结构化网格的点数据中    aGrid->GetPointData()->SetScalars(pointsScalars);    // 插入顶点单元到非结构化网格    aGrid->InsertNextCell(polyVertex->GetCellType(), polyVertex->GetPointIds());    // 将点云数据设置到polyData中    m_pointCloudStruct.m_polyData->SetPoints(m_pointCloudStruct.m_points);    // 创建顶点符号过滤器    vtkSmartPointer<vtkVertexGlyphFilter> glyphFilter = vtkSmartPointer<vtkVertexGlyphFilter>::New();    glyphFilter->SetInputData(m_pointCloudStruct.m_polyData);    glyphFilter->Update();    // 设置映射器的输入数据为非结构化网格    m_pointCloudStruct.m_dataSetMapper->SetInputData(aGrid);    // 启用标量可见性,即根据标量值来显示颜色    m_pointCloudStruct.m_dataSetMapper->ScalarVisibilityOn();    // 设置标量范围为0到m_pointCloudStruct.m_polyData中的点数减1    m_pointCloudStruct.m_dataSetMapper->SetScalarRange(0, m_pointCloudStruct.m_polyData->GetNumberOfPoints() - 1);    // 设置数据集映射器使用的查找表为lookup    m_pointCloudStruct.m_dataSetMapper->SetLookupTable(lookup);    // 设置演员的属性为点表示,并设置点大小    m_pointCloudStruct.m_actor->GetProperty()->SetRepresentationToPoints();    m_pointCloudStruct.m_actor->GetProperty()->SetPointSize(1);}// 按照指定的轴设定点云颜色属性// 极小值处设置为蓝色(0,0,255),并且蓝色通道的数值由255向中间位置按坐标变化递减至0,// 绿色通道的数值由0按坐标变化递增至255;中间位置设置为绿色(0,255,0),// 并且绿色通道的数值由255按坐标变化递减至0,红色通道数值由0按坐标变化递增至255;// 极大值位置设置为(255,0,0)vtkSmartPointer<vtkUnsignedCharArray> VTK1D2D3DWidget::setColorBaseAxis(char axis, vtkSmartPointer<vtkPoints> points){    vtkSmartPointer<vtkUnsignedCharArray> colors_rgb = vtkSmartPointer<vtkUnsignedCharArray>::New();    // 点云的极值,第一第二个元素分别是x的最小最大值,yz依次类推    double bounds[6];                 // 定义一个包含6个元素的数组,用于存储边界值    points->GetBounds(bounds);        // 调用points对象的GetBounds方法,将边界值存储到bounds数组中    double z = bounds[5] - bounds[4]; // 计算z轴的最大值和最小值之差,得到z轴的长度    double y = bounds[3] - bounds[2]; // 计算y轴的最大值和最小值之差,得到y轴的长度    double x = bounds[1] - bounds[0]; // 计算x轴的最大值和最小值之差,得到x轴的长度    double z_median = z / 2; // 计算z轴的中点    double y_median = y / 2; // 计算y轴的中点    double x_median = x / 2; // 计算x轴的中点    colors_rgb->SetNumberOfComponents(3); // 设置colors_rgb对象的颜色组分为3,表示RGB颜色    double r = 0, g = 0, b = 0; // 初始化红、绿、蓝三个颜色分量为0    double *val;                // 定义一个指向double类型的指针,用于存储颜色值    // 检查轴是否为x    if (axis == 'x')    {        // 遍历所有点        for (int i = 0; i < points->GetNumberOfPoints(); i++)        {            // 获取点云数据            val = points->GetPoint(i);            // 中间值为界,x值大于中间值的b组分为0,r组分逐渐变大            if ((val[0] - bounds[0]) > x_median)            {                // x值要先归一化再乘以255,不然数值将会超出255                r = (255 * ((val[0] - bounds[0] - x_median) / x_median)); ;                g = (255 * (1 - ((val[0] - bounds[0] - x_median) / x_median)));                b = 0;                colors_rgb->InsertNextTuple3(r, g, b);            }            // 中间值为界,x值小于中间值的r组分为0,b组分逐渐变大            else            {                // x值要先归一化再乘以255,不然数值将会超出255                r = 0;                g = (255 * ((val[0] - bounds[0]) / x_median));                b = (255 * (1 - ((val[0] - bounds[0]) / x_median))); ;                colors_rgb->InsertNextTuple3(r, g, b);            }        }    }    // 检查轴是否为y    else if (axis == 'y')    {        // 遍历所有点        for (int i = 0; i < points->GetNumberOfPoints(); i++)        {            // 获取点云数据            val = points->GetPoint(i);            // 中间值为界,y值大于中间值的b组分为0,r组分逐渐变大            if ((val[1] - bounds[2]) > y_median)            {                // y值要先归一化再乘以255,不然数值将会超出255                r = (255 * ((val[1] - bounds[2] - y_median) / y_median)); ;                g = (255 * (1 - ((val[1] - bounds[2] - y_median) / y_median)));                b = 0;                colors_rgb->InsertNextTuple3(r, g, b);            }            // 中间值为界,y值小于中间值的r组分为0,b组分逐渐变大            else            {                r = 0;                g = (255 * ((val[1] - bounds[2]) / y_median));                b = (255 * (1 - ((val[1] - bounds[2]) / y_median))); ;                colors_rgb->InsertNextTuple3(r, g, b);            }        }    }    // 检查轴是否为z    else if (axis == 'z')    {        // 遍历所有点        for (int i = 0; i < points->GetNumberOfPoints(); i++)        {            // 获取点云数据            val = points->GetPoint(i);            // 中间值为界,z值大于中间值的b组分为0,r组分逐渐变大            if ((val[2] - bounds[4]) > z_median)            {                // z值要先归一化再乘以255,不然数值将会超出255                r = (255 * ((val[2] - bounds[4] - z_median) / z_median)); ;                g = (255 * (1 - ((val[2] - bounds[4] - z_median) / z_median)));                b = 0;                colors_rgb->InsertNextTuple3(r, g, b);            }            // 中间值为界,z值小于中间值的r组分为0,b组分逐渐变大            else            {                r = 0;                g = (255 * ((val[2] - bounds[4]) / z_median));                b = (255 * (1 - ((val[2] - bounds[4]) / z_median)));                colors_rgb->InsertNextTuple3(r, g, b);            }        }    }    return colors_rgb;}// 复位点云🔁🔁🔁void VTK1D2D3DWidget::Reset_pointcloud(){#if VTK_MAJOR_VERSION <= 7    //清缩放    this->m_render->Clear();#endif    // 设置相机位置为原点 (0, 0, 0)    m_Renderer->GetActiveCamera()->SetPosition(0, 0, 0);          //相机位置    // 设置相机焦点位置为 (0, 0, 1),即相机看向Z轴正方向    m_Renderer->GetActiveCamera()->SetFocalPoint(0, 0, 1);        //焦点位置    // 设置相机朝上方向为 (0, -1, 0),即Y轴负方向    m_Renderer->GetActiveCamera()->SetViewUp(0, -1, 0);           //朝上方向    // 重置相机视角,使其适应当前场景    m_Renderer->ResetCamera();    // 刷新界面#if VTK_MAJOR_VERSION <= 7    // 在VTK版本7及以下,使用GetRenderWindow()->Render()刷新渲染窗口    ui->VTKPointCloud->GetRenderWindow()->Render();#else    // 在VTK版本8及以上,使用renderWindow()->Render()刷新渲染窗口    ui->VTKPointCloud->renderWindow()->Render();#endif}//////////////////////////////////////// 点云存储 ///////////////////////////////////////////////// 保存RGB点云数据到PLY文件bool VTK1D2D3DWidget::SaveRGBACSIIPlyFile(){    // 提示用户选择输出文件名    QString fileFilter = "PLY(*.ply);;OBJ(*.obj);;STL(*.stl);;"; // 文件过滤器,限定文件类型    QString selectedFilter = ""; // 用户选择的文件过滤器    QString selectedFilename = QFileDialog::getSaveFileName(this, // 打开文件保存对话框                                                            "Save file", // 对话框标题                                                            "./", // 默认目录                                                            fileFilter, // 文件过滤器                                                            &selectedFilter); // 输出用户选择的过滤器    if(selectedFilename.isEmpty()) // 如果用户没有选择文件名,返回false    {        return false;    }    bool ret = true; // 初始化返回值为true    selectedFilename += tr(".ply"); // 添加文件扩展名    QFile file(selectedFilename); // 创建QFile对象    if(!file.open(QIODevice::ReadWrite|QIODevice::Text)) // 尝试以读写和文本模式打开文件    {        //qDebug() << ("SavePlyFile()==>open file failed"); // 打印错误信息(已注释)        return false; // 如果打开文件失败,返回false    }    /* 保存点云数据 */    QString text = "";  // 初始化保存点云数据的字符串    int cnt = 0;        // 初始化点云计数器    for(int i = 0; i < m_pointCloudStruct.m_polyData->GetNumberOfPoints(); i++) // 遍历所有点    {        double *xyz = m_pointCloudStruct.m_polyData->GetPoint(i); // 获取点的坐标        double rgb[3]; // 定义RGB颜色数组        m_pointCloudStruct.m_lookupTable->GetColor(i, rgb); // 获取点的颜色        if(xyz[0]!=NULL && xyz[1]!=NULL && xyz[2]!=NULL) // 检查坐标是否有效        {            if(xyz[2] < 888888.0 && xyz[2] != NAN) // 检查Z坐标是否在有效范围内            {                // 将点的坐标和颜色转换为字符串并添加到text中                text.append(QString::number(xyz[0]) + " ");                text.append(QString::number(xyz[1]) + " ");                text.append(QString::number(xyz[2]) + " ");                text.append(QString::number((uint8_t)(rgb[0] * 255.0f)) + " ");                text.append(QString::number((uint8_t)(rgb[1] * 255.0f)) + " ");                text.append(QString::number((uint8_t)(rgb[2] * 255.0f)) + "\n");                cnt++; // 点云计数器加1            }        }    }    /* 保存文件头 */    QString ply_header = "ply\n"; // PLY文件格式标识    ply_header = ply_header + "format ascii 1.0\n" + // 文件格式为ASCII                 "element vertex " + QString::number(cnt) + "\n" + // 点的数量                 "comment Author Hypersen\n" + // 注释:作者                 "comment Format RGB\n" + // 注释:格式                 "comment Unit mm\n" + // 注释:单位                 (abs(m_x_interval) <= 1e-6 ? ("") : ("comment X_interval " + QString::number(m_x_interval, 'f', 4) + "\n")) + // 注释:X间隔                 (abs(m_y_interval) <= 1e-6 ? ("") : ("comment Y_interval " + QString::number(m_y_interval, 'f', 4) + "\n")) + // 注释:Y间隔                 "comment Size " + QString::number(cnt) + "\n" + // 注释:大小                 "property float x\n" + // 属性:X坐标                 "property float y\n" + // 属性:Y坐标                 "property float z\n" + // 属性:Z坐标                 "property uint8 red\n" + // 属性:红色通道                 "property uint8 green\n" + // 属性:绿色通道                 "property uint8 blue\n" + // 属性:蓝色通道                 "element face 0\n" + // 面的数量为0                 "property list uchar int vertex_indices\n" + // 属性:顶点索引                 "property list uchar float texcoord\n" + // 属性:纹理坐标                 "end_header\n"; // 文件头结束标识    file.write(ply_header.toStdString().c_str()); // 写入文件头    file.write(text.toLocal8Bit()); // 写入点云数据    /* 关闭文件 */    file.close(); // 关闭文件    return ret; // 返回操作结果}// 保存深度点云bool VTK1D2D3DWidget::SaveDepthCSIIPlyFile(){    // 提示用户输入输出文件名    QString fileFilter = "PLY(*.ply);;OBJ(*.obj);;STL(*.stl);;"; // 文件过滤器,限制文件类型    QString selectedFilter = ""; // 选择的文件过滤器    QString selectedFilename = QFileDialog::getSaveFileName(this, // 获取保存文件对话框                                                            "Save file", // 对话框标题                                                            "./", // 默认目录                                                            fileFilter, // 文件过滤器                                                            &selectedFilter); // 选择的文件过滤器    if(selectedFilename.isEmpty()) // 如果用户没有选择文件名,返回false    {        return false;    }    bool ret = true; // 返回值,默认为true    selectedFilename += tr(".ply"); // 添加文件扩展名    QFile file(selectedFilename); // 创建文件对象    if(!file.open(QIODevice::ReadWrite|QIODevice::Text)) // 打开文件,如果失败,返回false    {        //qDebug() << ("SavePlyFile()==>open file failed"); // 调试信息        return false;    }    /*保存点云数据*/    QString text = ""; // 存储点云数据的字符串    int cnt = 0; // 计数器,记录有效点的数量    for(int i = 0; i < m_pointCloudStruct.m_polyData->GetNumberOfPoints(); i++) // 遍历所有点    {        double *xyz = m_pointCloudStruct.m_polyData->GetPoint(i); // 获取点的坐标        //        if(xyz[0]!=NULL && xyz[1]!=NULL && xyz[2]!=NULL) // 检查坐标是否为空        //        {        text.append(QString::number(xyz[0]) + " "); // 添加x坐标        text.append(QString::number(xyz[1]) + " "); // 添加y坐标        text.append(QString::number(xyz[2]) + " " + "\n"); // 添加z坐标和换行符        cnt++; // 计数器加1        //        }    }    /*保存文件头*/    QString ply_header = "ply\n"; // PLY文件头    ply_header = ply_header + "format ascii 1.0\n" + // 文件格式                 "comment Created by (writer info not set)\n" + // 注释                 "comment Created " + Get_Current_Time() + "\n" + // 注释,包含当前时间                 "obj_info Generated by hypersen!\n" + // 对象信息                 "element vertex " + QString::number(cnt) + "\n" + // 点的数量                 "property float x\n" + // x坐标属性                 "property float y\n" + // y坐标属性                 "property float z\n" + // z坐标属性                 "end_header\n"; // 结束头信息    file.write(ply_header.toStdString().c_str()); // 写入文件头    file.write(text.toLocal8Bit()); // 写入点云数据    /*关闭文件*/    file.close(); // 关闭文件    return ret; // 返回结果}//////////////////////////////////////// 点云设置和获取 ///////////////////////////////////////////////// 用于设置点云的间隔void VTK1D2D3DWidget::SetPointInterval(float x_interval, float y_interval){    m_x_interval = x_interval;  // 设置点云在x轴上的间隔    m_y_interval = y_interval;  // 设置点云在y轴上的间隔}// 用于判断点云是否为空bool VTK1D2D3DWidget::isPointCloudEmpty(){    // 如果点云中的点数大于0,则返回false,表示点云不为空    if(m_pointCloudStruct.m_points->GetNumberOfPoints() > 0)        return false;    else        return true;  // 否则返回true,表示点云为空}// 用于启用或禁用保存按钮void VTK1D2D3DWidget::SaveEnable(bool enabel){    ui->PB_Save->setEnabled(enabel);}// 用于设置点云的类型void VTK1D2D3DWidget::SetPLYType(PointCloudType type){    m_pointCloudType = type;}// 用于清除或添加点云void VTK1D2D3DWidget::ClearPointcloud(bool flag){    if(flag)    {        m_Renderer->RemoveActor(m_pointCloudStruct.m_actor);  // 如果flag为true,从渲染器中移除点云    }    else    {        m_Renderer->AddActor(m_pointCloudStruct.m_actor);  // 如果flag为false,向渲染器中添加点云    }    //Reset_pointcloud();  // 注释掉的代码,可能用于重置点云,但当前未使用}// 用于设置背景颜色void VTK1D2D3DWidget::SetBackgroundColor(BackgroundColorType color){    if(color == COLOR_WHITE)    {        // 设置背景色为白色        m_Renderer->SetBackground(1.0, 1.0, 1.0);            // 设置页面底部颜色值为白色        m_Renderer->SetBackground2(0.529, 0.8078, 0.92157);  // 设置页面顶部颜色值为浅蓝色        m_Renderer->SetGradientBackground(1);                // 开启渐变色背景设置    }    else if(color == COLOR_BLACK)    {        // 设置背景色为黑色        m_Renderer->SetBackground(0.0, 0.0, 0.0);   // 设置页面底部颜色值为黑色        m_Renderer->SetBackground2(0.0, 0.0, 0.0);  // 设置页面顶部颜色值为黑色        m_Renderer->SetGradientBackground(0);       // 关闭渐变色背景设置    }    ui->VTKPointCloud->renderWindow()->Render();  // 重新渲染窗口以应用背景颜色设置}// 正常测试void VTK1D2D3DWidget::normaltest(){    // 实例化一个多边形数据对象cube    vtkPolyData *cube = vtkPolyData::New();    // 实例化一个点对象points    vtkPoints *points = vtkPoints::New();    // 单元对象    vtkCellArray *polys = vtkCellArray::New();    // 浮点数组对象    vtkFloatArray *scalars = vtkFloatArray::New();    // Load the point, cell, and data attributes.    for (int y=0; y < 1600; ++y)    {        for(int x = 0; x < 1600; ++x)        {            // 加载立方体的8个顶点            points->InsertPoint(x + y * 1600, x, y, 0.1 * y);            // 绑定标量数据            scalars->InsertTuple1(x + y * 1600, 0.1 * y);            if(y < 1599 && x < 1599)            {                vtkIdType p[4];                p[0] = x + y * 1600;                p[1] = x + 1 + y * 1600;                p[2] = x + 1 + (1 + y) * 1600;                p[3] = x + (1 + y) * 1600;                // 加载每个面由4个点组成的六个面                polys->InsertNextCell(4,p);            }        }    }    //与vtkPolyData型数据对象进行关联    cube->SetPoints(points);//进行点关联    points->Delete();    cube->SetPolys(polys);//进行面关联    polys->Delete();    cube->GetPointData()->SetScalars(scalars);//进行元组绑定    scalars->Delete();    //多边形数据送入图像界面进行绘制    m_pointCloudStruct.m_dataSetMapper->SetInputData(cube);//vtkPolyData数据输出给映射器输入    // 复位点云    Reset_pointcloud();}
*/

04

mainwindow.cpp

代码语言:javascript
复制
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* #include "mainwindow.h"#include "ui_mainwindow.h"#include <QMessageBox>// Qt高级线程#include <QFutureWatcher>#include <QtConcurrent/QtConcurrent>MainWindow::MainWindow(QWidget *parent)    : QMainWindow(parent)    , ui(new Ui::MainWindow){    ui->setupUi(this);    this->setWindowTitle(tr("VTK教程"));    m_isPointCloudFromOutside = false;  // 初始化外部点云数据标志为false    // 点云数据结构体点云数据初始化    m_pointCloudStruct.allPoints = vtkSmartPointer<vtkPoints>::New();       // 创建一个新的vtkPoints对象,用于存储所有点云点    m_pointCloudStruct.points    = vtkSmartPointer<vtkPoints>::New();       // 创建一个新的vtkPoints对象,用于存储点云点    m_pointCloudStruct.lookup    = vtkSmartPointer<vtkLookupTable>::New();  // 创建一个新的vtkLookupTable对象,用于颜色查找表    //设置点云文件点间隔值    connect(ui->m_VTK1D2D3DWidget, &VTK1D2D3DWidget::sig_GetPointInterval, this, &MainWindow::ToSaveIntervalInPLYFile);    //设置保存点云的方式    connect(ui->m_VTK1D2D3DWidget, &VTK1D2D3DWidget::sig_GetPCType, this, &MainWindow::SetPCType);    // 初始化模拟图像数据    m_imageData =  this->initializeImageData(3200, 3200, 0.1f);}MainWindow::~MainWindow(){    this->freeImageData(&m_imageData);    delete ui;}/** 初始化图像数据结构体(模拟数据,实际数据可能是相机拍摄的数据) */imageData_t MainWindow::initializeImageData(quint32 width, quint32 height, qreal point_interval){    // 图像测量数据    imageData_t data;    data.width = width;    data.height = height;    data.point_interval = point_interval;    // 分配内存    data.z = (float*)malloc(width * height * sizeof(float));    data.gray = (uint8_t*)malloc(width * height * sizeof(uint8_t));    data.rgb = (uint8_t*)malloc(width * height * 3 * sizeof(uint8_t));    // 深度图统计信息    data.depthMapInfo.height_maxValue  = -FLT_MAX;    data.depthMapInfo.height_miniValue = FLT_MAX;    // 填充模拟数据    for (uint32_t i = 0; i < height; ++i) {        for (uint32_t j = 0; j < width; ++j) {            // 计算当前点的索引            uint32_t index = i * width + j;            // 填充深度数据            if (i % 10 == 0 && j % 10 == 0) {                data.z[index] = 100.0f + (i * j) % 100; // 模拟有效深度数据            } else {                data.z[index] = INVALID_VALUE; // 无效点            }            // 更新统计信息            if (data.z[index] != INVALID_VALUE) {                if (data.z[index] > data.depthMapInfo.height_maxValue) {                    data.depthMapInfo.height_maxValue = data.z[index];                }                if (data.z[index] < data.depthMapInfo.height_miniValue) {                    data.depthMapInfo.height_miniValue = data.z[index];                }            }            // 填充灰度数据            data.gray[index] = (i * j) % 256;            // 填充RGB数据,形成绿色渐变            data.rgb[index * 3] = 0;                 // R            data.rgb[index * 3 + 1] = (i * j) % 256; // G            data.rgb[index * 3 + 2] = 0;             // B        }    }    return data;}/** 释放图像数据结构体的内存 */void MainWindow::freeImageData(imageData_t* data){    if (data->z)        free(data->z);    if (data->gray)        free(data->gray);    if (data->rgb)        free(data->rgb);}/** 获取当前系统时间 */QString Get_Current_Time(){    return QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");}/** 日志栏:操作提示和信息通知 */void MainWindow::AppendLog(const QString &textLog, Qt::GlobalColor color){    // 加锁,确保在多线程环境下更新文本时不会出现竞态条件    m_log_mutex.lock();    // 用于记录已添加的文本数量    static int text_count = 0;    // 先保存当前的文本颜色,以便在更改颜色后能够恢复    auto cur_text_color = ui->textEdit_Log->textColor();    // 根据text_id设置不同的文本颜色    switch (color)    {    case Qt::GlobalColor::darkGreen:        ui->textEdit_Log->setTextColor(QColor("darkgreen")); // 设置文本颜色为深绿色        break;    case  Qt::GlobalColor::red:        ui->textEdit_Log->setTextColor(QColor("red")); // 设置文本颜色为红色        break;    case  Qt::GlobalColor::magenta:        ui->textEdit_Log->setTextColor(QColor("magenta")); // 设置文本颜色为magenta色        break;    case Qt::GlobalColor::blue:        ui->textEdit_Log->setTextColor(QColor("steelblue")); // 设置文本颜色为蓝色        break;    default:        break;    }    // 如果已添加的文本数量超过500,则清空文本编辑器并重置计数器    if(text_count >= 500)    {        text_count = 0;        ui->textEdit_Log->clear();    }    // 增加已添加的文本数量    text_count++;    // 在文本编辑器中追加当前时间和信息内容    ui->textEdit_Log->append(Get_Current_Time()  + " --> "+ textLog);    // 最后恢复原来的文本颜色    ui->textEdit_Log->setTextColor(cur_text_color);    // 解锁,允许其他线程访问文本编辑器    m_log_mutex.unlock();}/** 切换RGB/深度点云显示 */void MainWindow::on_cb_type3D_currentIndexChanged(int index){    if(m_pointCloudStruct.allPoints->GetNumberOfPoints() == 0) {        QMessageBox::warning(this, "Warning", QObject::tr("点云为空"));        return;    }    // 开启显示哪种类型的点云    if(ui->cb_showPointCloud->isChecked())    {        if(index == 0)        {            // RGB点云            ui->m_VTK1D2D3DWidget->showPointCloud(m_pointCloudStruct.allPoints,                                                  m_pointCloudStruct.lookup, PointCloudType::PC_RGB);        }        else if(index == 1)        {            // 深度点云            ui->m_VTK1D2D3DWidget->showPointCloud(m_pointCloudStruct.points,                                                  m_pointCloudStruct.lookup, PointCloudType::PC_DEPTH);        }        // 切换至点云显示页        ui->tabWidget_display->setCurrentWidget(ui->tab_VTKPointCloud);    }}/** 设置被保存的点云文件的点间隔数据 */void MainWindow::ToSaveIntervalInPLYFile(){    // 判断是否设备连接    if(ui->m_VTK1D2D3DWidget->isPointCloudEmpty())    {        QMessageBox::warning(this, "保存", QObject::tr("当前窗口未检测到点云图像相关数据"));        return;    }    float point_interval = 0.0f;    if(m_isPointCloudFromOutside)    {        return;    }    else    {        // 深度图点间隔,单位mm 这个点可能来自MCU或者其他设备        // API_GetFloatParameter(devHandle,&point_interval);    }    // 对实时采集的点云的点间隔数据进行保存    ui->m_VTK1D2D3DWidget->SetPointInterval(point_interval, point_interval);}/** 设置当前保存的点云类型 */void MainWindow::SetPCType(){    // 判断当前显示的以及要保存的点云类型    if(ui->cb_type3D->currentIndex() == 0)    {        ui->m_VTK1D2D3DWidget->SetPLYType(PointCloudType::PC_RGB);    }    else if(ui->cb_type3D->currentIndex() == 1)    {        ui->m_VTK1D2D3DWidget->SetPLYType(PointCloudType::PC_DEPTH);    }}/** 点云黑色背景/点云白色背景 */void MainWindow::on_cb_PCBackgroundColor_currentIndexChanged(int index){    if(index == 0)    {        ui->m_VTK1D2D3DWidget->SetBackgroundColor(BackgroundColorType::COLOR_BLACK);    }    else if(index == 1)    {        ui->m_VTK1D2D3DWidget->SetBackgroundColor(BackgroundColorType::COLOR_WHITE);    }}/** 点云显示 */void MainWindow::on_cb_showPointCloud_clicked(bool checked){    if(!checked)    {        ui->m_VTK1D2D3DWidget->ClearPointcloud(true);        return;    }    else    {        //首次解析即可        if(!(m_pointCloudStruct.allPoints->GetNumberOfPoints() > 0) && !m_isPointCloudFromOutside)        {            // 解析点云            this->AnalysesPointcloud();        }        // 添加点云        ui->m_VTK1D2D3DWidget->ClearPointcloud(false);        if(ui->cb_type3D->currentIndex() == 0) // RGB点云        {            if(m_pointCloudStruct.allPoints->GetNumberOfPoints() > 0)            {                // 显示RGB点云                ui->m_VTK1D2D3DWidget->showPointCloud(m_pointCloudStruct.allPoints,                                                      m_pointCloudStruct.lookup, PointCloudType::PC_RGB);            }        }        else if(ui->cb_type3D->currentIndex() == 1) // 深度点云        {            if(m_pointCloudStruct.points->GetNumberOfPoints() > 0)            {                // 显示深度点云                ui->m_VTK1D2D3DWidget->showPointCloud(m_pointCloudStruct.points,                                                      m_pointCloudStruct.lookup, PointCloudType::PC_DEPTH);            }        }        ui->tabWidget_display->setCurrentWidget(ui->tab_VTKPointCloud);    }}/** 分析相机数据-灰度图-深度图-点云 */void MainWindow::AnalysesResultData(){    // 解析灰度、RGB    {        QImage gray(m_imageData.gray, m_imageData.width,                    m_imageData.height, QImage::Format_Grayscale8); // 图像的格式为 8 位灰度图像        // 常见的 QImage 格式对比        // QImage::Format_RGB32      // 32 位 RGB 格式,每个像素 4 字节 (R,G,B,保留)        // QImage::Format_ARGB32     // 32 位 ARGB 格式,每个像素 4 字节 (A,R,G,B)        // QImage::Format_RGB888     // 24 位 RGB 格式,每个像素 3 字节 (R,G,B)        // QImage::Format_Grayscale8 // 8 位灰度格式,每个像素 1 字节 (灰度值)        QImage rgb(m_imageData.rgb, m_imageData.width,                   m_imageData.height, QImage::Format_RGB888);      // 24位RGB格式,每个像素 3 字节 (R,G,B)        // 深拷贝,存储采集到的图像数据        m_resultGray = gray.copy(gray.rect());        // 深度图输出        m_resultRGB = rgb.copy(rgb.rect());        /**          深拷贝(Deep Copy)是指在复制一个对象时,不仅复制对象本身,还复制对象所引用的所有对象,从而创建一个完全独立的新对象。          深拷贝确保新对象与原对象之间没有任何共享的引用,因此对新对象的修改不会影响到原对象,反之亦然。        */    }    //解析深度    {        z_list.clear();        /* 结构体深拷贝拷贝的是存储z(深度)值的首地址,需要遍历存储 首地址~(首地址+width*height)区间的所有z值 */        int index = 0;        // 遍历图像的每个像素点        for (uint32_t x = 0; x < m_imageData.height; x++)        {            for (uint32_t y = 0; y < m_imageData.width; y++)            {                // 计算当前像素点在一维数组中的索引                index = x * m_imageData.width + y; // 从左到右,从上到下,累计像素个数m_result.width                // 检查当前像素点的z值是否为有效值                if (m_imageData.z[index] != INVALID_VALUE)                {                    // 注意:在并行环境中,append操作需要加锁或使用线程安全的数据结构                    {                        // 将有效的z值添加到z_list列表中                        z_list.append(m_imageData.z[index]);                        // 注释掉的调试代码,用于打印像素点的z值                        //qDebug() << "m_result[" << index << "] = " << m_result.z[index] <<endl;                    }                }            }        }    }    // 解析点云[点云显示]    if(ui->cb_showPointCloud->isChecked())    {        this->AnalysesPointcloud();    }}/** 分析点云数据 */void MainWindow::AnalysesPointcloud(){    vtkSmartPointer<vtkPoints> vtk_points = vtkSmartPointer<vtkPoints>::New();    vtkSmartPointer<vtkPoints> vtk_allPoints = vtkSmartPointer<vtkPoints>::New();    vtkSmartPointer<vtkLookupTable> vtk_lookup = vtkSmartPointer<vtkLookupTable>::New();    // 初始化像素索引为0    int pexilindex = 0;    // 获取结果矩阵的宽度和高度    int w = m_imageData.width;    int h = m_imageData.height;    // 为vtk_lookup分配内存,大小为h*w,每个元素最大值为256    vtk_lookup->Allocate(h* w, 256);    // 计算z_list中的最小值和最大值    //auto minmax_z = std::minmax_element(z_list.begin(), z_list.end());    // 重置点云数据结构中的所有点    m_pointCloudStruct.allPoints->Reset();    // 重置点云数据结构中的点    m_pointCloudStruct.points->Reset();    // 为点云数据结构中的查找表分配内存,大小为h*w,每个元素最大值为256    m_pointCloudStruct.lookup->Allocate(h * w, 256);    /*     * 循环遍历图像的每个像素点,计算并生成点云图     * 参数说明:     *   v: 当前像素的行坐标     *   u: 当前像素的列坐标     *   h: 图像的高度     *   w: 图像的宽度     */    for (int v = 0; v < h; v++)    {        for (int u = 0; u < w; u++)        {            // 计算当前像素在一维数组中的索引位置            pexilindex = v * w + u;            // 计算三维空间中的实际坐标            // x_t, y_t: 基于像素坐标和点间距计算            // z_t: 从深度数据中获取            double x_t = u * m_imageData.point_interval;            double y_t = v * m_imageData.point_interval;            double z_t = m_imageData.z[pexilindex];            // 将点插入到VTK点云数据集中            // 使用NAN表示无效点            vtk_allPoints->InsertNextPoint(x_t, y_t, z_t < INVALID_VALUE ? z_t : NAN);            // 判断z值是否有效(小于INVALID_VALUE)            if (z_t < INVALID_VALUE)            {                // 将有效点插入到有效点数据集中                vtk_points->InsertNextPoint(x_t, y_t, z_t);            }            else            {                // 将无效点的z值设为NAN                z_t = NAN;            }            // 设置RGB颜色表            // 将RGB值从0-255范围归一化到0.0-1.0范围            // pexilindex: 当前像素在图像中的索引位置            // m_result.rgb[pexilindex * 3]: 红色分量 (R)            // m_result.rgb[pexilindex * 3 + 1]: 绿色分量 (G)            // m_result.rgb[pexilindex * 3 + 2]: 蓝色分量 (B)            // 1: 不透明度(alpha值)            vtk_lookup->SetTableValue(pexilindex, m_imageData.rgb[pexilindex * 3] / 255.0,                    m_imageData.rgb[pexilindex * 3 + 1] / 255.0,                    m_imageData.rgb[pexilindex * 3 + 2] / 255.0, 1);        }    }    // 点云RGB构建部分    // 构建查找表,用于点云颜色映射    vtk_lookup->Build();    // 深拷贝部分    // 将点云的所有点数据进行深拷贝    m_pointCloudStruct.allPoints->DeepCopy(vtk_allPoints);    // 将点云的点数据进行深拷贝    m_pointCloudStruct.points->DeepCopy(vtk_points);    // 将点云的查找表进行深拷贝    m_pointCloudStruct.lookup->DeepCopy(vtk_lookup);}/** -显示点云 */void MainWindow::showPointCloud(){    if(ui->cb_showPointCloud->isChecked())    {        ui->m_VTK1D2D3DWidget->ClearPointcloud(false); // 如果flag为false,向渲染器中添加点云        if(ui->cb_type3D->currentIndex() == 0)        {            if(m_pointCloudStruct.allPoints->GetNumberOfPoints() > 0)            {                // 显示RGB点云                ui->m_VTK1D2D3DWidget->showPointCloud(m_pointCloudStruct.allPoints,                                                      m_pointCloudStruct.lookup, PointCloudType::PC_RGB);            }        }        else if(ui->cb_type3D->currentIndex() == 1)        {            if(m_pointCloudStruct.points->GetNumberOfPoints() > 0)            {                // 显示深度点云                ui->m_VTK1D2D3DWidget->showPointCloud(m_pointCloudStruct.points,                                                      m_pointCloudStruct.lookup, PointCloudType::PC_DEPTH);            }        }        m_isPointCloudFromOutside = false;        ui->m_VTK1D2D3DWidget->SaveEnable(true);    }    else    {        //清除点云窗口显示        ui->m_VTK1D2D3DWidget->ClearPointcloud(true);    }    AppendLog(QObject::tr("显示点云"), Qt::darkGreen);}/** 模拟开始 */void MainWindow::on_PB_modelBegin_clicked(){    this->AppendLog("模拟开始...",Qt::darkGreen);    // 使用 QtConcurrent::run 在后台线程中执行 AnalysesResultData    QFuture<void> future = QtConcurrent::run(this, &MainWindow::AnalysesResultData);    // 使用 QFutureWatcher 监听任务完成信号    QFutureWatcher<void> *watcher = new QFutureWatcher<void>(this);    connect(watcher, &QFutureWatcher<void>::finished, this, [this](){        this->AppendLog("模拟结束",Qt::darkGreen);        this->showPointCloud();    });    watcher->setFuture(future);}
*/

05

演示


原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档