Point Cloud Library (PCL)  1.11.1-dev
pyramid_feature_matching.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_PYRAMID_FEATURE_MATCHING_H_
43 #define PCL_REGISTRATION_IMPL_PYRAMID_FEATURE_MATCHING_H_
44 
45 #include <pcl/console/print.h>
46 #include <pcl/pcl_macros.h>
47 
48 namespace pcl {
49 
50 template <typename PointFeature>
51 float
53  const PyramidFeatureHistogramPtr& pyramid_a,
54  const PyramidFeatureHistogramPtr& pyramid_b)
55 {
56  // do a few consistency checks before and during the computation
57  if (pyramid_a->nr_dimensions != pyramid_b->nr_dimensions) {
58  PCL_ERROR("[pcl::PyramidFeatureMatching::comparePyramidFeatureHistograms] The two "
59  "given pyramids have different numbers of dimensions: %u vs %u\n",
60  pyramid_a->nr_dimensions,
61  pyramid_b->nr_dimensions);
62  return -1;
63  }
64  if (pyramid_a->nr_levels != pyramid_b->nr_levels) {
65  PCL_ERROR("[pcl::PyramidFeatureMatching::comparePyramidFeatureHistograms] The two "
66  "given pyramids have different numbers of levels: %u vs %u\n",
67  pyramid_a->nr_levels,
68  pyramid_b->nr_levels);
69  return -1;
70  }
71 
72  // calculate for level 0 first
73  if (pyramid_a->hist_levels[0].hist.size() != pyramid_b->hist_levels[0].hist.size()) {
74  PCL_ERROR("[pcl::PyramidFeatureMatching::comparePyramidFeatureHistograms] The two "
75  "given pyramids have different numbers of bins on level 0: %u vs %u\n",
76  pyramid_a->hist_levels[0].hist.size(),
77  pyramid_b->hist_levels[0].hist.size());
78  return -1;
79  }
80  float match_count_level = 0.0f;
81  for (std::size_t bin_i = 0; bin_i < pyramid_a->hist_levels[0].hist.size(); ++bin_i) {
82  if (pyramid_a->hist_levels[0].hist[bin_i] < pyramid_b->hist_levels[0].hist[bin_i])
83  match_count_level += static_cast<float>(pyramid_a->hist_levels[0].hist[bin_i]);
84  else
85  match_count_level += static_cast<float>(pyramid_b->hist_levels[0].hist[bin_i]);
86  }
87 
88  float match_count = match_count_level;
89  for (std::size_t level_i = 1; level_i < pyramid_a->nr_levels; ++level_i) {
90  if (pyramid_a->hist_levels[level_i].hist.size() !=
91  pyramid_b->hist_levels[level_i].hist.size()) {
92  PCL_ERROR(
93  "[pcl::PyramidFeatureMatching::comparePyramidFeatureHistograms] The two "
94  "given pyramids have different numbers of bins on level %u: %u vs %u\n",
95  level_i,
96  pyramid_a->hist_levels[level_i].hist.size(),
97  pyramid_b->hist_levels[level_i].hist.size());
98  return -1;
99  }
100 
101  float match_count_prev_level = match_count_level;
102  match_count_level = 0.0f;
103  for (std::size_t bin_i = 0; bin_i < pyramid_a->hist_levels[level_i].hist.size();
104  ++bin_i) {
105  if (pyramid_a->hist_levels[level_i].hist[bin_i] <
106  pyramid_b->hist_levels[level_i].hist[bin_i])
107  match_count_level +=
108  static_cast<float>(pyramid_a->hist_levels[level_i].hist[bin_i]);
109  else
110  match_count_level +=
111  static_cast<float>(pyramid_b->hist_levels[level_i].hist[bin_i]);
112  }
113 
114  float level_normalization_factor = powf(2.0f, static_cast<float>(level_i));
115  match_count +=
116  (match_count_level - match_count_prev_level) / level_normalization_factor;
117  }
118 
119  // include self-similarity factors
120  float self_similarity_a = static_cast<float>(pyramid_a->nr_features),
121  self_similarity_b = static_cast<float>(pyramid_b->nr_features);
122  PCL_DEBUG("[pcl::PyramidFeatureMatching::comparePyramidFeatureHistograms] Self "
123  "similarity measures: %f, %f\n",
124  self_similarity_a,
125  self_similarity_b);
126  match_count /= std::sqrt(self_similarity_a * self_similarity_b);
127 
128  return match_count;
129 }
130 
131 template <typename PointFeature>
133 : nr_dimensions(0)
134 , nr_levels(0)
135 , nr_features(0)
136 , feature_representation_(new DefaultPointRepresentation<PointFeature>)
137 , is_computed_(false)
138 , hist_levels()
139 {}
140 
141 template <typename PointFeature>
142 void
144  PointFeature>::PyramidFeatureHistogramLevel::initializeHistogramLevel()
145 {
146  std::size_t total_vector_size = 1;
147  for (std::vector<std::size_t>::iterator dim_it = bins_per_dimension.begin();
148  dim_it != bins_per_dimension.end();
149  ++dim_it)
150  total_vector_size *= *dim_it;
151 
152  hist.resize(total_vector_size, 0);
153 }
154 
155 template <typename PointFeature>
156 bool
157 PyramidFeatureHistogram<PointFeature>::initializeHistogram()
158 {
159  // a few consistency checks before starting the computations
161  PCL_ERROR("[pcl::PyramidFeatureHistogram::initializeHistogram] PCLBase initCompute "
162  "failed\n");
163  return false;
164  }
165 
166  if (dimension_range_input_.empty()) {
167  PCL_ERROR("[pcl::PyramidFeatureHistogram::initializeHistogram] Input dimension "
168  "range was not set\n");
169  return false;
170  }
171 
172  if (dimension_range_target_.empty()) {
173  PCL_ERROR("[pcl::PyramidFeatureHistogram::initializeHistogram] Target dimension "
174  "range was not set\n");
175  return false;
176  }
177 
178  if (dimension_range_input_.size() != dimension_range_target_.size()) {
179  PCL_ERROR("[pcl::PyramidFeatureHistogram::initializeHistogram] Input and target "
180  "dimension ranges do not agree in size: %u vs %u\n",
181  dimension_range_input_.size(),
182  dimension_range_target_.size());
183  return false;
184  }
185 
186  nr_dimensions = dimension_range_target_.size();
187  nr_features = input_->size();
188  float D = 0.0f;
189  for (std::vector<std::pair<float, float>>::iterator range_it =
190  dimension_range_target_.begin();
191  range_it != dimension_range_target_.end();
192  ++range_it) {
193  float aux = range_it->first - range_it->second;
194  D += aux * aux;
195  }
196  D = std::sqrt(D);
197  nr_levels = static_cast<std::size_t>(std::ceil(std::log2(D)));
198  PCL_DEBUG("[pcl::PyramidFeatureHistogram::initializeHistogram] Pyramid will have %u "
199  "levels with a hyper-parallelepiped diagonal size of %f\n",
200  nr_levels,
201  D);
202 
203  hist_levels.resize(nr_levels);
204  for (std::size_t level_i = 0; level_i < nr_levels; ++level_i) {
205  std::vector<std::size_t> bins_per_dimension(nr_dimensions);
206  std::vector<float> bin_step(nr_dimensions);
207  for (std::size_t dim_i = 0; dim_i < nr_dimensions; ++dim_i) {
208  bins_per_dimension[dim_i] = static_cast<std::size_t>(
209  std::ceil((dimension_range_target_[dim_i].second -
210  dimension_range_target_[dim_i].first) /
211  (powf(2.0f, static_cast<float>(level_i)) *
212  std::sqrt(static_cast<float>(nr_dimensions)))));
213  bin_step[dim_i] = powf(2.0f, static_cast<float>(level_i)) *
214  std::sqrt(static_cast<float>(nr_dimensions));
215  }
216  hist_levels[level_i] = PyramidFeatureHistogramLevel(bins_per_dimension, bin_step);
217 
218  PCL_DEBUG("[pcl::PyramidFeatureHistogram::initializeHistogram] Created vector of "
219  "size %u at level %u\nwith #bins per dimension:",
220  hist_levels.back().hist.size(),
221  level_i);
222  for (std::size_t dim_i = 0; dim_i < nr_dimensions; ++dim_i)
223  PCL_DEBUG("%u ", bins_per_dimension[dim_i]);
224  PCL_DEBUG("\n");
225  }
226 
227  return true;
228 }
229 
230 template <typename PointFeature>
231 unsigned int&
232 PyramidFeatureHistogram<PointFeature>::at(std::vector<std::size_t>& access,
233  std::size_t& level)
234 {
235  if (access.size() != nr_dimensions) {
236  PCL_ERROR(
237  "[pcl::PyramidFeatureHistogram::at] Cannot access histogram position because "
238  "the access point does not have the right number of dimensions\n");
239  return hist_levels.front().hist.front();
240  }
241  if (level >= hist_levels.size()) {
242  PCL_ERROR(
243  "[pcl::PyramidFeatureHistogram::at] Trying to access a too large level\n");
244  return hist_levels.front().hist.front();
245  }
246 
247  std::size_t vector_position = 0;
248  std::size_t dim_accumulator = 1;
249 
250  for (int i = static_cast<int>(access.size()) - 1; i >= 0; --i) {
251  vector_position += access[i] * dim_accumulator;
252  dim_accumulator *= hist_levels[level].bins_per_dimension[i];
253  }
254 
255  return hist_levels[level].hist[vector_position];
256 }
257 
258 template <typename PointFeature>
259 unsigned int&
260 PyramidFeatureHistogram<PointFeature>::at(std::vector<float>& feature,
261  std::size_t& level)
262 {
263  if (feature.size() != nr_dimensions) {
264  PCL_ERROR("[pcl::PyramidFeatureHistogram::at] The given feature vector does not "
265  "match the feature dimensions of the pyramid histogram: %u vs %u\n",
266  feature.size(),
267  nr_dimensions);
268  return hist_levels.front().hist.front();
269  }
270  if (level >= hist_levels.size()) {
271  PCL_ERROR(
272  "[pcl::PyramidFeatureHistogram::at] Trying to access a too large level\n");
273  return hist_levels.front().hist.front();
274  }
275 
276  std::vector<std::size_t> access;
277  for (std::size_t dim_i = 0; dim_i < nr_dimensions; ++dim_i)
278  access.push_back(static_cast<std::size_t>(
279  std::floor((feature[dim_i] - dimension_range_target_[dim_i].first) /
280  hist_levels[level].bin_step[dim_i])));
281 
282  return at(access, level);
283 }
284 
285 template <typename PointFeature>
286 void
287 PyramidFeatureHistogram<PointFeature>::convertFeatureToVector(
288  const PointFeature& feature, std::vector<float>& feature_vector)
289 {
290  // convert feature to vector representation
291  feature_vector.resize(feature_representation_->getNumberOfDimensions());
292  feature_representation_->vectorize(feature, feature_vector);
293 
294  // adapt the values from the input range to the target range
295  for (std::size_t i = 0; i < feature_vector.size(); ++i)
296  feature_vector[i] =
297  (feature_vector[i] - dimension_range_input_[i].first) /
298  (dimension_range_input_[i].second - dimension_range_input_[i].first) *
299  (dimension_range_target_[i].second - dimension_range_target_[i].first) +
300  dimension_range_target_[i].first;
301 }
302 
303 template <typename PointFeature>
304 void
306 {
307  if (!initializeHistogram())
308  return;
309 
310  for (const auto& point : *input_) {
311  std::vector<float> feature_vector;
312  convertFeatureToVector(point, feature_vector);
313  addFeature(feature_vector);
314  }
315 
316  is_computed_ = true;
317 }
318 
319 template <typename PointFeature>
320 void
321 PyramidFeatureHistogram<PointFeature>::addFeature(std::vector<float>& feature)
322 {
323  for (std::size_t level_i = 0; level_i < nr_levels; ++level_i)
324  at(feature, level_i)++;
325 }
326 
327 } // namespace pcl
328 
329 #define PCL_INSTANTIATE_PyramidFeatureHistogram(PointFeature) \
330  template class PCL_EXPORTS pcl::PyramidFeatureHistogram<PointFeature>;
331 
332 #endif /* PCL_REGISTRATION_IMPL_PYRAMID_FEATURE_MATCHING_H_ */
pcl_macros.h
Defines all the PCL and non-PCL macros used.
pcl
Definition: convolution.h:46
pcl::PyramidFeatureHistogram::PyramidFeatureHistogram
PyramidFeatureHistogram()
Empty constructor that instantiates the feature representation variable.
Definition: pyramid_feature_matching.hpp:132
pcl::PyramidFeatureHistogram::PyramidFeatureHistogramPtr
Ptr PyramidFeatureHistogramPtr
Definition: pyramid_feature_matching.h:74
pcl::DefaultPointRepresentation
DefaultPointRepresentation extends PointRepresentation to define default behavior for common point ty...
Definition: point_representation.h:179
pcl::PyramidFeatureHistogram
Class that compares two sets of features by using a multiscale representation of the features inside ...
Definition: pyramid_feature_matching.h:68
pcl::PyramidFeatureHistogram::comparePyramidFeatureHistograms
static float comparePyramidFeatureHistograms(const PyramidFeatureHistogramPtr &pyramid_a, const PyramidFeatureHistogramPtr &pyramid_b)
Static method for comparing two pyramid histograms that returns a floating point value between 0 and ...
Definition: pyramid_feature_matching.hpp:52
pcl::PyramidFeatureHistogram::compute
void compute()
The central method for inserting the feature set inside the pyramid and obtaining the complete pyrami...
Definition: pyramid_feature_matching.hpp:305
pcl::PCLBase< PointFeature >::initCompute
bool initCompute()
This method should get called before starting the actual computation.
Definition: pcl_base.hpp:138