百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

VTK学习之激光点云动态库封装(排水管道)

bigegpt 2024-08-08 11:51 2 浏览

目前各行各业都应用了激光点云,包括目前非常火的自动驾驶行业,本人目前在排水管道检测行业,因此封装了应用于排水管道的点云库。激光雷达测得点云数据存储下来后,解析出坐标点,然后传递到函数入口中,即可获得三维点云模型。

处理点云数据的工具有很多,这里没有直接采用OpenGL和D3D,而选择了封装得比较好,容易上手的vtk,本示例是基于vtk9.0+vs2019,封装好的库使用C#进行调用测试。废话不多说,直接上代码:

首先是vtk.h头文件,这里将要用的vtk头文件都包含进来。

#pragma once
#include "vtkSmartPointer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderer.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkInteractorStyleTrackballCamera.h"
#include "vtkCylinderSource.h"
#include "vtkSphereSource.h"
#include "vtkPolyDataMapper.h"
#include "vtkActor.h"
#include "vtkBMPReader.h"
#include "vtkJPEGReader.h"
#include "vtkTexture.h"
#include "vtkLight.h"
#include "vtkCamera.h"
#include <vtkLine.h>
#include <vtkCellArray.h>
#include <vtkTubeFilter.h>
#include <vtkLineSource.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkConeSource.h>
#include <vtkCubeSource.h>
#include <vtkStructuredPointsReader.h>
#include <vtkSphereSource.h>
#include <vtkMarchingCubes.h>
#include <vtkXMLPolyDataReader.h>//加载模型数据
#include <vtkTextureMapToCylinder.h>//采用圆柱作为中介
#include <vtkOrientationMarkerWidget.h>
#include <vtkAxesActor.h>
#include <vtkImageData.h> 
#include <vtkImageViewer2.h>
#include <vtkTransformTextureCoords.h>
#include <vtkPoints.h>
#include <vtkLine.h>
#include <vtkCellArray.h>
#include <vtkPolyDataWriter.h>
#include <vtk3DSImporter.h>
#include <vtkStructuredPoints.h>
#include <vtkStructuredPointsReader.h>
#include <vtkVolumeTexture.h>
#include <vtkColorTransferFunction.h>
#include <vtkPiecewiseFunction.h>
#include <vtkImageShiftScale.h>
#include <vtkOpenGLGPUVolumeRayCastMapper.h>
#include <vtkVolumeProperty.h>
#include <vtkTransform.h>
#include <vtkUnsignedCharArray.h> 
#include <vtkCellData.h>
#include <vtkIdTypeArray.h>
 
//解决no override found for ""
#include "vtkAutoInit.h"
#include <vtkImageResliceMapper.h>
 
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
VTK_MODULE_INIT(vtkRenderingVolumeOpenGL2);
 
 
vtkSmartPointer< vtkOrientationMarkerWidget> widget;
vtkSmartPointer<vtkRenderWindow> renderWindow;
vtkSmartPointer<vtkRenderer> renderer;
vtkSmartPointer<vtkActor> actor;
vtkSmartPointer<vtkPolyData> polyData;
 
int diameter = 600;   //管道直径
int totalFrameCnt = 0;//总帧数

然后是外部调用头文件ZYPointCloudLib.h,声明函数:

#pragma once
#define NOMINMAX 
 
#include <string>
#include <wtypes.h> 
#include <vector>
 
/// <summary>
/// 雷达帧数据
/// </summary>
struct FrameData
{
    float* points;   //点坐标x,y,z循环(x为行走方向,为当前距离值)
    int pointsLen;      //points指针数组长度
    float distance;  //当前距离(单位:m)
    float circleX;   //拟合圆心坐标X(单位:mm)
    float circleY;   //拟合圆心坐标Y(单位:mm)
};
 
/// <summary>
/// 视图
/// </summary>
enum  CamOrientation
{
    Front = 0,
    Back,
    Left,
    Right,
    Up,
    Down,
    Axonometric
};
 
