PCL可视化-点选、框选(封装为类)

Pcl开发的应用中,当需要用户对点云做一些操作的时候,需要从屏幕拾取点云中某点的三维坐标。关于如何运用PCL实现这部分功能,很多介绍都是基于控制台程序的,调用写在main函数里,但是大部分时候,我们需要把这些事情封装在类中,这时候有一些小小的差别。

这次学习主要是记录了对回调机制的理解,封装pcl的选点的回调函数为类的成员函数,完成拾取屏幕坐标点的功能,也记录下完整的类和调用部分的代码。   

 

一、关键函数(中间函数)

在理解代码之前,要先明确三个概念:中间函数、回调函数、起始函数。

回调函数最好理解,是作为参数被传入的、后又被调用的函数。中间函数,是需要回调函数作为参数的那个函数,在第三方库中应用回调,通常是库函数去担任中间函数的角色。在传入一个回调函数之前,中间函数是不完整的。换句话说,程序可以在运行时,通过登记不同的回调函数,来决定、改变中间函数的行为,因此程序变得灵活。最后是起始函数,起始函数和回调函数是属于同一层级的函数,是中间函数的调用者,虽然这个函数可能就一两行,但是理解起始函数的作用对理解回调机制也很重要。

这三个部分共同作用,实现回调,有了高层(起始函数)调用底层(中间函数),底层再回过头来调用高层(回调函数)的过程,应该就是"回调"的含义。

 

(一)点云的点选功能

A.中间函数:registerPointPickingCallback

 函数第二个参数涉及的类:pcl::visualization::PointPickingEvent,是我们写回调函数的时候第一个参数的类类型。

(二)点云的框选功能:

A.中间函数:registerAreaPickingCallback()

 函数第二个参数涉及的类:pcl::visualization::AreaPickingEvent:,是我们写回调函数的时候第一个参数的类类型。

B.bool pcl::visualization::AreaPickingEvent::getPointsIndices ( std::vector< int > & indices ) const

利用此函数可以获取视窗中选择的点云数据的索引,根据索引又可以获得确定的离散点数据。

二、封装在类中

差别主要是registerKeyboardCallback函数的调用。在大部分的例子中,都是在控制台程序的main函数中直接调用,只有两个参数,非类中调用的函数原型;

registerKeyboardCallback (
        void (*callback) (const pcl::visualization::KeyboardEvent&, void*),
         void* cookie = NULL)

其中,KeyboardEvent是我们自定义的函数,在这里它是一个回调函数。也就是是一个通过函数指针来调用的函数。需要把函数的指针(地址)作为参数传递给另一个函数,用这个指针被用来调用其所指向的函数时。KeyboardEvent在里面包含了我们在点击、框选等动作时的处理声明。

当keyboard_callback函数是类的一部分时,需要指定类的实例,以便编译器能够确定要使用keyboard_callback函数的实例。这里调用的函数的参数传递有了差别,多了第二个参数 T& instance:调用这个中间函数的类的实例,可以传this指针。

registerKeyboardCallback (
        void (T::*callback) (const pcl::visualization::KeyboardEvent&, void*), 
        T& instance, 
        void* cookie = NULL)

 

三、完整代码 

1、.h

#pragma once 
#include "stdafx.h" 
#include "utility.h" 
#include 
#include 
#include 
#include   
#include  
#include  
#include  
#include   
#include  
#include   

using namespace pcl;
typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud PointCloudT;

class PtPicking
{
public:
	PtPicking(std::string filename); 
	void reset();
	~PtPicking();

	void keyboardEventOccurred(const visualization::KeyboardEvent &event, void* junk);
	void areaPicking(); //框选点云、
	void pointPicking(); //单点选取
	void ptActicPicking(); //屏幕选点
	pcl::PointXYZ randomPoint();
	void spin();
protected:
	boost::mutex cloud_mutex;
	int num = 0;
	boost::shared_ptr viewer;
	pcl::PointCloud::Ptr baseCloud;     //加载的原始点云
	pcl::PointCloud::Ptr clicked_points_3d;  //被选中的点云,可以用reset充值

	void areaPicking_callback(const pcl::visualization::AreaPickingEvent & event, void * args);
	void pointPicking_callback(const pcl::visualization::PointPickingEvent & event, void * args);
	void PtActivePick_callback(const pcl::visualization::PointPickingEvent & event, void * args);
};

2、.cpp

#pragma once
#include "stdafx.h"
#include "PtPicking.h" 
PtPicking::~PtPicking()
{
} 
PtPicking::PtPicking(std::string filename) {
	 
	clicked_points_3d.reset(new pcl::PointCloud);
	baseCloud.reset(new pcl::PointCloud());

	if (pcl::io::loadPCDFile(filename, *baseCloud))
	{
		std::cerr << "ERROR: Cannot open file " << filename << "! Aborting..." << std::endl;
		return;
	}
	reset(); 
};

void PtPicking:: reset()
{
    // Create cloud
	clicked_points_3d.reset(new pcl::PointCloud);
	// Create viewer 
	viewer.reset(new pcl::visualization::PCLVisualizer("viewer"));
	viewer->addCoordinateSystem(1);
	viewer->addPointCloud(baseCloud, "cloud");
	viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud");
}

void PtPicking::keyboardEventOccurred(const visualization::KeyboardEvent &event, void* junk) {

	if (event.getKeySym() == "r" && event.keyDown()) {
		baseCloud->push_back(randomPoint());
		viewer->updatePointCloud(baseCloud, "cloud");
	}
};

