点云外接立方体(3D物体包容盒)
使用pcl::MomentOfInertiaEstimation
类来获取基于偏心率和惯性矩的描述符。该类还允许提取云的轴对齐和定向的边界框。但是提取的OBB并非最小可能的边界框。
原理简述¶
包围体(包容盒)是一个简单的几何空间,里面包含着复杂形状的物体。为物体添加包围体的目的是快速的进行碰撞检测或者进行精确的碰撞检测之前进行过滤(即当包围体碰撞,才进行精确碰撞检测和处理)。包围体类型包括球体、轴对齐包围盒(AABB)、有向包围盒(OBB)、8-DOP以及凸壳(CONVEX HULL)。
常见包容盒( Bounding Volumes)分类:
- 包容球:SPHERE 用球体包围整个几何体,用于相交测试很方便,但是其紧密型差,周围空隙较大,当物体变形后,包围球需要重新计算。当对象进行旋转运动时,包围球不需要做任何更新,这是包围球的优势,即当几何对象频繁进行旋转运动时,使用包围球效率较高。
- AABB包容盒:Axially Aligned Bounding Box,3D环境下的AABB盒即一个六面体,每个边都平行于一个坐标平面,较简单,但紧密性较差,当物体旋转、形变之后需要对AABB进行更新。本身的长宽高根据物体大小而定。
- OBB包容盒:Oriented Bounding Box,此方法紧密型较好,可以降低参与相交测试的包容盒数目,因此性能要优于AABB和包容球。当物体发生旋转,仅需对OBB进行相同的旋转即可,但是当物体形变后,更新OBB的代价较大,故不适用那些软体的对象。
如上图所示,还有K-DOP,CONVEX HULL等包容盒,越靠右,包容效果好、越紧密。但是检测速度更慢,也更消耗内存资源。
#include <vector>
#include <thread>
#include <pcl/features/moment_of_inertia_estimation.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/cloud_viewer.h>
using namespace std::chrono_literals;
int main (int argc, char** argv)
{
//if (argc != 2)
// return (0);
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ> ());
if (pcl::io::loadPCDFile ("table_scene_lms400_downsampled.pcd", *cloud) == -1)
return (-1);
pcl::MomentOfInertiaEstimation <pcl::PointXYZ> feature_extractor;
feature_extractor.setInputCloud (cloud);
feature_extractor.compute ();
std::vector <float> moment_of_inertia;
std::vector <float> eccentricity;
pcl::PointXYZ min_point_AABB;
pcl::PointXYZ max_point_AABB;
pcl::PointXYZ min_point_OBB;
pcl::PointXYZ max_point_OBB;
pcl::PointXYZ position_OBB;
Eigen::Matrix3f rotational_matrix_OBB;
float major_value, middle_value, minor_value;
Eigen::Vector3f major_vector, middle_vector, minor_vector;
Eigen::Vector3f mass_center;
feature_extractor.getMomentOfInertia (moment_of_inertia);
feature_extractor.getEccentricity (eccentricity);
feature_extractor.getAABB (min_point_AABB, max_point_AABB);
feature_extractor.getOBB (min_point_OBB, max_point_OBB, position_OBB, rotational_matrix_OBB);
feature_extractor.getEigenValues (major_value, middle_value, minor_value);
feature_extractor.getEigenVectors (major_vector, middle_vector, minor_vector);
feature_extractor.getMassCenter (mass_center);
pcl::visualization::PCLVisualizer::Ptr viewer (new pcl::visualization::PCLVisualizer ("3D Viewer"));
viewer->setBackgroundColor (0, 0, 0);
viewer->addCoordinateSystem (1);
viewer->initCameraParameters ();
viewer->addPointCloud<pcl::PointXYZ> (cloud, "sample cloud");
viewer->addCube (min_point_AABB.x, max_point_AABB.x, min_point_AABB.y, max_point_AABB.y, min_point_AABB.z, max_point_AABB.z, 1.0, 1.0, 0.0, "AABB");
viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION, pcl::visualization::PCL_VISUALIZER_REPRESENTATION_WIREFRAME, "AABB");
Eigen::Vector3f position (position_OBB.x, position_OBB.y, position_OBB.z);
Eigen::Quaternionf quat (rotational_matrix_OBB);
viewer->addCube (position, quat, max_point_OBB.x - min_point_OBB.x, max_point_OBB.y - min_point_OBB.y, max_point_OBB.z - min_point_OBB.z, "OBB");
viewer->setShapeRenderingProperties(pcl::visualization::PCL_VISUALIZER_REPRESENTATION, pcl::visualization::PCL_VISUALIZER_REPRESENTATION_WIREFRAME, "OBB");
pcl::PointXYZ center (mass_center (0), mass_center (1), mass_center (2));
pcl::PointXYZ x_axis (major_vector (0) + mass_center (0), major_vector (1) + mass_center (1), major_vector (2) + mass_center (2));
pcl::PointXYZ y_axis (middle_vector (0) + mass_center (0), middle_vector (1) + mass_center (1), middle_vector (2) + mass_center (2));
pcl::PointXYZ z_axis (minor_vector (0) + mass_center (0), minor_vector (1) + mass_center (1), minor_vector (2) + mass_center (2));
viewer->addLine (center, x_axis, 1.0f, 0.0f, 0.0f, "major eigen vector");
viewer->addLine (center, y_axis, 0.0f, 1.0f, 0.0f, "middle eigen vector");
viewer->addLine (center, z_axis, 0.0f, 0.0f, 1.0f, "minor eigen vector");
while(!viewer->wasStopped())
{
viewer->spinOnce (100);
std::this_thread::sleep_for(100ms);
}
return (0);
}
黄色立方体为AABB包容盒,白色立方体为OBB包容盒
点云模板匹配
#include <limits>
#include <fstream>
#include <vector>
#include <Eigen/Core>
#include <pcl/memory.h>
#include <pcl/pcl_macros.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/pcd_io.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/filters/passthrough.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/features/normal_3d.h>
#include <pcl/features/fpfh.h>
#include <pcl/registration/ia_ransac.h>
class FeatureCloud
{
public:
// A bit of shorthand
typedef pcl::PointCloud<pcl::PointXYZ> PointCloud;
typedef pcl::PointCloud<pcl::Normal> SurfaceNormals;
typedef pcl::PointCloud<pcl::FPFHSignature33> LocalFeatures;
typedef pcl::search::KdTree<pcl::PointXYZ> SearchMethod;
FeatureCloud () :
search_method_xyz_ (new SearchMethod),
normal_radius_ (0.02f),
feature_radius_ (0.02f)
{}
~FeatureCloud () {}
// Process the given cloud
void
setInputCloud (PointCloud::Ptr xyz)
{
xyz_ = xyz;
processInput ();
}
// Load and process the cloud in the given PCD file
void
loadInputCloud (const std::string &pcd_file)
{
xyz_ = PointCloud::Ptr (new PointCloud);
pcl::io::loadPCDFile (pcd_file, *xyz_);
processInput ();
}
// Get a pointer to the cloud 3D points
PointCloud::Ptr
getPointCloud () const
{
return (xyz_);
}
// Get a pointer to the cloud of 3D surface normals
SurfaceNormals::Ptr
getSurfaceNormals () const
{
return (normals_);
}
// Get a pointer to the cloud of feature descriptors
LocalFeatures::Ptr
getLocalFeatures () const
{
return (features_);
}
protected:
// Compute the surface normals and local features
void
processInput ()
{
computeSurfaceNormals ();
computeLocalFeatures ();
}
// Compute the surface normals
void
computeSurfaceNormals ()
{
normals_ = SurfaceNormals::Ptr (new SurfaceNormals);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> norm_est;
norm_est.setInputCloud (xyz_);
norm_est.setSearchMethod (search_method_xyz_);
norm_est.setRadiusSearch (normal_radius_);
norm_est.compute (*normals_);
}
// Compute the local feature descriptors
void
computeLocalFeatures ()
{
features_ = LocalFeatures::Ptr (new LocalFeatures);
pcl::FPFHEstimation<pcl::PointXYZ, pcl::Normal, pcl::FPFHSignature33> fpfh_est;
fpfh_est.setInputCloud (xyz_);
fpfh_est.setInputNormals (normals_);
fpfh_est.setSearchMethod (search_method_xyz_);
fpfh_est.setRadiusSearch (feature_radius_);
fpfh_est.compute (*features_);
}
private:
// Point cloud data
PointCloud::Ptr xyz_;
SurfaceNormals::Ptr normals_;
LocalFeatures::Ptr features_;
SearchMethod::Ptr search_method_xyz_;
// Parameters
float normal_radius_;
float feature_radius_;
};
class TemplateAlignment
{
public:
// A struct for storing alignment results
struct Result
{
float fitness_score;
Eigen::Matrix4f final_transformation;
PCL_MAKE_ALIGNED_OPERATOR_NEW
};
TemplateAlignment () :
min_sample_distance_ (0.05f),
max_correspondence_distance_ (0.01f*0.01f),
nr_iterations_ (500)
{
// Initialize the parameters in the Sample Consensus Initial Alignment (SAC-IA) algorithm
sac_ia_.setMinSampleDistance (min_sample_distance_);
sac_ia_.setMaxCorrespondenceDistance (max_correspondence_distance_);
sac_ia_.setMaximumIterations (nr_iterations_);
}
~TemplateAlignment () {}
// Set the given cloud as the target to which the templates will be aligned
void
setTargetCloud (FeatureCloud &target_cloud)
{
target_ = target_cloud;
sac_ia_.setInputTarget (target_cloud.getPointCloud ());
sac_ia_.setTargetFeatures (target_cloud.getLocalFeatures ());
}
// Add the given cloud to the list of template clouds
void
addTemplateCloud (FeatureCloud &template_cloud)
{
templates_.push_back (template_cloud);
}
// Align the given template cloud to the target specified by setTargetCloud ()
void
align (FeatureCloud &template_cloud, TemplateAlignment::Result &result)
{
sac_ia_.setInputSource (template_cloud.getPointCloud ());
sac_ia_.setSourceFeatures (template_cloud.getLocalFeatures ());
pcl::PointCloud<pcl::PointXYZ> registration_output;
sac_ia_.align (registration_output);
result.fitness_score = (float) sac_ia_.getFitnessScore (max_correspondence_distance_);
result.final_transformation = sac_ia_.getFinalTransformation ();
}
// Align all of template clouds set by addTemplateCloud to the target specified by setTargetCloud ()
void
alignAll (std::vector<TemplateAlignment::Result, Eigen::aligned_allocator<Result> > &results)
{
results.resize (templates_.size ());
for (std::size_t i = 0; i < templates_.size (); ++i)
{
align (templates_[i], results[i]);
}
}
// Align all of template clouds to the target cloud to find the one with best alignment score
int
findBestAlignment (TemplateAlignment::Result &result)
{
// Align all of the templates to the target cloud
std::vector<Result, Eigen::aligned_allocator<Result> > results;
alignAll (results);
// Find the template with the best (lowest) fitness score
float lowest_score = std::numeric_limits<float>::infinity ();
int best_template = 0;
for (std::size_t i = 0; i < results.size (); ++i)
{
const Result &r = results[i];
if (r.fitness_score < lowest_score)
{
lowest_score = r.fitness_score;
best_template = (int) i;
}
}
// Output the best alignment
result = results[best_template];
return (best_template);
}
private:
// A list of template clouds and the target to which they will be aligned
std::vector<FeatureCloud> templates_;
FeatureCloud target_;
// The Sample Consensus Initial Alignment (SAC-IA) registration routine and its parameters
pcl::SampleConsensusInitialAlignment<pcl::PointXYZ, pcl::PointXYZ, pcl::FPFHSignature33> sac_ia_;
float min_sample_distance_;
float max_correspondence_distance_;
int nr_iterations_;
};
// Align a collection of object templates to a sample point cloud
int
main (int argc, char **argv)
{
if (argc < 3)
{
printf ("No target PCD file given!\n");
return (-1);
}
// Load the object templates specified in the object_templates.txt file
std::vector<FeatureCloud> object_templates;
std::ifstream input_stream (argv[1]);
object_templates.resize (0);
std::string pcd_filename;
while (input_stream.good ())
{
std::getline (input_stream, pcd_filename);
if (pcd_filename.empty () || pcd_filename.at (0) == '#') // Skip blank lines or comments
continue;
FeatureCloud template_cloud;
template_cloud.loadInputCloud (pcd_filename);
object_templates.push_back (template_cloud);
}
input_stream.close ();
// Load the target cloud PCD file
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud (new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile (argv[2], *cloud);
// Preprocess the cloud by...
// ...removing distant points
const float depth_limit = 1.0;
pcl::PassThrough<pcl::PointXYZ> pass;
pass.setInputCloud (cloud);
pass.setFilterFieldName ("z");
pass.setFilterLimits (0, depth_limit);
pass.filter (*cloud);
// ... and downsampling the point cloud
const float voxel_grid_size = 0.005f;
pcl::VoxelGrid<pcl::PointXYZ> vox_grid;
vox_grid.setInputCloud (cloud);
vox_grid.setLeafSize (voxel_grid_size, voxel_grid_size, voxel_grid_size);
//vox_grid.filter (*cloud); // Please see this http://www.pcl-developers.org/Possible-problem-in-new-VoxelGrid-implementation-from-PCL-1-5-0-td5490361.html
pcl::PointCloud<pcl::PointXYZ>::Ptr tempCloud (new pcl::PointCloud<pcl::PointXYZ>);
vox_grid.filter (*tempCloud);
cloud = tempCloud;
// Assign to the target FeatureCloud
FeatureCloud target_cloud;
target_cloud.setInputCloud (cloud);
// Set the TemplateAlignment inputs
TemplateAlignment template_align;
for (std::size_t i = 0; i < object_templates.size (); ++i)
{
template_align.addTemplateCloud (object_templates[i]);
}
template_align.setTargetCloud (target_cloud);
// Find the best template alignment
TemplateAlignment::Result best_alignment;
int best_index = template_align.findBestAlignment (best_alignment);
const FeatureCloud &best_template = object_templates[best_index];
// Print the alignment fitness score (values less than 0.00002 are good)
printf ("Best fitness score: %f\n", best_alignment.fitness_score);
// Print the rotation matrix and translation vector
Eigen::Matrix3f rotation = best_alignment.final_transformation.block<3,3>(0, 0);
Eigen::Vector3f translation = best_alignment.final_transformation.block<3,1>(0, 3);
printf ("\n");
printf (" | %6.3f %6.3f %6.3f | \n", rotation (0,0), rotation (0,1), rotation (0,2));
printf ("R = | %6.3f %6.3f %6.3f | \n", rotation (1,0), rotation (1,1), rotation (1,2));
printf (" | %6.3f %6.3f %6.3f | \n", rotation (2,0), rotation (2,1), rotation (2,2));
printf ("\n");
printf ("t = < %0.3f, %0.3f, %0.3f >\n", translation (0), translation (1), translation (2));
// Save the aligned template for visualization
pcl::PointCloud<pcl::PointXYZ> transformed_cloud;
pcl::transformPointCloud (*best_template.getPointCloud (), transformed_cloud, best_alignment.final_transformation);
pcl::io::savePCDFileBinary ("output.pcd", transformed_cloud);
return (0);
}