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
47namespace 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
58 Eigen::Matrix2f covariance = Eigen::Matrix2f::Identity();
59
60 public:
61 RegistrationResult() : convergenceCriteria(iterations, transform.matrix(), correspondences)
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:
119 Eigen::Matrix2f
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
constexpr T c
void setInputCloud(const typename pcl::PointCloud< PointT >::ConstPtr &cloud)
RegistrationResult align()
Eigen::Matrix2f computeCovariance(const decltype(wykobi::Model::polygon)&polygon, const RegistrationResult &result)
PointToModelICP(const wykobi::Model &model)
Eigen::Isometry3f alignStep(const Eigen::Isometry3f &prior, pcl::Correspondences &correspondences)
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
void project(pcl::PointCloud< pcl::PointXYZ > &cloud, wykobi::Polygon polygon)
This file offers overloads of toIce() and fromIce() functions for STL container types.
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
Eigen::Vector3f toEigen(const pcl::PointXYZ &pt)
pcl::PointXYZRGBL PointT
Definition Common.h:30
double norm(const Point &a)
Definition point.hpp:102
double distance(const Point &a, const Point &b)
Definition point.hpp:95
Eigen::Isometry3f transform
pcl::Correspondences correspondences
pcl::registration::DefaultConvergenceCriteria< float > convergenceCriteria
wykobi::Polygon polygon
Definition models.h:35