How to use iterative closest point
This document demonstrates using the Iterative Closest Point algorithm in your code which can determine if one PointCloud is just a rigid transformation of another by minimizing the distances between the points of two pointclouds and rigidly transforming them.
The code
1#include <iostream>
2#include <pcl/io/pcd_io.h>
3#include <pcl/point_types.h>
4#include <pcl/registration/icp.h>
5
6int
7 main ()
8{
9 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in (new pcl::PointCloud<pcl::PointXYZ>(5,1));
10 pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out (new pcl::PointCloud<pcl::PointXYZ>);
11
12 // Fill in the CloudIn data
13 for (auto& point : *cloud_in)
14 {
15 point.x = 1024 * rand() / (RAND_MAX + 1.0f);
16 point.y = 1024 * rand() / (RAND_MAX + 1.0f);
17 point.z = 1024 * rand() / (RAND_MAX + 1.0f);
18 }
19
20 std::cout << "Saved " << cloud_in->size () << " data points to input:" << std::endl;
21
22 for (auto& point : *cloud_in)
23 std::cout << point << std::endl;
24
25 *cloud_out = *cloud_in;
26
27 std::cout << "size:" << cloud_out->size() << std::endl;
28 for (auto& point : *cloud_out)
29 point.x += 0.7f;
30
31 std::cout << "Transformed " << cloud_in->size () << " data points:" << std::endl;
32
33 for (auto& point : *cloud_out)
34 std::cout << point << std::endl;
35
36 pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;
37 icp.setInputSource(cloud_in);
38 icp.setInputTarget(cloud_out);
39
40 pcl::PointCloud<pcl::PointXYZ> Final;
41 icp.align(Final);
42
43 std::cout << "ICP has " << (icp.hasConverged()?"converged":"not converged") << ", score: " <<
44 icp.getFitnessScore() << std::endl;
45 std::cout << icp.getFinalTransformation() << std::endl;
46
47 return (0);
48}
The explanation
Now, let’s breakdown this code piece by piece.
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/registration/icp.h>
these are the header files that contain the definitions for all of the classes which we will use.
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_in (new pcl::PointCloud<pcl::PointXYZ>(5,1));
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_out (new pcl::PointCloud<pcl::PointXYZ>);
Creates two pcl::PointCloud<pcl::PointXYZ> boost shared pointers and initializes them. The type of each point is set to PointXYZ in the pcl namespace which is:
// \brief A point structure representing Euclidean xyz coordinates. struct PointXYZ { float x; float y; float z; };
The lines:
// Fill in the CloudIn data
for (auto& point : *cloud_in)
{
point.x = 1024 * rand() / (RAND_MAX + 1.0f);
point.y = 1024 * rand() / (RAND_MAX + 1.0f);
point.z = 1024 * rand() / (RAND_MAX + 1.0f);
}
std::cout << "Saved " << cloud_in->size () << " data points to input:" << std::endl;
for (auto& point : *cloud_in)
std::cout << point << std::endl;
*cloud_out = *cloud_in;
std::cout << "size:" << cloud_out->size() << std::endl;
for (auto& point : *cloud_out)
point.x += 0.7f;
fill in the PointCloud structure with random point values, and set the appropriate parameters (width, height, is_dense). Also, they output the number of points saved, and their actual data values.
Then:
std::cout << "Transformed " << cloud_in->size () << " data points:" << std::endl;
for (auto& point : *cloud_out)
std::cout << point << std::endl;
pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp;
performs a simple rigid transform on the pointcloud and again outputs the data values.
icp.setInputSource(cloud_in);
icp.setInputTarget(cloud_out);
This creates an instance of an IterativeClosestPoint and gives it some useful information. “icp.setInputSource(cloud_in);” sets cloud_in as the PointCloud to begin from and “icp.setInputTarget(cloud_out);” sets cloud_out as the PointCloud which we want cloud_in to look like.
Next,
pcl::PointCloud<pcl::PointXYZ> Final;
icp.align(Final);
std::cout << "ICP has " << (icp.hasConverged()?"converged":"not converged") << ", score: " <<
icp.getFitnessScore() << std::endl;
Creates a pcl::PointCloud<pcl::PointXYZ> to which the IterativeClosestPoint can save the resultant cloud after applying the algorithm. If the two PointClouds align correctly (meaning they are both the same cloud merely with some kind of rigid transformation applied to one of them) then icp.hasConverged() = 1 (true). It then outputs the fitness score of the final transformation and some information about it.
Compiling and running the program
Add the following lines to your CMakeLists.txt file:
1cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
2
3project(iterative_closest_point)
4
5find_package(PCL 1.2 REQUIRED)
6
7include_directories(${PCL_INCLUDE_DIRS})
8link_directories(${PCL_LIBRARY_DIRS})
9add_definitions(${PCL_DEFINITIONS})
10
11add_executable (iterative_closest_point iterative_closest_point.cpp)
12target_link_libraries (iterative_closest_point ${PCL_LIBRARIES})
After you have made the executable, you can run it. Simply do:
$ ./iterative_closest_point
You will see something similar to:
Saved 5 data points to input:
0.352222 -0.151883 -0.106395
-0.397406 -0.473106 0.292602
-0.731898 0.667105 0.441304
-0.734766 0.854581 -0.0361733
-0.4607 -0.277468 -0.916762
size:5
Transformed 5 data points:
1.05222 -0.151883 -0.106395
0.302594 -0.473106 0.292602
-0.0318983 0.667105 0.441304
-0.0347655 0.854581 -0.0361733
0.2393 -0.277468 -0.916762
[pcl::SampleConsensusModelRegistration::setInputCloud] Estimated a sample
selection distance threshold of: 0.200928
[pcl::IterativeClosestPoint::computeTransformation] Number of
correspondences 4 [80.000000%] out of 5 points [100.0%], RANSAC rejected:
1 [20.000000%].
[pcl::IterativeClosestPoint::computeTransformation] Convergence reached.
Number of iterations: 1 out of 0. Transformation difference: 0.700001
ICP has converged, score: 1.95122e-14
1 4.47035e-08 -3.25963e-09 0.7
2.98023e-08 1 -1.08499e-07 -2.98023e-08
1.30385e-08 -1.67638e-08 1 1.86265e-08
0 0 0 1