Laser point cloud dynamic library package for VTK learning (drainage pipe)

        At present, laser point clouds are applied in all walks of life, including the current very popular autopilot industry. I am currently in the drainage pipe inspection industry, so I encapsulated the point cloud library used in drainage pipes. After the point cloud data measured by the lidar is stored, the coordinate points are parsed out, and then passed to the function entry to obtain the 3D point cloud model.

        There are many tools for processing point cloud data. Instead of using OpenGL and D3D directly, we choose vtk, which is better packaged and easy to use. This example is based on vtk9.0+vs2019, and the packaged library uses C# to call and test. Not much nonsense, just go to the code:

        The first is the vtk.h header file, where all the vtk header files to be used are included.

#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;//总帧数

        Then the external call header file ZYPointCloudLib.h declares the function:

#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();
}
 
 
 

        Compile and generate ZYPointCloudLib.dll. The next step is to test, use winform test:

        First import the C++ function:

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); 

    }
}

        Then create a PictureBox and Button in Form1, as shown below:

Background code:

   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);
        }


    }

Copy all the dependent VTK to the running directory, and then open the radar file for testing:

Guess you like

Origin blog.csdn.net/whf227/article/details/120780347