edge_weight_computer.h
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2014-, Open Perception, Inc.
6  *
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * * Redistributions of source code must retain the above copyright
14  * notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  * * Neither the name of the copyright holder(s) nor the names of its
20  * contributors may be used to endorse or promote products derived
21  * from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  *
36  */
37 
38 #ifndef PCL_GRAPH_EDGE_WEIGHT_COMPUTER_H
39 #define PCL_GRAPH_EDGE_WEIGHT_COMPUTER_H
40 
41 #include <boost/function.hpp>
42 
44 #include "point_cloud_graph.h"
46 
47 namespace pcl::graph
48 {
49 
50  /** This class computes edge weights for a given point cloud graph.
51  *
52  * The compute() function interates over graph edges and calculates their
53  * weights based on the data contained in their end vertices. The general
54  * form of the weighting function used in calculation is fixed, however the
55  * user is provided with a means to customize it.
56  *
57  * For a pair of vertices \f$v_i\f$ and \f$v_j\f$ connected by an edge, the
58  * weight is computed as a product of \f$k\f$ independent *terms*:
59  *
60  * \f[
61  * w_{ij} =
62  * Term_1\left(v_i,v_j\right) \cdot
63  * \dots \cdot
64  * Term_k\left(v_i,v_j\right)
65  * \f]
66  *
67  * Each term has the following form:
68  *
69  * \f[
70  * Term\left(v_i,v_j\right) =
71  * \phi\left(\frac{d\left(v_i,v_j\right)}
72  * {\bar{d}\left(v_i,v_j\right)}, \sigma\right)
73  * \f]
74  *
75  * \f$\phi\left(\cdot,\cdot\right)\f$ is a *balancing function*. Its first
76  * argument is a value calculated from the vertex data, and the second
77  * argument controls the *influence* of the term. An example of a balancing
78  * function is Gaussian:
79  *
80  * \f[
81  * \phi\left(x,\sigma\right) =
82  * \exp{\left\{-\frac{x}{\sigma}\right\}}
83  * \f]
84  *
85  * The value that is fed to the balancing function is calculated based on
86  * the data in the vertices; it consists of a *distance* between vertices
87  * \f$d\left(v_i,v_j\right)\f$ (in numerator) and a *normalization* term
88  * \f$\bar{d}\left(v_i,v_j\right)\f$ (in denominator). The former is
89  * called "distance" because typically it is indeed a distance function,
90  * such as Euclidean distance between 3D points, or angular distance
91  * between point normals. The normalization term is needed to account for
92  * the cases when the distance has a significant global variation over the
93  * graph.
94  *
95  *
96  * Predefined terms
97  * ----------------
98  *
99  * A few commonly used terms are predefined; they are presented below using
100  * the following notation. Given a point cloud graph vertex \f$v_i\f$,
101  * \f$p_i\f$ denotes the 3D coordinates of the associated point, \f$n_i\f$
102  * denotes the normal orientation of the associated point, \f${rgb}_i\f$ is a
103  * vector consisting of R, G, and B components of the color of the
104  * associated point, and \f$c_i\f$ denotes the curvature of the associated
105  * point.
106  *
107  * - \ref terms::XYZ "XYZ" term (Euclidean distance between points)
108  *
109  * \f$d_{xyz}(v_i,v_j) = ||p_i-p_j||^2\f$
110  *
111  * - \ref terms::Normal "Normal" term (angular distance between normals)
112  *
113  * \f$d_{normal}(v_i,v_j) = \frac{||n_i-n_j||^2}{2}\f$
114  *
115  * - \ref terms::Curvature "Curvature" term (product of point curvatures)
116  *
117  * \f$d_{curvature}(v_i,v_j) = c_i \cdot c_j\f$
118  *
119  * - \ref terms::RGB "RGB" term (Euclidean distance in RGB space)
120  *
121  * \f$d_{xyz}(v_i,v_j) = ||rgb_i-rgb_j||^2\f$
122  *
123  *
124  * Normalization
125  * -------------
126  *
127  * Three types of normalization (defined by the \ref NormalizationType enum)
128  * are available:
129  *
130  * - \ref NORMALIZATION_NONE "No normalization"
131  *
132  * No normalization, the term is left as is.
133  *
134  * - \ref NORMALIZATION_GLOBAL "Global normalization"
135  *
136  * Normalize term by the average value of the corresponding distance over
137  * all edges in the graph.
138  *
139  * - \ref NORMALIZATION_LOCAL "Local normalization"
140  *
141  * Normalize term by the average value of the corresponding distance over
142  * all edges incident to the end points of the edge.
143  *
144  *
145  * Small weight policies
146  * ---------------------
147  *
148  * After the edge weights were computed, there is an optional step where
149  * the weights or the edge set of the graph may be modified to ensure that
150  * there are no edges with weights that are "too small". This is useful
151  * e.g. if the further processing involves solving linear systems based on
152  * adjacency matrix of the graph. Leaving smallish weights might lead to
153  * numerical problems in that case.
154  *
155  * The threshold is controlled by the setSmallWeightThreshold() function.
156  * The policies are defined by the \ref SmallWeightPolicy enum and may be
157  * set using setSmallWeightPolicy() function.
158  *
159  * Usage
160  * -----
161  *
162  * The following code snippet demonstrates a typical usage of the
163  * EdgeWeightComputer class:
164  *
165  * ~~~{.cpp}
166  * using namespace pcl::graph;
167  *
168  * // Typedef a point cloud graph with internal edge weight map
169  * typedef point_cloud_graph<pcl::PointXYZRGB,
170  * boost::vecS,
171  * boost::undirectedS,
172  * boost::property<boost::edge_weight_t, float>
173  * boost::listS> Graph;
174  * // Create a graph
175  * Graph graph;
176  * // Add vertices and edges
177  * // ...
178  *
179  * // Create edge weight computer
180  * EdgeWeightComputer<Graph> computer;
181  * // Add XYZ term with 1.0 influence
182  * computer.addTerm<terms::XYZ> (1.0f);
183  * // Add Normal term with 2.0 influence and a discounting multiplier
184  * // for convex edges
185  * computer.addTerm<terms::Normal> (1.0f, 2.0f);
186  * // Add RGB term with 1.0 influence and local normalization
187  * computer.addTerm<terms::RGB> (1.0f, EdgeWeightComputer<Graph>::NORMALIZATION_LOCAL);
188  * // Compute edge weights;
189  * computer.compute (graph);
190  * ~~~
191  *
192  * \author Sergey Alexandrov
193  * \ingroup graph */
194  template <typename GraphT>
195  class PCL_EXPORTS EdgeWeightComputer
196  {
197 
198  BOOST_CONCEPT_ASSERT((pcl::graph::PointCloudGraphConcept<GraphT>));
199 
200  public:
202  typedef boost::function<float(float, float)> TermBalancingFunction;
204 
205  /** Different normalization types that could be applied to a term. */
207  {
208  /// No normalization
210  /// Global normalization
212  /// Local normalization
213  NORMALIZATION_LOCAL
214  };
215 
216  /** Policy which controls what happens to edges with small weights
217  * (i.e. below user-specified threshold). */
219  {
220  /// Do nothing, leave the weights and edges as is.
222  /// Coerce the weight to the threshold. This guarantees that there
223  /// are no edges in the graph with weight below threshold, and at the
224  /// same time does not modify the edge set of the graph.
226  /// Remove edges with weights below threshold. This guarantees that
227  /// there are no edges in the graph with weight below threshold, but
228  /// may modify the edge set of the graph.
230  };
231 
232  /** Construct a weight computer with default settings.
233  *
234  * By default the small weight threshold is set to zero, and the edges
235  * with small weights are ignored. The default term balancing function
236  * is Gaussian. */
238  policy_(SMALL_WEIGHT_IGNORE),
239  threshold_(0.0f),
240  balancing_function_(&EdgeWeightComputer<GraphT>::gaussian)
241  {
242  }
243 
244  /** Compute weights for the edges in a given graph.
245  *
246  * This function iterates over graph edges and calculates their
247  * weights based on the data contained in their end vertices. See
248  * class documentation for more information.
249  *
250  * This version stores computed weights in *external* property map.
251  *
252  * \param[in] graph a point cloud graph
253  * \param[in] weights an external edge weight property map */
254  template <typename EdgeWeightMap>
255  void compute(GraphT& graph, EdgeWeightMap weights);
256 
257  /** Compute weights for the edges in a given graph.
258  *
259  * This function interates over graph edges and calculates their
260  * weights based on the data contained in their end vertices. See
261  * class documentation for more information.
262  *
263  * This version stores computed weights in *internal* property map.
264  *
265  * \param[in] graph a point cloud graph */
266  inline void
267  compute(GraphT& graph)
268  {
269  compute(graph, boost::get(boost::edge_weight, graph));
270  }
271 
272  /** Add a term to the edge weighting function.
273  *
274  * \param[in] influence \f$\sigma\f$ that will be passed to the
275  * balancing function
276  * \param[in] convex_influence_multiplier an influence multiplier
277  * that will be applied if the edge is convex
278  * (default: 1.0, i.e. no diffirence between concave
279  * and convex edges).
280  * \param[in] normalization normalization type for the term
281  * (default: NORMALIZATION_NONE, i.e. no
282  * normalization). */
283  template <typename TermT>
284  void
285  addTerm(float influence,
286  float convex_influence_multiplier = 1.0,
287  NormalizationType normalization = NORMALIZATION_NONE)
288  {
289  addTermImpl<TermT>(
290  influence,
291  convex_influence_multiplier,
292  normalization,
293  typename boost::mpl::apply<typename TermT::is_compatible, PointT>::type());
294  }
295 
296  /** Add a term to the edge weighting function.
297  *
298  * This is an overloaded function provided for convenience. See the
299  * documentation for addTerm(). */
300  template <typename TermT>
301  void
302  addTerm(float influence, NormalizationType normalization)
303  {
304  addTermImpl<TermT>(
305  influence,
306  1.0,
307  normalization,
308  typename boost::mpl::apply<typename TermT::is_compatible, PointT>::type());
309  }
310 
311  /** Set the policy for edges with small (below threshold) weights. */
312  inline void
314  {
315  policy_ = policy;
316  }
317 
318  /** Set the threshold for edge weights. */
319  inline void
320  setSmallWeightThreshold(float threshold)
321  {
322  threshold_ = threshold;
323  }
324 
325  /** Set the function used to balance the contributions of the terms. */
326  inline void
328  {
329  balancing_function_ = func;
330  }
331 
332  private:
333  /** Gaussian, used as a balancing function by default. */
334  static float
335  gaussian(float val, float influence)
336  {
337  return (influence > 0.0 ? std::exp(-val / influence) : 1.0);
338  };
339 
340  /** Internal helper structure used to represent a term in the weighting
341  * function, implemented by the edge weight computer. */
342  struct Term
343  {
344 
345  typedef boost::function<float(const PointT&, const PointT&)> ComputeFunction;
346 
347  ComputeFunction compute_;
348  float influence_;
349  float convex_influence_multiplier_;
350 
351  Term(ComputeFunction f, float i, float c) :
352  compute_(f), influence_(i), convex_influence_multiplier_(c)
353  {
354  }
355 
356  inline float
357  getInfluence(bool is_convex = false) const
358  {
359  return (influence_ * (is_convex ? convex_influence_multiplier_ : 1.0));
360  }
361  };
362 
363  /** Internal helper structure used to represent a globally normalized
364  * term in the weighting function, implemented by the edge weight
365  * computer. */
366  struct GloballyNormalizedTerm : Term
367  {
368 
369  GloballyNormalizedTerm(typename Term::ComputeFunction f, float i, float c) :
370  Term(f, i, c)
371  {
372  }
373 
374  void
375  init(size_t num_edges)
376  {
377  edge_weights_.resize(num_edges, 0.0f);
378  total_weight_ = 0.0f;
379  }
380 
381  float
382  round1(const PointT& p1, const PointT& p2, size_t edge_id)
383  {
384  float weight = this->compute_(p1, p2);
385  edge_weights_[edge_id] = weight;
386  total_weight_ += weight;
387  return (weight);
388  }
389 
390  void
391  extract()
392  {
393  average_ = total_weight_ / edge_weights_.size();
394  }
395 
396  float
397  round2(size_t edge_id)
398  {
399  return (edge_weights_[edge_id] / average_);
400  }
401 
402  std::vector<float> edge_weights_;
403  float total_weight_;
404  float average_;
405  };
406 
407  /** Internal helper structure used to represent a locally normalized
408  * term in the weighting function, implemented by the edge weight
409  * computer. */
410  struct LocallyNormalizedTerm : Term
411  {
412 
413  LocallyNormalizedTerm(typename Term::ComputeFunction f, float i, float c) :
414  Term(f, i, c)
415  {
416  }
417 
418  void
419  init(size_t num_edges, size_t num_vertices)
420  {
421  edge_weights_.resize(num_edges, 0.0f);
422  vertex_sums_.resize(num_vertices, 0.0f);
423  vertex_degrees_.resize(num_vertices, 0);
424  }
425 
426  float
427  round1(const PointT& p1,
428  const PointT& p2,
429  size_t vertex1_id,
430  size_t vertex2_id,
431  size_t edge_id)
432  {
433  float weight = this->compute_(p1, p2);
434  edge_weights_[edge_id] = weight;
435  vertex_sums_[vertex1_id] += weight;
436  vertex_sums_[vertex2_id] += weight;
437  ++vertex_degrees_[vertex1_id];
438  ++vertex_degrees_[vertex2_id];
439  return (weight);
440  }
441 
442  void
443  extract()
444  {
445  for (size_t i = 0; i < vertex_sums_.size(); ++i)
446  if (vertex_degrees_[i])
447  {
448  vertex_sums_[i] /= vertex_degrees_[i];
449  }
450  }
451 
452  float
453  round2(size_t vertex1_id, size_t vertex2_id, size_t edge_id) const
454  {
455  float n = (vertex_sums_[vertex1_id] + vertex_sums_[vertex2_id]) / 2.0;
456  float weight =
457  (n > 0.0f && this->getInfluence() > 0.0f) ? edge_weights_[edge_id] / n : 0.0f;
458  return (weight);
459  }
460 
461  std::vector<float> edge_weights_;
462  std::vector<float> vertex_sums_;
463  std::vector<size_t> vertex_degrees_;
464  };
465 
466  template <typename TermT>
467  void
468  addTermImpl(float influence,
469  float convex_influence_multiplier,
470  NormalizationType normalization,
471  boost::mpl::bool_<true>)
472  {
473  switch (normalization)
474  {
475  case NORMALIZATION_NONE:
476  {
477  terms_.push_back(Term(
478  TermT::template compute<PointT>, influence, convex_influence_multiplier));
479  break;
480  }
481  case NORMALIZATION_GLOBAL:
482  {
483  g_terms_.push_back(GloballyNormalizedTerm(
484  TermT::template compute<PointT>, influence, convex_influence_multiplier));
485  break;
486  }
487  case NORMALIZATION_LOCAL:
488  {
489  l_terms_.push_back(LocallyNormalizedTerm(
490  TermT::template compute<PointT>, influence, convex_influence_multiplier));
491  break;
492  }
493  }
494  }
495 
496  /** No-op implementation of addTerm(), instantiated for terms that are
497  * not compatible with the graph point type. */
498  template <typename TermT>
499  void
500  addTermImpl(float influence,
501  float convex_influence_multiplier,
502  NormalizationType normalization,
503  boost::mpl::bool_<false>)
504  {
505  }
506 
507  std::vector<Term> terms_;
508  std::vector<GloballyNormalizedTerm> g_terms_;
509  std::vector<LocallyNormalizedTerm> l_terms_;
510 
511  SmallWeightPolicy policy_;
512  float threshold_;
513  TermBalancingFunction balancing_function_;
514  };
515 
516 } // namespace pcl::graph
517 
518 #include "edge_weight_computer.hpp"
519 
520 #endif /* PCL_GRAPH_EDGE_WEIGHT_COMPUTER_H */
pcl::graph::EdgeWeightComputer::setSmallWeightThreshold
void setSmallWeightThreshold(float threshold)
Set the threshold for edge weights.
Definition: edge_weight_computer.h:320
pcl::graph::EdgeWeightComputer
This class computes edge weights for a given point cloud graph.
Definition: edge_weight_computer.h:195
pcl::graph::EdgeWeightComputer::SMALL_WEIGHT_COERCE_TO_THRESHOLD
@ SMALL_WEIGHT_COERCE_TO_THRESHOLD
Coerce the weight to the threshold.
Definition: edge_weight_computer.h:225
boost::shared_ptr
Definition: IceGridAdmin.h:53
pcl::graph::EdgeWeightComputer::addTerm
void addTerm(float influence, float convex_influence_multiplier=1.0, NormalizationType normalization=NORMALIZATION_NONE)
Add a term to the edge weighting function.
Definition: edge_weight_computer.h:285
c
constexpr T c
Definition: UnscentedKalmanFilterTest.cpp:46
PointCloudGraphConcept
pcl::graph::EdgeWeightComputer::setTermBalancingFunction
void setTermBalancingFunction(TermBalancingFunction func)
Set the function used to balance the contributions of the terms.
Definition: edge_weight_computer.h:327
pcl::graph::EdgeWeightComputer::Ptr
boost::shared_ptr< EdgeWeightComputer > Ptr
Definition: edge_weight_computer.h:203
pcl::graph
Definition: common.h:45
pcl::graph::EdgeWeightComputer::NormalizationType
NormalizationType
Different normalization types that could be applied to a term.
Definition: edge_weight_computer.h:206
point_cloud_graph_concept.h
edge_weight_computer.hpp
pcl::graph::EdgeWeightComputer::EdgeWeightComputer
EdgeWeightComputer()
Construct a weight computer with default settings.
Definition: edge_weight_computer.h:237
pcl::graph::EdgeWeightComputer::compute
void compute(GraphT &graph)
Compute weights for the edges in a given graph.
Definition: edge_weight_computer.h:267
pcl::graph::EdgeWeightComputer::SMALL_WEIGHT_IGNORE
@ SMALL_WEIGHT_IGNORE
Do nothing, leave the weights and edges as is.
Definition: edge_weight_computer.h:221
armarx::PointT
pcl::PointXYZRGBL PointT
Definition: Common.h:30
pcl::graph::point_cloud_graph_traits::point_type
Graph::point_type point_type
The type of PCL points bundled in vertices.
Definition: point_cloud_graph.h:544
edge_weight_computer_terms.h
pcl::graph::EdgeWeightComputer::addTerm
void addTerm(float influence, NormalizationType normalization)
Add a term to the edge weighting function.
Definition: edge_weight_computer.h:302
float
#define float
Definition: 16_Level.h:22
pcl::graph::EdgeWeightComputer::NORMALIZATION_NONE
@ NORMALIZATION_NONE
No normalization.
Definition: edge_weight_computer.h:209
pcl::graph::EdgeWeightComputer::NORMALIZATION_GLOBAL
@ NORMALIZATION_GLOBAL
Global normalization.
Definition: edge_weight_computer.h:211
pcl::graph::EdgeWeightComputer::TermBalancingFunction
boost::function< float(float, float)> TermBalancingFunction
Definition: edge_weight_computer.h:202
pcl::graph::EdgeWeightComputer::setSmallWeightPolicy
void setSmallWeightPolicy(SmallWeightPolicy policy)
Set the policy for edges with small (below threshold) weights.
Definition: edge_weight_computer.h:313
pcl::graph::EdgeWeightComputer::SMALL_WEIGHT_REMOVE_EDGE
@ SMALL_WEIGHT_REMOVE_EDGE
Remove edges with weights below threshold.
Definition: edge_weight_computer.h:229
pcl::graph::EdgeWeightComputer::PointT
pcl::graph::point_cloud_graph_traits< GraphT >::point_type PointT
Definition: edge_weight_computer.h:201
point_cloud_graph.h
pcl::graph::EdgeWeightComputer::SmallWeightPolicy
SmallWeightPolicy
Policy which controls what happens to edges with small weights (i.e.
Definition: edge_weight_computer.h:218