/// <summary>
/// 绑定显示控件句柄
/// </summary>
/// <param name="hwd">控件句柄</param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
extern "C" _declspec(dllexport) void* _stdcall BindingHandle(HWND hwd, int width, int height);
 
/// <summary>
/// 传入数据
/// </summary>
/// <param name="frameDatas">帧数据</param>
/// <param name="frameCount">帧总数</param>
/// <returns></returns>
extern "C" _declspec(dllexport) int _stdcall EntryData(FrameData * frameDatas, int frameCount);
 
/// <summary>
/// 设置管道参数
/// </summary>
/// <param name="pipeDiameter">管道直径</param>
/// <returns></returns>
extern "C" _declspec(dllexport) int _stdcall SetPipePara(int pipeDiameter);
 
/// <summary>
/// 设置视角方向
/// </summary>
/// <param name="t_camOrientation">视角方向</param>
/// <returns></returns>
extern "C" _declspec(dllexport) void _stdcall SetCameraOrientation(CamOrientation t_camOrientation);
 
 
/// <summary>
/// 选择帧
/// </summary>
/// <param name="index">index(0-max)</param>
/// <returns></returns>
extern "C" _declspec(dllexport) void _stdcall SelectFrame(int index);

ZYPointCloudLib.cpp:

// ZYPointCloudLib.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
 
#include <iostream>
#include "ZYPointCloudLib.h"
#include "vtk.h"
 
/// <summary>
/// 绑定控件
/// </summary>
/// <param name="hwd"></param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns></returns>
void* _stdcall BindingHandle(HWND hwd, int width, int height)
{
    if (renderWindow == NULL)
    {
        renderWindow = vtkSmartPointer<vtkRenderWindow> ::New();
        renderer = vtkSmartPointer<vtkRenderer> ::New();
        actor = vtkSmartPointer<vtkActor> ::New();
        renderWindow->SetParentId(hwd);
        renderWindow->SetSize(width, height);
       
        renderer->AddActor(actor);
        renderWindow->AddRenderer(renderer);
 
        //获取渲染窗口上发生的鼠标,键盘,事件事件
        vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New();
        iren->SetRenderWindow(renderWindow);
        
        //设置鼠标交互方式
        vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
        iren->SetInteractorStyle(style);
        iren->Initialize();
 
        //左下角坐标系
        vtkSmartPointer< vtkAxesActor> axes = vtkSmartPointer< vtkAxesActor>::New();
        //以Widget方式,在左下角的视口中显示坐标系,可进行鼠标交互
        if (widget == NULL)
            widget = vtkSmartPointer< vtkOrientationMarkerWidget>::New();
        widget->SetOutlineColor(0.9300, 0.5700, 0.1300);
        widget->SetOrientationMarker(axes);
        widget->SetInteractor(iren);
        widget->SetViewport(0.0, 0.0, 0.2, 0.2);
        widget->SetEnabled(1);     //使可用(显示)
        widget->InteractiveOff();  //禁止拖动    
    }  
    else
    {
        renderWindow->SetParentId(hwd);
        renderWindow->SetSize(width, height);
  
    }
 
    renderWindow->Render();
    return renderWindow;
}
 
/// <summary>
/// 传入数据
/// </summary>
/// <param name="frameDatas"></param>
/// <param name="frameCount"></param>
/// <returns></returns>
int _stdcall EntryData(FrameData* frameDatas, int frameCount)
{
    if (polyData == NULL)
        polyData  = vtkSmartPointer<vtkPolyData>::New();
 
    vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
    vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
 
    totalFrameCnt = frameCount;
    for (int i = 0; i < frameCount; i++)
    {
        int cnt = frameDatas[i].pointsLen/3;
        vtkIdType* idtype =new vtkIdType[cnt];
        for (int j = 0; j < cnt; j++)
        {
            float x = frameDatas[i].points[j * 3];
            float y = frameDatas[i].points[j * 3 + 1];
            float z = frameDatas[i].points[j * 3 + 2];
            idtype[j] = points->InsertNextPoint(x, y, z);
        }
        cells->InsertNextCell(cnt, idtype);   //第一个参数值标是cell中点的个数,第二个参数指向那些点的坐标数据。          
        delete[] idtype;
        idtype = nullptr;
    }
   
    polyData->SetPoints(points);//SetPoints设置几何数据点的坐标;
    polyData->SetVerts(cells);  //SetVerts将vtkCellArray按照离散点拓扑结构处理;设置定义顶点的单元阵列。
 
 
    mapper->SetInputData(polyData);
   
    actor->SetMapper(mapper);
    actor->GetProperty()->SetRepresentationToWireframe();
    renderer->ResetCamera();
    renderWindow->Render();
    return 1;
}
 
