一、可视化Visualization
1.1 可视化的基本概念
可视化(Visualization)是利用计算机图形学和图像处理技术,将数据转换成图形或图像在屏幕上显示出来,并进行交互处理的理论、方法和技术。
1.2 PCLVisualizer介绍
class pcl: : visualization: : PCL Visualizer
类PCLVisuafizer为PCL可视化3D点云的主要类。其内部实现了添加各种3D对象以及交互实现等,比其他类实现的功能更齐全。
类PCLVisualizer的关键函数说明:
- PCL Visualizer (int &.argc, char **argv, canst std: : string &.name="",PCLVisualizerlnteractorStyle *style= PCLVisualizerlnteractorStyle:: New (),const bool create_mteractor true):重构函数,其中name为创建窗口名,style为交互类实现,默认为PCLVisualizerlnteractorStyle对象,create_interactor设置是否重建 交互对象,默认是创建的。
- void spin () 调用内部渲染函数,重新渲染输出。
- void spinOnce (int time 1, bool force_redraw false):调用内部渲染函数一次,重新渲染输出时间最大不超过time,单位ms,force_ redraw设置是否强制 重新绘制。
- void addCoordinateSystem (double scale =1.0 , int viewport 0):在坐标原点(0,0,0)添加指示坐标轴,viewport为需要添加的视口,默认在所有视口中都添加,scale设置指示坐标轴的放大系数。
- void setBackgroundColor (const double &.r, const double &.g, canst double &.b, int viewport = O) 设置指定viewport 视口的背景色。
等等还有很多函数。
1.3 CloudViewer介绍
class pcl: : visualization: : CloudViewer
类CloudViewer实现创建点云可视化的窗口,以及相关的可视化功能。
CloudViewer的关键成员函数
- CloudViewer (const std: :string &.window_name) :构建可视化点云窗口,窗口名为window_name。
- void showCloud (const ColorCloud: : ConstPtr &.cloud, const std: : string&. cloudname = “cloud”): 在可视化点云窗口中显示 cloud对应的点云,考虑到多个点云用键值cloudname 来限定具体是哪个点云。
- bool wasStopped (int millis_to_ wait= 1) :判断用户是否已关闭窗口,如果是则需要注销窗口,millis_to_wait为在注销窗口之前的等待。
- void runOnVisualizationThread (VizCallable x, const std: : string & key = " callable") :在窗口运行期间处理x 回调函数,key为键值标识此回调函数,直到窗口关闭,可以多次使用回调函数。**重点:**此X为回调函数(返回值为空,参数是pcl::visualization::PCLVisualizer &viewer)
- void run On VisualizationThreadOnce (VizCallable x) 同上,但只调用回询函数一次。
a、CloudViewer实现:
// 使用CloudViewer,CloudViewer是PCLVisualizer的子类,所以可以传参,进行设置背景颜色,
// CloudViewer是没有setBackground属性
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);
VTK_MODULE_INIT(vtkInteractionStyle);
#include<thread>
#include<pcl/visualization/cloud_viewer.h>
#include<pcl/point_cloud.h>
#include<pcl/io/pcd_io.h>
#include<pcl/point_types.h>
using namespace std;
static int num = 0;
//int user_data;
// 设置背景
void viewerOneOff(pcl::visualization::PCLVisualizer &viewer) {
viewer.setBackgroundColor(1, 0.5, 1); // 设置背景颜色
// 设置点云尺寸大小
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3);
}
//设置文字
void viewerText(pcl::visualization::PCLVisualizer &viewer) {
stringstream s; // 定义字符串
s << " viewer loop " << num++ ; // 输出文字
viewer.removeShape("Text", 0); // 删除“Text”形状
//viewer.addText(s.str(), 100, 200, "Text", 0); // 定义文字框
viewer.setPosition(100, 200);
pcl::PointXYZ position;
position.x = 1.0;
position.y = 1.0;
position.z = 1.0;
viewer.addText3D(s.str(), position, 0.3, 0.3, 0.3,0.4,"Text",0); // 将Text3D添加到“Text”形状里
// user_data++;
}
void getCube(pcl::visualization::PCLVisualizer& viewer) {
num++;
//cout << "num : "<< num << endl;
string cube = "cube" + num;
float r = rand() + 0.1;
float b = rand() + 0.2;
float g = rand() + 0.3;
viewer.addCube(0, 1, 0, 1, 0, 1, r, g, b, "cube" + num , 0);
//std::cout <<cube<<" run !!!" << std::endl;
}
int main() {
pcl::visualization::CloudViewer viewer("可视化");
//pcl::PointCloud<pcl::PointXYZ> cloud; // 在viewer.showCloud()作为参数是会报错的,因为其接受Ptr参数
// 当参数为PointXYZRGB就会加载点云颜色数据,如果为PointXYZ,则会输出白色数据。
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZRGB>());
pcl::io::loadPCDFile("maize.pcd", *cloud);
//if (pcl::io::loadPCDFile("maize.pcd", *cloud)) {
// cout << "w文件不存在" << endl;
// return -1;
//}
// 加载点云数据
viewer.showCloud(cloud);
// 一次性线程,加载一次viewerOneOff
viewer.runOnVisualizationThreadOnce(viewerOneOff);
// 多次加载
viewer.runOnVisualizationThread(viewerText);
while (!viewer.wasStopped()) {
//user_data++;
// 可以加一些其他的活动
std::this_thread::sleep_for(100ms);
viewer.runOnVisualizationThreadOnce(getCube);
}
return 0;
}
运行结果:
1.3 Range_image_visualizer的介绍
可视化深度图像的两种方法,在3D视窗中以点云形式进行可视化(深度图像来源于点云),另一种是将深度值映射为颜色,从而以彩色图像方式可视化深度图像。
#include <iostream>
#include <boost/thread/thread.hpp> // boost中的线程
#include <pcl/common/common_headers.h>
#include <pcl/pcl_macros.h>
#include <pcl/range_image/range_image.h> // 深度图像
#include <pcl/io/pcd_io.h> // 读取pcd数据
#include <pcl/visualization/range_image_visualizer.h> // 深度图像可视化要通过这个头文件
#include <pcl/visualization/pcl_visualizer.h> // 可视化
#include <pcl/console/parse.h>
typedef pcl::PointXYZ PointType; // 重定义
// 全局参数
float angular_resolution = 0.5f; // 分辨率
pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME; // 定义深度图像坐标系为照相机坐标系
pcl::RangeImage::CoordinateFrame coordinate_frame1 = pcl::RangeImage::LASER_FRAME; // 定义深度图像坐标系为照相机坐标系
bool live_update = false; // 设置更新为false
// -----打印帮助-----
void
printUsage(const char* progName)
{
std::cout << "\n\nUsage: " << progName << " [options] <scene.pcd>\n\n"
<< "Options:\n"
<< "-------------------------------------------\n"
<< "-r <float> angular resolution in degrees (default " << angular_resolution << ")\n"
<< "-c <int> coordinate frame (default " << (int)coordinate_frame << ")\n"
<< "-l live update - update the range image according to the selected view in the 3D viewer.\n"
<< "-h this help\n"
<< "\n\n";
}
// 深度图像位置,Eigen::Affine3f&可以保存摄像头位置
void
setViewerPose(pcl::visualization::PCLVisualizer& viewer, const Eigen::Affine3f& viewer_pose)
{
Eigen::Vector3f pos_vector = viewer_pose * Eigen::Vector3f(0, 0, 0);
Eigen::Vector3f look_at_vector = viewer_pose.rotation() * Eigen::Vector3f(0, 0, 1) + pos_vector;
Eigen::Vector3f up_vector = viewer_pose.rotation() * Eigen::Vector3f(0, -1, 0);
// 设置摄像头位置,前三个参数是摄像头的位置,中间三个参数是视向,最后三个参数是向上方向
viewer.setCameraPosition(pos_vector[0], pos_vector[1], pos_vector[2], look_at_vector[0], look_at_vector[1],
look_at_vector[2], up_vector[0], up_vector[1], up_vector[2]);
//viewer.camera_.pos[0] = pos_vector[0];
//viewer.camera_.pos[1] = pos_vector[1];
//viewer.camera_.pos[2] = pos_vector[2];
//viewer.camera_.focal[0] = look_at_vector[0];
//viewer.camera_.focal[1] = look_at_vector[1];
//viewer.camera_.focal[2] = look_at_vector[2];
//viewer.camera_.view[0] = up_vector[0];
//viewer.camera_.view[1] = up_vector[1];
//viewer.camera_.view[2] = up_vector[2];
viewer.updateCamera();
}
// -----Main-----
int
main03(int argc, char** argv)
{
//解析命令行参数
if (pcl::console::find_argument(argc, argv, "-h") >= 0)
{
printUsage(argv[0]);
return 0;
}
if (pcl::console::find_argument(argc, argv, "-l") >= 0)
{
live_update = true;
std::cout << "Live update is on.\n";
}
if (pcl::console::parse(argc, argv, "-r", angular_resolution) >= 0)
std::cout << "Setting angular resolution to " << angular_resolution << "deg.\n";
int tmp_coordinate_frame; // 坐标系
// 命令行解析
if (pcl::console::parse(argc, argv, "-c", tmp_coordinate_frame) >= 0)
{
coordinate_frame = pcl::RangeImage::CoordinateFrame(tmp_coordinate_frame);
std::cout << "Using coordinate frame " << (int)coordinate_frame << ".\n";
}
angular_resolution = pcl::deg2rad(angular_resolution);
// 读取给定的pcd点云文件或者自行创建随机点云
pcl::PointCloud<PointType>::Ptr point_cloud_ptr(new pcl::PointCloud<PointType>);
pcl::PointCloud<PointType>& point_cloud = *point_cloud_ptr;
Eigen::Affine3f scene_sensor_pose(Eigen::Affine3f::Identity());
std::vector<int> pcd_filename_indices = pcl::console::parse_file_extension_argument(argc, argv, "pcd");
if (!pcd_filename_indices.empty())
{
std::string filename = argv[pcd_filename_indices[0]];
if (pcl::io::loadPCDFile(filename, point_cloud) == -1)
{
std::cout << "Was not able to open file \"" << filename << "\".\n";
printUsage(argv[0]);
return 0;
}
scene_sensor_pose = Eigen::Affine3f(Eigen::Translation3f(point_cloud.sensor_origin_[0],
point_cloud.sensor_origin_[1],
point_cloud.sensor_origin_[2])) *
Eigen::Affine3f(point_cloud.sensor_orientation_);
}
else
{
std::cout << "\nNo *.pcd file given => Genarating example point cloud.\n\n";
for (float x = -0.5f; x <= 0.5f; x += 0.01f)
{
for (float y = -0.5f; y <= 0.5f; y += 0.01f)
{
PointType point; point.x = x; point.y = y; point.z = 2.0f - y;
point_cloud.points.push_back(point);
}
}
point_cloud.width = (int)point_cloud.points.size();
point_cloud.height = 1;
}
//从点云创建深度图像对象
float noise_level = 0.0;
float min_range = 0.0f;
int border_size = 1;
// boost::shared_ptr共享指针,代表该指针在整个程序全局中都可以使用,不用担心内存错误
boost::shared_ptr<pcl::RangeImage> range_image_ptr(new pcl::RangeImage);
pcl::RangeImage& range_image = *range_image_ptr;
range_image.createFromPointCloud(point_cloud, angular_resolution, pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose, coordinate_frame, noise_level, min_range, border_size);
//创建3D视图并且添加点云进行显示
pcl::visualization::PCLVisualizer viewer("3D Viewer");
viewer.setBackgroundColor(1, 1, 1);
// 自定义点云颜色(白色)
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> range_image_color_handler(range_image_ptr, 0, 0, 0);
// 添加点云数据到viewer中,第一个参数是图像指针,第二个参数是颜色属性,第三个参数是ID号
viewer.addPointCloud(range_image_ptr, range_image_color_handler, "range image");
// 设置点云尺寸
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "range image");
viewer.addCoordinateSystem (1.0f); // 添加坐标系
pcl::visualization::PointCloudColorHandlerCustom<PointType> point_cloud_color_handler (point_cloud_ptr, 150, 150, 150);
viewer.addPointCloud (point_cloud_ptr, point_cloud_color_handler, "original point cloud");
viewer.initCameraParameters(); // 初始化摄像机参数(按照默认方向)
setViewerPose(viewer, range_image.getTransformationToWorldSystem()); // 设置深度图像的位置
//显示深度图像
pcl::visualization::RangeImageVisualizer range_image_widget("Range image");
range_image_widget.showRangeImage(range_image);
//主循环
while (!viewer.wasStopped())
{
// 深度图像渲染一次
range_image_widget.spinOnce();
// 3D viewer渲染一次
viewer.spinOnce();
// 更新间隔10ms
pcl_sleep(0.01);
if (live_update)
{
scene_sensor_pose = viewer.getViewerPose();
range_image.createFromPointCloud(point_cloud, angular_resolution, pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose, pcl::RangeImage::LASER_FRAME, noise_level, min_range, border_size);
range_image_widget.showRangeImage(range_image);
}
}
}
程序运行:首先通过cmake把cpp文件和CmakeLists文件进行编译生成Debug文件,然后再命令行中输入命令运行程序。
./range_image_visualization.exe -h :是帮助的意思,可以看一下命令的含义所在。
./range_image_visualization.exe rabbit.pcd -r:是当分辨率为0.5时的深度图像。但是不是动态的。
./range_image_visualization.exe rabbit.pcd -l:是动态深度图像,当移动3D Viewer中的模型位置时,深度图像也会发生变化。
深度的描述:图中不可区视域(代表无限远)用浅绿光表示,远距离区域(范围无限远激光,扫描时可获取信息)用淡蓝色表示,注意两者虽然都是无穷远,但前者是在激光扫描不可见区域,后者是在可见区域的无穷远。
1.4 实例三
ModelCoefficients 介绍:可以定义ModelCoefficients 得到各种各样的图形。前提是知道图形的函数方程,如下ax+by+cz+d = 0是一个平面方程,所以
pcl::ModelCoefficients coeffs;
coeffs.values.push_back(0.0); // 是a的值
coeffs.values.push_back(0.0); // b的值
coeffs.values.push_back(1.0); // c的值
coeffs.values.push_back(0.0); // d的值
分屏的介绍:意思就是在把一个Viewer分成n块。这个就要利用到viewport
int v1(0); // 定义一个viewport为0
viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1); // 创建一个视口,索引为v1,(0.0,0.0)可以看成一个窗口左上角的位置,
(0.5,1.0)代表窗口x轴的一半(宽度),1.0代表窗口的高度
viewer->setBackgroundColor(0, 0, 0, v1); / /定义背景颜色
int v2(0); // 定义另一个viewport为1
viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2);
viewer->setBackgroundColor(0.3, 0.3, 0.3, v2); // 定义背景颜色
上面几行代码创建新的视口,所需的4个参数是视口在X轴的最小值、最大值、 Y轴的最小值、最大值,取值在0~1。
我们创建的视口分布于窗口的左半部分,最后 一个字符串参数,用来唯一标志该视口,在其他改变该视口内容的调用中需要以该唯 一标志为参数
/* \author Geoffrey Biggs */
#include <iostream>
#include <boost/thread/thread.hpp>
#include <pcl/common/common_headers.h>
#include <pcl/common/common_headers.h>
#include <pcl/features/normal_3d.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/console/parse.h>
// 帮助
void
printUsage1(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";
}
boost::shared_ptr<pcl::visualization::PCLVisualizer> simpleVis(pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud)
{
//创建3D窗口并添加点云
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(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);
}
boost::shared_ptr<pcl::visualization::PCLVisualizer> rgbVis(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud)
{
//创建3D窗口并添加点云
boost::shared_ptr<pcl::visualization::PCLVisualizer> 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();
return (viewer);
}
boost::shared_ptr<pcl::visualization::PCLVisualizer> customColourVis(pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud)
{
//创建3D窗口并添加点云
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> single_color(cloud, 0, 255, 0);
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);
}
boost::shared_ptr<pcl::visualization::PCLVisualizer> normalsVis(
pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud, pcl::PointCloud<pcl::Normal>::ConstPtr normals)
{
//创建3D窗口并添加点云其包括法线
boost::shared_ptr<pcl::visualization::PCLVisualizer> 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->addPointCloudNormals<pcl::PointXYZRGB, pcl::Normal>(cloud, normals, 10, 0.05, "normals");
viewer->addCoordinateSystem(1.0);
viewer->initCameraParameters();
return (viewer);
}
boost::shared_ptr<pcl::visualization::PCLVisualizer> shapesVis(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud)
{
//创建3D窗口并添加点云
boost::shared_ptr<pcl::visualization::PCLVisualizer> 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");
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);
}
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewportsVis(
pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud, pcl::PointCloud<pcl::Normal>::ConstPtr normals1, pcl::PointCloud<pcl::Normal>::ConstPtr normals2)
{
// 创建3D窗口并添加显示点云其包括法线
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->initCameraParameters();
int v1(0);
viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1);
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);
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)
{
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer = *static_cast<boost::shared_ptr<pcl::visualization::PCLVisualizer> *> (viewer_void);
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)
{
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer = *static_cast<boost::shared_ptr<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);
}
}
boost::shared_ptr<pcl::visualization::PCLVisualizer> interactionCustomizationVis()
{
cout << "Interaction...."<<endl;
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);
viewer->addCoordinateSystem(1.0);
viewer->registerKeyboardCallback(keyboardEventOccurred, (void*)&viewer);
viewer->registerMouseCallback (mouseEventOccurred, (void*)&viewer);
return (viewer);
}
// -----Main-----
int
main04(int argc, char** argv)
{
// 解析命令行参数
if (pcl::console::find_argument(argc, argv, "-h") >= 0)
{
printUsage1(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
{
printUsage1(argv[0]);
return 0;
}
// 自行创建一随机点云
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 << "Genarating example point clouds.\n\n";
// 以椭圆为边线沿z轴拉伸获取其点云,并赋予红绿蓝渐变色。
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 * cosf(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;
uint32_t rgb = (static_cast<uint32_t>(r) << 16 |
static_cast<uint32_t>(g) << 8 | static_cast<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;
// 0.05为搜索半径获取点云法线
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);
// 0.1为搜索半径获取点云法线
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals2(new pcl::PointCloud<pcl::Normal>);
ne.setRadiusSearch(0.1);
ne.compute(*cloud_normals2);
boost::shared_ptr<pcl::visualization::PCLVisualizer> 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();
}
// 主循环
while (!viewer->wasStopped())
{
viewer->spinOnce(100);
boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}
}
运行结果:一个是分窗口的结果,一个是添加图形的结果
二、深度图像
2.1 深度图像的概念
深度图像(Depth Images)也被称为距离影像(Range Images), 是指将从图像采集器到场景中各点的距离(深度)值作为像素值的图像,
它直接反映了景物可见表面的几何形状,利用它可以很方便的解决3D目标描述中的许多问题。 深度图像经过坐标转换可以计算为
点云数据,有规则及必要信息的点云数据也可以反算为深度图像数据。
2.2 深度图像的创建
#include <pcl/range_image/range_image.h>
#include<pcl/visualization/range_image_visualizer.h>
int main(int argc, char** argv) {
pcl::PointCloud<pcl::PointXYZ> pointCloud; // 定义点云
//生成数据
for (float y = -0.5f; y <= 0.5f; y += 0.01f) {
for (float z = -0.5f; z <= 0.5f; z += 0.01f) {
pcl::PointXYZ point;
point.x = 2.0f - y;
point.y = y;
point.z = z;
pointCloud.points.push_back(point); // 将point添加到点云数据中
}
}
pointCloud.width = (uint32_t)pointCloud.points.size();
pointCloud.height = 1;
//以1度为角分辨率,从上面创建的点云创建深度图像。
float angularResolution = (float)(1.0f * (M_PI / 180.0f));
// 1度转弧度
float maxAngleWidth = (float)(360.0f * (M_PI / 180.0f));
// 360.0度转弧度
float maxAngleHeight = (float)(180.0f * (M_PI / 180.0f));
// 180.0度转弧度
Eigen::Affine3f sensorPose = (Eigen::Affine3f)Eigen::Translation3f(0.0f, 0.0f, 0.0f);
// 观测角度
pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME;
// 深度图像遵循的坐标系统
float noiseLevel = 0.00; // 归一化的的Z缓冲区来创建深度图像
float minRange = 0.0f; // 所有在这个半径范围内的数据被忽略
int borderSize = 1; // 将在图像周围留下当前视点不可见点的边界
pcl::RangeImage rangeImage; // 定义深度图像
// 从点云数据来创建深度图像
rangeImage.createFromPointCloud(pointCloud, angularResolution, maxAngleWidth, maxAngleHeight, sensorPose, coordinate_frame, noiseLevel, minRange, borderSize);
std::cout << rangeImage << "\n";
//显示深度图像
pcl::visualization::RangeImageVisualizer range_image_widget("Range image");
range_image_widget.showRangeImage(rangeImage);
//主循环
while (!range_image_widget.wasStopped())
{
// 深度图像渲染一次
range_image_widget.spinOnce();
// 更新间隔10ms
pcl_sleep(0.01);
}
}
这部分定义了创建深度图像时需要的设置参数,将角度分辨率定义为1°意味着由邻近的像素点所对应的每个光束之间相差1°,
maxAngleWidth=360和maxAngleHeight= 180意味着,进行模拟的距离传感器对周围的环境拥有一个完整的360° 视角,
用户在任何数据集下都可以使用此设置,因为最终获取的深度图像将被裁剪到有空间物体存在的区域范围。但是,用户可以
通过减小数值来节省一些计算资源,例如:对于传感器后面没有可以观测的点时,一个水平视角为18矿的激光扫描仪,即
maxAngleWidth= 180 就足够了这样只需要观察距离传感器前面就可以了,因为后面没有需要观察的场景。sensorPose定义
了模拟深度图像获取传感器的6自由度位置,其原始值为横滚角roll,俯仰角pitch、偏航角yaw都为0,coordinate_ frame= CAMERA_FRAME
说明系统的X轴是向右的,Y轴是向下的,Z轴是向前的,另外一个选择是LASER_FRAME,其X轴向前,Y轴向左,Z轴向上。noiseLevel= 0是指使用
一个归一化的Z缓冲器来创建深度图像,但是如果想让邻近点集都落在同一 个像素单元,用户可以设置一个较高的值,例如noiseLevel= 0. 05
可以理解为,深度距离值是通过查询点半径为5cm的圆内包含的点用来平均计算而得到的。如果minRange>O, 则所有模拟器所在位置半径minRange
内的邻近点都将被忽略,即为盲区。在栽剪图像时,如果borderSize>O,将在图像周围留下当前视点不可见点的边界。
2.3 深度图像边界的提取
#include <iostream>
#include <boost/thread/thread.hpp>
#include <pcl/range_image/range_image.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/range_image_visualizer.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/features/range_image_border_extractor.h>
#include <pcl/console/parse.h>
typedef pcl::PointXYZ PointType;
// --------------------
// -----参数-----
// --------------------
float angular_resolution = 0.5f; // 设置角度分辨率
pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME; // 设置坐标系
bool setUnseenToMaxRange = false; // 设置所有不能观察的点都是远距离的
// --------------
// -----帮助-----
// --------------
void
printUsage(const char* progName)
{
std::cout << "\n\nUsage: " << progName << " [options] <scene.pcd>\n\n"
<< "Options:\n"
<< "-------------------------------------------\n"
<< "-r <float> angular resolution in degrees (default " << angular_resolution << ")\n"
<< "-c <int> coordinate frame (default " << (int)coordinate_frame << ")\n"
<< "-m Treat all unseen points to max range\n"
<< "-h this help\n"
<< "\n\n";
}
// --------------
// -----主函数-----
// --------------
int
main02(int argc, char** argv)
{
// --------------------------------------
// -----解析命令行参数-----
// --------------------------------------
if (pcl::console::find_argument(argc, argv, "-h") >= 0)
{
printUsage(argv[0]);
return 0;
}
if (pcl::console::find_argument(argc, argv, "-m") >= 0)
{
setUnseenToMaxRange = true;
cout << "Setting unseen values in range image to maximum range readings.\n";
}
int tmp_coordinate_frame;
if (pcl::console::parse(argc, argv, "-c", tmp_coordinate_frame) >= 0)
{
coordinate_frame = pcl::RangeImage::CoordinateFrame(tmp_coordinate_frame);
cout << "Using coordinate frame " << (int)coordinate_frame << ".\n";
}
if (pcl::console::parse(argc, argv, "-r", angular_resolution) >= 0)
cout << "Setting angular resolution to " << angular_resolution << "deg.\n";
angular_resolution = pcl::deg2rad(angular_resolution);
// ------------------------------------------------------------------
// -----读取pcd文件,如果没有给出pcd文件则创建一个示例点云-----
// ------------------------------------------------------------------
pcl::PointCloud<PointType>::Ptr point_cloud_ptr(new pcl::PointCloud<PointType>);
pcl::PointCloud<PointType>& point_cloud = *point_cloud_ptr;
pcl::PointCloud<pcl::PointWithViewpoint> far_ranges;
Eigen::Affine3f scene_sensor_pose(Eigen::Affine3f::Identity());
std::vector<int> pcd_filename_indices = pcl::console::parse_file_extension_argument(argc, argv, "pcd");
if (!pcd_filename_indices.empty())
{
std::string filename = argv[pcd_filename_indices[0]];
if (pcl::io::loadPCDFile(filename, point_cloud) == -1)
{
cout << "Was not able to open file \"" << filename << "\".\n";
printUsage(argv[0]);
return 0;
}
scene_sensor_pose = Eigen::Affine3f(Eigen::Translation3f(point_cloud.sensor_origin_[0], point_cloud.sensor_origin_[1], point_cloud.sensor_origin_[2])) *Eigen::Affine3f(point_cloud.sensor_orientation_);
std::string far_ranges_filename = pcl::getFilenameWithoutExtension(filename) + "_far_ranges.pcd";
if (pcl::io::loadPCDFile(far_ranges_filename.c_str(), far_ranges) == -1)
std::cout << "Far ranges file \"" << far_ranges_filename << "\" does not exists.\n";
}
else
{
cout << "\nNo *.pcd file given => Genarating example point cloud.\n\n";
for (float x = -0.5f; x <= 0.5f; x += 0.01f)
{
for (float y = -0.5f; y <= 0.5f; y += 0.01f)
{
PointType point; point.x = x; point.y = y; point.z = 2.0f - y;
point_cloud.points.push_back(point);
}
}
point_cloud.width = (int)point_cloud.points.size(); point_cloud.height = 1;
}
// -----------------------------------------------
// -----从点云创建深度图像-----
// -----------------------------------------------
float noise_level = 0.0;
float min_range = 0.0f;
int border_size = 1;
boost::shared_ptr<pcl::RangeImage> range_image_ptr(new pcl::RangeImage);
pcl::RangeImage& range_image = *range_image_ptr;
range_image.createFromPointCloud(point_cloud, angular_resolution, pcl::deg2rad(360.0f), pcl::deg2rad(180.0f), scene_sensor_pose, coordinate_frame, noise_level, min_range, border_size);
range_image.integrateFarRanges(far_ranges);
if (setUnseenToMaxRange)
range_image.setUnseenToMaxRange();
// --------------------------------------------
// -----打开三维浏览器并添加点云-----
// --------------------------------------------
pcl::visualization::PCLVisualizer viewer("3D Viewer");
viewer.setBackgroundColor(1, 1, 1);
viewer.addCoordinateSystem(1.0f);
pcl::visualization::PointCloudColorHandlerCustom<PointType> point_cloud_color_handler(point_cloud_ptr, 0, 0, 0);
viewer.addPointCloud(point_cloud_ptr, point_cloud_color_handler, "original point cloud");
//PointCloudColorHandlerCustom<pcl::PointWithRange> range_image_color_handler (range_image_ptr, 150, 150, 150);
//viewer.addPointCloud (range_image_ptr, range_image_color_handler, "range image");
//viewer.setPointCloudRenderingProperties (PCL_VISUALIZER_POINT_SIZE, 2, "range image");
// -------------------------
// -----提取边界-----
// -------------------------
pcl::RangeImageBorderExtractor border_extractor(&range_image);
pcl::PointCloud<pcl::BorderDescription> border_descriptions;
border_extractor.compute(border_descriptions);
// ----------------------------------
// -----在三维浏览器中显示点集-----
// ----------------------------------
pcl::PointCloud<pcl::PointWithRange>::Ptr border_points_ptr(new pcl::PointCloud<pcl::PointWithRange>), veil_points_ptr(new pcl::PointCloud<pcl::PointWithRange>), shadow_points_ptr(new pcl::PointCloud<pcl::PointWithRange>);
pcl::PointCloud<pcl::PointWithRange>& border_points = *border_points_ptr, &veil_points = *veil_points_ptr, &shadow_points = *shadow_points_ptr;
for (int y = 0; y < (int)range_image.height; ++y)
{
for (int x = 0; x < (int)range_image.width; ++x)
{
if (border_descriptions.points[y*range_image.width + x].traits[pcl::BORDER_TRAIT__OBSTACLE_BORDER])
border_points.points.push_back(range_image.points[y*range_image.width + x]);
if (border_descriptions.points[y*range_image.width + x].traits[pcl::BORDER_TRAIT__VEIL_POINT])
veil_points.points.push_back(range_image.points[y*range_image.width + x]);
if (border_descriptions.points[y*range_image.width + x].traits[pcl::BORDER_TRAIT__SHADOW_BORDER])
shadow_points.points.push_back(range_image.points[y*range_image.width + x]);
}
}
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> border_points_color_handler(border_points_ptr, 0, 255, 0);
viewer.addPointCloud<pcl::PointWithRange>(border_points_ptr, border_points_color_handler, "border points");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 7, "border points");
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> veil_points_color_handler(veil_points_ptr, 255, 0, 0);
viewer.addPointCloud<pcl::PointWithRange>(veil_points_ptr, veil_points_color_handler, "veil points");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 7, "veil points");
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointWithRange> shadow_points_color_handler(shadow_points_ptr, 0, 255, 255);
viewer.addPointCloud<pcl::PointWithRange>(shadow_points_ptr, shadow_points_color_handler, "shadow points");
viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 7, "shadow points");
//-------------------------------------
// -----在深度图像中显示点集-----
// ------------------------------------
pcl::visualization::RangeImageVisualizer* range_image_borders_widget = NULL;
range_image_borders_widget =
pcl::visualization::RangeImageVisualizer::getRangeImageBordersWidget(range_image, -std::numeric_limits<float>::infinity(), std::numeric_limits<float>::infinity(), false, border_descriptions, "Range image with borders");
// -------------------------------------
//--------------------
// -----主循环-----
//--------------------
while (!viewer.wasStopped())
{
range_image_borders_widget->spinOnce();
viewer.spinOnce();
pcl_sleep(0.01);
}
}
pcl::RangeImageBorderExtractor border_extractor(&range_image);
pcl::PointCloud<pcl::BorderDescription> border_descriptions;
border_extractor.compute(border_descriptions);
此部分创建了 RangelmageBorderExtractor 这个对象,给了它深度图像,并且计算后存储边界信息在 border_descriptions 中。
运行结果。
./range_image_border_extraction.exe rabbit.pcd -r :设置角度分辨率为0.5时的结果
./range_image_border_extraction.exe rabbit.pcd -c:设置不同的坐标系
./range_image_border_extraction.exe rabbit.pcd -m:如果用户没有这些远距离的点云,则可采用命令行参数-m,这样设置所有不能观察到的点都为远距离的
./range_image_border_extraction.exe rabbit.pcd -r :设置角度分辨率为0.5时的运行结果
./range_image_border_extraction.exe rabbit.pcd -m的运行结果如下
三、总结
本周学习了可视化和深度图像的表示方法,明白了使用这些代码的意义所在,这些代码都来源于点云库PCL学习教程和PCL官方文档。