前言

各种光学传感器技术的发展,包含物体三维结构信息的深度图像数据获取开始普及。尤其时kinect等设备的出现。

什么是深度图像?
定义:深度图像(Depth Images),也被称为距离影像(Range Images),是指将图像采集器到场景中各点的距离(深度)值作为像素值的图像。

它直接反应了景物可见表面的几何形状,利用它可以很方便的解决3D目标描述中的许多问题。

获取深度图像的方法:

  • 激光雷达深度成像法
  • 计算机立体视觉成像
  • 坐标测量机法
  • 莫尔条纹法
  • 结构光法等

深度图像经过坐标转换可以计算为点云数据,有规则及必要信息的点云数据可以反算为深度图像数据。

PCL 中的深度图像

在PCL中 操作和表达深度图像主要依据 pcl_range_image 库中包含的两个类

为什么PCL要把深度图像的概念引入呢?
主要是由于深度图像与点云 近邻检索方式不同
点云数据通过kd-tree等索引来对数据进行检索,而深度图和图像类似,可通过上下左右等近邻来直接索引。

所以为了使用深度图的计算方法,以提高效率等,可以将点云转化为深度图像

将点云转化为深度图像

Code

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include <pcl/range_image/range_image.h>//深度图像头文件

操作pcl深度图像时应该包含的头文件
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    pcl::PointCloud<pcl::PointXYZ>::Ptr pointCloud(new pcl::PointCloud<pcl::PointXYZ>);//声明点云

    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;//声明pcl的一个点
            //赋值点云里点 的 xyz坐标
            point.x = 2.0f - y;
            point.y = y;
            point.z = z;
            pointCloud->points.push_back(point);
        }
    }
    //赋值点云的宽度与高度
    pointCloud->width = (uint32_t) pointCloud->points.size();
    pointCloud->height = 1;

生成一个矩形的点云

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    //给点云显示出来   
    // 显示直通滤波后的点云
    /* 创建显示 类 实例 */
    pcl::visualization::CloudViewer viewer1("Cloud Viewer "); 

    /* 显示点云 */
    viewer1.showCloud(pointCloud);

        /*持续显示*/
    while (!viewer1.wasStopped ())
    {
    }

将刚生成的点云显示出来

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    //设置角度分辨率  弧度 相邻像素点 所对应的每束 光束之间相差 1 度
    float angularResolution = (float)( 1.0f*(M_PI/180.0f) );
    //模拟的距离传感器对周围环境 有360度的视场角
    float maxAngleWidth =  (float)( 360.0f*(M_PI/180.0f) );
    //相当于前面所有的 视角
    float maxAngleHeight =  (float)( 180.0f*(M_PI/180.0f) );

    //传感器位置
    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;
    //最小距离
    float minRange = 0.0f;
    //在剪裁图像时若果 此值>0,将在图像周围留下当前视点不可见的边界
    int borderSize = 1;

这部分定义了创建深度图像时需要设置的参数

  • angularResolution 角度分辨率 1度 即 相邻像素点 所对应的每束 光束之间相差 1 度
  • maxAngleWidth maxAngleHeight 传感器的水平视场角和垂直视场角
  • sensorPose 传感器的6自由度位置
  • coordinate_frame CAMERA_FRAME 系统的X轴向右,Y轴向下,Z轴向前 如果是LASER_FRAME 则x向前,y向左,z向上
  • noiseLevel 此值为0,则意味着使用一个归一化的Z缓冲器来创建深度图像, 为0.05可以理解为深度距离通过查询点半径为5cm的圆内包含的点以平均计算得到的。
  • minRange 就是盲区的概念
  • borderSize 在剪裁图像时若果 此值>0,将在图像周围留下当前视点不可见的边界
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    pcl::RangeImage rangeImage;//声明深度图像

    /* 将点云转为 深度图像的 操作 */
    rangeImage.createFromPointCloud(*pointCloud, angularResolution, maxAngleWidth, maxAngleHeight, sensorPose, coordinate_frame, noiseLevel, minRange, borderSize);

    //输出图像的一些信息
    std::cout << rangeImage << "\n";

在上一块设定了所有的参数后
就可以通过调用 createFromPointCloud 将点云转换为深度图像了。
该函数的参数很多,就是上面设置的。

打印结果

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

CMakeLIst.txt

# 声明要求的 cmake 最低版本
cmake_minimum_required(VERSION 2.8 )
# 声明工程名称
project(range_image_creation)
# 添加c++ 11 标准支持
set(CMAKE_CXX_FLAGS "-std=c++11")
# 寻找PCL的库
find_package(PCL REQUIRED COMPONENT common io visualization filters)
link_directories(${PCL_LIBRARY_DIRS})
# 添加头文件
include_directories(${PCL_INCLUDE_DIRS})
add_definitions( ${PCL_DEFINITIONS} )
#添加一个可执行程序
add_executable(range_image_creation range_image_creation.cpp)
#链接PCL 库
target_link_libraries(range_image_creation ${PCL_LIBRARIES})

从深度图像中提取边界

什么是深度图像的边界:从前景跨越到背景的位置定义为边界

关于边界点云集 关心的有 三 类

  • 物体边界:物体最外层与阴影边界的可见点集
  • 阴影边界:毗连于遮挡的背景上的点集
  • Veil点集:被遮挡物边界和阴影边界直接的内插点

下面通过代码实例 展示 提取三种边界

Code

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#include <pcl/features/range_image_border_extractor.h>// 深度图边界提取 模块