int _stdcall SetPipePara(int pipeDiameter)
{
    diameter = pipeDiameter;
    return 1;
}
 
int _stdcall SetMarkArray(DrawMarks drawMarks)
{
    return 0;
}
 
 
int _stdcall SetMarksVisible(bool visible)
{
    return 0;
}
 
 
/// <summary>
/// 设置视图方向
/// </summary>
/// <param name="t_camOrientation"></param>
void _stdcall SetCameraOrientation(CamOrientation t_camOrientation)
{
    if (renderer == NULL)
        return;
     vtkCamera* camera = renderer->GetActiveCamera();
     if (renderer->GetActiveCamera())
     {
         switch (t_camOrientation)
         {
             case  CamOrientation::Left:
             {
                 camera->SetPosition(1, 0, 0); //相机位置
                 break;
             }
             case  CamOrientation::Right:
             {
                 camera->SetPosition(-1, 0, 0);
                 break;
             }
             case  CamOrientation::Front:
             {
                 camera->SetPosition(0, -1, 0);
                 break;
             }
             case  CamOrientation::Back:
             {
                 camera->SetPosition(0, 1, 0);
                 break;
             }
             case  CamOrientation::Up:
             {
                 camera->SetPosition(0, 0, -1);
                 break;
             }
             case  CamOrientation::Down:
             {
                 camera->SetPosition(0, 0, 1);
                 break;
             }
         }
         camera->SetViewUp(0, 0, 1);    //设置相机朝上方向  
         camera->SetFocalPoint(0, 0, 0);//相机焦点:从相机看向的点
         renderer->SetActiveCamera(camera);
         renderer->ResetCamera();
         renderWindow->Render();
     }
}
 
 
/// <summary>
/// 选择帧
/// </summary>
/// <param name="index">index(0-max)</param>
/// <returns></returns>
void _stdcall SelectFrame(int index)
{
    unsigned char red[3]{ 255,0,0 };
    unsigned char white[3]{ 255,255,255 };
    vtkNew<vtkUnsignedCharArray> cellColor;
    cellColor->SetNumberOfComponents(3);
    int n = polyData->GetNumberOfCells();
    for (int i = 0; i < n; i++)
    {
        vtkCell* cell = polyData->GetCell(i);
        int cnt = cell->GetPoints()->GetNumberOfPoints();
        if (i == index)
            cellColor->InsertNextTypedTuple(red);
        else
            cellColor->InsertNextTypedTuple(white);
    }
    polyData->GetCellData()->SetScalars(cellColor);
    renderer->Render();
    renderWindow->Render();
}

编译生成 ZYPointCloudLib.dll。接下来就是测试了,使用winform测试:

先导入C++函数:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
 
namespace Sample
{
  
    public class PointCloudLib
    {
        /// <summary>
        /// 雷达帧数据
        /// </summary>
        public struct FrameData
        {
            public IntPtr points;     //点坐标x,y,z循环(x为行走方向,为当前距离值)
            public int pointsLen;      //points数组长度
            public float circleX;    //拟合圆心坐标X(单位:mm)
            public float circleY;    //拟合圆心坐标Y(单位:mm)
            public float radius;     //拟合圆半径(单位:mm)
        };
 
        /// <summary>
        /// 视图
        /// </summary>
        public enum CamOrientation
        {
            Front,
            Back,
            Left,
            Right,
            Up,
            Down,
            Axonometric
        };
 
        public const string dllPath = "ZYPointCloudLib.dll";
 
