深度图像(Depth Images
),也被称为距离影像(Range Images
),是指将图像采集器采集到的场景中各点的距离(深度)值作为像素值的图像,它直接反映了景物可见表面的几何形状,利用它可以很方便地解决3D
目标描述中的许多问题。
从一个点云创建一个深度图像
首先引入深度图像的头文件。
#include <pcl/range_image/range_image.h> // 深度图像头文件
再定义创建深度图像需要设置的参数。
//以1度为角分辨率,从上面创建的点云创建深度图像。
float angularResolution = (float)(1.0f * (M_PI / 180.0f));
/*模拟深度传感器在水平方向的最大采样角度:360°视角。*/
float maxAngleWidth = (float)(360.0f * (M_PI / 180.0f));
/*模拟深度传感器在垂直方向的最大采样角度:180°视角。*/
float maxAngleHeight = (float)(180.0f * (M_PI / 180.0f));
/*
sensorPose定义模拟深度传感器的6自由度位置:
横滚角roll、俯仰角pitch、偏航角yaw,默认初始值均为0。
*/
Eigen::Affine3f sensorPose = (Eigen::Affine3f)Eigen::Translation3f(0.0f, 0.0f, 0.0f); // 采集位置
/*
定义坐标轴正方向:
CAMERA_FRAME:X轴向右,Y轴向下,Z轴向前;
LASER_FRAME:X轴向前,Y轴向左,Z轴向上;
*/
pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME; // 深度图像遵循的坐标系统
/*
noiseLevel设置周围点对当前点深度值的影响:
如 noiseLevel=0.05,可以理解为,深度距离值是通过查询点半径为 Scm 的圆内包含的点用来平均计算而得到的。
*/
float noiseLevel = 0.00;
/*
min_range设置最小的获取距离,小于最小获取距离的位置为盲区
*/
float minRange = 0.0f;
/*border_size获得深度图像的边缘的宽度:在裁剪图像时,如果 borderSize>O ,将在图像周围留下当前视点不可见点的边界*/
int borderSize = 1;
最后创建深度图像。
pcl::RangeImage::Ptr range_image_ptr(new pcl::RangeImage);
pcl::RangeImage& range_image = *range_image_ptr;
range_image.createFromPointCloud(pointCloud, angularResolution, maxAngleWidth, maxAngleHeight, sensorPose, coordinate_frame, noiseLevel, minRange, borderSize);
深度图像继承与PointCloud类,它的点类型具有x,y,z和range距离字段,共有三种类型的点集,有效点集是距离大于0的点集,当前试点不可见点集是x=y=z=NAN且值域为负无穷大,远距离点集是x=y=z=NAN且值域为无穷大 。
从深度图像中提取边界
从前景跨越到背景的位置定义为边界。我们对三种类型的点集感兴趣:
- 物体边界,这是物体最外层和阴影边界的可见点集;
-
阴影边界,毗连于遮挡的背景上的点集;
-
Veil点集,在被遮挡物边界和阴影边界之间的内插点。
它们是由激光雷达获取的3D距离数据中的典型数据类型。如图所示:
首先在PCL1.11
中运行示例代码需要添加一个头文件,,否则会报错。
#include <pcl/common/file_io.h> // for getFilenameWithoutExtension
提取边界信息时很重要的一点是区分深度图像中的当前视点的不可见点的集合和应该可见但处于传感器获取距离范围外的点集,后者可以标记为典型边界,然而前者则不能成为边界。因此,如果后者的测量者存在,则提供哪些超出传感器距离获取范围外的数据对于边界提取是十分有用的。如下代码中使用提供的一个数据文件,该文件中包含这些数据。
std::string far_ranges_filename = pcl::getFilenameWithoutExtension(filename) + "_far_ranges.pcd"; // ?对该函数的用法尚存在疑问?
将远距离的数据并入到深度图像中。
range_image.integrateFarRanges(far_ranges);
提取边界,创建RangeImageBorderExtractor
对象,传入深度图像。
pcl::RangeImageBorderExtractor border_extractor(&range_image);
pcl::PointCloud<pcl::BorderDescription> border_descriptions;
border_extractor.compute(border_descriptions); // 将计算后的得到的边界信息存储在border_descriptions中
点云到深度图的变换与曲面重建
优势:点云诗句需要通过kd tree等索引来对数据进行检索,而深度图像和图像类似,可以通过上下左右等近邻来直接进行索引。
首先声明RangeImage
结构和RangeImagePlanar
等类对应的头文件。
#include <pcl/range_image/range_image.h>
#include <pcl/range_image/range_image_planar.h>
创建RangeImagePlanar
对象,利用该对象的函数createFromPointWithFixedSize()
进行深度图像的生成。该函数还需要提供成像时相机的位姿,以及成像时遵循的坐标系统。
Eigen::Affine3f sensorPose;
sensorPose.setIdentity();
pcl::RangeImage::CoordinateFrame coordinate_frame = pcl::RangeImage::CAMERA_FRAME;
float noiseLevel = 0.00; // 成像时模拟噪声的水平
float minRange = 0.0f; // 成像时考虑该阈值外的点
pcl::RangeImagePlanar::Ptr rangeImage(new pcl::RangeImagePlanar);
rangeImage->createFromPointCloudWithFixedSize(*cloud, width, height, cx, cy, fx, fy, sensorPose, coordinate_frame); // 生成深度图像
什么是相机位姿?相机的位姿(pose)就是相机的位置和姿态的合称,它描述了世界坐标系与相机坐标系之间的转换关系。
完成从点云到深度图像的生成后,利用该深度图像作为输入,来使用曲面重建模块中的简单三角化类生成曲面模型。
pcl::OrganizedFastMesh<pcl::PointWithRange>::Ptr tri(new pcl::OrganizedFastMesh<pcl::PointWithRange>);
pcl::search::KdTree<pcl::PointWithRange>::Ptr tree(new pcl::search::KdTree<pcl::PointWithRange>);
tree->setInputCloud(rangeImage); // 深度图像作为输入
pcl::PolygonMesh triangles;
tri->setTrianglePixelSize(size); // 控制重建曲面的精细程度
tri->setInputCloud(rangeImage);
tri->setSearchMethod(tree);
tri->setTriangulationType((pcl::OrganizedFastMesh<pcl::PointWithRange>::TriangulationType)type);
tri->reconstruct(triangles);