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 <fstream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>  
#include <pcl/visualization/cloud_viewer.h> 
#include <iostream> 
#include <pcl/io/io.h> 
#include <pcl/io/ply_io.h>  
#include <iostream> 
#include <pcl/console/parse.h>  

using namespace pcl;
typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> 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<pcl::visualization::PCLVisualizer> viewer;
	pcl::PointCloud<pcl::PointXYZ>::Ptr baseCloud;     //加載的原始點雲
	pcl::PointCloud<pcl::PointXYZ>::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<PointT>);
	baseCloud.reset(new pcl::PointCloud<PointT>());

	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<PointT>);
	// 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<int > 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<pcl::PointXYZ> 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<PointT> 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<PointT> 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++實現 

封裝在類中的例子 

回調機制的理解 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章