//框选事件的调用
void PtPicking::areaPicking()
{ 
	// Register Keyboard Event 
	//viewer->registerKeyboardCallback(&dummyClass::keyboardEventOccurred, *this);
	viewer->registerAreaPickingCallback(&PtPicking::areaPicking_callback, *this);//由于点云数据写成了成员变量,所以这里第三个参数不用传
	std::cout << "press X to strat or ending picking, then press 'Q'..." << std::endl;
}

//点选的调用
void PtPicking::pointPicking()
{  
	cloud_mutex.lock();    // for not overwriting the point cloud 
	viewer->registerPointPickingCallback(&PtPicking::pointPicking_callback, *this);
	std::cout << "Shift+click on three floor points, then press 'Q'..." << std::endl; 
	cloud_mutex.unlock(); 

}

//点选的调用(这个比较灵活,可选屏幕任意位置)
void PtPicking::ptActicPicking()
{
	cloud_mutex.lock();    // for not overwriting the point cloud 
	viewer->registerPointPickingCallback(&PtPicking::PtActivePick_callback, *this);
	std::cout << "Shift+click on three floor points, then press 'Q'..." << std::endl;
	cloud_mutex.unlock();

}

 
//框选事件的回调函数,
//款选屏幕上的一部分点云
//选择方式:输入一个x表示开始或者结束。两次x输入期间用鼠标左键框选点云,可以多次框选。输入q则腿粗选择
void PtPicking::areaPicking_callback(const pcl::visualization::AreaPickingEvent& event, void *args)
{
	struct callback_args * data = (struct callback_args *)args;//点云数据 & 可视化窗口
	std::vector indices;
	if (event.getPointsIndices(indices) == false)
		return;
	for (size_t i = 0; i < indices.size(); i++)
	{
		clicked_points_3d->points.push_back(baseCloud->points.at(indices[i]));
	}
	pcl::visualization::PointCloudColorHandlerCustom red(clicked_points_3d, 255, 0, 0); 
	viewer->removePointCloud("clicked_points");
	viewer->addPointCloud(clicked_points_3d, red, "clicked_points");
	viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 10, "clicked_points");
	for (int i = 0; i < clicked_points_3d->points.size(); i++)
		std::cout << clicked_points_3d->points[i].x << std::endl;
	std::cout << "clicked_points_3d->points.size()" << clicked_points_3d->points.size() << std::endl;

}

//点选事件的回调函数
//点选屏幕上的点云
//选择方式:按住shift,鼠标左键点选
void PtPicking::pointPicking_callback(const pcl::visualization::PointPickingEvent& event, void *args)
{
	struct callback_args * data = (struct callback_args *)args;//点云数据 & 可视化窗口
	if (event.getPointIndex() == -1)
		return;
	PointT  current_point;
	event.getPoint(current_point.x, current_point.y, current_point.z);
	clicked_points_3d->points.push_back(current_point);
	//Draw clicked points in red:
	pcl::visualization::PointCloudColorHandlerCustom red(clicked_points_3d, 255, 0, 0);
	viewer->removePointCloud("clicked_points"); 
	viewer->addPointCloud(clicked_points_3d, red, "clicked_points");
	viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 10, "clicked_points"); 
	std::cout << current_point.x << " " << current_point.y << " " << current_point.z << std::endl;

}

//点选事件的回调函数
//点选事件——点击屏幕上的任一点(pointPicking_callback()是一定要点击点云数据上的点)
//选择方式:按住shift,鼠标左键选择。键盘输入 Q 则退出选择
void PtPicking::PtActivePick_callback(const pcl::visualization::PointPickingEvent& event, void *args)
{ 
	std::cout << "Picking event active" << std::endl;
	PointT current_point;
	if (event.getPointIndex() != -1)
	{
		float x, y, z;
		event.getPoint(current_point.x, current_point.y, current_point.z);
		//std::cout << x << ";" << y << ";" << z << std::endl;
		clicked_points_3d->points.push_back(current_point);

	}
	// Draw clicked points in red:  
	pcl::visualization::PointCloudColorHandlerCustom red(clicked_points_3d, 255, 0, 0);
	viewer->removePointCloud("clicked_points");
	viewer->addPointCloud(clicked_points_3d, red, "clicked_points");
	viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 10, "clicked_points");
	std::cout << current_point.x << " " << current_point.y << " " << current_point.z << std::endl;

}

pcl::PointXYZ PtPicking::randomPoint() {
	pcl::PointXYZ pt;
	pt.x = (double)rand() / RAND_MAX * 10 - 5;
	pt.y = (double)rand() / RAND_MAX * 10 - 5;
	pt.z = (double)rand() / RAND_MAX * 10 - 5;
	return pt;
};

void PtPicking::spin() {
	viewer->spin(); 
};

3、调用的方法

调用时需要输入pcd文件路径

	char filePCD[32];
	printf("请输入pcd文件路径");
	scanf("%s", filePCD);/*输入文件名*/ 
	PtPicking dc(filePCD);
	//dc.pointPicking(); 
	//dc.ptActicPicking();
	dc.areaPicking();
	dc.spin();  

参考链接

基于PCL的屏幕选点、框选点云、单点选取等c++实现 

封装在类中的例子 

回调机制的理解 

你可能感兴趣的:(三维,pcl)