Point Cloud Library (PCL) 1.12.0
Loading...
Searching...
No Matches
ppf_registration.hpp
1/*
2 * Software License Agreement (BSD License)
3 *
4 * Point Cloud Library (PCL) - www.pointclouds.org
5 * Copyright (c) 2011, Alexandru-Eugen Ichim
6 * Willow Garage, Inc
7 * Copyright (c) 2012-, Open Perception, Inc.
8 *
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * * Redistributions in binary form must reproduce the above
18 * copyright notice, this list of conditions and the following
19 * disclaimer in the documentation and/or other materials provided
20 * with the distribution.
21 * * Neither the name of the copyright holder(s) nor the names of its
22 * contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 *
38 * $Id$
39 *
40 */
41
42#ifndef PCL_REGISTRATION_IMPL_PPF_REGISTRATION_H_
43#define PCL_REGISTRATION_IMPL_PPF_REGISTRATION_H_
44
45#include <pcl/common/transforms.h>
46#include <pcl/features/pfh.h>
47#include <pcl/features/pfh_tools.h> // for computePairFeatures
48#include <pcl/features/ppf.h>
49#include <pcl/registration/ppf_registration.h>
50//////////////////////////////////////////////////////////////////////////////////////////////
51template <typename PointSource, typename PointTarget>
52void
62
63//////////////////////////////////////////////////////////////////////////////////////////////
64template <typename PointSource, typename PointTarget>
65void
67 PointCloudSource& output, const Eigen::Matrix4f& guess)
68{
69 if (!search_method_) {
70 PCL_ERROR("[pcl::PPFRegistration::computeTransformation] Search method not set - "
71 "skipping computeTransformation!\n");
72 return;
73 }
74
75 if (guess != Eigen::Matrix4f::Identity()) {
76 PCL_ERROR("[pcl::PPFRegistration::computeTransformation] setting initial transform "
77 "(guess) not implemented!\n");
78 }
79
80 const auto aux_size = static_cast<std::size_t>(
81 std::floor(2 * M_PI / search_method_->getAngleDiscretizationStep()));
82
83 const std::vector<unsigned int> tmp_vec(aux_size, 0);
84 std::vector<std::vector<unsigned int>> accumulator_array(input_->size(), tmp_vec);
85
86 PCL_INFO("Accumulator array size: %u x %u.\n",
87 accumulator_array.size(),
88 accumulator_array.back().size());
89
90 PoseWithVotesList voted_poses;
91 // Consider every <scene_reference_point_sampling_rate>-th point as the reference
92 // point => fix s_r
93 float f1, f2, f3, f4;
94 for (index_t scene_reference_index = 0;
95 scene_reference_index < static_cast<index_t>(target_->size());
96 scene_reference_index += scene_reference_point_sampling_rate_) {
97 Eigen::Vector3f scene_reference_point =
98 (*target_)[scene_reference_index].getVector3fMap(),
99 scene_reference_normal =
100 (*target_)[scene_reference_index].getNormalVector3fMap();
101
102 float rotation_angle_sg =
103 std::acos(scene_reference_normal.dot(Eigen::Vector3f::UnitX()));
104 bool parallel_to_x_sg =
105 (scene_reference_normal.y() == 0.0f && scene_reference_normal.z() == 0.0f);
106 Eigen::Vector3f rotation_axis_sg =
107 (parallel_to_x_sg)
108 ? (Eigen::Vector3f::UnitY())
109 : (scene_reference_normal.cross(Eigen::Vector3f::UnitX()).normalized());
110 Eigen::AngleAxisf rotation_sg(rotation_angle_sg, rotation_axis_sg);
111 Eigen::Affine3f transform_sg(
112 Eigen::Translation3f(rotation_sg * ((-1) * scene_reference_point)) *
113 rotation_sg);
114
115 // For every other point in the scene => now have pair (s_r, s_i) fixed
116 pcl::Indices indices;
117 std::vector<float> distances;
118 scene_search_tree_->radiusSearch((*target_)[scene_reference_index],
119 search_method_->getModelDiameter() / 2,
120 indices,
121 distances);
122 for (const auto& scene_point_index : indices)
123 // for(std::size_t i = 0; i < target_->size (); ++i)
124 {
125 // size_t scene_point_index = i;
126 if (scene_reference_index != scene_point_index) {
127 if (/*pcl::computePPFPairFeature*/ pcl::computePairFeatures(
128 (*target_)[scene_reference_index].getVector4fMap(),
129 (*target_)[scene_reference_index].getNormalVector4fMap(),
130 (*target_)[scene_point_index].getVector4fMap(),
131 (*target_)[scene_point_index].getNormalVector4fMap(),
132 f1,
133 f2,
134 f3,
135 f4)) {
136 std::vector<std::pair<std::size_t, std::size_t>> nearest_indices;
137 search_method_->nearestNeighborSearch(f1, f2, f3, f4, nearest_indices);
138
139 // Compute alpha_s angle
140 Eigen::Vector3f scene_point = (*target_)[scene_point_index].getVector3fMap();
141
142 Eigen::Vector3f scene_point_transformed = transform_sg * scene_point;
143 float alpha_s =
144 std::atan2(-scene_point_transformed(2), scene_point_transformed(1));
145 if (std::sin(alpha_s) * scene_point_transformed(2) < 0.0f)
146 alpha_s *= (-1);
147 alpha_s *= (-1);
148
149 // Go through point pairs in the model with the same discretized feature
150 for (const auto& nearest_index : nearest_indices) {
151 std::size_t model_reference_index = nearest_index.first;
152 std::size_t model_point_index = nearest_index.second;
153 // Calculate angle alpha = alpha_m - alpha_s
154 float alpha =
155 search_method_->alpha_m_[model_reference_index][model_point_index] -
156 alpha_s;
157 unsigned int alpha_discretized = static_cast<unsigned int>(
158 std::floor(alpha) +
159 std::floor(M_PI / search_method_->getAngleDiscretizationStep()));
160 accumulator_array[model_reference_index][alpha_discretized]++;
161 }
162 }
163 else
164 PCL_ERROR("[pcl::PPFRegistration::computeTransformation] Computing pair "
165 "feature vector between points %u and %u went wrong.\n",
166 scene_reference_index,
167 scene_point_index);
168 }
169 }
170
171 std::size_t max_votes_i = 0, max_votes_j = 0;
172 unsigned int max_votes = 0;
173
174 for (std::size_t i = 0; i < accumulator_array.size(); ++i)
175 for (std::size_t j = 0; j < accumulator_array.back().size(); ++j) {
176 if (accumulator_array[i][j] > max_votes) {
177 max_votes = accumulator_array[i][j];
178 max_votes_i = i;
179 max_votes_j = j;
180 }
181 // Reset accumulator_array for the next set of iterations with a new scene
182 // reference point
183 accumulator_array[i][j] = 0;
184 }
185
186 Eigen::Vector3f model_reference_point = (*input_)[max_votes_i].getVector3fMap(),
187 model_reference_normal =
188 (*input_)[max_votes_i].getNormalVector3fMap();
189 float rotation_angle_mg =
190 std::acos(model_reference_normal.dot(Eigen::Vector3f::UnitX()));
191 bool parallel_to_x_mg =
192 (model_reference_normal.y() == 0.0f && model_reference_normal.z() == 0.0f);
193 Eigen::Vector3f rotation_axis_mg =
194 (parallel_to_x_mg)
195 ? (Eigen::Vector3f::UnitY())
196 : (model_reference_normal.cross(Eigen::Vector3f::UnitX()).normalized());
197 Eigen::AngleAxisf rotation_mg(rotation_angle_mg, rotation_axis_mg);
198 Eigen::Affine3f transform_mg(
199 Eigen::Translation3f(rotation_mg * ((-1) * model_reference_point)) *
200 rotation_mg);
201 Eigen::Affine3f max_transform =
202 transform_sg.inverse() *
203 Eigen::AngleAxisf((static_cast<float>(max_votes_j) -
204 std::floor(static_cast<float>(M_PI) /
205 search_method_->getAngleDiscretizationStep())) *
206 search_method_->getAngleDiscretizationStep(),
207 Eigen::Vector3f::UnitX()) *
208 transform_mg;
209
210 voted_poses.push_back(PoseWithVotes(max_transform, max_votes));
211 }
212 PCL_DEBUG("Done with the Hough Transform ...\n");
213
214 // Cluster poses for filtering out outliers and obtaining more precise results
215 PoseWithVotesList results;
216 clusterPoses(voted_poses, results);
217
218 pcl::transformPointCloud(*input_, output, results.front().pose);
219
220 transformation_ = final_transformation_ = results.front().pose.matrix();
221 converged_ = true;
222}
223
224//////////////////////////////////////////////////////////////////////////////////////////////
225template <typename PointSource, typename PointTarget>
226void
230{
231 PCL_INFO("Clustering poses ...\n");
232 // Start off by sorting the poses by the number of votes
233 sort(poses.begin(), poses.end(), poseWithVotesCompareFunction);
234
235 std::vector<PoseWithVotesList> clusters;
236 std::vector<std::pair<std::size_t, unsigned int>> cluster_votes;
237 for (std::size_t poses_i = 0; poses_i < poses.size(); ++poses_i) {
238 bool found_cluster = false;
239 for (std::size_t clusters_i = 0; clusters_i < clusters.size(); ++clusters_i) {
240 if (posesWithinErrorBounds(poses[poses_i].pose,
241 clusters[clusters_i].front().pose)) {
242 found_cluster = true;
243 clusters[clusters_i].push_back(poses[poses_i]);
244 cluster_votes[clusters_i].second += poses[poses_i].votes;
245 break;
246 }
247 }
248
249 if (!found_cluster) {
250 // Create a new cluster with the current pose
251 PoseWithVotesList new_cluster;
252 new_cluster.push_back(poses[poses_i]);
253 clusters.push_back(new_cluster);
254 cluster_votes.push_back(std::pair<std::size_t, unsigned int>(
255 clusters.size() - 1, poses[poses_i].votes));
256 }
257 }
258
259 // Sort clusters by total number of votes
260 std::sort(cluster_votes.begin(), cluster_votes.end(), clusterVotesCompareFunction);
261 // Compute pose average and put them in result vector
262 /// @todo some kind of threshold for determining whether a cluster has enough votes or
263 /// not... now just taking the first three clusters
264 result.clear();
265 std::size_t max_clusters = (clusters.size() < 3) ? clusters.size() : 3;
266 for (std::size_t cluster_i = 0; cluster_i < max_clusters; ++cluster_i) {
267 PCL_INFO("Winning cluster has #votes: %d and #poses voted: %d.\n",
268 cluster_votes[cluster_i].second,
269 clusters[cluster_votes[cluster_i].first].size());
270 Eigen::Vector3f translation_average(0.0, 0.0, 0.0);
271 Eigen::Vector4f rotation_average(0.0, 0.0, 0.0, 0.0);
272 for (typename PoseWithVotesList::iterator v_it =
273 clusters[cluster_votes[cluster_i].first].begin();
274 v_it != clusters[cluster_votes[cluster_i].first].end();
275 ++v_it) {
276 translation_average += v_it->pose.translation();
277 /// averaging rotations by just averaging the quaternions in 4D space - reference
278 /// "On Averaging Rotations" by CLAUS GRAMKOW
279 rotation_average += Eigen::Quaternionf(v_it->pose.rotation()).coeffs();
280 }
281
282 translation_average /=
283 static_cast<float>(clusters[cluster_votes[cluster_i].first].size());
284 rotation_average /=
285 static_cast<float>(clusters[cluster_votes[cluster_i].first].size());
286
287 Eigen::Affine3f transform_average;
288 transform_average.translation().matrix() = translation_average;
289 transform_average.linear().matrix() =
290 Eigen::Quaternionf(rotation_average).normalized().toRotationMatrix();
291
292 result.push_back(PoseWithVotes(transform_average, cluster_votes[cluster_i].second));
293 }
294}
295
296//////////////////////////////////////////////////////////////////////////////////////////////
297template <typename PointSource, typename PointTarget>
298bool
300 Eigen::Affine3f& pose1, Eigen::Affine3f& pose2)
301{
302 float position_diff = (pose1.translation() - pose2.translation()).norm();
303 Eigen::AngleAxisf rotation_diff_mat(
304 (pose1.rotation().inverse().lazyProduct(pose2.rotation()).eval()));
305
306 float rotation_diff_angle = std::abs(rotation_diff_mat.angle());
307
308 return (position_diff < clustering_position_diff_threshold_ &&
309 rotation_diff_angle < clustering_rotation_diff_threshold_);
310}
311
312//////////////////////////////////////////////////////////////////////////////////////////////
313template <typename PointSource, typename PointTarget>
314bool
318{
319 return (a.votes > b.votes);
320}
321
322//////////////////////////////////////////////////////////////////////////////////////////////
323template <typename PointSource, typename PointTarget>
324bool
326 const std::pair<std::size_t, unsigned int>& a,
327 const std::pair<std::size_t, unsigned int>& b)
328{
329 return (a.second > b.second);
330}
331
332//#define PCL_INSTANTIATE_PPFRegistration(PointSource,PointTarget) template class
333// PCL_EXPORTS pcl::PPFRegistration<PointSource, PointTarget>;
334
335#endif // PCL_REGISTRATION_IMPL_PPF_REGISTRATION_H_
Iterator class for point clouds with or without given indices.
shared_ptr< KdTreeFLANN< PointT, Dist > > Ptr
Class that registers two point clouds based on their sets of PPFSignatures.
std::vector< PoseWithVotes, Eigen::aligned_allocator< PoseWithVotes > > PoseWithVotesList
typename PointCloudTarget::ConstPtr PointCloudTargetConstPtr
void setInputTarget(const PointCloudTargetConstPtr &cloud) override
Provide a pointer to the input target (e.g., the point cloud that we want to align the input source t...
virtual void setInputTarget(const PointCloudTargetConstPtr &cloud)
Provide a pointer to the input target (e.g., the point cloud that we want to align the input source t...
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.
PCL_EXPORTS bool computePairFeatures(const Eigen::Vector4f &p1, const Eigen::Vector4f &n1, const Eigen::Vector4f &p2, const Eigen::Vector4f &n2, float &f1, float &f2, float &f3, float &f4)
Compute the 4-tuple representation containing the three angles and one distance between two points re...
Definition bfgs.h:10
__device__ __forceinline__ float3 normalized(const float3 &v)
Definition utils.hpp:101
__device__ __host__ __forceinline__ float norm(const float3 &v1, const float3 &v2)
__device__ __host__ __forceinline__ float3 cross(const float3 &v1, const float3 &v2)
Definition utils.hpp:107
detail::int_type_t< detail::index_type_size, detail::index_type_signed > index_t
Type used for an index in PCL.
Definition types.h:112
#define M_PI
Definition pcl_macros.h:201
Structure for storing a pose (represented as an Eigen::Affine3f) and an integer for counting votes.