旋转投影统计(RoPs)特征
在本教程中,我们将学习如何使用pcl::ROPSEstimation类来提取点特性。在这门课中实现的特征提取方法是由Yulan Guo, Ferdous Sohel, Mohammed Bennamoun, Min Lu and Jianwei Wanalso在他们的文章《旋转投影统计用于三维局部表面描述和目标识别》中提出的。
理论基础
特征提取方法的思想如下。有了网格和一组必须计算特征的点,我们执行一些简单的步骤。首先,对于给定的兴趣点,局部表面被裁剪。局部曲面由给定支撑半径内的点和三角形组成。对给定的局部表面LRF(局部参考帧)进行了计算。LRF只是向量的一个三元组,关于如何计算这些向量的综合信息可以在本文中找到。真正重要的是利用这些向量我们可以提供点云旋转的不变性。为此,我们简单地平移局部表面的点,使感兴趣的点成为原点,然后旋转局部表面,使LRF向量与Ox、Oy和Oz轴对齐。完成这些之后,我们就开始特征提取。对于每一个坐标轴Ox, Oy, Oz执行以下步骤,我们将这些坐标轴称为当前轴:
- 局部曲面绕当前轴旋转一定角度;
- 将旋转后的局部曲面的点投影到XY、XZ、YZ三个平面上;
- 对于每一个投影分布矩阵的建立,这个矩阵只是简单地显示有多少点落在每个bin上。bin的数表示矩阵维数,为算法参数,为支撑半径;
- 计算每个分布矩阵的中心矩:M11、M12、M21、M22, E,其中E为香农熵;
- 然后将计算值连接起来形成子特性。
我们重复这些步骤几次。迭代次数取决于给定的旋转次数。不同轴的子特性被连接起来形成最终的RoPS描述符。
代码
在本教程中,我们将使用皇后数据集中的模型。您可以选择任何其他点云,但是为了使代码工作,您需要使用三角剖分算法来获得多边形。你可在此找到建议的模型:
- 点-包含点云
- 索引——包含必须计算RoPs的点的索引
- 三角形——包含多边形
接下来你需要做的是在任何你喜欢的编辑器中创建一个文件rops_features .cpp,并在其中复制以下代码:
#include <pcl/features/rops_estimation.h>
#include <pcl/io/pcd_io.h>
int main (int argc, char** argv)
{
if (argc != 4)
return (-1);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ> ());
if (pcl::io::loadPCDFile (argv[1], *cloud) == -1)
return (-1);
pcl::PointIndicesPtr indices (new pcl::PointIndices);
std::ifstream indices_file;
indices_file.open (argv[2], std::ifstream::in);
for (std::string line; std::getline (indices_file, line);)
{
std::istringstream in (line);
unsigned int index = 0;
in >> index;
indices->indices.push_back (index - 1);
}
indices_file.close ();
std::vector <pcl::Vertices> triangles;
std::ifstream triangles_file;
triangles_file.open (argv[3], std::ifstream::in);
for (std::string line; std::getline (triangles_file, line);)
{
pcl::Vertices triangle;
std::istringstream in (line);
unsigned int vertex = 0;
in >> vertex;
triangle.vertices.push_back (vertex - 1);
in >> vertex;
triangle.vertices.push_back (vertex - 1);
in >> vertex;
triangle.vertices.push_back (vertex - 1);
triangles.push_back (triangle);
}
float support_radius = 0.0285f;
unsigned int number_of_partition_bins = 5;
unsigned int number_of_rotations = 3;
pcl::search::KdTree<pcl::PointXYZ>::Ptr search_method (new pcl::search::KdTree<pcl::PointXYZ>);
search_method->setInputCloud (cloud);
pcl::ROPSEstimation <pcl::PointXYZ, pcl::Histogram <135> > feature_estimator;
feature_estimator.setSearchMethod (search_method);
feature_estimator.setSearchSurface (cloud);
feature_estimator.setInputCloud (cloud);
feature_estimator.setIndices (indices);
feature_estimator.setTriangles (triangles);
feature_estimator.setRadiusSearch (support_radius);
feature_estimator.setNumberOfPartitionBins (number_of_partition_bins);
feature_estimator.setNumberOfRotations (number_of_rotations);
feature_estimator.setSupportRadius (support_radius);
pcl::PointCloud<pcl::Histogram <135> >::Ptr histograms (new pcl::PointCloud <pcl::Histogram <135> > ());
feature_estimator.compute (*histograms);
return (0);
}
解释
现在让我们研究一下这段代码的目的。
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ> ());
if (pcl::io::loadPCDFile (argv[1], *cloud) == -1)
return (-1);
这些行只是从.pcd文件加载点云。
pcl::PointIndicesPtr indices (new pcl::PointIndices);
std::ifstream indices_file;
indices_file.open (argv[2], std::ifstream::in);
for (std::string line; std::getline (indices_file, line);)
{
std::istringstream in (line);
unsigned int index = 0;
in >> index;
indices->indices.push_back (index - 1);
}
indices_file.close ();
这里加载了必须计算RoPS特性的点的索引。如果你想要的,您可以对它进行注释,并为云中的每个点计算特性。
std::vector <pcl::Vertices> triangles;
std::ifstream triangles_file;
triangles_file.open (argv[3], std::ifstream::in);
for (std::string line; std::getline (triangles_file, line);)
{
pcl::Vertices triangle;
std::istringstream in (line);
unsigned int vertex = 0;
in >> vertex;
triangle.vertices.push_back (vertex - 1);
in >> vertex;
triangle.vertices.push_back (vertex - 1);
in >> vertex;
triangle.vertices.push_back (vertex - 1);
triangles.push_back (triangle);
}
这些行加载关于多边形的信息。如果只有点云而不是网格,可以用三角剖分的代码替换它们。
float support_radius = 0.0285f;
unsigned int number_of_partition_bins = 5;
unsigned int number_of_rotations = 3;
这些代码定义了重要的算法参数:支持局部曲面裁剪的半径、用于形成分布矩阵的分区桶数和旋转数。最后一个参数影响描述符的长度。
pcl::search::KdTree<pcl::PointXYZ>::Ptr search_method (new pcl::search::KdTree<pcl::PointXYZ>);
search_method->setInputCloud (cloud);
这些行设置了算法将使用的搜索方法。
pcl::ROPSEstimation <pcl::PointXYZ, pcl::Histogram <135> > feature_estimator;
feature_estimator.setSearchMethod (search_method);
feature_estimator.setSearchSurface (cloud);
feature_estimator.setInputCloud (cloud);
feature_estimator.setIndices (indices);
feature_estimator.setTriangles (triangles);
feature_estimator.setRadiusSearch (support_radius);
feature_estimator.setNumberOfPartitionBins (number_of_partition_bins);
feature_estimator.setNumberOfRotations (number_of_rotations);
feature_estimator.setSupportRadius (support_radius);
这里是pcl::ROPSEstimation类实例化的地方。它有两个参数:
- PointInT - 输入点的类型;
- PointOutT - 输出点的类型。
之后,我们立即设置了用于特征计算的所有必要数据的输入。
pcl::PointCloud<pcl::Histogram <135> >::Ptr histograms (new pcl::PointCloud <pcl::Histogram <135> > ());
feature_estimator.compute (*histograms);
这里是启动计算过程的地方。
编译和运行程序
在CMakeLists.txt文件中添加以下行:
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(rops_feature)
find_package(PCL 1.8 REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
add_executable (rops_feature rops_feature.cpp)
target_link_libraries (rops_feature ${PCL_LIBRARIES})
完成可执行文件后,就可以运行它了。只是做的事:
$ ./rops_feature points.pcd indices.txt triangles.txt