PointToModelICP.h
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @author Fabian Reister ( fabian dot reister at kit dot edu )
17  * @date 2021
18  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
19  * GNU General Public License
20  */
21 
22 #pragma once
23 
24 #include <cstddef>
25 #include <iterator>
26 
27 #include <Eigen/Core>
28 #include <Eigen/Geometry>
29 
30 #include <pcl/PointIndices.h>
31 #include <pcl/common/transforms.h>
32 #include <pcl/correspondence.h>
33 #include <pcl/point_cloud.h>
34 #include <pcl/point_types.h>
35 #include <pcl/registration/correspondence_rejection_distance.h>
36 #include <pcl/registration/correspondence_rejection_median_distance.h>
37 #include <pcl/registration/correspondence_rejection_var_trimmed.h>
38 #include <pcl/registration/default_convergence_criteria.h>
39 #include <pcl/registration/transformation_estimation_2D.h>
40 
43 
44 #include "models.h"
45 #include "wykobi_utils.h"
46 
47 namespace armarx
48 {
49 
51  {
52  Eigen::Isometry3f transform = Eigen::Isometry3f::Identity();
53  int iterations{0};
54  pcl::Correspondences correspondences;
55 
56  pcl::registration::DefaultConvergenceCriteria<float> convergenceCriteria;
57 
59 
60  public:
62  {
63  }
64  };
65 
66  inline Eigen::Vector3f
67  toEigen(const pcl::PointXYZ& pt)
68  {
69  return Eigen::Vector3f{pt.x, pt.y, pt.z};
70  }
71 
72  template <typename PointT = pcl::PointXYZ>
74  {
75  public:
77  model(model),
78  tmpCloudWithPrior(new pcl::PointCloud<PointT>),
79  tmpCloudProjected(new pcl::PointCloud<PointT>)
80  {
81  }
82 
83  void
84  setInputCloud(const typename pcl::PointCloud<PointT>::ConstPtr& cloud)
85  {
86  inputCloud = cloud;
87  }
88 
91  {
92  if (inputCloud->empty())
93  {
94  ARMARX_WARNING << "Input point cloud is empty!";
95  return {};
96  }
97 
98  RegistrationResult result;
99 
100  // visualizer.addPointCloud(cloudSource, "cloudSource");
101 
102  // visualizer.addPointCloud(cloudSource, "projection");
103 
104  do
105  {
106  ARMARX_INFO << result.transform.matrix();
107  result.transform = alignStep(result.transform, result.correspondences);
108  result.iterations++;
109  } while (not result.convergenceCriteria.hasConverged());
110 
111  ARMARX_INFO << "Converged after " << result.iterations << " iterations.";
112 
113  result.covariance = computeCovariance(model.polygon, result);
114 
115  return result;
116  }
117 
118  protected:
120  computeCovariance(const decltype(wykobi::Model::polygon)& polygon,
121  const RegistrationResult& result)
122  {
123  // use a tmp variable to avoid unnecessary memory allocation
124  auto& tmpCloud = tmpCloudWithPrior;
125 
126  pcl::transformPointCloud(*inputCloud, *tmpCloud, result.transform);
127  wykobi::project(*tmpCloud, polygon);
128 
129  pcl::PointIndices cloudIndices;
130  cloudIndices.indices.reserve(result.correspondences.size());
131  std::transform(result.correspondences.begin(),
132  result.correspondences.end(),
133  std::back_inserter(cloudIndices.indices),
134  [](const auto& c) { return c.index_query; });
135 
136  Eigen::Matrix3f covarianceMatrix;
137  pcl::computeCovarianceMatrix(*tmpCloud, cloudIndices, covarianceMatrix);
138 
139  return covarianceMatrix.block<2, 2>(0, 0);
140  }
141 
142  Eigen::Isometry3f
143  alignStep(const Eigen::Isometry3f& prior, pcl::Correspondences& correspondences)
144  {
145  correspondences.clear();
146  correspondences.reserve(inputCloud->size());
147 
148  // use the prior to transform point cloud into a promising configuration
149  pcl::transformPointCloud(*inputCloud, *tmpCloudWithPrior, prior);
150 
151  *tmpCloudProjected = *tmpCloudWithPrior;
152 
153  // project the points onto the polygon
154  wykobi::project(*tmpCloudProjected, model.polygon);
155 
156  ARMARX_INFO << tmpCloudProjected->size() << " correspondences";
157 
158  // To my future me: zip -> transform -> if
159  for (size_t i = 0; i < tmpCloudProjected->size(); i++)
160  {
161  const auto& ptWithPrior = tmpCloudWithPrior->points.at(i);
162  const auto& ptProjected = tmpCloudProjected->points.at(i);
163 
164  const float distance = (toEigen(ptWithPrior) - toEigen(ptProjected)).norm();
165 
166  const int qi = static_cast<int>(i);
167  correspondences.emplace_back(qi, qi, distance);
168  }
169 
170  ARMARX_CHECK(inputCloud->size() == tmpCloudProjected->size());
171 
172  // reject outliers
173  // pcl::registration::CorrespondenceRejectorMedianDistance correspondenceCalculator;
174  // pcl::registration::CorrespondenceRejectorVarTrimmed correspondenceCalculator;
175  pcl::registration::CorrespondenceRejectorDistance correspondenceCalculator;
176  correspondenceCalculator.setMaximumDistance(.1);
177 
178  pcl::Correspondences remainingCorrespondences;
179 
180  correspondenceCalculator.setInputSource<PointT>(tmpCloudWithPrior);
181  correspondenceCalculator.setInputTarget<PointT>(tmpCloudProjected);
182  correspondenceCalculator.getRemainingCorrespondences(correspondences,
183  remainingCorrespondences);
184 
185  const std::size_t nCorrespondencesBefore = correspondences.size();
186  const std::size_t nCorrespondencesAfter = remainingCorrespondences.size();
187 
188  ARMARX_INFO << "Rejected " << (nCorrespondencesBefore - nCorrespondencesAfter)
189  << " outliers.";
190  // ARMARX_INFO << "Distance " << correspondenceCalculator.getTrimmedDistance();
191 
192  correspondences = remainingCorrespondences;
193 
194  // estimate transformation from non-projected points (without prior) to projected ones
195  Eigen::Isometry3f localTrafo = Eigen::Isometry3f::Identity();
196  estimator.estimateRigidTransformation(
197  *inputCloud, *tmpCloudProjected, correspondences, localTrafo.matrix());
198 
199  return localTrafo;
200  }
201 
202  private:
203  int iterations{0};
204  const wykobi::Model model;
205 
206  typename pcl::PointCloud<PointT>::ConstPtr inputCloud;
207  typename pcl::PointCloud<PointT>::Ptr tmpCloudWithPrior;
208  typename pcl::PointCloud<PointT>::Ptr tmpCloudProjected;
209 
210  typename pcl::registration::DefaultConvergenceCriteria<float>::Ptr convergenceCriteria;
211 
212  pcl::registration::TransformationEstimation2D<PointT, PointT> estimator;
213  };
214 
215  // interactive
216  // pcl::visualization::PCLVisualizer &visualizer
217 
218  // visualizer.updatePointCloud(cloudTarget, "projection");
219  // // visualizer.updatePointCloudPose("cloudSource", transformation);
220 
221  // visualizer.spinOnce(1000);
222 
223 } // namespace armarx
armarx::PointToModelICP::computeCovariance
Eigen::Matrix2f computeCovariance(const decltype(wykobi::Model::polygon)&polygon, const RegistrationResult &result)
Definition: PointToModelICP.h:120
armarx::wykobi::Model
Definition: models.h:32
armarx::RegistrationResult::transform
Eigen::Isometry3f transform
Definition: PointToModelICP.h:52
armarx::PointToModelICP::align
RegistrationResult align()
Definition: PointToModelICP.h:90
pcl
Definition: pcl_point_operators.cpp:3
wykobi_utils.h
armarx::RegistrationResult
Definition: PointToModelICP.h:50
armarx::PointToModelICP::alignStep
Eigen::Isometry3f alignStep(const Eigen::Isometry3f &prior, pcl::Correspondences &correspondences)
Definition: PointToModelICP.h:143
armarx::RegistrationResult::convergenceCriteria
pcl::registration::DefaultConvergenceCriteria< float > convergenceCriteria
Definition: PointToModelICP.h:56
armarx::RegistrationResult::correspondences
pcl::Correspondences correspondences
Definition: PointToModelICP.h:54
c
constexpr T c
Definition: UnscentedKalmanFilterTest.cpp:46
armarx::RegistrationResult::iterations
int iterations
Definition: PointToModelICP.h:53
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
GfxTL::Identity
void Identity(MatrixXX< N, N, T > *a)
Definition: MatrixXX.h:570
armarx::toEigen
Eigen::Vector3f toEigen(const pcl::PointXYZ &pt)
Definition: PointToModelICP.h:67
GfxTL::Matrix2f
MatrixXX< 2, 2, float > Matrix2f
Definition: MatrixXX.h:648
armarx::wykobi::Model::polygon
wykobi::Polygon polygon
Definition: models.h:35
armarx::wykobi::project
void project(pcl::PointCloud< pcl::PointXYZ > &cloud, wykobi::Polygon polygon)
Definition: wykobi_utils.h:50
armarx::PointT
pcl::PointXYZRGBL PointT
Definition: Common.h:30
models.h
armarx::transform
auto transform(const Container< InputT, Alloc > &in, OutputT(*func)(InputT const &)) -> Container< OutputT, typename std::allocator_traits< Alloc >::template rebind_alloc< OutputT >>
Convenience function (with less typing) to transform a container of type InputT into the same contain...
Definition: algorithm.h:351
ExpressionException.h
PointCloud
Definition: PointCloud.h:85
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
armarx::PointToModelICP::setInputCloud
void setInputCloud(const typename pcl::PointCloud< PointT >::ConstPtr &cloud)
Definition: PointToModelICP.h:84
distance
double distance(const Point &a, const Point &b)
Definition: point.hpp:95
Logging.h
GfxTL::Matrix3f
MatrixXX< 3, 3, float > Matrix3f
Definition: MatrixXX.h:649
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:193
armarx::RegistrationResult::covariance
Eigen::Matrix2f covariance
Definition: PointToModelICP.h:58
armarx::PointToModelICP
Definition: PointToModelICP.h:73
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::RegistrationResult::RegistrationResult
RegistrationResult()
Definition: PointToModelICP.h:61
norm
double norm(const Point &a)
Definition: point.hpp:102