
项目简介
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 视角操作方式。
数据结构
点云数据结构
// 点云数据typedefstruct { vtkSmartPointer<vtkPoints> points; // 点云数据点 vtkSmartPointer<vtkPoints> allPoints; // 所有点云数据点 vtkSmartPointer<vtkLookupTable> lookup; // 查找表 }In_PointCloudStruct; 图像测量数据结构
// 图像测量数据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
#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
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释
* #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
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释
* #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 删除。