点云可视化

点云的可视化

可视化(Visualization)是利用计算机图形学和图像处理技术,将数据转换成图形或图像在屏幕显示出来,并且进行交互处理的理论、方法和技术。

PCL中pcl_visualization库中提供了可视化相关的数据结构和组件,其主要是为了可视化其他模块算法处理后的结果,可直观的反馈给用户。其依赖于pcl_common、pcl_range_image、pcl_kdtree、pcl_IO模块以及VTK外部开源可视化库。下面给出2个常用的可视化类。

1 简单点云可视化

  • #include //头文件
  • pcl::visualization::CloudViewer //类名

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

1.2 完整代码

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

1.3 运行结果

点云可视化_第1张图片

从结果中可以看到:

  • viewerOneOff函数中的count2只执行了一次
  • 而viewerPsycho函数中的count是不断的在累加的

2 PCLvisualizer可视类

PCLvisualizaer可视化类是PCL中功能最全的可视化类,与CloudViewer可视化类相比,PCLvisualizaer使用更复杂,但同时也具有更为全面的功能,如显示法线,绘制多种形状与多个窗口等。

2.1 单个点云可视化

//输入点云的类型为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);
}

2.2 点云颜色可视化

  • 显示点云自带的颜色

这部分代码与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);
}

2.3 点云法线可视化

点云法线特征是点云非常重要的基础特征

//这里是直接选择传入计算好的点云法线
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

2.4 绘制形状

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

2.5 多视窗显示

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

2.6 自定义交互事件

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

2.7 完整代码

/* \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);
    }
}

2.8运行结果

由于我是在Windows下编写程序的,直接vs2019中运行,会出现如下页面

点云可视化_第2张图片

这是因为直接运行的时候无法给main函数传参,可有下述解决办法:

  • vs中按下ctrl + ` 打开终端
  • 使用cd命令进入到可执行程序的目录下,具体位置会在上图中的红色方框中出现

点云可视化_第3张图片

  • 执行命令 .\PCLTutorials.exe [option],如下图

在这里插入图片描述

  • 最后的运行效果,即simpleVis的显示效果

点云可视化_第4张图片

  • 当然还有其他选项,这里就不再一一展示了,感兴趣的可以自己每个都试一下

3 PCLPlotter 直方图可视化

PCLPlotter提供了一个直接简单的绘图接口,可以绘制许多类型的二维图形,包括多项式函数和特征直方图,比如FPFH等。

利用PCLPlotter绘制图形,通常分为以下4步:第一步,声明绘图对象PLCPlotter;第二步,利用addPlotData()函数,添加绘图所需的函数或数据;第三步,添加窗口特性,可以调整窗口大小和设置标题(非必须);第四步,显示绘图。

3.1 关键代码说明

  1. 使用addPlotData()函数为plotter对象添加数据

  2. addPlotData()函数接受多种不同类型的输入

    1. 双数值输入

      1. vector<pair<double, double>> data;
        ...对data的赋值操作...
        plotter->addPlotDate(data, "data");
        
    2. 文件输入

      1. plotter->addPlotData("data.txt");
        
    3. 多项式函数

      1. //定义多项式
        //多项式的定义规则,以下面两行为例,定义一个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");	//添加数据
        
    4. 商函数

      1. //根据前面所说定义两个多项式
        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);
        
    5. 自定义函数 y=f(x)

      1. //自定义哈数,其中value通过x轴的值隐式的传进去
        double identity(double val)
        {
            return val;
        }
        //添加数据
        plotter->addPlotData(identity, -10, 10, "identity");
        

//1.使用addPlotData()函数为plotter对象添加数据

//2.addPlotData()函数接受多种不同类型的输入

3.2 完整代码

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


3.3 运行结果

点云可视化_第5张图片

你可能感兴趣的:(PCL,自动驾驶,c++)