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 
43 #include "point_cloud_graph.h"
45 
47 
48 namespace pcl::graph
49 {
50 
51  /** This class computes edge weights for a given point cloud graph.
52  *
53  * The compute() function interates over graph edges and calculates their
54  * weights based on the data contained in their end vertices. The general
55  * form of the weighting function used in calculation is fixed, however the
56  * user is provided with a means to customize it.
57  *
58  * For a pair of vertices \f$v_i\f$ and \f$v_j\f$ connected by an edge, the
59  * weight is computed as a product of \f$k\f$ independent *terms*:
60  *
61  * \f[
62  * w_{ij} =
63  * Term_1\left(v_i,v_j\right) \cdot
64  * \dots \cdot
65  * Term_k\left(v_i,v_j\right)
66  * \f]
67  *
68  * Each term has the following form:
69  *
70  * \f[
71  * Term\left(v_i,v_j\right) =
72  * \phi\left(\frac{d\left(v_i,v_j\right)}
73  * {\bar{d}\left(v_i,v_j\right)}, \sigma\right)
74  * \f]
75  *
76  * \f$\phi\left(\cdot,\cdot\right)\f$ is a *balancing function*. Its first
77  * argument is a value calculated from the vertex data, and the second
78  * argument controls the *influence* of the term. An example of a balancing
79  * function is Gaussian:
80  *
81  * \f[
82  * \phi\left(x,\sigma\right) =
83  * \exp{\left\{-\frac{x}{\sigma}\right\}}
84  * \f]
85  *
86  * The value that is fed to the balancing function is calculated based on
87  * the data in the vertices; it consists of a *distance* between vertices
88  * \f$d\left(v_i,v_j\right)\f$ (in numerator) and a *normalization* term
89  * \f$\bar{d}\left(v_i,v_j\right)\f$ (in denominator). The former is
90  * called "distance" because typically it is indeed a distance function,
91  * such as Euclidean distance between 3D points, or angular distance
92  * between point normals. The normalization term is needed to account for
93  * the cases when the distance has a significant global variation over the
94  * graph.
95  *
96  *
97  * Predefined terms
98  * ----------------
99  *
100  * A few commonly used terms are predefined; they are presented below using
101  * the following notation. Given a point cloud graph vertex \f$v_i\f$,
102  * \f$p_i\f$ denotes the 3D coordinates of the associated point, \f$n_i\f$
103  * denotes the normal orientation of the associated point, \f${rgb}_i\f$ is a
104  * vector consisting of R, G, and B components of the color of the
105  * associated point, and \f$c_i\f$ denotes the curvature of the associated
106  * point.
107  *
108  * - \ref terms::XYZ "XYZ" term (Euclidean distance between points)
109  *
110  * \f$d_{xyz}(v_i,v_j) = ||p_i-p_j||^2\f$
111  *
112  * - \ref terms::Normal "Normal" term (angular distance between normals)
113  *
114  * \f$d_{normal}(v_i,v_j) = \frac{||n_i-n_j||^2}{2}\f$
115  *
116  * - \ref terms::Curvature "Curvature" term (product of point curvatures)
117  *
118  * \f$d_{curvature}(v_i,v_j) = c_i \cdot c_j\f$
119  *
120  * - \ref terms::RGB "RGB" term (Euclidean distance in RGB space)
121  *
122  * \f$d_{xyz}(v_i,v_j) = ||rgb_i-rgb_j||^2\f$
123  *
124  *
125  * Normalization
126  * -------------
127  *
128  * Three types of normalization (defined by the \ref NormalizationType enum)
129  * are available:
130  *
131  * - \ref NORMALIZATION_NONE "No normalization"
132  *
133  * No normalization, the term is left as is.
134  *
135  * - \ref NORMALIZATION_GLOBAL "Global normalization"
136  *
137  * Normalize term by the average value of the corresponding distance over
138  * all edges in the graph.
139  *
140  * - \ref NORMALIZATION_LOCAL "Local normalization"
141  *
142  * Normalize term by the average value of the corresponding distance over
143  * all edges incident to the end points of the edge.
144  *
145  *
146  * Small weight policies
147  * ---------------------
148  *
149  * After the edge weights were computed, there is an optional step where
150  * the weights or the edge set of the graph may be modified to ensure that
151  * there are no edges with weights that are "too small". This is useful
152  * e.g. if the further processing involves solving linear systems based on
153  * adjacency matrix of the graph. Leaving smallish weights might lead to
154  * numerical problems in that case.
155  *
156  * The threshold is controlled by the setSmallWeightThreshold() function.
157  * The policies are defined by the \ref SmallWeightPolicy enum and may be
158  * set using setSmallWeightPolicy() function.
159  *
160  * Usage
161  * -----
162  *
163  * The following code snippet demonstrates a typical usage of the
164  * EdgeWeightComputer class:
165  *
166  * ~~~{.cpp}
167  * using namespace pcl::graph;
168  *
169  * // Typedef a point cloud graph with internal edge weight map
170  * typedef point_cloud_graph<pcl::PointXYZRGB,
171  * boost::vecS,
172  * boost::undirectedS,
173  * boost::property<boost::edge_weight_t, float>
174  * boost::listS> Graph;
175  * // Create a graph
176  * Graph graph;
177  * // Add vertices and edges
178  * // ...
179  *
180  * // Create edge weight computer
181  * EdgeWeightComputer<Graph> computer;
182  * // Add XYZ term with 1.0 influence
183  * computer.addTerm<terms::XYZ> (1.0f);
184  * // Add Normal term with 2.0 influence and a discounting multiplier
185  * // for convex edges
186  * computer.addTerm<terms::Normal> (1.0f, 2.0f);
187  * // Add RGB term with 1.0 influence and local normalization
188  * computer.addTerm<terms::RGB> (1.0f, EdgeWeightComputer<Graph>::NORMALIZATION_LOCAL);
189  * // Compute edge weights;
190  * computer.compute (graph);
191  * ~~~
192  *
193  * \author Sergey Alexandrov
194  * \ingroup graph */
195  template <typename GraphT>
196  class PCL_EXPORTS EdgeWeightComputer
197  {
198 
199  BOOST_CONCEPT_ASSERT((pcl::graph::PointCloudGraphConcept<GraphT>));
200 
201  public:
202 
204  typedef boost::function<float (float, float)> TermBalancingFunction;
206 
207  /** Different normalization types that could be applied to a term. */
209  {
210  /// No normalization
212  /// Global normalization
214  /// Local normalization
215  NORMALIZATION_LOCAL
216  };
217 
218  /** Policy which controls what happens to edges with small weights
219  * (i.e. below user-specified threshold). */
221  {
222  /// Do nothing, leave the weights and edges as is.
224  /// Coerce the weight to the threshold. This guarantees that there
225  /// are no edges in the graph with weight below threshold, and at the
226  /// same time does not modify the edge set of the graph.
228  /// Remove edges with weights below threshold. This guarantees that
229  /// there are no edges in the graph with weight below threshold, but
230  /// may modify the edge set of the graph.
232  };
233 
234  /** Construct a weight computer with default settings.
235  *
236  * By default the small weight threshold is set to zero, and the edges
237  * with small weights are ignored. The default term balancing function
238  * is Gaussian. */
240  : policy_(SMALL_WEIGHT_IGNORE)
241  , threshold_(0.0f)
242  , balancing_function_(&EdgeWeightComputer<GraphT>::gaussian)
243  {
244  }
245 
246  /** Compute weights for the edges in a given graph.
247  *
248  * This function iterates over graph edges and calculates their
249  * weights based on the data contained in their end vertices. See
250  * class documentation for more information.
251  *
252  * This version stores computed weights in *external* property map.
253  *
254  * \param[in] graph a point cloud graph
255  * \param[in] weights an external edge weight property map */
256  template <typename EdgeWeightMap> void
257  compute(GraphT& graph, EdgeWeightMap weights);
258 
259  /** Compute weights for the edges in a given graph.
260  *
261  * This function interates over graph edges and calculates their
262  * weights based on the data contained in their end vertices. See
263  * class documentation for more information.
264  *
265  * This version stores computed weights in *internal* property map.
266  *
267  * \param[in] graph a point cloud graph */
268  inline void
269  compute(GraphT& graph)
270  {
271  compute(graph, boost::get(boost::edge_weight, graph));
272  }
273 
274  /** Add a term to the edge weighting function.
275  *
276  * \param[in] influence \f$\sigma\f$ that will be passed to the
277  * balancing function
278  * \param[in] convex_influence_multiplier an influence multiplier
279  * that will be applied if the edge is convex
280  * (default: 1.0, i.e. no diffirence between concave
281  * and convex edges).
282  * \param[in] normalization normalization type for the term
283  * (default: NORMALIZATION_NONE, i.e. no
284  * normalization). */
285  template <typename TermT> void
286  addTerm(float influence,
287  float convex_influence_multiplier = 1.0,
288  NormalizationType normalization = NORMALIZATION_NONE)
289  {
290  addTermImpl<TermT> (influence, convex_influence_multiplier, normalization,
291  typename boost::mpl::apply<typename TermT::is_compatible, PointT>::type());
292  }
293 
294  /** Add a term to the edge weighting function.
295  *
296  * This is an overloaded function provided for convenience. See the
297  * documentation for addTerm(). */
298  template <typename TermT> void
299  addTerm(float influence,
300  NormalizationType normalization)
301  {
302  addTermImpl<TermT> (influence, 1.0, normalization,
303  typename boost::mpl::apply<typename TermT::is_compatible, PointT>::type());
304  }
305 
306  /** Set the policy for edges with small (below threshold) weights. */
307  inline void
309  {
310  policy_ = policy;
311  }
312 
313  /** Set the threshold for edge weights. */
314  inline void
315  setSmallWeightThreshold(float threshold)
316  {
317  threshold_ = threshold;
318  }
319 
320  /** Set the function used to balance the contributions of the terms. */
321  inline void
323  {
324  balancing_function_ = func;
325  }
326 
327  private:
328 
329  /** Gaussian, used as a balancing function by default. */
330  static float
331  gaussian(float val, float influence)
332  {
333  return (influence > 0.0 ? std::exp(-val / influence) : 1.0);
334  };
335 
336  /** Internal helper structure used to represent a term in the weighting
337  * function, implemented by the edge weight computer. */
338  struct Term
339  {
340 
341  typedef boost::function<float (const PointT&, const PointT&)> ComputeFunction;
342 
343  ComputeFunction compute_;
344  float influence_;
345  float convex_influence_multiplier_;
346 
347  Term(ComputeFunction f, float i, float c)
348  : compute_(f)
349  , influence_(i)
350  , convex_influence_multiplier_(c)
351  {
352  }
353 
354  inline float
355  getInfluence(bool is_convex = false) const
356  {
357  return (influence_ * (is_convex ? convex_influence_multiplier_ : 1.0));
358  }
359 
360  };
361 
362  /** Internal helper structure used to represent a globally normalized
363  * term in the weighting function, implemented by the edge weight
364  * computer. */
365  struct GloballyNormalizedTerm : Term
366  {
367 
368  GloballyNormalizedTerm(typename Term::ComputeFunction f, float i, float c)
369  : Term(f, i, c)
370  {
371  }
372 
373  void
374  init(size_t num_edges)
375  {
376  edge_weights_.resize(num_edges, 0.0f);
377  total_weight_ = 0.0f;
378  }
379 
380  float
381  round1(const PointT& p1, const PointT& p2, size_t edge_id)
382  {
383  float weight = this->compute_(p1, p2);
384  edge_weights_[edge_id] = weight;
385  total_weight_ += weight;
386  return (weight);
387  }
388 
389  void
390  extract()
391  {
392  average_ = total_weight_ / edge_weights_.size();
393  }
394 
395  float
396  round2(size_t edge_id)
397  {
398  return (edge_weights_[edge_id] / average_);
399  }
400 
401  std::vector<float> edge_weights_;
402  float total_weight_;
403  float average_;
404 
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,
454  size_t vertex2_id,
455  size_t edge_id) const
456  {
457  float n = (vertex_sums_[vertex1_id] + vertex_sums_[vertex2_id]) / 2.0;
458  float weight = (n > 0.0f && this->getInfluence() > 0.0f)
459  ? edge_weights_[edge_id] / n
460  : 0.0f;
461  return (weight);
462  }
463 
464  std::vector<float> edge_weights_;
465  std::vector<float> vertex_sums_;
466  std::vector<size_t> vertex_degrees_;
467 
468  };
469 
470  template <typename TermT> void
471  addTermImpl(float influence,
472  float convex_influence_multiplier,
473  NormalizationType normalization,
474  boost::mpl::bool_<true>)
475  {
476  switch (normalization)
477  {
478  case NORMALIZATION_NONE:
479  {
480  terms_.push_back(Term(TermT::template compute<PointT>, influence, convex_influence_multiplier));
481  break;
482  }
483  case NORMALIZATION_GLOBAL:
484  {
485  g_terms_.push_back(GloballyNormalizedTerm(TermT::template compute<PointT>, influence, convex_influence_multiplier));
486  break;
487  }
488  case NORMALIZATION_LOCAL:
489  {
490  l_terms_.push_back(LocallyNormalizedTerm(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> void
499  addTermImpl(float influence,
500  float convex_influence_multiplier,
501  NormalizationType normalization,
502  boost::mpl::bool_<false>)
503  {
504  }
505 
506  std::vector<Term> terms_;
507  std::vector<GloballyNormalizedTerm> g_terms_;
508  std::vector<LocallyNormalizedTerm> l_terms_;
509 
510  SmallWeightPolicy policy_;
511  float threshold_;
512  TermBalancingFunction balancing_function_;
513 
514  };
515 
516 }
517 
518 
519 #include "edge_weight_computer.hpp"
520 
521 #endif /* PCL_GRAPH_EDGE_WEIGHT_COMPUTER_H */
522 
pcl::graph::EdgeWeightComputer::setSmallWeightThreshold
void setSmallWeightThreshold(float threshold)
Set the threshold for edge weights.
Definition: edge_weight_computer.h:315
pcl::graph::EdgeWeightComputer
This class computes edge weights for a given point cloud graph.
Definition: edge_weight_computer.h:196
pcl::graph::EdgeWeightComputer::SMALL_WEIGHT_COERCE_TO_THRESHOLD
@ SMALL_WEIGHT_COERCE_TO_THRESHOLD
Coerce the weight to the threshold.
Definition: edge_weight_computer.h:227
boost::shared_ptr
Definition: IceGridAdmin.h:51
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:286
c
constexpr T c
Definition: UnscentedKalmanFilterTest.cpp:43
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:322
pcl::graph::EdgeWeightComputer::Ptr
boost::shared_ptr< EdgeWeightComputer > Ptr
Definition: edge_weight_computer.h:205
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:208
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:239
pcl::graph::EdgeWeightComputer::compute
void compute(GraphT &graph)
Compute weights for the edges in a given graph.
Definition: edge_weight_computer.h:269
pcl::graph::EdgeWeightComputer::SMALL_WEIGHT_IGNORE
@ SMALL_WEIGHT_IGNORE
Do nothing, leave the weights and edges as is.
Definition: edge_weight_computer.h:223
armarx::PointT
pcl::PointXYZRGBL PointT
Definition: Common.h:28
pcl::graph::EdgeWeightComputer::TermBalancingFunction
boost::function< float(float, float)> TermBalancingFunction
Definition: edge_weight_computer.h:204
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:560
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:299
float
#define float
Definition: 16_Level.h:22
pcl::graph::EdgeWeightComputer::NORMALIZATION_NONE
@ NORMALIZATION_NONE
No normalization.
Definition: edge_weight_computer.h:211
pcl::graph::EdgeWeightComputer::NORMALIZATION_GLOBAL
@ NORMALIZATION_GLOBAL
Global normalization.
Definition: edge_weight_computer.h:213
pcl::graph::EdgeWeightComputer::setSmallWeightPolicy
void setSmallWeightPolicy(SmallWeightPolicy policy)
Set the policy for edges with small (below threshold) weights.
Definition: edge_weight_computer.h:308
pcl::graph::EdgeWeightComputer::SMALL_WEIGHT_REMOVE_EDGE
@ SMALL_WEIGHT_REMOVE_EDGE
Remove edges with weights below threshold.
Definition: edge_weight_computer.h:231
pcl::graph::EdgeWeightComputer::PointT
pcl::graph::point_cloud_graph_traits< GraphT >::point_type PointT
Definition: edge_weight_computer.h:203
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:220