本次博文介绍固定点拼接点云,kinect拍摄两幅画面,二者之间旋转10度,运行环境vs2012 pcl1.7.2
使用方法:
1.采样一致性初始配准算法SAC-IA,粗略估计初始变换矩阵
2.ICP算法,精确配准
原始点云(拼接前,隔着10度)
正视图
俯视图
代码:
#include <iostream>
#include <vector>
#include <Eigen/Core>
#include <pcl/registration/icp.h>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
#include <pcl/io/pcd_io.h>
#include <pcl/filters/statistical_outlier_removal.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/features/normal_3d.h>
#include <pcl/features/fpfh.h>
#include <pcl/registration/ia_ransac.h>
#include <pcl/visualization/cloud_viewer.h>
using namespace std;
using namespace pcl;
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.5f),
feature_radius_ (0.5f),
voxel_grid_size (0.07f)
{
}
~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 (tempCloud);
}
// 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 ()
{
mysample();
computeSurfaceNormals ();
computeLocalFeatures ();
}
void mysample()
{
gridsample = PointCloud::Ptr (new PointCloud);
tempCloud = PointCloud::Ptr (new PointCloud);
pcl::VoxelGrid<pcl::PointXYZ> vox_grid;
vox_grid.setInputCloud (xyz_);
vox_grid.setLeafSize (voxel_grid_size, voxel_grid_size, voxel_grid_size);
vox_grid.filter (*gridsample);
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(gridsample);
sor.setMeanK(50);
sor.setStddevMulThresh(1.0);
sor.filter(*tempCloud);
cout<<"cloud size before filtering:"<<xyz_->size()<<endl;
cout<<"cloud size after filtering:"<<tempCloud->size()<<endl;
}
// Compute the surface normals
void computeSurfaceNormals ()
{
normals_ = SurfaceNormals::Ptr (new SurfaceNormals);
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> norm_est;
norm_est.setInputCloud (tempCloud);
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 (tempCloud);
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_;
PointCloud::Ptr gridsample;
PointCloud::Ptr tempCloud;
SurfaceNormals::Ptr normals_;
LocalFeatures::Ptr features_;
SearchMethod::Ptr search_method_xyz_;
// Parameters
float normal_radius_;
float feature_radius_;
float voxel_grid_size;
};
class TemplateAlignment
{
public:
TemplateAlignment () :
min_sample_distance_ (0.01f),
max_correspondence_distance_ (0.01f*0.01f),
nr_iterations_ (300)
{
// Intialize the parameters in the Sample Consensus Intial Alignment (SAC-IA) algorithm
sac_ia_.setMinSampleDistance (min_sample_distance_);
sac_ia_.setMaxCorrespondenceDistance (max_correspondence_distance_);
sac_ia_.setMaximumIterations (nr_iterations_);
}
~TemplateAlignment () {}
void setSourceCloud(FeatureCloud &source_cloud)
{
sac_ia_.setInputCloud (source_cloud.getPointCloud ());
sac_ia_.setSourceFeatures (source_cloud.getLocalFeatures ());
}
void setTargetCloud (FeatureCloud &target_cloud)
{
sac_ia_.setInputTarget (target_cloud.getPointCloud ());
sac_ia_.setTargetFeatures (target_cloud.getLocalFeatures ());
}
// Align the given template cloud to the target specified by setTargetCloud ()
void align ()
{
pcl::PointCloud<pcl::PointXYZ> registration_output;
sac_ia_.align (registration_output);
fitness_score = (float) sac_ia_.getFitnessScore (max_correspondence_distance_);
final_transformation = sac_ia_.getFinalTransformation ();
}
Eigen::Matrix4f getMatrix()
{
return final_transformation;
}
float getScore()
{
return fitness_score;
}
private:
// 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 fitness_score;
float max_correspondence_distance_;
Eigen::Matrix4f final_transformation;
int nr_iterations_;
};
class MyICP
{
public:
MyICP ()
{
// Intialize the parameters in the ICP algorithm
icp.setMaxCorrespondenceDistance(0.01);
icp.setTransformationEpsilon(1e-7);
icp.setEuclideanFitnessEpsilon(1);
icp.setMaximumIterations(100);
}
~MyICP () {}
void setSourceCloud(PointCloud<PointXYZ>::ConstPtr source_cloud)
{
icp.setInputCloud(source_cloud);
}
void setTargetCloud (PointCloud<PointXYZ>::ConstPtr target_cloud)
{
icp.setInputTarget(target_cloud);
}
// Align the given template cloud to the target specified by setTargetCloud ()
void align (PointCloud<PointXYZ> &temp)
{
pcl::PointCloud<pcl::PointXYZ> registration_output;
icp.align (temp);
fitness_score = icp.getFitnessScore();
final_transformation = icp.getFinalTransformation ();
}
float getScore()
{
return fitness_score;
}
Eigen::Matrix4f getMatrix()
{
return final_transformation;
}
private:
IterativeClosestPoint<PointXYZ, PointXYZ> icp;
Eigen::Matrix4f final_transformation;
float fitness_score;
};
int main (int argc, char **argv)
{
int begin = 0;
int end = 2;
std::vector<FeatureCloud> object_templates;
std::stringstream ss;
TemplateAlignment my_alignment;
MyICP my_icp;
Eigen::Matrix4f GlobalTransform = Eigen::Matrix4f::Identity (), pairTransform, pairTransform2;
PointCloud<PointXYZRGB>::Ptr result (new PointCloud<PointXYZRGB>);
PointCloud<PointXYZRGB>::Ptr my_cloud (new PointCloud<PointXYZRGB>);
PointCloud<PointXYZRGB>::Ptr Color_in (new PointCloud<PointXYZRGB>);
PointCloud<PointXYZRGB>::Ptr Color_out (new PointCloud<PointXYZRGB>);
PointCloud<PointXYZRGB> Final_Color;
PointCloud<PointXYZ>::Ptr temp (new PointCloud<PointXYZ>);
PointCloud<PointXYZ> temp2;
ss.str("");
ss<<"color_"<<begin<<".pcd";
if(io::loadPCDFile<PointXYZRGB>(ss.str(),*Color_in)==-1)//*打开点云文件
{
PCL_ERROR("Couldn't read file test_pcd.pcd\n");
return(-1);
}
//load data
for(int j = begin;j < end;j++)
{
std::stringstream ss;
ss << j << ".pcd";
FeatureCloud template_cloud;
template_cloud.loadInputCloud (ss.str());
object_templates.push_back (template_cloud);
}
Final_Color = *Color_in;
for (size_t i = begin + 1; i < begin + object_templates.size (); ++i)
{
cout<<i<<endl;
//cout<<"first size:"<<object_templates[i-1].getPointCloud()->size()<<", second size:"<<object_templates[i].getPointCloud()->size()<<endl;
my_alignment.setTargetCloud(object_templates[i-1-begin]);
my_alignment.setSourceCloud(object_templates[i-begin]);
my_alignment.align();
cout<<"sac_ia fitness score:"<<my_alignment.getScore()<<endl;
//update the global transform
pairTransform = my_alignment.getMatrix();
//print matrix
printf ("\n");
printf (" | %6.3f %6.3f %6.3f %6.3f| \n", pairTransform (0,0), pairTransform (0,1), pairTransform (0,2), pairTransform (0,3));
printf ("R = | %6.3f %6.3f %6.3f %6.3f| \n", pairTransform (1,0), pairTransform (1,1), pairTransform (1,2), pairTransform (1,3));
printf (" | %6.3f %6.3f %6.3f %6.3f| \n", pairTransform (2,0), pairTransform (2,1), pairTransform (2,2), pairTransform (2,3));
printf (" | %6.3f %6.3f %6.3f %6.3f| \n", pairTransform (3,0), pairTransform (3,1), pairTransform (3,2), pairTransform (3,3));
GlobalTransform = GlobalTransform * pairTransform;
//GlobalTransform = pairTransform;
//transform current pair into the global transform
pcl::transformPointCloud (*object_templates[i-begin].getPointCloud(), *temp, GlobalTransform);
my_icp.setSourceCloud(temp);
my_icp.setTargetCloud(object_templates[i-1-begin].getPointCloud());
my_icp.align(temp2);
cout<<"icp fitness score:"<<my_icp.getScore()<<endl;
pairTransform2 = my_icp.getMatrix();
printf ("\n");
printf (" | %6.3f %6.3f %6.3f %6.3f| \n", pairTransform2 (0,0), pairTransform2 (0,1), pairTransform2 (0,2), pairTransform2 (0,3));
printf ("R = | %6.3f %6.3f %6.3f %6.3f| \n", pairTransform2 (1,0), pairTransform2 (1,1), pairTransform2 (1,2), pairTransform2 (1,3));
printf (" | %6.3f %6.3f %6.3f %6.3f| \n", pairTransform2 (2,0), pairTransform2 (2,1), pairTransform2 (2,2), pairTransform2 (2,3));
printf (" | %6.3f %6.3f %6.3f %6.3f| \n", pairTransform2 (3,0), pairTransform2 (3,1), pairTransform2 (3,2), pairTransform2 (3,3));
GlobalTransform = GlobalTransform * pairTransform2;
ss.str("");
ss<<"color_"<<i<<".pcd";
if(pcl::io::loadPCDFile<pcl::PointXYZRGB>(ss.str(),*Color_out)==-1)//*打开点云彩色文件
{
PCL_ERROR("Couldn't read file test_pcd.pcd\n");
return(-1);
}
//transform current pair into the global transform
pcl::transformPointCloud (*Color_out, *result, GlobalTransform);
Final_Color = Final_Color + *result;
}
//构造拼接临时的点云
for(int i=0;i< Final_Color.points.size();i++)
{
pcl::PointXYZRGB basic_point;
basic_point.x = Final_Color.points[i].x;
basic_point.y = Final_Color.points[i].y;
basic_point.z = Final_Color.points[i].z;
basic_point.r = Final_Color.points[i].r;
basic_point.g = Final_Color.points[i].g;
basic_point.b = Final_Color.points[i].b;
my_cloud->points.push_back(basic_point);
}
pcl::visualization::CloudViewer viewer("My Cloud Viewer");
viewer.showCloud(my_cloud);
while(!viewer.wasStopped())
{
}
return (0);
}
结果如下,角度不见了~~
别高兴太早,这套算法如果这么牛逼,我也不用这么蛋疼了。如果用他拼接360度,必定失败,如果有用这个方法能搞定连续多幅图片拼接的朋友,请私信我。
下面是我用NDT方法,连续拼接90度的结果,只能这样了。。。
Filtered cloud contains 540
ndt fitness score:0.0227071
| 0.985 0.007 -0.174 0.003|
R = | -0.007 1.000 0.002 0.000|
| 0.174 -0.000 0.985 -0.006|
| 0.000 0.000 0.000 1.000|
Filtered cloud contains 420
ndt fitness score:0.0343324
| 0.989 0.040 -0.146 0.005|
R = | -0.037 0.999 0.021 -0.005|
| 0.146 -0.015 0.989 -0.005|
| 0.000 0.000 0.000 1.000|
Filtered cloud contains 552
ndt fitness score:0.0802134
| 0.968 -0.016 -0.249 0.152|
R = | 0.021 1.000 0.016 -0.014|
| 0.248 -0.020 0.969 -0.012|
| 0.000 0.000 0.000 1.000|
Filtered cloud contains 926
ndt fitness score:0.0198928
| 0.978 -0.015 -0.210 0.148|
R = | 0.019 1.000 0.017 -0.024|
| 0.209 -0.020 0.978 0.016|
| 0.000 0.000 0.000 1.000|
Filtered cloud contains 575
ndt fitness score:0.0492542
| 0.962 -0.007 -0.273 0.085|
R = | 0.006 1.000 -0.001 -0.002|
| 0.273 -0.000 0.962 -0.009|
| 0.000 0.000 0.000 1.000|
Filtered cloud contains 412
ndt fitness score:0.00171811
| 0.992 -0.024 -0.127 0.001|
R = | 0.023 1.000 -0.007 -0.000|
| 0.127 0.004 0.992 0.003|
| 0.000 0.000 0.000 1.000|
Filtered cloud contains 295
ndt fitness score:0.00152303
| 0.983 -0.001 -0.182 0.086|
R = | 0.003 1.000 0.010 0.038|
| 0.182 -0.011 0.983 0.090|
| 0.000 0.000 0.000 1.000|
Filtered cloud contains 191
ndt fitness score:0.023204
| 0.975 -0.080 -0.208 0.121|
R = | 0.092 0.995 0.047 -0.142|
| 0.203 -0.065 0.977 0.103|
| 0.000 0.000 0.000 1.000|
Filtered cloud contains 133
ndt fitness score:0.00556793
| 0.983 0.003 -0.184 0.008|
R = | -0.004 1.000 -0.002 0.000|
| 0.184 0.003 0.983 0.011|
| 0.000 0.000 0.000 1.000|