20 #include <pcl/filters/voxel_grid.h> 21 #include <pcl/keypoints/harris_3d.h> 22 #include <pcl/features/boundary.h> 23 #include <pcl/features/multiscale_feature_persistence.h> 24 #include <pcl/features/fpfh.h> 25 #include <pcl/registration/correspondence_estimation.h> 26 #include <pcl/registration/correspondence_rejection_sample_consensus.h> 27 #include <pcl/registration/correspondence_rejection_one_to_one.h> 28 #include <pcl/registration/transformation_estimation_svd.h> 29 #include <pcl/segmentation/extract_clusters.h> 30 #include <pcl/common/common.h> 42 correspondences_(new pcl::Correspondences)
65 ROS_INFO(
"Not running initial alignment method");
83 ROS_INFO(
"Initial alignment method: NORMALS");
86 pcl::IndicesPtr source_non_boundary_indices(
new std::vector<int>);
87 pcl::IndicesPtr target_non_boundary_indices(
new std::vector<int>);
88 pcl::IndicesPtr keypoints_indices(
new std::vector<int>);
93 PointCloudRGB::Ptr source_cloud_no_boundary(
new PointCloudRGB);
94 PointCloudRGB::Ptr target_cloud_no_boundary(
new PointCloudRGB);
97 Filter::extractIndices(
source_cloud_, source_cloud_no_boundary, source_non_boundary_indices);
98 Filter::extractIndices(
target_cloud_, target_cloud_no_boundary, target_non_boundary_indices);
99 Filter::extractIndices(
source_normals_, source_normals_no_boundary, source_non_boundary_indices);
100 Filter::extractIndices(
target_normals_, target_normals_no_boundary, target_non_boundary_indices);
103 std::vector<pcl::PointIndices> source_cluster_indices;
104 std::vector<pcl::PointIndices> target_cluster_indices;
105 obtainNormalsCluster(source_cloud_no_boundary, source_normals_no_boundary, source_cluster_indices);
106 obtainNormalsCluster(target_cloud_no_boundary, target_normals_no_boundary, target_cluster_indices);
114 ROS_ERROR(
"Failed to obtain dominant normals");
131 PointCloudNormal::Ptr normals,
132 std::vector<pcl::PointIndices>& cluster_indices)
139 const float tolerance = 0.05f;
140 const double eps_angle = 20 * (M_PI / 180.0);
141 const double PERCENTAJE = 0.025;
142 const unsigned int min_cluster_size = (int)normals->size()*PERCENTAJE;
144 pcl::KdTree<pcl::PointXYZRGB>::Ptr tree(
new pcl::KdTreeFLANN<pcl::PointXYZRGB>());
145 tree->setInputCloud(cloud);
146 ROS_INFO(
"Clustering normals with min cluster size: %d...", min_cluster_size);
148 pcl::extractEuclideanClusters(*cloud, *normals, tolerance, tree, cluster_indices, eps_angle, min_cluster_size);
150 ROS_INFO(
"Clusters found: %zd", cluster_indices.size());
152 if(cluster_indices.size() == 0)
154 ROS_ERROR(
"Failed to obtain normals cluster");
155 pcl::PointIndices::Ptr point_indices(
new pcl::PointIndices);
156 for(
int i=0; i<cloud->size(); i++)
157 point_indices->indices.push_back(i);
159 cluster_indices.push_back(*point_indices);
164 std::vector<pcl::PointIndices> cluster_indices,
165 PointCloudNormal::Ptr dominant_normals)
167 for (std::vector<pcl::PointIndices>::const_iterator it=cluster_indices.begin(); it!=cluster_indices.end(); it++)
170 for (
const auto &index : it->indices)
171 normal_cluster->push_back((*normals)[index]);
173 Eigen::Vector4f vector_dominant_normal=normal_cluster->points[0].getNormalVector4fMap();
174 for (
const auto &normal : normal_cluster->points)
176 Eigen::Vector4f vector_normal = normal.getNormalVector4fMap();
177 vector_dominant_normal += vector_normal;
179 vector_dominant_normal.normalize();
181 p.normal_x = vector_dominant_normal.x();
182 p.normal_y = vector_dominant_normal.y();
183 p.normal_z = vector_dominant_normal.z();
185 dominant_normals->points.push_back(p);
190 PointCloudNormal::Ptr target_normals)
192 std::vector<int> s_idx, t_idx;
195 if(s_idx.size() != t_idx.size() || s_idx.size()<2)
ROS_ERROR(
"Index sizes not match");
198 ROS_INFO(
"Found correspondent normals");
200 Eigen::Matrix4f transform;
201 transform.setIdentity();
205 transform.block<3,3>(0,0) = target_rot * source_rot.transpose();
208 Eigen::Vector4f translation = transform * diff_pose;
209 transform(0,3) = translation[0];
210 transform(1,3) = translation[1];
211 transform(2,3) = translation[2];
214 if(transform != Eigen::Matrix4f::Zero())
224 int current_col,
int current_row)
226 int ncols = matrix.row(0).size() - (current_col+1);
227 Eigen::MatrixXd v(1, ncols);
228 v = matrix.block(current_row, current_col+1, 1, ncols);
234 Eigen::MatrixXi source_idx_m, Eigen::MatrixXi target_idx_m,
235 std::vector<int>& source_indx, std::vector<int>& target_indx)
239 if(source_m.rows()<2 || target_m.rows()<2)
241 ROS_WARN(
"Couldn't start findIdx on matrix of size < 2");
242 source_indx.push_back(0);
243 target_indx.push_back(0);
249 for(
int s_col=0; s_col<(source_m.cols()-2) && count<2; s_col++)
251 for (
int s_row=0; s_row<source_m.rows(); s_row++)
254 if (std::find(source_indx.begin(), source_indx.end(), s_row) == source_indx.end())
256 for (
int t_row=0; t_row<target_m.rows() && !found; t_row++)
258 if (std::find(target_indx.begin(), target_indx.end(), t_row) == target_indx.end())
264 Eigen::VectorXd v =
nextVector(target_m, t_col, t_row);
270 source_indx.push_back(s_row);
271 target_indx.push_back(t_row);
273 source_indx.push_back(source_idx_m(s_row,s_col));
274 target_indx.push_back(target_idx_m(t_row,t_col));
284 if(count<2)
return false;
290 PointCloudNormal::Ptr target_dominant_normal,
291 std::vector<int>& source_idx, std::vector<int>& target_idx)
293 size_t source_size = source_dominant_normal->size();
294 int source_m_size = (int)source_size - 1 ;
295 size_t target_size = target_dominant_normal->size();
296 int target_m_size = (int)target_size - 1 ;
298 if(source_m_size==1 || target_m_size==1)
ROS_WARN(
"Matrix size=one could case some errors");
300 std::vector<double> source_angle_vector, target_angle_vector;
301 Eigen::MatrixXd source_m(source_m_size, source_m_size);
302 Eigen::MatrixXd target_m(target_m_size, target_m_size);
303 Eigen::MatrixXi source_idx_m(source_m_size, source_m_size);
304 Eigen::MatrixXi target_idx_m(target_m_size, target_m_size);
306 matrixFromAngles(source_dominant_normal, source_m_size, source_m, source_idx_m);
307 matrixFromAngles(target_dominant_normal, target_m_size, target_m, target_idx_m);
309 if(source_m.row(0).size() == target_m.row(0).size())
312 ROS_INFO(
"No rows coincident. Search for approximations...");
314 if(source_m.rows() < target_m.rows())
316 if(!
findIdx(source_m, target_m, source_idx_m, target_idx_m, source_idx, target_idx))
321 if(!
findIdx(target_m, source_m, target_idx_m, source_idx_m, target_idx, source_idx))
328 std::vector<int>& index)
330 Eigen::Vector3f v1, v2;
335 Eigen::Vector3f orto_v = v1.cross(v2);
338 Eigen::Matrix3f coord_sys;
339 coord_sys.col(0) = v1;
340 coord_sys.col(1) = orto_v;
341 coord_sys.col(2) = v1.cross(orto_v).normalized();
347 Eigen::MatrixXd& matrix,
348 Eigen::MatrixXi& index_matrix)
350 for (
int row=0; row<size; row++)
352 pcl::Normal n = normal->points[row];
353 Eigen::Vector4f init_v = n.getNormalVector4fMap();
355 for(
int col=0, j=row+1; col<size; col++, j++)
358 n = normal->points[j];
359 Eigen::Vector4f v = n.getNormalVector4fMap();
360 matrix(row,col) = pcl::getAngle3D(init_v, v);
361 index_matrix(row,col) = j;
367 PointCloudNormal::Ptr normals,
368 PointCloudRGB::Ptr keypoints_cloud,
369 PointCloudFPFH::Ptr features_cloud)
371 pcl::IndicesPtr keypoints_indices(
new std::vector<int>);
375 ROS_INFO(
"Initial alignment method: HARRIS");
377 obtainFeatures(cloud, normals, keypoints_indices, features_cloud);
382 ROS_INFO(
"Initial alignment method: BOUNDARY");
383 pcl::IndicesPtr non_keypoints_indices(
new std::vector<int>);
386 if (
obtainFeatures(cloud, normals, keypoints_indices, features_cloud) == -1)
392 ROS_INFO(
"Initial alignment method: MULTISCALE");
400 PointCloudNormal::Ptr normals,
401 PointCloudRGB::Ptr keypoints_cloud,
402 pcl::IndicesPtr keypoints_indices)
407 pcl::PointCloud<pcl::PointXYZI>::Ptr keypoints(
new pcl::PointCloud<pcl::PointXYZI>);
408 pcl::HarrisKeypoint3D<pcl::PointXYZRGB, pcl::PointXYZI> detector;
409 detector.setNonMaxSupression(
true);
411 detector.setInputCloud(cloud);
412 detector.setNormals(normals);
413 detector.setThreshold(1e-6);
414 detector.setRadius(keypoint_radius);
415 detector.compute(*keypoints);
416 pcl::PointIndicesConstPtr k_indices = detector.getKeypointsIndices();
418 if (k_indices->indices.empty())
return -1;
420 *keypoints_indices = k_indices->indices;
421 Filter::extractIndices(cloud, keypoints_cloud, keypoints_indices);
427 PointCloudNormal::Ptr normals,
428 PointCloudRGB::Ptr keypoints_cloud,
429 pcl::IndicesPtr keypoints_indices,
430 pcl::IndicesPtr non_keypoints_indices)
432 pcl::PointCloud<pcl::Boundary> boundaries;
433 pcl::BoundaryEstimation<pcl::PointXYZRGB, pcl::Normal, pcl::Boundary> detector;
434 detector.setInputCloud(cloud);
435 detector.setInputNormals(normals);
436 detector.setKSearch(30);
437 detector.setAngleThreshold(1.047
f);
438 detector.compute(boundaries);
440 if(boundaries.size() != cloud->size())
return -1;
442 for (
int i=0; i<cloud->size(); i++)
444 if (boundaries.points[i].boundary_point == 1)
446 keypoints_indices->push_back(i);
447 keypoints_cloud->points.push_back(cloud->points[i]);
450 non_keypoints_indices->push_back(i);
453 if(keypoints_indices->size() == 0)
return -1;
459 PointCloudNormal::Ptr normals,
460 PointCloudRGB::Ptr keypoints_cloud,
461 pcl::IndicesPtr keypoints_indices,
462 PointCloudFPFH::Ptr features)
464 pcl::FPFHEstimation<pcl::PointXYZRGB, pcl::Normal, pcl::FPFHSignature33>::Ptr fest(
465 new pcl::FPFHEstimation<pcl::PointXYZRGB, pcl::Normal, pcl::FPFHSignature33>);
466 fest->setInputCloud(cloud);
467 fest->setInputNormals(normals);
468 pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree(
new pcl::search::KdTree<pcl::PointXYZRGB>());
469 fest->setSearchMethod(tree);
471 std::vector<float> scale_values{0.5f, 1.0f, 1.5f};
472 pcl::MultiscaleFeaturePersistence<pcl::PointXYZRGB, pcl::FPFHSignature33> fper;
473 fper.setScalesVector(scale_values);
474 fper.setAlpha(0.45
f);
475 fper.setFeatureEstimator(fest);
476 fper.setDistanceMetric(pcl::CS);
477 fper.determinePersistentFeatures(*features, keypoints_indices);
479 Filter::extractIndices(cloud, keypoints_cloud, keypoints_indices);
481 if(keypoints_indices->size() == 0)
return -1;
482 if(keypoints_indices->size() != features->size())
return -1;
488 PointCloudNormal::Ptr normals,
489 pcl::IndicesPtr keypoints_indices,
490 PointCloudFPFH::Ptr features)
495 pcl::search::KdTree<pcl::PointXYZRGB>::Ptr tree(
new pcl::search::KdTree<pcl::PointXYZRGB>());
496 pcl::FPFHEstimation<pcl::PointXYZRGB, pcl::Normal, pcl::FPFHSignature33>::Ptr fest(
497 new pcl::FPFHEstimation<pcl::PointXYZRGB, pcl::Normal, pcl::FPFHSignature33>);
498 fest->setInputCloud(cloud);
499 fest->setInputNormals(normals);
500 fest->setIndices(keypoints_indices);
501 fest->setSearchMethod(tree);
502 fest->setRadiusSearch(feature_radius);
503 fest->compute(*features);
505 if (features->size() == 0)
return -1;
512 pcl::registration::CorrespondenceEstimation<pcl::FPFHSignature33, pcl::FPFHSignature33> cest;
521 pcl::registration::CorrespondenceRejectorSampleConsensus<pcl::PointXYZRGB> rejector;
524 rejector.setInlierThreshold(1.5);
525 rejector.setRefineModel(
true);
528 ROS_INFO(
"th: %f", rejector.getInlierThreshold());
534 pcl::registration::CorrespondenceRejectorOneToOne rejector;
542 pcl::registration::TransformationEstimationSVD<pcl::PointXYZRGB, pcl::PointXYZRGB> trans_est;
554 pcl::transformPointCloud(*cloud, *cloud,
rigid_tf_);
559 pcl::transformPointCloud(*cloud, *cloud, tf);
PointCloudNormal::Ptr target_normals_
Target normals.
static void printTransform(const Eigen::Matrix4f &transform)
Print on console transform values with matrix format.
Eigen::Vector4f source_centroid_
Source centroid computed.
PointCloudNormal::Ptr target_dominant_normals_
Target dominant normals. Just used int NORMALS method.
PointCloudRGB::Ptr source_keypoints_
Source keypoints.
void applyTFtoCloud()
Once initial alignment is finished, apply rigid transformation to source cloud.
static bool getNormals(PointCloudRGB::Ptr &cloud, double normal_radius, PointCloudNormal::Ptr &normals)
Get the normals for input cloud with specified normal's radius.
pcl::PointCloud< pcl::FPFHSignature33 > PointCloudFPFH
PointCloudFPFH::Ptr source_features_
Source features.
InitialAlignment(PointCloudRGB::Ptr target_cloud, PointCloudRGB::Ptr source_cloud)
Construct a new Initial Alignment object.
void estimateTransform()
Get transform from computed correspondences.
PointCloudRGB::Ptr aligned_cloud_
Source pointcloud aligned with target cloud.
Eigen::VectorXd nextVector(Eigen::MatrixXd matrix, int current_col, int current_row)
Get next matrix vector with extracted current col and row.
static bool searchForSameRows(Eigen::MatrixXd source_m, Eigen::MatrixXd target_m, std::vector< int > &source_indx, std::vector< int > &target_indx)
Check if both matrix have a coinciden row.
void obtainNormalsCluster(PointCloudRGB::Ptr cloud, PointCloudNormal::Ptr normals, std::vector< pcl::PointIndices > &cluster_indices)
Cluster clouds normals.
bool transform_exists_
If true, rigid transformation is complete.
void runNormalsBasedAlgorithm()
If set method is NORMALS run algorithm. Cluster normals and found correspondences between dominant no...
static int findOnVector(double value, const Eigen::VectorXd v, double threshold)
Find value on vector with given threshold. Returns index vector of value found. If not found returns ...
pcl::CorrespondencesPtr correspondences_
Correspondences between source and target.
AlignmentMethod
Define possible Alignment methods.
void getTransformationFromNormals(PointCloudNormal::Ptr source_normals, PointCloudNormal::Ptr target_normals)
Get transformation between cloud based on normals correspondences.
void rejectOneToOneCorrespondences()
Reject computed correspondences.
void getAlignedCloud(PointCloudRGB::Ptr aligned_cloud)
Get the aligned cloud object.
void obtainCorrespondences()
Get correspondences between clouds from computed features.
static void getVectorFromNormal(PointCloudNormal::Ptr normal, double idx, Eigen::Vector3f &vector)
Obtain normal values as a vector.
static void colorizeCloud(PointCloudRGB::Ptr cloud_rgb, int R, int G, int B)
Apply RGB values to cloud.
static bool isValidCloud(PointCloudXYZ::Ptr cloud)
Check whether cloud contains data and is not empty.
Eigen::Vector4f target_centroid_
Target centroid computed.
static void onePointCloud(PointCloudRGB::Ptr cloud, int size, PointCloudRGB::Ptr one_point_cloud)
Apply extract indices filter to input cloud with given indices.
void obtainDominantNormals(PointCloudNormal::Ptr normals, std::vector< pcl::PointIndices > cluster_indices, PointCloudNormal::Ptr dominant_normals)
Obtain dominant normal in given cluster.
int obtainFeatures(PointCloudRGB::Ptr cloud, PointCloudNormal::Ptr normals, pcl::IndicesPtr keypoints_indices, PointCloudFPFH::Ptr features)
Obtain cloud features based on given keypoints.
Eigen::Matrix3f getCoordinateSystem(PointCloudNormal::Ptr normals, std::vector< int > &index)
Create coordinate system from corresponden normals.
void run()
Perform initial alignment.
int obtainBoundaryKeypoints(PointCloudRGB::Ptr cloud, PointCloudNormal::Ptr normals, PointCloudRGB::Ptr keypoints_cloud, pcl::IndicesPtr keypoints_indices, pcl::IndicesPtr non_keypoints_indices)
Obtain cloud keypoints based on boundary method.
Eigen::Matrix4f getRigidTransform()
Get the rigid transform object.
pcl::PointCloud< pcl::PointXYZRGB > PointCloudRGB
static void cloudToROSMsg(PointCloudRGB::Ptr cloud, sensor_msgs::PointCloud2 &cloud_msg, const std::string &frameid="world")
Convert XYZRGB cloud to PointCloud2.
PointCloudRGB::Ptr target_keypoints_
Target keypoints.
AlignmentMethod method_
The alignment method.
PointCloudRGB::Ptr target_cloud_
Target pointcloud.
void matrixFromAngles(PointCloudNormal::Ptr normal, size_t size, Eigen::MatrixXd &matrix, Eigen::MatrixXi &index_matrix)
Construct matrix based on angles between normals.
pcl::PointCloud< pcl::Normal > PointCloudNormal
void getAlignedCloudROSMsg(sensor_msgs::PointCloud2 &aligned_cloud_msg)
Get the aligned cloud msg in PointCloud2 format.
bool normalsCorrespondences(PointCloudNormal::Ptr source_dominant_normal, PointCloudNormal::Ptr target_dominant_normal, std::vector< int > &source_idx, std::vector< int > &target_idx)
Found correspondences between dominant normals.
double normal_radius_
Radius to compute normals.
PointCloudNormal::Ptr source_dominant_normals_
Source dominant normals. Just used int NORMALS method.
static double computeCloudResolution(PointCloudXYZ::Ptr cloud)
Obtain the resolution of the cloud.
void rejectCorrespondences()
Reject computed correspondences.
int obtainHarrisKeypoints(PointCloudRGB::Ptr cloud, PointCloudNormal::Ptr normals, PointCloudRGB::Ptr keypoints_cloud, pcl::IndicesPtr keypoints_indices)
Obtain cloud keypoints based on harris method.
bool findIdx(Eigen::MatrixXd source_m, Eigen::MatrixXd target_m, Eigen::MatrixXi source_idx_m, Eigen::MatrixXi target_idx_m, std::vector< int > &source_indx, std::vector< int > &target_indx)
Function to get index for Source matrix value in Target matrix.
Eigen::Matrix4f rigid_tf_
Transformation matrix as result of initial alignment.
void setMethod(AlignmentMethod method)
Set the AlignmentMethod object.
void setRadiusFactor(double align_factor)
Set the Radius Factor object.
PointCloudFPFH::Ptr target_features_
Target features.
int obtainMultiScaleKeypointsAndFeatures(PointCloudRGB::Ptr cloud, PointCloudNormal::Ptr normals, PointCloudRGB::Ptr keypoints_cloud, pcl::IndicesPtr keypoints_indices, PointCloudFPFH::Ptr features)
Obtain cloud keypoints based on multiscale method.
PointCloudRGB::Ptr source_cloud_
Source pointcloud.
PointCloudNormal::Ptr source_normals_
Source normals.
int obtainKeypointsAndFeatures(PointCloudRGB::Ptr cloud, PointCloudNormal::Ptr normals, PointCloudRGB::Ptr keypoints_cloud, PointCloudFPFH::Ptr features_cloud)
Obtain keypoints and features based on set method.
void runKeypointsBasedAlgorithm()
Run keypoints based algorithm. Compute keypoints and features, found correspondences and get transfor...
double radius_factor_
Factor to apply to radius for computations.