c#界面中实现点云动态显示

c#界面中实现点云动态显示

我在之前的文章中介绍了关于C#界面中实现点云显示的解决方案,整个过程是基于c++开发的动态库实现的。
如果对本文即将介绍的C#实现点云实时显示感兴趣的可以先前往c#系统界面中可视化PCL点云数据简单了解一下实现的流程。

开发工具:PCL C# C++

在使用本文提供的方法前,需要掌握的知识点有:
1、C++ dll动态库的开发
2、PCL点云读取和点云显示
3、C#与C++动态库之间的调用
4、安装好可使用的PCL工具包

需要说明的是本文介绍的内容是将pcl实时显示的功能嵌入c#开发的软件中,相当于是上一篇文章的升级版。

开发流程(思路与上一篇类似)

0、首先系统启动时采用委托自动实例化dll中用于点云显示的模块(将显示位置的句柄传递给pcl显示窗口)
1、c#端选择提前准备好的点云文件(对于实时显示部分,本文是模拟激光雷达的数据。从指定的文件夹中读取提前采集好的数据然后存储起来,使用的时候读取的点云中一次输出)
2、数据读取完后返回存储数据位置的地址,这个地方是方便我们后期可以从C#端直接读取点云
3、实时显示:将数据的地址再次发送到dll中

代码实现

本文同样提供全套的可执行代码
源码地址:https://download.csdn.net/download/qq_43627520/85024460
有咨询方面的需求可通过邮箱与我联系:1499961892@qq.com
效果展示:
在这里插入图片描述

c#端

在这里插入图片描述
该部分与上一篇文章的差异:
1、增加了实时显示的功能区
2、在文件读取阶段增加了点云文件夹地址提取
委托部分结构没有变化,只是新增了一个句柄

 				IntPtr m_hwnd1 = (IntPtr)0;
                IntPtr m_hwnd2 = (IntPtr)0;
                // 点云界面初始化
                if (0 == type)
                {
                    if (pictureBox1.IsHandleCreated == true && pictureBox1.IsHandleCreated == true)
                    {
                        m_hwnd1 = pictureBox1.Handle;
                        m_hwnd2 = pictureBox2.Handle;
                        dll_ShowPointCloud.EX_SysInit(m_hwnd1, m_hwnd2); //界面点云显示初始化
                    }
                }

其中文件读取部分的代码如下:

