38#ifndef PCL_REGISTRATION_IMPL_IA_FPCS_H_
39#define PCL_REGISTRATION_IMPL_IA_FPCS_H_
43#include <pcl/common/utils.h>
44#include <pcl/registration/ia_fpcs.h>
45#include <pcl/registration/transformation_estimation_3point.h>
46#include <pcl/sample_consensus/sac_model_plane.h>
49template <
typename Po
intT>
55 const float max_dist_sqr = max_dist * max_dist;
56 const std::size_t s = cloud.
size();
61 float mean_dist = 0.f;
64 std::vector<float> dists_sqr(2);
67#pragma omp parallel for \
70 firstprivate(ids, dists_sqr) \
71 reduction(+:mean_dist, num) \
72 firstprivate(s, max_dist_sqr) \
73 num_threads(nr_threads)
74 for (
int i = 0; i < 1000; i++) {
76 if (dists_sqr[1] < max_dist_sqr) {
77 mean_dist += std::sqrt(dists_sqr[1]);
82 return (mean_dist / num);
86template <
typename Po
intT>
93 const float max_dist_sqr = max_dist * max_dist;
94 const std::size_t s = indices.size();
99 float mean_dist = 0.f;
102 std::vector<float> dists_sqr(2);
105#if OPENMP_LEGACY_CONST_DATA_SHARING_RULE
106#pragma omp parallel for \
108 shared(tree, cloud, indices) \
109 firstprivate(ids, dists_sqr) \
110 reduction(+:mean_dist, num) \
111 num_threads(nr_threads)
113#pragma omp parallel for \
115 shared(tree, cloud, indices, s, max_dist_sqr) \
116 firstprivate(ids, dists_sqr) \
117 reduction(+:mean_dist, num) \
118 num_threads(nr_threads)
120 for (
int i = 0; i < 1000; i++) {
121 tree.
nearestKSearch((*cloud)[indices[rand() % s]], 2, ids, dists_sqr);
122 if (dists_sqr[1] < max_dist_sqr) {
123 mean_dist += std::sqrt(dists_sqr[1]);
128 return (mean_dist / num);
132template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
156 reg_name_ =
"pcl::registration::FPCSInitialAlignment";
164template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
177#pragma omp parallel default(none) shared(abort, all_candidates, timer) \
178 num_threads(nr_threads_)
181 const unsigned int seed =
182 static_cast<unsigned int>(std::time(NULL)) ^ omp_get_thread_num();
184 PCL_DEBUG(
"[%s::computeTransformation] Using seed=%u\n",
reg_name_.c_str(), seed);
185#pragma omp for schedule(dynamic)
188#pragma omp flush(abort)
192 all_candidates[i] = candidates;
206 std::vector<pcl::Indices> matches;
211 if (!candidates.empty())
212 all_candidates[i] = candidates;
218 abort = (!candidates.empty() ? candidates[0].fitness_score <
score_threshold_
222#pragma omp flush(abort)
237template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
242 const unsigned int seed = std::time(
nullptr);
244 PCL_DEBUG(
"[%s::initCompute] Using seed=%u\n",
reg_name_.c_str(), seed);
252 PCL_ERROR(
"[%s::initCompute] Source or target dataset not given!\n",
261 target_index = index++;
268 const int ss =
static_cast<int>(
indices_->size());
269 const int sample_fraction_src = std::max(1,
static_cast<int>(ss /
nr_samples_));
272 for (
int i = 0; i < ss; i++)
273 if (rand() % sample_fraction_src == 0)
291 const int min_iterations = 4;
292 const float diameter_fraction = 0.3f;
295 Eigen::Vector4f pt_min, pt_max;
315 std::log(1.0 - std::pow((
double)
approx_overlap_, (
double)min_iterations));
317 static_cast<int>(first_est / (diameter_fraction *
approx_overlap_ * 2.f));
344template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
351 Eigen::VectorXf coefficients(4);
354 Eigen::Vector4f centre_pt;
355 float nearest_to_plane = FLT_MAX;
364 pcl::Indices base_triple(base_indices.begin(), base_indices.end() - 1);
365 plane.computeModelCoefficients(base_triple, coefficients);
369 const PointTarget* pt1 = &((*target_)[base_indices[0]]);
370 const PointTarget* pt2 = &((*target_)[base_indices[1]]);
371 const PointTarget* pt3 = &((*target_)[base_indices[2]]);
374 const PointTarget* pt4 = &((*target_)[target_index]);
379 float d4 = (pt4->getVector3fMap() - centre_pt.head(3)).squaredNorm();
383 if (d1 < too_close_sqr || d2 < too_close_sqr || d3 < too_close_sqr ||
390 if (dist_to_plane < nearest_to_plane) {
391 base_indices[3] = target_index;
392 nearest_to_plane = dist_to_plane;
397 if (nearest_to_plane != FLT_MAX) {
400 setupBase(base_indices, ratio);
410template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
419 base_indices[0] = (*target_indices_)[rand() % nr_points];
420 auto* index1 = &base_indices[0];
424 auto* index2 = &(*target_indices_)[rand() % nr_points];
425 auto* index3 = &(*target_indices_)[rand() % nr_points];
428 (*target_)[*index2].getVector3fMap() - (*target_)[*index1].getVector3fMap();
430 (*target_)[*index3].getVector3fMap() - (*target_)[*index1].getVector3fMap();
432 u.cross(v).squaredNorm();
438 base_indices[1] = *index2;
439 base_indices[2] = *index3;
444 return (best_t == 0.f ? -1 : 0);
448template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
453 float best_t = FLT_MAX;
455 pcl::Indices temp(base_indices.begin(), base_indices.end());
458 for (
auto i = copy.begin(), i_e = copy.end(); i != i_e; ++i)
459 for (
auto j = copy.begin(), j_e = copy.end(); j != j_e; ++j) {
463 for (
auto k = copy.begin(), k_e = copy.end(); k != k_e; ++k) {
464 if (k == j || k == i)
467 auto l = copy.begin();
468 while (l == i || l == j || l == k)
482 ratio[0] = ratio_temp[0];
483 ratio[1] = ratio_temp[1];
491template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
497 Eigen::Vector3f u = (*target_)[base_indices[1]].getVector3fMap() -
498 (*target_)[base_indices[0]].getVector3fMap();
499 Eigen::Vector3f v = (*target_)[base_indices[3]].getVector3fMap() -
500 (*target_)[base_indices[2]].getVector3fMap();
501 Eigen::Vector3f w = (*target_)[base_indices[0]].getVector3fMap() -
502 (*target_)[base_indices[2]].getVector3fMap();
510 float D = a * c - b * b;
511 float sN = 0.f, sD = D;
512 float tN = 0.f, tD = D;
522 sN = (b * e - c * d);
523 tN = (a * e - b * d);
558 else if ((-d + b) > a)
568 ratio[0] = (std::abs(sN) <
small_error_) ? 0.f : sN / sD;
569 ratio[1] = (std::abs(tN) <
small_error_) ? 0.f : tN / tD;
571 Eigen::Vector3f x = w + (ratio[0] * u) - (ratio[1] * v);
576template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
585 float ref_norm_angle =
586 (
use_normals_ ? ((*target_normals_)[idx1].getNormalVector3fMap() -
594 for (; it_out != it_out_e; it_out++) {
595 auto it_in = it_out + 1;
596 const PointSource* pt1 = &(*input_)[*it_out];
597 for (; it_in != it_in_e; it_in++) {
598 const PointSource* pt2 = &(*input_)[*it_in];
605 const NormalT* pt1_n = &((*source_normals_)[*it_out]);
606 const NormalT* pt2_n = &((*source_normals_)[*it_in]);
609 (pt1_n->getNormalVector3fMap() - pt2_n->getNormalVector3fMap()).norm();
611 (pt1_n->getNormalVector3fMap() + pt2_n->getNormalVector3fMap()).norm();
613 float norm_diff = std::min<float>(std::abs(norm_angle_1 - ref_norm_angle),
614 std::abs(norm_angle_2 - ref_norm_angle));
615 if (norm_diff > max_norm_diff)
626 return (pairs.empty() ? -1 : 0);
630template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
634 std::vector<pcl::Indices>& matches,
637 const float (&ratio)[2])
653 cloud_e->resize(pairs_a.size() * 2);
654 PointCloudSourceIterator it_pt = cloud_e->begin();
655 for (
const auto& pair : pairs_a) {
656 const PointSource* pt1 = &((*input_)[pair.index_match]);
657 const PointSource* pt2 = &((*input_)[pair.index_query]);
660 for (
int i = 0; i < 2; i++, it_pt++) {
661 it_pt->x = pt1->x + ratio[i] * (pt2->x - pt1->x);
662 it_pt->y = pt1->y + ratio[i] * (pt2->y - pt1->y);
663 it_pt->z = pt1->z + ratio[i] * (pt2->z - pt1->z);
669 tree_e->setInputCloud(cloud_e);
672 std::vector<float> dists_sqr;
675 for (
const auto& pair : pairs_b) {
676 const PointTarget* pt1 = &((*input_)[pair.index_match]);
677 const PointTarget* pt2 = &((*input_)[pair.index_query]);
680 for (
const float& r : ratio) {
682 pt_e.x = pt1->x + r * (pt2->x - pt1->x);
683 pt_e.y = pt1->y + r * (pt2->y - pt1->y);
684 pt_e.z = pt1->z + r * (pt2->z - pt1->z);
688 for (
const auto&
id : ids) {
692 pairs_a[
static_cast<int>(std::floor((
float)(
id / 2.f)))].index_match;
694 pairs_a[
static_cast<int>(std::floor((
float)(
id / 2.f)))].index_query;
695 match_indices[2] = pair.index_match;
696 match_indices[3] = pair.index_query;
702 matches.push_back(match_indices);
708 return (!matches.empty() ? 0 : -1);
712template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
736template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
740 std::vector<pcl::Indices>& matches,
743 candidates.resize(1);
744 float fitness_score = FLT_MAX;
747 for (
auto& match : matches) {
748 Eigen::Matrix4f transformation_temp;
756 if (
validateMatch(base_indices, match, correspondences_temp, transformation_temp) <
766 candidates[0].fitness_score = fitness_score;
767 candidates[0].transformation = transformation_temp;
768 correspondences_temp.erase(correspondences_temp.end() - 1);
769 candidates[0].correspondences = correspondences_temp;
774template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
782 Eigen::Vector4f centre_base{0, 0, 0, 0}, centre_match{0, 0, 0, 0};
786 PointTarget centre_pt_base;
787 centre_pt_base.x = centre_base[0];
788 centre_pt_base.y = centre_base[1];
789 centre_pt_base.z = centre_base[2];
791 PointSource centre_pt_match;
792 centre_pt_match.x = centre_match[0];
793 centre_pt_match.y = centre_match[1];
794 centre_pt_match.z = centre_match[2];
799 auto it_match_orig = match_indices.begin();
800 for (
auto it_base = base_indices.cbegin(), it_base_e = base_indices.cend();
801 it_base != it_base_e;
802 it_base++, it_match_orig++) {
805 float best_diff_sqr = FLT_MAX;
808 for (
const auto& match_index : copy) {
812 float diff_sqr = std::abs(dist_sqr_1 - dist_sqr_2);
814 if (diff_sqr < best_diff_sqr) {
815 best_diff_sqr = diff_sqr;
816 best_index = match_index;
822 *it_match_orig = best_index;
827template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
833 Eigen::Matrix4f& transformation)
837 correspondences_temp.erase(correspondences_temp.end() - 1);
848 std::size_t nr_points = correspondences_temp.size();
850 for (std::size_t i = 0; i < nr_points; i++)
852 target_->points[base_indices[i]]);
859template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
869 std::size_t nr_points = source_transformed.size();
870 std::size_t terminate_value =
871 fitness_score > 1 ? 0
872 :
static_cast<std::size_t
>((1.f - fitness_score) * nr_points);
874 float inlier_score_temp = 0;
876 std::vector<float> dists_sqr;
877 PointCloudSourceIterator it = source_transformed.begin();
879 for (std::size_t i = 0; i < nr_points; it++, i++) {
881 tree_->nearestKSearch(*it, 1, ids, dists_sqr);
885 if (nr_points - i + inlier_score_temp < terminate_value)
890 inlier_score_temp /=
static_cast<float>(nr_points);
891 float fitness_score_temp = 1.f - inlier_score_temp;
893 if (fitness_score_temp > fitness_score)
896 fitness_score = fitness_score_temp;
901template <
typename Po
intSource,
typename Po
intTarget,
typename NormalT,
typename Scalar>
904 finalCompute(
const std::vector<MatchingCandidates>& candidates)
907 int nr_candidates =
static_cast<int>(candidates.size());
909 float best_score = FLT_MAX;
910 for (
int i = 0; i < nr_candidates; i++) {
911 const float& fitness_score = candidates[i][0].fitness_score;
912 if (fitness_score < best_score) {
913 best_score = fitness_score;
919 if (!(best_index < 0)) {
PointCloudConstPtr input_
bool initCompute()
This method should get called before starting the actual computation.
shared_ptr< const PointCloud< PointT > > ConstPtr
bool target_cloud_updated_
Matrix4 final_transformation_
typename KdTreeReciprocal::Ptr KdTreeReciprocalPtr
pcl::PointCloud< PointSource > PointCloudSource
typename PointCloudSource::Ptr PointCloudSourcePtr
CorrespondencesPtr correspondences_
TransformationEstimationPtr transformation_estimation_
pcl::search::KdTree< PointSource > KdTreeReciprocal
PointCloudTargetConstPtr target_
SampleConsensusModelPlane defines a model for 3D plane segmentation.
double getTimeSeconds() const
Retrieve the time in seconds spent since the last call to reset().
virtual void finalCompute(const std::vector< MatchingCandidates > &candidates)
Final computation of best match out of vector of best matches.
void setupBase(pcl::Indices &base_indices, float(&ratio)[2])
Setup the base (four coplanar points) by ordering the points and computing intersection ratios and se...
bool use_normals_
Use normals flag.
int selectBaseTriangle(pcl::Indices &base_indices)
float max_edge_diff_
Maximal difference between the length of the base edges and valid match edges.
virtual int bruteForceCorrespondences(int idx1, int idx2, pcl::Correspondences &pairs)
Search for corresponding point pairs given the distance between two base points.
float delta_
Delta value of 4pcs algorithm (standard = 1.0).
int selectBase(pcl::Indices &base_indices, float(&ratio)[2])
Select an approximately coplanar set of four points from the source cloud.
float max_base_diameter_sqr_
Estimated squared metric overlap between source and target.
float coincidation_limit_
Maximal distance between coinciding intersection points to find valid matches.
virtual int validateTransformation(Eigen::Matrix4f &transformation, float &fitness_score)
Validate the transformation by calculating the number of inliers after transforming the source cloud.
virtual int determineBaseMatches(const pcl::Indices &base_indices, std::vector< pcl::Indices > &matches, const pcl::Correspondences &pairs_a, const pcl::Correspondences &pairs_b, const float(&ratio)[2])
Determine base matches by combining the point pair candidate and search for coinciding intersection p...
virtual void linkMatchWithBase(const pcl::Indices &base_indices, pcl::Indices &match_indices, pcl::Correspondences &correspondences)
Sets the correspondences between the base B and the match M by using the distance of each point to th...
virtual void handleMatches(const pcl::Indices &base_indices, std::vector< pcl::Indices > &matches, MatchingCandidates &candidates)
Method to handle current candidate matches.
float max_inlier_dist_sqr_
Maximal squared point distance between source and target points to count as inlier.
bool normalize_delta_
Normalize delta flag.
NormalsConstPtr target_normals_
Normals of target point cloud.
NormalsConstPtr source_normals_
Normals of source point cloud.
pcl::IndicesPtr target_indices_
A pointer to the vector of target point indices to use after sampling.
float max_mse_
Maximal mean squared errors of a transformation calculated from a candidate match.
int checkBaseMatch(const pcl::Indices &match_indices, const float(&ds)[4])
Check if outer rectangle distance of matched points fit with the base rectangle.
int nr_samples_
The number of points to uniformly sample the source point cloud.
float approx_overlap_
Estimated overlap between source and target (standard = 0.5).
pcl::IndicesPtr source_indices_
A pointer to the vector of source point indices to use after sampling.
float segmentToSegmentDist(const pcl::Indices &base_indices, float(&ratio)[2])
int nr_threads_
Number of threads for parallelization (standard = 1).
virtual int validateMatch(const pcl::Indices &base_indices, const pcl::Indices &match_indices, const pcl::Correspondences &correspondences, Eigen::Matrix4f &transformation)
Validate the matching by computing the transformation between the source and target based on the four...
float max_norm_diff_
Maximum normal difference of corresponding point pairs in degrees (standard = 90).
float fitness_score_
Resulting fitness score of the best match.
float diameter_
Estimated diamter of the target point cloud.
FPCSInitialAlignment()
Constructor.
float max_pair_diff_
Maximal difference between corresponding point pairs in source and target.
const float small_error_
Definition of a small error.
int max_runtime_
Maximum allowed computation time in seconds (standard = 0 => ~unlimited).
void computeTransformation(PointCloudSource &output, const Eigen::Matrix4f &guess) override
Rigid transformation computation method.
float score_threshold_
Score threshold to stop calculation with success.
virtual bool initCompute()
Internal computation initialization.
search::KdTree is a wrapper class which inherits the pcl::KdTree class for performing search function...
int nearestKSearch(const PointT &point, int k, Indices &k_indices, std::vector< float > &k_sqr_distances) const override
Search for the k-nearest neighbors for the given query point.
void setInputCloud(const PointCloudConstPtr &cloud, const IndicesConstPtr &indices=IndicesConstPtr()) override
Provide a pointer to the input dataset.
Define standard C methods to do distance calculations.
Define methods for measuring time spent in code blocks.
void getMinMax3D(const pcl::PointCloud< PointT > &cloud, PointT &min_pt, PointT &max_pt)
Get the minimum and maximum values on each of the 3 (x-y-z) dimensions in a given pointcloud.
void transformPointCloud(const pcl::PointCloud< PointT > &cloud_in, pcl::PointCloud< PointT > &cloud_out, const Eigen::Matrix< Scalar, 4, 4 > &transform, bool copy_all_fields)
Apply a rigid transform defined by a 4x4 matrix.
unsigned int compute3DCentroid(ConstCloudIterator< PointT > &cloud_iterator, Eigen::Matrix< Scalar, 4, 1 > ¢roid)
Compute the 3D (X-Y-Z) centroid of a set of points and return it as a 3D vector.
double pointToPlaneDistance(const Point &p, double a, double b, double c, double d)
Get the distance from a point to a plane (unsigned) defined by ax+by+cz+d=0.
std::vector< MatchingCandidate, Eigen::aligned_allocator< MatchingCandidate > > MatchingCandidates
void ignore(const T &...)
Utility function to eliminate unused variable warnings.
float squaredEuclideanDistance(const PointType1 &p1, const PointType2 &p2)
Calculate the squared euclidean distance between the two given points.
std::vector< pcl::Correspondence, Eigen::aligned_allocator< pcl::Correspondence > > Correspondences
shared_ptr< Indices > IndicesPtr
float getMeanPointDensity(const typename pcl::PointCloud< PointT >::ConstPtr &cloud, float max_dist, int nr_threads=1)
Compute the mean point density of a given point cloud.
IndicesAllocator<> Indices
Type used for indices in PCL.
float euclideanDistance(const PointType1 &p1, const PointType2 &p2)
Calculate the euclidean distance between the two given points.
Correspondence represents a match between two entities (e.g., points, descriptors,...