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
47namespace 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
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,
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
519
520#endif /* PCL_GRAPH_EDGE_WEIGHT_COMPUTER_H */
#define float
Definition 16_Level.h:22
constexpr T c
void addTerm(float influence, float convex_influence_multiplier=1.0, NormalizationType normalization=NORMALIZATION_NONE)
Add a term to the edge weighting function.
void addTerm(float influence, NormalizationType normalization)
Add a term to the edge weighting function.
EdgeWeightComputer()
Construct a weight computer with default settings.
SmallWeightPolicy
Policy which controls what happens to edges with small weights (i.e.
@ SMALL_WEIGHT_COERCE_TO_THRESHOLD
Coerce the weight to the threshold.
@ SMALL_WEIGHT_REMOVE_EDGE
Remove edges with weights below threshold.
@ SMALL_WEIGHT_IGNORE
Do nothing, leave the weights and edges as is.
void setTermBalancingFunction(TermBalancingFunction func)
Set the function used to balance the contributions of the terms.
boost::shared_ptr< EdgeWeightComputer > Ptr
pcl::graph::point_cloud_graph_traits< GraphT >::point_type PointT
void setSmallWeightPolicy(SmallWeightPolicy policy)
Set the policy for edges with small (below threshold) weights.
void compute(GraphT &graph)
Compute weights for the edges in a given graph.
NormalizationType
Different normalization types that could be applied to a term.
@ NORMALIZATION_LOCAL
Local normalization.
@ NORMALIZATION_GLOBAL
Global normalization.
boost::function< float(float, float)> TermBalancingFunction
void setSmallWeightThreshold(float threshold)
Set the threshold for edge weights.
void compute(GraphT &graph, EdgeWeightMap weights)
Compute weights for the edges in a given graph.
A PointCloudGraph is a graph that has PCL points bundled in vertices and can be viewed as a PCL point...
constexpr auto n() noexcept
Graph::point_type point_type
The type of PCL points bundled in vertices.