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#窗体实时显示点云