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)
#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);
};
#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();
};
调用时需要输入pcd文件路径
char filePCD[32];
printf("请输入pcd文件路径");
scanf("%s", filePCD);/*输入文件名*/
PtPicking dc(filePCD);
//dc.pointPicking();
//dc.ptActicPicking();
dc.areaPicking();
dc.spin();
基于PCL的屏幕选点、框选点云、单点选取等c++实现
封装在类中的例子
回调机制的理解