这里主要研究PCL库中的Spin Image描述子,具体文献如下:
- Johnson, A.E., Spin-Images: A Representation for 3-D Surface Matching. Carnegie Mellon University, 1997.
- Johnson, A.E. and M. Hebert, Surface matching for object recognition in complex three-dimensional scenes. Image and Vision Computing, 1998. 16(9): p. 635-651.
- Johnson, A.E. and M. Hebert, Using spin images for efficient object recognition in cluttered 3D scenes. IEEE Transactions on Pattern Analysis and Machine Intelligence, 1999. 21(5): p. 433-449.
Spin Image的主要参数有:
- image_width_:图片宽度,是指图片有多少格 (行和列的格子数目是一样的,都是通过这个参数设置);
- search_radius_点的搜索半径,即圆柱坐标系的R,也是查找点的时候设置的查找半径,即FLANN中的R;
- bin_size:即图片格子的大小,对应图片的分辨率 该参数由image_width_和search_radius_共同决定,pcl库中的代码如下:
// OK, we are interested in the points of the cylinder of height 2*r and
// base radius r, where r = m_dBinSize * in_iImageWidth
// it can be embedded to the sphere of radius sqrt(2) * m_dBinSize * in_iImageWidth
// suppose that points are uniformly distributed, so we lose ~40%
// according to the volumes ratio
double bin_size = 0.0;
if (is_radial_)
bin_size = search_radius_ / image_width_;
else
bin_size = search_radius_ / image_width_ / sqrt(2.0);
- min_pts_neighb_:该参数限定了圆柱坐标系内出现的最少点数,即R半径范围内的点数要大于该值,否则会出现中断;
- support_angle_cos_:该参数限定R半径内搜索到的点的法线与关键点的法线的夹角,该值是余弦值,范围在0-1之间,该参数可以减少与关键点法线方向相反的点,增加鲁棒性,对应文献的说法是:“However, a small support angle is necessary for robustness to clutter and occlusion.”
2-D points 转为spin image的转变是通过线性插值实现,原理如下图:
最后将spin image矩阵转为直方图的形式。
PCL提取spin image的代码如下:
// Feature_demo.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <pcl/io/ply_io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/features/normal_3d.h>
#include <pcl/features/spin_image.h>
//#include "spin_image.h"
//#include "spin_image.hpp"
int main(int, char** argv)
{
std::string filename = "E:/3DData/santong/scene/01.ply";
std::cout << "Reading " << filename << std::endl;
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
if (pcl::io::loadPLYFile <pcl::PointXYZ>(filename.c_str(), *cloud) == -1)
// load the file
{
PCL_ERROR("Couldn't read file");
return (-1);
}
std::cout << "Loaded " << cloud->points.size() << " points." << std::endl;
// Compute the normals
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> normal_estimation;
normal_estimation.setInputCloud(cloud);
pcl::search::KdTree<pcl::PointXYZ>::Ptr kdtree(new pcl::search::KdTree<pcl::PointXYZ>);
normal_estimation.setSearchMethod(kdtree);
pcl::PointCloud<pcl::Normal>::Ptr normals(new pcl::PointCloud< pcl::Normal>);
//normal_estimation.setRadiusSearch(30);
normal_estimation.setKSearch(8);
normal_estimation.compute(*normals);
// Setup spin image computation
pcl::SpinImageEstimation<pcl::PointXYZ, pcl::Normal, pcl::Histogram<153> > spin_image_descriptor(8, 0.5, 10);
spin_image_descriptor.setInputCloud(cloud);
spin_image_descriptor.setInputNormals(normals);
// Use the same KdTree from the normal estimation
spin_image_descriptor.setSearchMethod(kdtree);
pcl::PointCloud<pcl::Histogram<153> >::Ptr spin_images(new pcl::PointCloud<pcl::Histogram<153> >);
spin_image_descriptor.setRadiusSearch(8);
// Actually compute the spin images
spin_image_descriptor.compute(*spin_images);
std::cout << "SI output points.size (): " << spin_images->points.size() << std::endl;
// Display and retrieve the spin image descriptor vector for the first point.
pcl::Histogram<153> first_descriptor = spin_images->points[0];
std::cout << first_descriptor << std::endl;
return 0;
}