可视化(Visualization)是利用计算机图形学和图像处理技术,将数据转换成图形或图像在屏幕显示出来,并且进行交互处理的理论、方法和技术。
PCL中pcl_visualization库中提供了可视化相关的数据结构和组件,其主要是为了可视化其他模块算法处理后的结果,可直观的反馈给用户。其依赖于pcl_common、pcl_range_image、pcl_kdtree、pcl_IO模块以及VTK外部开源可视化库。下面给出2个常用的可视化类。
1 简单点云可视化
如果只是想简单的程序中的点云图像,只需使用下面的几行代码即可
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBA>);
pcl::io::loadPCDFile("D:\\code\\pcl\\PointCloudData\\pcd\\milk.pcd", *cloud);
pcl::visualization::CloudViewer viewer("Cloud Viewer"); //创建对象
viewer.showCloud(cloud);
while (!viewer.wasStopped())
{
//do something...
}
如果要在多线程中显示点云,那么需要使用两个特殊函数,这样可以避免并发的可视化问题
//函数原型
void runOnVisualizationThreadOnce (VizCallable x);
//该函数在可视化的时候只调用一次
//viewerOneOff是回调函数
viewer.runOnVisualizationThreadOnce(viewerOneOff);
//函数原型
void runOnVisualizationThread (VizCallable x, const std::string& key = "callable");
//该函数在渲染输出时每次都调用
//viewerPsycho是回调函数
viewer.runOnVisualizationThread(viewerPsycho);
#include
#include
#include
#include
int user_data;
void
viewerOneOff(pcl::visualization::PCLVisualizer& viewer)
{
viewer.setBackgroundColor(1.0, 0.5, 1.0); //背景颜色设置
pcl::PointXYZ o; //球的圆心位置
o.x = 1.0;
o.y = 0;
o.z = 0;
viewer.addSphere(o, 0.25, "sphere", 0); //添加圆球对象
std::cout << "i only run once" << std::endl;
static unsigned count = 0;
std::stringstream ss;
ss << "loop one time: " << count++;
viewer.addText(ss.str(), 100, 200, "text", 0);
}
void
viewerPsycho(pcl::visualization::PCLVisualizer& viewer)
{
static unsigned count2 = 0;
std::stringstream ss;
ss << "Once per viewer loop: " << count2++;
viewer.removeShape("text2", 0);
viewer.addText(ss.str(), 200, 300, "text2", 0); //添加文本
//FIXME: possible race condition here:
user_data++;
}
int
main()
{
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGBA>);
pcl::io::loadPCDFile("D:\\code\\pcl\\PointCloudData\\pcd\\milk.pcd", *cloud);
pcl::visualization::CloudViewer viewer("Cloud Viewer"); //创建对象
//blocks until the cloud is actually rendered
viewer.showCloud(cloud);
//use the following functions to get access to the underlying more advanced/powerful
//PCLVisualizer
//This will only get called once
//该函数在可视化的时候只调用一次
//viewerOneOff是回调函数
viewer.runOnVisualizationThreadOnce(viewerOneOff);
//This will get called once per visualization iteration
//该函数在渲染输出时每次都调用
//viewerPsycho是回调函数
viewer.runOnVisualizationThread(viewerPsycho);
while (!viewer.wasStopped())
{
//you can also do cool processing here
//FIXME: Note that this is running in a separate thread from viewerPsycho
//and you should guard against race conditions yourself...
user_data++;
}
return 0;
}
从结果中可以看到:
PCLvisualizaer可视化类是PCL中功能最全的可视化类,与CloudViewer可视化类相比,PCLvisualizaer使用更复杂,但同时也具有更为全面的功能,如显示法线,绘制多种形状与多个窗口等。
//输入点云的类型为pcl::PointXYZ
pcl::visualization::PCLVisualizer::Ptr simpleVis(pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud)
{
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0); //设置点云背景颜色,0,0,0为黑色
viewer->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud"); //将点云添加到视窗对象中
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud"); //设置点云的属性, 这里代表设置点云的每个点的大小为1
viewer->addCoordinateSystem(1.0); //设置点云显示的尺寸大小
viewer->initCameraParameters(); //设置相机参数,使用默认的角度和方向观察点云
return (viewer);
}
当然点云的显示还少不了一段代码
while (!viewer->wasStopped())
{
viewer->spinOnce(100); //调用spinOnce给视窗处理事件的时间
std::this_thread::sleep_for(100ms);
}
这部分代码与simpleVis中的基本机一样,需要注意的是传入的点云是带有颜色特征的
//输入点云的类型为pcl::PointXYZRGB,表示输入点云可能带有颜色特征
pcl::visualization::PCLVisualizer::Ptr rgbVis(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud)
{
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb(cloud); //显示点云自带的颜色
//viewer->addPointCloud(cloud, rgb, "sample cloud");
viewer->addPointCloud<pcl::PointXYZRGB>(cloud, "sample cloud"); //直接这一行代码,能够实现上面两行代码的功能
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "sample cloud"); //属性设置,设置点大小为5
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
return (viewer);
}
由于是自定义点云颜色,所以这里的传入参数类型又变回了pcl::PointXYZ
pcl::visualization::PCLVisualizer::Ptr customColourVis(pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud)
{
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color(cloud, 0, 255, 0); //自定义点云颜色特征,顺序是RGB,这里将点云设置成大家喜欢的绿色
viewer->addPointCloud<pcl::PointXYZ>(cloud, single_color, "sample cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
return (viewer);
}
点云法线特征是点云非常重要的基础特征
//这里是直接选择传入计算好的点云法线
pcl::visualization::PCLVisualizer::Ptr normalsVis(
pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud, pcl::PointCloud<pcl::Normal>::ConstPtr normals)
{
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb(cloud);
viewer->addPointCloud<pcl::PointXYZRGB>(cloud, rgb, "sample cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");
//每10个点显示一个法线,每个法线的长度为0.05
viewer->addPointCloudNormals<pcl::PointXYZRGB, pcl::Normal>(cloud, normals, 10, 0.05, "normals");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
return (viewer);
}
法线的计算
pcl::NormalEstimation<pcl::PointXYZRGB, pcl::Normal> ne; //
ne.setInputCloud(point_cloud_ptr);
pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZRGB>());
ne.setSearchMethod(tree); //搜索方法
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals1(new pcl::PointCloud<pcl::Normal>);
ne.setRadiusSearch(0.05); //搜索半径
ne.compute(*cloud_normals1); //计算的结果保存在cloud_normals1
pcl::visualization::PCLVisualizer::Ptr shapesVis(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud)
{
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb(cloud);
viewer->addPointCloud<pcl::PointXYZRGB>(cloud, rgb, "sample cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
//在点云中添加一条从点云第一个点到最后一个点的线段
viewer->addLine<pcl::PointXYZRGB>(cloud->points[0],
cloud->points[cloud->size() - 1], "line");
//在点云中添加一个以第一个点为中心,半径为0.2的球体,(0.5,0.5,0.0)为颜色
viewer->addSphere(cloud->points[0], 0.2, 0.5, 0.5, 0.0, "sphere");
//在点云中添加一个平面
pcl::ModelCoefficients coeffs;
coeffs.values.push_back(0.0);
coeffs.values.push_back(0.0);
coeffs.values.push_back(1.0);
coeffs.values.push_back(0.0);
viewer->addPlane(coeffs, "plane");
coeffs.values.clear();
//在点云中添加一个锥型
coeffs.values.push_back(0.3);
coeffs.values.push_back(0.3);
coeffs.values.push_back(0.0);
coeffs.values.push_back(0.0);
coeffs.values.push_back(1.0);
coeffs.values.push_back(0.0);
coeffs.values.push_back(5.0);
viewer->addCone(coeffs, "cone");
return (viewer);
}
pcl::visualization::PCLVisualizer::Ptr viewportsVis(
pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud, pcl::PointCloud<pcl::Normal>::ConstPtr normals1, pcl::PointCloud<pcl::Normal>::ConstPtr normals2)
{
// --------------------------------------------------------
// -----Open 3D viewer and add point cloud and normals-----
// --------------------------------------------------------
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->initCameraParameters();
int v1(0); //用于区分不同的视窗
viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1); //创建视窗1, 左边
viewer->setBackgroundColor(0, 0, 0, v1);
viewer->addText("Radius: 0.01", 10, 10, "v1 text", v1);
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb(cloud);
viewer->addPointCloud<pcl::PointXYZRGB>(cloud, rgb, "sample cloud1", v1);
int v2(0);
viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2); //创建视窗2,右边
viewer->setBackgroundColor(0.3, 0.3, 0.3, v2);
viewer->addText("Radius: 0.1", 10, 10, "v2 text", v2);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZRGB> single_color(cloud, 0, 255, 0);
viewer->addPointCloud<pcl::PointXYZRGB>(cloud, single_color, "sample cloud2", v2);
//不同的点云需要单独设置属性
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud1");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud2");
viewer->addCoordinateSystem(1.0);
//为不同的点云添加法线
viewer->addPointCloudNormals<pcl::PointXYZRGB, pcl::Normal>(cloud, normals1, 10, 0.05, "normals1", v1);
viewer->addPointCloudNormals<pcl::PointXYZRGB, pcl::Normal>(cloud, normals2, 10, 0.05, "normals2", v2);
return (viewer);
}
pcl::visualization::PCLVisualizer::Ptr interactionCustomizationVis()
{
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
viewer->addCoordinateSystem(1.0);
viewer->registerKeyboardCallback(keyboardEventOccurred, (void*)viewer.get()); //注册键盘事件
viewer->registerMouseCallback(mouseEventOccurred, (void*)viewer.get()); //注册鼠标事件
return (viewer);
}
具体的键盘事件与鼠标事件
unsigned int text_id = 0; //全局变量,供键盘事件与鼠标事件一起使用
//键盘事件主要完成功能,当r键按下的事件,将屏幕上的文本清除
void keyboardEventOccurred(const pcl::visualization::KeyboardEvent& event,
void* viewer_void)
{
pcl::visualization::PCLVisualizer* viewer = static_cast<pcl::visualization::PCLVisualizer*> (viewer_void);
//判断键盘r键是否按下
if (event.getKeySym() == "r" && event.keyDown())
{
std::cout << "r was pressed => removing all text" << std::endl;
char str[512];
for (unsigned int i = 0; i < text_id; ++i)
{
sprintf(str, "text#%03d", i);
viewer->removeShape(str); //移除文本
}
text_id = 0;
}
}
//鼠标事件主要完成功能,当鼠标左键按下的时候,在按下的位置显示一段文本,并在控制台将该位置的坐标打印出来
void mouseEventOccurred(const pcl::visualization::MouseEvent& event,
void* viewer_void)
{
pcl::visualization::PCLVisualizer* viewer = static_cast<pcl::visualization::PCLVisualizer*> (viewer_void);
//判断鼠标左键是否按下
if (event.getButton() == pcl::visualization::MouseEvent::LeftButton &&
event.getType() == pcl::visualization::MouseEvent::MouseButtonRelease)
{
//打印坐标
std::cout << "Left mouse button released at position (" << event.getX() << ", " << event.getY() << ")" << std::endl;
char str[512];
sprintf(str, "text#%03d", text_id++);
viewer->addText("clicked here", event.getX(), event.getY(), str);
}
}
最后这里有一篇对点云上色写的比较的详细的博客
https://blog.csdn.net/SGL_LGS/article/details/106300195
/* \author Geoffrey Biggs */
#include
#include
#include
#include
#include
#include
#include
using namespace std::chrono_literals;
// --------------
// -----Help-----
// --------------
//帮助说明
void printUsage(const char* progName) //帮助说明
{
std::cout << "\n\nUsage: " << progName << " [options]\n\n"
<< "Options:\n"
<< "-------------------------------------------\n"
<< "-h this help\n"
<< "-s Simple visualisation example\n"
<< "-r RGB colour visualisation example\n"
<< "-c Custom colour visualisation example\n"
<< "-n Normals visualisation example\n"
<< "-a Shapes visualisation example\n"
<< "-v Viewports example\n"
<< "-i Interaction Customization example\n"
<< "\n\n";
}
pcl::visualization::PCLVisualizer::Ptr simpleVis(pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud)
{
// --------------------------------------------
// -----Open 3D viewer and add point cloud-----
// --------------------------------------------
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0); //设置点云背景颜色,0,0,0为黑色
viewer->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud"); //将点云添加到视窗对象中
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "sample cloud");
viewer->addCoordinateSystem(1.0); //设置点云显示的尺寸大小
viewer->initCameraParameters(); //设置相机参数,使用默认的角度和方向观察点云
return (viewer);
}
pcl::visualization::PCLVisualizer::Ptr rgbVis(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud)
{
// --------------------------------------------
// -----Open 3D viewer and add point cloud-----
// --------------------------------------------
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb(cloud); //显示点云自带的颜色
//viewer->addPointCloud(cloud, rgb, "sample cloud");
viewer->addPointCloud<pcl::PointXYZRGB>(cloud, "sample cloud"); //直接这一行代码,能够实现上面两行代码的功能
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "sample cloud"); //属性设置,设置点大小为5
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
return (viewer);
}
pcl::visualization::PCLVisualizer::Ptr customColourVis(pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud)
{
// --------------------------------------------
// -----Open 3D viewer and add point cloud-----
// --------------------------------------------
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color(cloud, 0, 255, 0); //自定义点云颜色特征,顺序是RGB,这里将点云设置成大家喜欢的绿色
viewer->addPointCloud<pcl::PointXYZ>(cloud, single_color, "sample cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
return (viewer);
}
pcl::visualization::PCLVisualizer::Ptr normalsVis(
pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud, pcl::PointCloud<pcl::Normal>::ConstPtr normals)
{
// --------------------------------------------------------
// -----Open 3D viewer and add point cloud and normals-----
// --------------------------------------------------------
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb(cloud);
viewer->addPointCloud<pcl::PointXYZRGB>(cloud, rgb, "sample cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");
//每10个点显示一个法线,每个法线的长度为0.05
viewer->addPointCloudNormals<pcl::PointXYZRGB, pcl::Normal>(cloud, normals, 10, 0.05, "normals");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
return (viewer);
}
pcl::visualization::PCLVisualizer::Ptr shapesVis(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud)
{
// --------------------------------------------
// -----Open 3D viewer and add point cloud-----
// --------------------------------------------
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb(cloud);
viewer->addPointCloud<pcl::PointXYZRGB>(cloud, rgb, "sample cloud");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
//------------------------------------
//-----Add shapes at cloud points-----
//------------------------------------
//在点云中添加一条从点云第一个点到最后一个点的线段
viewer->addLine<pcl::PointXYZRGB>(cloud->points[0],
cloud->points[cloud->size() - 1], "line");
//在点云中添加一个以第一个点为中心,半径为0.2的球体,(0.5,0.5,0.0)为颜色
viewer->addSphere(cloud->points[0], 0.2, 0.5, 0.5, 0.0, "sphere");
//---------------------------------------
//-----Add shapes at other locations-----
//---------------------------------------
//在点云中添加一个平面
pcl::ModelCoefficients coeffs;
coeffs.values.push_back(0.0);
coeffs.values.push_back(0.0);
coeffs.values.push_back(1.0);
coeffs.values.push_back(0.0);
viewer->addPlane(coeffs, "plane");
coeffs.values.clear();
//在点云中添加一个锥型
coeffs.values.push_back(0.3);
coeffs.values.push_back(0.3);
coeffs.values.push_back(0.0);
coeffs.values.push_back(0.0);
coeffs.values.push_back(1.0);
coeffs.values.push_back(0.0);
coeffs.values.push_back(5.0);
viewer->addCone(coeffs, "cone");
return (viewer);
}
pcl::visualization::PCLVisualizer::Ptr viewportsVis(
pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud, pcl::PointCloud<pcl::Normal>::ConstPtr normals1, pcl::PointCloud<pcl::Normal>::ConstPtr normals2)
{
// --------------------------------------------------------
// -----Open 3D viewer and add point cloud and normals-----
// --------------------------------------------------------
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->initCameraParameters();
int v1(0); //用于区分不同的视窗
viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1); //创建视窗1,左边
viewer->setBackgroundColor(0, 0, 0, v1);
viewer->addText("Radius: 0.01", 10, 10, "v1 text", v1);
pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB> rgb(cloud);
viewer->addPointCloud<pcl::PointXYZRGB>(cloud, rgb, "sample cloud1", v1);
int v2(0);
viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2); //创建视窗2,右边
viewer->setBackgroundColor(0.3, 0.3, 0.3, v2);
viewer->addText("Radius: 0.1", 10, 10, "v2 text", v2);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZRGB> single_color(cloud, 0, 255, 0);
viewer->addPointCloud<pcl::PointXYZRGB>(cloud, single_color, "sample cloud2", v2);
//不同的点云需要单独设置属性
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud1");
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "sample cloud2");
viewer->addCoordinateSystem(1.0);
//为不同的点云添加法线
viewer->addPointCloudNormals<pcl::PointXYZRGB, pcl::Normal>(cloud, normals1, 10, 0.05, "normals1", v1);
viewer->addPointCloudNormals<pcl::PointXYZRGB, pcl::Normal>(cloud, normals2, 10, 0.05, "normals2", v2);
return (viewer);
}
unsigned int text_id = 0;
void keyboardEventOccurred(const pcl::visualization::KeyboardEvent& event,
void* viewer_void)
{
//将void* 类型的viewer转换为pcl::visualization::PCLVisualizer*
pcl::visualization::PCLVisualizer* viewer = static_cast<pcl::visualization::PCLVisualizer*> (viewer_void);
//判断键盘r键是否按下
if (event.getKeySym() == "r" && event.keyDown())
{
std::cout << "r was pressed => removing all text" << std::endl;
char str[512];
for (unsigned int i = 0; i < text_id; ++i)
{
sprintf(str, "text#%03d", i);
viewer->removeShape(str); //移除文本
}
text_id = 0;
}
}
void mouseEventOccurred(const pcl::visualization::MouseEvent& event,
void* viewer_void)
{
//将void* 类型的viewer转换为pcl::visualization::PCLVisualizer*
pcl::visualization::PCLVisualizer* viewer = static_cast<pcl::visualization::PCLVisualizer*> (viewer_void);
//判断鼠标左键是否按下
if (event.getButton() == pcl::visualization::MouseEvent::LeftButton &&
event.getType() == pcl::visualization::MouseEvent::MouseButtonRelease)
{
//打印坐标
std::cout << "Left mouse button released at position (" << event.getX() << ", " << event.getY() << ")" << std::endl;
char str[512];
sprintf(str, "text#%03d", text_id++);
viewer->addText("clicked here", event.getX(), event.getY(), str);
}
}
pcl::visualization::PCLVisualizer::Ptr interactionCustomizationVis()
{
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
viewer->addCoordinateSystem(1.0);
//注册键盘事件,第二个参数是回调函数的第二个参数,将viewer自己传过去,需要传过去的类型为void*
viewer->registerKeyboardCallback(keyboardEventOccurred, (void*)viewer.get());
//注册鼠标事件,第二个参数是回调函数的第二个参数,将viewer自己传过去,需要传过去的类型为void*
viewer->registerMouseCallback(mouseEventOccurred, (void*)viewer.get());
return (viewer);
}
// --------------
// -----Main-----
// --------------
int
main(int argc, char** argv)
{
// --------------------------------------
// -----Parse Command Line Arguments-----
// --------------------------------------
if (pcl::console::find_argument(argc, argv, "-h") >= 0)
{
printUsage(argv[0]);
return 0;
}
bool simple(false), rgb(false), custom_c(false), normals(false),
shapes(false), viewports(false), interaction_customization(false);
//命令行参数识别,以及相应的处理
if (pcl::console::find_argument(argc, argv, "-s") >= 0)
{
simple = true;
std::cout << "Simple visualisation example\n";
}
else if (pcl::console::find_argument(argc, argv, "-c") >= 0)
{
custom_c = true;
std::cout << "Custom colour visualisation example\n";
}
else if (pcl::console::find_argument(argc, argv, "-r") >= 0)
{
rgb = true;
std::cout << "RGB colour visualisation example\n";
}
else if (pcl::console::find_argument(argc, argv, "-n") >= 0)
{
normals = true;
std::cout << "Normals visualisation example\n";
}
else if (pcl::console::find_argument(argc, argv, "-a") >= 0)
{
shapes = true;
std::cout << "Shapes visualisation example\n";
}
else if (pcl::console::find_argument(argc, argv, "-v") >= 0)
{
viewports = true;
std::cout << "Viewports example\n";
}
else if (pcl::console::find_argument(argc, argv, "-i") >= 0)
{
interaction_customization = true;
std::cout << "Interaction Customization example\n";
}
else
{
printUsage(argv[0]);
return 0;
}
// ------------------------------------
// -----Create example point cloud-----
// ------------------------------------
pcl::PointCloud<pcl::PointXYZ>::Ptr basic_cloud_ptr(new pcl::PointCloud<pcl::PointXYZ>);
pcl::PointCloud<pcl::PointXYZRGB>::Ptr point_cloud_ptr(new pcl::PointCloud<pcl::PointXYZRGB>);
std::cout << "Generating example point clouds.\n\n";
// We're going to make an ellipse extruded along the z-axis. The colour for
// the XYZRGB cloud will gradually go from red to green to blue.
std::uint8_t r(255), g(15), b(15);
//点云创建
for (float z(-1.0); z <= 1.0; z += 0.05)
{
for (float angle(0.0); angle <= 360.0; angle += 5.0)
{
pcl::PointXYZ basic_point;
basic_point.x = 0.5 * std::cos(pcl::deg2rad(angle));
basic_point.y = sinf(pcl::deg2rad(angle));
basic_point.z = z;
basic_cloud_ptr->points.push_back(basic_point);
pcl::PointXYZRGB point;
point.x = basic_point.x;
point.y = basic_point.y;
point.z = basic_point.z;
std::uint32_t rgb = (static_cast<std::uint32_t>(r) << 16 |
static_cast<std::uint32_t>(g) << 8 | static_cast<std::uint32_t>(b));
point.rgb = *reinterpret_cast<float*>(&rgb);
point_cloud_ptr->points.push_back(point);
}
if (z < 0.0)
{
r -= 12;
g += 12;
}
else
{
g -= 12;
b += 12;
}
}
basic_cloud_ptr->width = (int)basic_cloud_ptr->points.size();
basic_cloud_ptr->height = 1;
point_cloud_ptr->width = (int)point_cloud_ptr->points.size();
point_cloud_ptr->height = 1;
// ----------------------------------------------------------------
// -----Calculate surface normals with a search radius of 0.05-----
// ----------------------------------------------------------------
//计算法线1
pcl::NormalEstimation<pcl::PointXYZRGB, pcl::Normal> ne;
ne.setInputCloud(point_cloud_ptr);
pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree(new pcl::search::KdTree<pcl::PointXYZRGB>());
ne.setSearchMethod(tree);
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals1(new pcl::PointCloud<pcl::Normal>);
ne.setRadiusSearch(0.05);
ne.compute(*cloud_normals1);
// ---------------------------------------------------------------
// -----Calculate surface normals with a search radius of 0.1-----
// ---------------------------------------------------------------
//计算法线2
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals2(new pcl::PointCloud<pcl::Normal>);
ne.setRadiusSearch(0.1);
ne.compute(*cloud_normals2);
//实际显示的viewer
pcl::visualization::PCLVisualizer::Ptr viewer;
if (simple)
{
viewer = simpleVis(basic_cloud_ptr);
}
else if (rgb)
{
viewer = rgbVis(point_cloud_ptr);
}
else if (custom_c)
{
viewer = customColourVis(basic_cloud_ptr);
}
else if (normals)
{
viewer = normalsVis(point_cloud_ptr, cloud_normals2);
}
else if (shapes)
{
viewer = shapesVis(point_cloud_ptr);
}
else if (viewports)
{
viewer = viewportsVis(point_cloud_ptr, cloud_normals1, cloud_normals2);
}
else if (interaction_customization)
{
viewer = interactionCustomizationVis();
}
//--------------------
// -----Main loop-----
//--------------------
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
std::this_thread::sleep_for(100ms);
}
}
由于我是在Windows下编写程序的,直接vs2019中运行,会出现如下页面
这是因为直接运行的时候无法给main函数传参,可有下述解决办法:
PCLPlotter提供了一个直接简单的绘图接口,可以绘制许多类型的二维图形,包括多项式函数和特征直方图,比如FPFH等。
利用PCLPlotter绘制图形,通常分为以下4步:第一步,声明绘图对象PLCPlotter;第二步,利用addPlotData()函数,添加绘图所需的函数或数据;第三步,添加窗口特性,可以调整窗口大小和设置标题(非必须);第四步,显示绘图。
使用addPlotData()函数为plotter对象添加数据
addPlotData()函数接受多种不同类型的输入
双数值输入
vector<pair<double, double>> data;
...对data的赋值操作...
plotter->addPlotDate(data, "data");
文件输入
plotter->addPlotData("data.txt");
多项式函数
//定义多项式
//多项式的定义规则,以下面两行为例,定义一个3维向量,全部赋初值为0
//func2[0],func2[1],func2[2]分别代表多项式x^0,x^1,x^2前面的系数
std::vector<double> func2(3, 0);
func2[2] = 1; //y = x^2 y = 0 + 0*x + 1*x^2
plotter->addPlotData(func2, -10, 10, "y = x^2"); //添加数据
商函数
//根据前面所说定义两个多项式
std::vector<double> func1(1, 0);
func1[0] = 1; //y = 1
std::vector<double> func2(3, 0);
func2[2] = 1; //y = x^2 y = 0 + 0*x + 1*x^2
//接着插入数据
/*这里使用make_pair生成一个pair
* pair中的前面一个参数func1代表分数的分子,func2代表分母
* 其他参数依次为:x轴负向范围,x轴正向返回,该图的图例说明,绘制100个点,该图使用点来绘制
*/
plotter->addPlotData(std::make_pair(func1, func2), -10, 10, "y = 1/x^2", 100, vtkChart::POINTS);
自定义函数 y=f(x)
//自定义哈数,其中value通过x轴的值隐式的传进去
double identity(double val)
{
return val;
}
//添加数据
plotter->addPlotData(identity, -10, 10, "identity");
//1.使用addPlotData()函数为plotter对象添加数据
//2.addPlotData()函数接受多种不同类型的输入
#include //plotter头文件
#include
#include
#include
#include //for std::abs()
using namespace std;
using namespace pcl::visualization;
void
generateData(double* ax, double* acos, double* asin, int numPoints)
{
double inc = 7.5 / (numPoints - 1);
for (int i = 0; i < numPoints; ++i)
{
ax[i] = i * inc;
acos[i] = std::cos(i * inc);
asin[i] = sin(i * inc);
}
}
//.....................callback functions defining Y= f(X)....................
//自定义的y=f(x)的函数
double
step(double val)
{
if (val > 0)
return (double)(int)val;
else
return (double)((int)val - 1);
}
double
identity(double val)
{
return val;
}
//............................................................................
int
main(int argc, char* argv[])
{
//defining a plotter
PCLPlotter* plotter = new PCLPlotter("My Plotter");
//setting some properties
plotter->setShowLegend(true); //显示图例,即右上角的文本
//generating point correspondances
int numPoints = 69;
double ax[100], acos[100], asin[100];
generateData(ax, acos, asin, numPoints);
//adding plot data
plotter->addPlotData(ax, acos, numPoints, "cos");
plotter->addPlotData(ax, asin, numPoints, "sin");
//display for 2 seconds
plotter->spinOnce(3000);
plotter->clearPlots();
//...................plotting implicit functions and custom callbacks....................
//make a fixed axis
//设置y轴的范围
plotter->setYRange(-20, 20);
//defining polynomials
//定义多项式
std::vector<double> func1(1, 0);
func1[0] = 1; //y = 1
std::vector<double> func2(3, 0);
func2[2] = 1; //y = x^2 y = 0 + 0*x + 1*x^2
std::vector<double> func3(4, 0);
func3[3] = 1; //y = x^3 y = 0 + 0*x + 0*x^2 + 1*x^3
//func1作为分子,func2作为分母, -10,10是x轴的范围,"y = 1/x^2",为显示标签,位于右上角, 100为显示的点的个数,vtkChart::POINTS为以点来绘制
plotter->addPlotData(std::make_pair(func1, func2), -10, 10, "y = 1/x^2", 100, vtkChart::POINTS);
plotter->spinOnce(2000);
plotter->addPlotData(func2, -10, 10, "y = x^2");
plotter->spinOnce(2000);
plotter->addPlotData(func3, -10, 10, "y = x^3");
plotter->spinOnce(2000);
//callbacks
plotter->addPlotData(identity, -10, 10, "identity");
plotter->spinOnce(2000);
plotter->addPlotData(std::abs, -10, 10, "abs");
plotter->spinOnce(2000);
plotter->addPlotData(step, -10, 10, "step", 100, vtkChart::POINTS);
plotter->spinOnce(2000);
plotter->clearPlots();
//........................A simple animation..............................
//下面运行的结果是一段动画
//图形在-100x^2 至 100x^2之间变化
std::vector<double> fsq(3, 0);
fsq[2] = -100; //y = x^2
while (plotter->wasStopped())
{
if (fsq[2] == 100) fsq[2] = -100;
fsq[2]++;
char str[50];
sprintf(str, "y = %dx^2", (int)fsq[2]);
plotter->addPlotData(fsq, -10, 10, str);
plotter->spinOnce(100);
plotter->clearPlots();
}
return 1;
}