包含相关的头文件
用到pcl库中的 深度图 边界提取 模块 的时候 需要包含此文件
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    //创建点云
    pcl::PointCloud<pcl::PointXYZ>::Ptr pointCloud(new pcl::PointCloud<pcl::PointXYZ>);//声明点云

    for(float x=-0.5f;x<=0.5f;x+=0.01f)
    {
        for (float y=-0.5f; y<=0.5f; y+=0.01f) 
        {
            pcl::PointXYZ point;//声明pcl的一个点
            //赋值点云里点 的 xyz坐标
            point.x = x;
            point.y = y;
            point.z = 2.0f - y;
            pointCloud->points.push_back(point);
        }
    }
    //赋值点云的宽度与高度
    pointCloud->width = (uint32_t) pointCloud->points.size();
    pointCloud->height = 1;  
    //设置角度分辨率  弧度 相邻像素点 所对应的每束 光束之间相差 1 度
    float angularResolution = (float)( 1.0f*(M_PI/180.0f) );
    //模拟的距离传感器对周围环境 有360度的视场角
    float maxAngleWidth =  (float)( 360.0f*(M_PI/180.0f) );
    //相当于前面所有的 视角
    float maxAngleHeight =  (float)( 180.0f*(M_PI/180.0f) );
    //传感器位置
    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;
    //最小距离
    float minRange = 0.0f;
    //在剪裁图像时若果 此值>0,将在图像周围留下当前视点不可见的边界
    int borderSize = 1;
    pcl::RangeImage rangeImage;//声明深度图像
    /* 将点云转为 深度图像的 操作 */
    rangeImage.createFromPointCloud(*pointCloud, angularResolution, maxAngleWidth, maxAngleHeight, sensorPose, coordinate_frame, noiseLevel, minRange, borderSize);
    rangeImage.setUnseenToMaxRange ();
    //输出图像的一些信息
    std::cout << rangeImage << "\n";
    //显示点云
    pcl::visualization::PCLVisualizer viewer ("3D Viewer");
    viewer.setBackgroundColor (1, 1, 1);
    viewer.addCoordinateSystem (1.0f);
    pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> point_cloud_color_handler (pointCloud, 0, 0, 0);
    viewer.addPointCloud (pointCloud, point_cloud_color_handler, "original point cloud");

自己创建一个点云 然后 转为深度图像 和 上一节内容 一样

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    pcl::RangeImageBorderExtractor border_extractor(&rangeImage);

创建 RangeImageBorderExtractor(深度图像边界提取类) 并赋予深度图像

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /* 创建边界 的点云  pcl::BorderDescription 是一种点云类型  */
    pcl::PointCloud<pcl::BorderDescription> border_descriptions;

创建边界 的点云 pcl::BorderDescription 是一种点云类型
这种点云类型不常见,也就是在提取边界时使用。 主要的功能是 存放 三种点云边界(物体边界、阴影边界、Veil点集)


里面含义三种成员 x、y、traits
怎么使用 会在下面展示

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /*声明 物体边界 的 点云 指针*/
    pcl::PointCloud<pcl::PointWithRange>::Ptr border_points_ptr(new pcl::PointCloud<pcl::PointWithRange>);
    /*声明 Veil边界 的 点云 指针*/
    pcl::PointCloud<pcl::PointWithRange>::Ptr veil_points_ptr(new pcl::PointCloud<pcl::PointWithRange>);
    /*声明 阴影边界 的 点云 指针*/
    pcl::PointCloud<pcl::PointWithRange>::Ptr shadow_points_ptr(new pcl::PointCloud<pcl::PointWithRange>);
    /* 对应的引用 */
    pcl::PointCloud<pcl::PointWithRange> & border_points = *border_points_ptr;
    pcl::PointCloud<pcl::PointWithRange> & veil_points = * veil_points_ptr; 
    pcl::PointCloud<pcl::PointWithRange> & shadow_points = *shadow_points_ptr;

声明三种边界的点云指针和其对应引用

同样 pcl::PointWithRange 也是一种点云类型
含有x、y、z和range
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    //遍历深度图像的每一个点
    for (int y=0; y< (int)rangeImage.height; ++y)//每一行
    {
        for (int x=0; x< (int)rangeImage.width; ++x)//每一列
        {
            /*判断是否为物体边界*/
        if (border_descriptions.points[y*rangeImage.width + x].traits[pcl::BORDER_TRAIT__OBSTACLE_BORDER])
            border_points.points.push_back (rangeImage.points[y*rangeImage.width + x]);//如果是存入对应点云
            /*判断是否为Veil边界*/
        if (border_descriptions.points[y*rangeImage.width + x].traits[pcl::BORDER_TRAIT__VEIL_POINT])
            veil_points.points.push_back (rangeImage.points[y*rangeImage.width + x]);//如果是存入对应点云
            /*判断是否为阴影边界*/
        if (border_descriptions.points[y*rangeImage.width + x].traits[pcl::BORDER_TRAIT__SHADOW_BORDER])
            shadow_points.points.push_back (rangeImage.points[y*rangeImage.width + x]);//如果是存入对应点云
        }
    }

遍历深度图像的每一个点
判断 这个点 是否时边界点 并且是 什么点
这个就是 pcl::BorderDescription traits 的 用法 traits[pcl::BORDER_TRAIT__OBSTACLE_BORDER]
可以判断是哪种边界
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /* 显示 边界 点云  */
    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");

显示 边界 点云

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

CMakeList.txt

和上面的基本一致 ,但是用到了 features 模块 加入即可

# 寻找PCL的库
find_package(PCL REQUIRED COMPONENT common io visualization filters features)

Result


提取的边界改为绿色 尺寸变大


测试了下其它pcd文件的边界提取