private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog file = new OpenFileDialog();

                file.InitialDirectory = ".";
                file.Filter = "所有文件(*.*)|*.*";
                file.ShowDialog();
                if (file.FileName != string.Empty)
                {
                    pcdFile = file.FileName;   //获得文件的绝对路径 pcdFile为选定的pcd文件路 也可以是ply文件
                    pcdPath = Path.GetDirectoryName(file.FileName); //获取文件夹的路径 pcdPath是存储点云的文件夹路径
                    Console.WriteLine("Path:" + pcdPath);
                    label1.Text = pcdFile;
                    int res = dll_ShowPointCloud.EX_PointCloud_Init(pcdFile,pcdPath);
                    Console.WriteLine("returnPath:"+ DLL_ShowPointCloud.readFilePtrPath);
                    if (res == 0)
                    {
                        label2.Text = "点云读取完毕";
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

点云显示动态库开发

在动态库这块相比于之前发生了一个较大的变化,因为涉及到实时显示,所以对整体实现的过程进行了调整。

动态库的调用接口

#define DLL_API extern "C" _declspec(dllexport)
#include "FlowPCLshow.hpp"
#include "pointProcess.hpp"
#include <boost/thread/thread.hpp>

# define POINTTYPE pcl::PointXYZI

PointCloudProcess<POINTTYPE>* pointCloudProcess = NULL;

PointCloudShowWindows<POINTTYPE>* pointCloudShowWindows = NULL;

// 定义点云结构体
struct DataPointXYZI
{
	float x, y, z, i;
}dataPointXYZI;

typedef  struct DataPointCloud
{
	int size = 0;
	std::vector<DataPointXYZI> data;

}dataPointCloud;

//设置钩子函数
DLL_API int __stdcall EX_PCLSHOW_SetCallback(PCLSHOW_PResHook callback, int imode)
{
	try
	{
		pointCloudShowWindows->pCLSHOW_PResHook = callback;
		return 1;
	}
	catch (...)
	{
		return 0;
	}
}


//文件读取
void loadFile(std::string path, pcl::PointCloud<POINTTYPE>::Ptr cloud) {
	std::string fileType = path.substr(path.find_last_of('.') + 1);//获取文件后缀
	if (fileType == "ply") {
		if (pcl::io::loadPLYFile <POINTTYPE>(path, *cloud) == -1)
		{
			std::cout << "Cloud reading .ply failed." << std::endl;
		}
	}
	else if (fileType == "pcd") {
		if (pcl::io::loadPCDFile <POINTTYPE>(path, *cloud) == -1)
		{
			std::cout << "Cloud reading .pcd failed." << std::endl;
		}
	}
	//cout << "point cloud size:" << cloud->size() << endl;
}
//界面初始化
DLL_API int Sys_Init(HWND InitWindow, HWND INPUTWindow) {
	try {
		cout << "Sys_Init start" << endl;
		pointCloudShowWindows = new PointCloudShowWindows<POINTTYPE>();
		int ShowSR = pointCloudShowWindows->PCL_SHOW_Init((HWND)InitWindow); //初始化结果
		int input = pointCloudShowWindows->PCL_SHOW_INPUT((HWND)INPUTWindow); //初始化结果
		cout << "input end" << endl;
		return input;
	}
	catch (...) {
		return 0;
	}
}
// 点云显示动态库
DLL_API int PointCloud_Show(int itype) {
	try {
		if (1 == itype) {
			// 显示输入点云
			pointCloudShowWindows->PCL_Show_InitCloud();
		}
		else {
			//显示彩色点云
			pointCloudShowWindows->PCL_Show_PointCloud();
		}
		return 1;
	}
	catch (...) {
		return 0;
	}
}
// 设置的全局变量
std::vector<pcl::PointCloud<POINTTYPE>>* p;
pcl::PointCloud<POINTTYPE>::Ptr inputCloud(new pcl::PointCloud<POINTTYPE>);
std::vector<pcl::PointCloud<POINTTYPE>> data_cloud;
std::vector<pcl::PointCloud<POINTTYPE>> cloud_list;
int threadPclShowStop = 1;

DLL_API std::vector<pcl::PointCloud<POINTTYPE>>* PointCloud_Init(char* filePathName, char* PathName) {
	//pcl::PointCloud<pcl::PointXYZ>::Ptr inputCloud(new pcl::PointCloud<pcl::PointXYZ>);
	cout << "filePathName:" << filePathName << endl;
	loadFile(filePathName, inputCloud);
	pointCloudShowWindows->PCL_SHOW_WinTypeSet();
	pointCloudShowWindows->PCL_Updata_InitCloud(inputCloud);
	data_cloud.push_back(*inputCloud);
	cout << "filePathName:" << filePathName << endl;
	pointCloudProcess = new PointCloudProcess<POINTTYPE>();
	if (PathName != "") {
		pointCloudProcess->PointCloudList(PathName);
		cloud_list = pointCloudProcess->pointCloudList;
	}
	pointCloudProcess->~PointCloudProcess();
	p = &cloud_list;
	cout << "ptr path:" << p << endl;
	return p;
}
//用于控制点云实时显示的中断过程
DLL_API int Thread_PclShow() {
	threadPclShowStop = 0;
	return threadPclShowStop;
}
//读取指定地址中的点云文件进行实时显示
DLL_API int LoadDataFromPtr(std::vector<pcl::PointCloud<POINTTYPE>>* Path) {
	cout << "LoadDataFromPtr:" << Path << endl;
	pcl::PointCloud<POINTTYPE> input;
	std::vector<pcl::PointCloud<POINTTYPE>> data;
	data = *Path;
	std::cout << "data size:" << data.size() << endl;
	threadPclShowStop = 1;
	int i = 0;
	while(1){
		if (threadPclShowStop == 0) {
			std::cout << "LoadDataFromPtr->input stop!" << endl;
			break;
		}
		input = data.at(i);
		pointCloudShowWindows->PCL_Updata_InputCloud(input.makeShared());
		pointCloudShowWindows->input_viewer->spinOnce(100);
		i++;
		if (i == data.size()) {
			//break;
			i = 0;
		}
	}
	threadPclShowStop = 1;
	return threadPclShowStop;
}
// 结束时释放点云显示空间
DLL_API int StopAllClass() {
	pointCloudShowWindows->~PointCloudShowWindows();
	return 1;
}

上述代码中涉及到的点云读取类 pointCloudProcess 的实现过程如下:


template<typename PointT>
PointCloudProcess<PointT>::PointCloudProcess()
{
}

template<typename PointT>
PointCloudProcess<PointT>::~PointCloudProcess()
{
}
template<typename PointT>
typename pcl::PointCloud<PointT>::Ptr PointCloudProcess<PointT>::load_File(std::string filePath) {
	typename pcl::PointCloud<PointT>::Ptr cloud(new typename pcl::PointCloud<PointT>);
	std::string fileType = filePath.substr(filePath.find_last_of('.') + 1);//获取文件后缀
	if (fileType == "ply") {
		if (pcl::io::loadPLYFile <PointT>(filePath, *cloud) == -1)
		{
			std::cout << "Cloud reading .ply failed." << std::endl;
		}
	}
	else if (fileType == "pcd") {
		if (pcl::io::loadPCDFile <PointT>(filePath, *cloud) == -1)
		{
			std::cout << "Cloud reading .pcd failed." << std::endl;
		}
	}
	cout << "point cloud size:" << cloud->size() << endl;
	return cloud;
}
//获取所有的数据
template<typename PointT>
std::vector<typename pcl::PointCloud<PointT>> PointCloudProcess<PointT>::PointCloudList(std::string dataPath)
{
	std::vector<std::string> pathList = getFilesList(dataPath);
	typename pcl::PointCloud<PointT>::Ptr loadCloud(new typename pcl::PointCloud<PointT>);
	for (int i = 0; i < pathList.size(); i++) {
		std ::string file = pathList[i];
		loadCloud = load_File(file);
		pointCloudList.push_back(*loadCloud);
	}
	return pointCloudList;
}
/** 读取文件夹数据*/
template<typename PointT>
std::vector<std::string> PointCloudProcess<PointT>::getFilesList(std::string Path) {
	std::string PathList = Path + "/*.*";
	std::vector<std::string> allFilePath;
	intptr_t handle;
	_finddata_t findData;
	handle = _findfirst(PathList.c_str(), &findData);
	//检测是否成功
	if (handle == -1) {
		cout << "can not found the file ... " << endl;
		return allFilePath;
	}
	do
	{
		if (findData.attrib & _A_SUBDIR) //是否含有子目录
		{
			//若该子目录为"."或"..",则进行下一次循环,否则输出子目录名,并进入下一次搜索
			if (strcmp(findData.name, ".") == 0 || strcmp(findData.name, "..") == 0)
				continue;

			// 在目录后面加上"\\"和搜索到的目录名进行下一次搜索
			std::string dirNew = Path + "/" + findData.name;
			std::vector<std::string> tempPath = getFilesList(dirNew);
			allFilePath.insert(allFilePath.end(), tempPath.begin(), tempPath.end());

		}
		else //不是子目录,即是文件,则输出文件名和文件的大小
		{
			std::string filePath = Path + "/" + findData.name;
			allFilePath.push_back(filePath);
			cout << filePath << "\t" << findData.size << " bytes.\n";
		}
	} while (_findnext(handle, &findData) == 0);
	return allFilePath;
}

为了使我们写的函数可以适用于不同数据类型的点云(xyz,xyzi,xyzrgb),我们使用了template<typename PointT>来定义类中点云的数据类型

点云显示

在上一篇文章的基础上新增了一个点云初始化的窗口

// 用于更新实时显示的点云
template<typename PointT>
int PointCloudShowWindows<PointT>::PCL_Updata_InputCloud(typename pcl::PointCloud<PointT>::Ptr Cloud) {
	Input_cloud.reset(new typename pcl::PointCloud<PointT>);
	Input_cloud->resize(Cloud->size());
	Input_cloud = Cloud;

	pcl::visualization::PointCloudColorHandlerGenericField<PointT> fildColor(Input_cloud, "z"); // 按照x字段进行渲染
	input_viewer->removeAllPointClouds();
	input_viewer->addPointCloud<PointT>(Input_cloud, fildColor, "sample"); // 显示点云,其中fildColor为颜色显示
	return 0;
}
//初始化实时显示点云的窗口
template<typename PointT>
int PointCloudShowWindows<PointT>::PCL_SHOW_INPUT(HWND InputCloudDispWindow)
{
	try
	{
		input_viewer.reset(new pcl::visualization::PCLVisualizer("Input_Viewer", false));//初始化viewer对象

		//m_viewer->setShowFPS(false);

		input_viewer->addCoordinateSystem(1.0);

		input_viewer->setBackgroundColor(0, 0, 0);//设置背景颜色

		input_viewer->initCameraParameters();//初始化相机的参数

		input_viewer_win = input_viewer->getRenderWindow();//将view中的渲染窗口的句柄传递给vtk window
		input_viewer_iren = vtkRenderWindowInteractor::New();//初始化vtkwindow交互的对象 
		input_viewer->resetCamera();//使点云显示在屏幕中间,并绕中心操作
		RECT Input_Window;

		::GetClientRect(InputCloudDispWindow, &Input_Window);

		input_viewer_i_ui_w = Input_Window.right - Input_Window.left;
		input_viewer_i_ui_h = Input_Window.bottom - Input_Window.top;

		input_viewer_win->SetSize(input_viewer_i_ui_w, input_viewer_i_ui_h);//根据当前窗口的大小设置vtk 窗口的大小
		//m_win->SetSize(300, 300);//根据当前窗口的大小设置vtk 窗口的大小

		input_viewer_win->SetParentId(InputCloudDispWindow);//设置vtk窗口的句柄
		//input_viewer_iren->SetRenderWindow(input_viewer_win);//将vtk交互对象与vtk window绑定 
		input_viewer->createInteractor();
		input_viewer_win->Render();//开始渲染
		return 1;

	}
	catch (...)
	{
		return 0;
	}

}


template<typename PointT>
int PointCloudShowWindows<PointT>::PCL_SHOW_WinTypeSet() {
	Init_viewer->setCameraPosition(11.6749, 17.4117, 27.2977, -0.404992, -0.6874, 0.602879);
	Init_viewer->setCameraClipDistances(0.160147, 160.147);

	input_viewer->setCameraPosition(11.6749, 17.4117, 27.2977, -0.404992, -0.6874, 0.602879);
	input_viewer->setCameraClipDistances(0.160147, 160.147);
	return 0;
}
template<typename PointT>
int PointCloudShowWindows<PointT>::PCL_Show_InitCloud()
{
	try
	{
		Init_viewer->removeAllPointClouds();//将前一次点云移除  
		pcl::visualization::PointCloudColorHandlerGenericField<PointT> fildColor(Init_cloud, "z"); // 按照x字段进行渲染
		Init_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample"); // 用于改变显示点云的尺寸。用户可以利用该方法控制点云在视窗中的显示方式。
		Init_viewer->addPointCloud<PointT>(Init_cloud, fildColor, "sample"); // 显示点云,其中fildColor为颜色显示

		Init_viewer->spinOnce();

		return 1;
	}
	catch (...)
	{
		return 0;
	}
}

最终效果

C#窗体实时显示点云

  • 4
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LuLaDe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值