        [DllImport(dllPath, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr BindingHandle( IntPtr hwd, int width, int height);
 
 
        [DllImport(dllPath, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public static extern int EntryData(FrameData[] frameDatas, int frameCount);
 
        [DllImport(dllPath, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public static extern int SetPipePara(int pipeDiameter);
 
        [DllImport(dllPath, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public static extern void SetCameraOrientation(CamOrientation  camOrientation);
 
 
        [DllImport(dllPath, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
        public static extern void SelectFrame(int index); 
 
    }
}

然后在Form1创建一个PictureBox和Button,如下图:

后台代码:

   public partial class Form1 : Form
    {
        IntPtr vtkPtr;
        int curIndex = 0;
        public Form1()
        {
            InitializeComponent();
            cmbViewAngle.SelectedIndex = 0;
            vtkPtr = PointCloudLib.BindingHandle(pictureBox1.Handle, pictureBox1.Width, pictureBox1.Height);
        }
 
 
        /// <summary>
        /// 打开文件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            int ret = -1; ;
            OpenFileDialog fileDia = new OpenFileDialog();
            fileDia.Filter = "*.rad|*.rad";
            if (fileDia.ShowDialog() == DialogResult.OK)
            {
                ILidarFile lidarFile =  FileManagerLib.Lidar.LidarFile.GetInstance(fileDia.FileName);
                LidData lidData = new LidData();
                bool result = lidarFile.ReadAllData(true, out lidData);
                if(result)
                {
                    PointCloudLib.FrameData[] frameDatas = new PointCloudLib.FrameData[lidData.frameDatas.Length];
                    for (int i = 0; i < lidData.frameDatas.Length; i++)
                    {
                        List<PointF> CoordinatePoints = DataProcess.DataProcessing(lidData.frameDatas[i].FrameData, 0, lidData.frameDatas[i].ValidDataCount, 0, 360, true);
                        float[] points = new float[CoordinatePoints.Count * 3];
                        frameDatas[i].pointsLen = CoordinatePoints.Count * 3;
                        int index = 0;
                        for (int j = 0; j < CoordinatePoints.Count; j++)
                        {
                            points[index] = lidData.frameDatas[i].Distance * 1000;
                            points[index + 1] = CoordinatePoints[j].X;
                            points[index + 2] = CoordinatePoints[j].Y;
                            index += 3;
                        }
                        frameDatas[i].points = Marshal.AllocHGlobal(CoordinatePoints.Count * 3* sizeof(float));
                        Marshal.Copy(points, 0, frameDatas[i].points, points.Length);
                        frameDatas[i].circleX = lidData.frameDatas[i].CircleX;
                        frameDatas[i].circleY = lidData.frameDatas[i].CircleY;
                        frameDatas[i].radius = lidData.frameDatas[i].FittingCircleDia / 2;
                    }
                    ret = PointCloudLib.SetPipePara(lidData.headInfo.PipeSize[0]);
 
                    ret = PointCloudLib.EntryData(frameDatas, frameDatas.Length);
                }
                else
                {
                    MessageBox.Show("打开文件失败!");
 
                }
 
               
                
 
            }
        }
 
 
        /// <summary>
        /// 窗体大小变化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Resize(object sender, EventArgs e)
        {
            vtkPtr = PointCloudLib.BindingHandle(pictureBox1.Handle, pictureBox1.Width, pictureBox1.Height);
 
 
 
        }
 
        /// <summary>
        /// 选择视角
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbViewAngle_SelectedIndexChanged(object sender, EventArgs e)
        {
            int index = this.cmbViewAngle.SelectedIndex;
            if(index>=0)
                PointCloudLib.SetCameraOrientation((PointCloudLib.CamOrientation)index);
        }
 
 
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnFront_Click(object sender, EventArgs e)
        {
            curIndex--;
            PointCloudLib.SelectFrame(curIndex);
        }
 
        private void btnNext_Click(object sender, EventArgs e)
        {
            curIndex++;
            PointCloudLib.SelectFrame(curIndex);
        }
 
 
    }

将依赖的VTK全部复制到运行目录下,然后打开雷达文件测试:

相关推荐

【Docker 新手入门指南】第十章:Dockerfile

Dockerfile是Docker镜像构建的核心配置文件,通过预定义的指令集实现镜像的自动化构建。以下从核心概念、指令详解、最佳实践三方面展开说明,帮助你系统掌握Dockerfile的使用逻...

Windows下最简单的ESP8266_ROTS_ESP-IDF环境搭建与腾讯云SDK编译

前言其实也没啥可说的,只是我感觉ESP-IDF对新手来说很不友好,很容易踩坑,尤其是对业余DIY爱好者搭建环境非常困难,即使有官方文档,或者网上的其他文档,但是还是很容易踩坑,多研究,记住两点就行了,...

python虚拟环境迁移(python虚拟环境conda)

主机A的虚拟环境向主机B迁移。前提条件:主机A和主机B已经安装了virtualenv1.主机A操作如下虚拟环境目录:venv进入虚拟环境:sourcevenv/bin/active(1)记录虚拟环...

Python爬虫进阶教程(二):线程、协程

简介线程线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合和堆栈共同组成。线程的引入减小了程序并发执行时的开销,提高了操作系统的并发性能...

基于网络安全的Docker逃逸(docker)

如何判断当前机器是否为Docker容器环境Metasploit中的checkcontainer模块、(判断是否为虚拟机,checkvm模块)搭配学习教程1.检查根目录下是否存在.dockerenv文...

Python编程语言被纳入浙江高考,小学生都开始学了

今年9月份开始的新学期,浙江省三到九年级信息技术课将同步替换新教材。其中,新初二将新增Python编程课程内容。新高一信息技术编程语言由VB替换为Python,大数据、人工智能、程序设计与算法按照教材...

CentOS 7下安装Python 3.10的完整过程

1.安装相应的编译工具yum-ygroupinstall"Developmenttools"yum-yinstallzlib-develbzip2-develope...

如何在Ubuntu 20.04上部署Odoo 14

Odoo是世界上最受欢迎的多合一商务软件。它提供了一系列业务应用程序,包括CRM,网站,电子商务,计费,会计,制造,仓库,项目管理,库存等等,所有这些都无缝集成在一起。Odoo可以通过几种不同的方式进...

Ubuntu 系统安装 PyTorch 全流程指南

当前环境:Ubuntu22.04,显卡为GeForceRTX3080Ti1、下载显卡驱动驱动网站:https://www.nvidia.com/en-us/drivers/根据自己的显卡型号和...

spark+python环境搭建(python 环境搭建)

最近项目需要用到spark大数据相关技术,周末有空spark环境搭起来...目标spark,python运行环境部署在linux服务器个人通过vscode开发通过远程python解释器执行代码准备...

centos7.9安装最新python-3.11.1(centos安装python环境)

centos7.9安装最新python-3.11.1centos7.9默认安装的是python-2.7.5版本,安全扫描时会有很多漏洞,比如:Python命令注入漏洞(CVE-2015-2010...

Linux系统下,五大步骤安装Python

一、下载Python包网上教程大多是通过官方地址进行下载Python的,但由于国内网络环境问题,会导致下载很慢,所以这里建议通过国内镜像进行下载例如:淘宝镜像http://npm.taobao.or...

centos7上安装python3(centos7安装python3.7.2一键脚本)

centos7上默认安装的是python2,要使用python3则需要自行下载源码编译安装。1.安装依赖yum-ygroupinstall"Developmenttools"...

利用本地数据通过微调方式训练 本地DeepSeek-R1 蒸馏模型

网络上相应的教程基本都基于LLaMA-Factory进行,本文章主要顺着相应的教程一步步实现大模型的微调和训练。训练环境:可自行定义,mac、linux或者window之类的均可以,本文以ma...

【法器篇】天啦噜,库崩了没备份(天啦噜是什么意思?)

背景数据库没有做备份,一天突然由于断电或其他原因导致无法启动了,且设置了innodb_force_recovery=6都无法启动,里面的数据怎么才能恢复出来?本例采用解析建表语句+表空间传输的方式进行...