Ocean
LineEvaluator.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_CV_DETECTOR_LINE_EVALUATOR_H
9 #define META_OCEAN_CV_DETECTOR_LINE_EVALUATOR_H
10 
12 
13 #include "ocean/base/Median.h"
15 
16 #include "ocean/math/FiniteLine2.h"
17 
18 #include <unordered_map>
19 #include <unordered_set>
20 
21 namespace Ocean
22 {
23 
24 namespace CV
25 {
26 
27 namespace Detector
28 {
29 
30 /**
31  * This class implements an evalutator for line segments.
32  * @ingroup cvdetector
33  */
35 {
36  public:
37 
38  /**
39  * Definition of an id identifying e.g., a specific line.
40  */
41  typedef unsigned int Id;
42 
43  /**
44  * Definition of an unordered set of ids.
45  */
46  typedef std::unordered_set<Id> IdSet;
47 
48  /**
49  * Definition of an unordered map mapping ids to sets of ids.
50  */
51  typedef std::unordered_map<Id, IdSet> IdToIdSetMap;
52 
53  /**
54  * This class is the base class for all line matches.
55  * Each match is composed of at least a match type and an id of the source line.
56  */
57  class LineMatch
58  {
59  public:
60 
61  /**
62  * Definition of individual match types.
63  */
64  enum MatchType
65  {
66  /// An invalid type.
68  /// A perfect match, @see PerfectLineMath.
70  /// A partial match, @see PartialLineMatch.
72  /// A complex match, @see ComplexLineMatch.
74  };
75 
76  public:
77 
78  /**
79  * Destructs a match object.
80  */
81  virtual ~LineMatch();
82 
83  /**
84  * Returns the type of the match.
85  * @return The match's type
86  */
87  virtual MatchType matchType() const = 0;
88 
89  /**
90  * Returns the id of the source line.
91  * @return The match's source line id
92  */
93  inline Id sourceId() const;
94 
95  protected:
96 
97  /**
98  * Creates a new match object.
99  * @param sourceId The id of the source line, must be valid
100  */
101  explicit inline LineMatch(const Id sourceId);
102 
103  protected:
104 
105  /// The id of the source line.
107  };
108 
109  /**
110  * Definition of a shared pointer for a LineMatch object.
111  * @see LineMatch.
112  */
113  typedef std::shared_ptr<LineMatch> LineMatchRef;
114 
115  /**
116  * Definition of an unordered multi map mapping ids to match objects.
117  */
118  typedef std::unordered_multimap<Id, LineMatchRef> LineMatchMap;
119 
120  /**
121  * This class implements a perfect match between a source line and a target line.
122  * A perfect match is given whenever two lines are almost equal (end points and direction).<br>
123  * A source line may have several perfect matches to several individual target lines.<br>
124  * Below, the scheme of a perfect match is depicted:
125  * <pre>
126  * ground truth lines: ++++++++++++++
127  * evaluation lines: --------------
128  * </pre>
129  * @see PartialLineMatch, ComplexLineMatch.
130  */
132  {
133  public:
134 
135  /**
136  * Creates a new match object.
137  * @param sourceId The id of the source line, must be valid
138  * @param targetId The id of the target line, must be valid
139  * @param angle The (absolute) angle between source and target lines in radian, with range [0, PI/2]
140  * @param maximalDistance The maximal distance between infinite source line and target line, with range [0, infinity)
141  */
142  inline PerfectLineMatch(const Id sourceId, const Id targetId, const double angle, const double maximalDistance);
143 
144  /**
145  * Returns the type of the match.
146  * @return The match's type
147  */
148  MatchType matchType() const override;
149 
150  /**
151  * Returns the id of the target line.
152  * @return The target line's id
153  */
154  inline Id targetId() const;
155 
156  /**
157  * Returns the angle between the source and the target line.
158  * @return The (absolute) angle between both lines in radian, with range [0, PI/2]
159  */
160  inline double angle() const;
161 
162  /**
163  * Returns maximal distance between infinite source line and target line.
164  * @return The maximal distance, with range [0, infinity)
165  */
166  inline double maximalDistance() const;
167 
168  protected:
169 
170  /// The id of the target line.
171  unsigned int targetId_;
172 
173  /// The absolute angle between both lines, with range [0, PI/2]
174  double angle_;
175 
176  /// The maximal distance between infinite source line and target line, with range [0, infinity)
178  };
179 
180  /**
181  * This class implements a partial match between one source line and several target lines.
182  * A partial match is given whenever several target lines can be combined to cover a source line.<br>
183  * Below, the scheme of a partial match is depicted:
184  * <pre>
185  * ground truth lines: ++++++++++++++++++++++
186  * evaluation lines: -------- -------- ----
187  * </pre>
188  * @see PerfectLineMatch, ComplexLineMatch.
189  */
191  {
192  public:
193 
194  /**
195  * Creates a new partial match object.
196  * @param sourceId The id of the soruce line, must be valid
197  * @param targetIds The ids of the target line matching with the source line, at least one valid id
198  * @param coverage The coverage of the source line (the amout the target lines cover the source line), with range (0, 1 + borderEps]
199  * @param medianAngle The median angle of between the source line and all target lines in radian, with range [0, PI/2]
200  * @param medianDistance The median distance from all target lines to the source lines, with range [0, infinity)
201  */
202  inline PartialLineMatch(const Id sourceId, const IdSet& targetIds, const double coverage, const double medianAngle, const double medianDistance);
203 
204  /**
205  * Creates a new partial match object.
206  * @param sourceId The id of the soruce line, must be valid
207  * @param targetIds The ids of the target line matching with the source line, at least one valid id, will be moved
208  * @param coverage The coverage of the source line (the amout the target lines cover the source line), with range (0, 1 + borderEps]
209  * @param medianAngle The median angle of between the source line and all target lines in radian, with range [0, PI/2]
210  * @param medianDistance The median distance from all target lines to the source lines, with range [0, infinity)
211  */
212  inline PartialLineMatch(const Id sourceId, IdSet&& targetIds, const double coverage, const double medianAngle, const double medianDistance);
213 
214  /**
215  * Returns the type of the match.
216  * @return The match's type
217  */
218  MatchType matchType() const override;
219 
220  /**
221  * Returns the ids of all target lines belonging to the partial match.
222  * @return The target ids, at least one
223  */
224  inline const IdSet& targetIds() const;
225 
226  /**
227  * The amout the target lines cover the source line.
228  * @return The coverage amout, with range (0, 1 + borderEps]
229  */
230  inline double coverage() const;
231 
232  /**
233  * Returns the median angle between the source line and all target lines.
234  * @return The median angle in radian, with range [0, PI/2]
235  */
236  inline double medianAngle() const;
237 
238  /**
239  * Returns the median distance between the source line and all target lines.
240  * @return The median distance, with range [0, infinity)
241  */
242  inline double medianDistance() const;
243 
244  protected:
245 
246  /// The ids of all target lines.
248 
249  /// The coverage of the source line (the amout the target lines cover the source line), with range (0, 1 + borderEps]
250  double coverage_;
251 
252  /// The median angle between the source line and all target lines, in radian, with range [0, PI/2]
253  double medianAngle_;
254 
255  /// The median distance between the source line and all target lines, with range [0, infinity)
257  };
258 
259  /**
260  * This class implements a complex match between one source line and several target lines.
261  * A complex match is given whenever several source lines match to portions of several target lines.<br>
262  * The complex match is still defined for one source line, in combination with all portions of target lines.<br>
263  * Below, the scheme of a complex match is depicted:
264  * <pre>
265  * ground truth lines: ++++++++++++++++++++++ +++++++++++++ ++++++++++
266  * evaluation lines: -------- ----------------------- -------- -----
267  * </pre>
268  * @see PerfectLineMatch.
269  */
271  {
272  public:
273 
274  /**
275  * Creates a new match object.
276  * @param sourceId The id of the soruce line, must be valid
277  * @param targetIds The ids of the target line matching with the source line, at least one valid id
278  * @param coverage The coverage of the source line (the amout the target lines cover the source line), with range (0, 1]
279  * @param medianAngle The median angle of between the source line and all target lines in radian, with range [0, PI/2]
280  * @param medianDistance The median distance from all target lines to the source lines, with range [0, infinity)
281  * @param connectedSourceIds The ids of all sibling/connected source lines which have been investigated during the match creation
282  * @param connectedTargetIds The ids of all sibling/connected target lines which have been investigated during the match creation, at least one
283  */
284  inline ComplexLineMatch(const Id sourceId, const IdSet& targetIds, const double coverage, const double medianAngle, const double medianDistance, const IdSet& connectedSourceIds, const IdSet& connectedTargetIds);
285 
286  /**
287  * Returns the type of the match.
288  * @return The match's type
289  */
290  MatchType matchType() const override;
291 
292  /**
293  * Returns the ids of all sibling/connected source lines which have been investigated during the match creation.
294  * @return The source lines' ids
295  */
296  inline const IdSet& connectedSourceIds() const;
297 
298  /**
299  * Returns the ids of all sibling/connected target lines which have been investigated during the match creation.
300  * @return The target lines' ids
301  */
302  inline const IdSet& connectedTargetIds() const;
303 
304  protected:
305 
306  /// The ids of all sibling/connected source lines which have been investigated during the match creation.
308 
309  /// The ids of all sibling/connected target lines which have been investigated during the match creation.
311  };
312 
313  /**
314  * Definition of individual strategies to determine the distance between two line segments.
315  */
317  {
318  /**
319  * The end points of the evaluation line are projected onto the infinite ground truth line,
320  * and the maximal distance between end points and projected points is determined.<br>
321  * This measure has the property that very long evaluation lines and very short ground truth lines
322  * may result in a large distance although the overlapping area seems to be quite close.
323  */
325 
326  /**
327  * The end points of the ground truth lines are projected onto the infinite evaluation line,
328  * and the maximal distance between end points and projected points is determined.<br>
329  * This measure has the property that very long ground truth lines and very short evaluation lines
330  * may result in a large distance although the overlapping area seems to be quite close.
331  */
333 
334  /**
335  * This measure combines 'DM_PROJECTED_ONTO_GROUND_TRUTH' and 'DM_PROJECTED_ONTO_EVALUATION_LINE'.
336  * The distance is the minimal distance of both measures.<br>
337  * Thus, this measure has the property that combinations of long and small lines end up with smaller distances.
338  */
340  };
341 
342  protected:
343 
344  /**
345  * Definition of a set holding a pair of ids.
346  */
347  typedef std::unordered_set<unsigned long long> Id64Set;
348 
349  public:
350 
351  /**
352  * Checks whether two given lines are overlapping up to some extend and determines some overlapping metrics.
353  * This function can determine out-of-border distances for the projected end points of the evaluation line, see FiniteLineT2::nearestPointOnInfiniteLine() for more details.
354  * The paris of resulting distance values are sorted so that the following holds: outOfBorderDistance0 <= outOfBorderDistance1, locationOnLine0 <= locationOnLine1.
355  * @param lineGroundTruth The ground truth line (actually one of both lines) while all optional resulting location values are determined in relation to this line, must be valid
356  * @param lineEvaluation The evaluation line (actually the second line), must be valid
357  * @param angleThresholdCos The angle between two lines so that both lines count as similar, given in the cosine value of the angle: angleThresholdCos = cos(angleThreshold), with range [0, 1]
358  * @param distanceThresholdPixels The maixmal distance between two lines so that both lines count as similar, depending on the defined distance measure 'distanceMeasure', with range [0, infinity)
359  * @param distanceMeasure The distance measure to be applied when comparing with the provided distance theshold 'distanceThresholdPixels'
360  * @param projectedLength Optional resulting length of the projected evaluation line on the ground truth line, with range [0, infinity)
361  * @param outOfBorderDistance0 Optional resulting distance between the projected point (of the first end point of the evaluation line) on the infinite ground truth line and the closest end point of the (finite) ground truth line, with range (-infinity, 0]
362  * @param outOfBorderDistance1 Optional resulting distance between the projected point (of the second end point of the evaluation line) on the infinite ground truth line and the closest end point of the (finite) ground truth line, with range [0, infinity)
363  * @param locationOnLine0 Optional resulting location of the projected point (of the first end point of the evaluation line) on the infinite ground truth line in relation to the finite ground truth line so that the following holds lineGroundTruth.point0() + lineGroundTruth.direction() * locationOnLine0, with range (-infinity, infinity)
364  * @param locationOnLine1 Optional resulting location of the projected point (of the second end point of the evaluation line) on the infinite ground truth line in relation to the finite ground truth line so that the following holds lineGroundTruth.point1() + lineGroundTruth.direction() * locationOnLine1, with range (-infinity, infinity)
365  * @return True, if so
366  * @tparam T The data type of a scalar, either 'float' or 'double'
367  * @see determineOverlappingAmount(), FiniteLineT2::nearestPointOnInfiniteLine().
368  */
369  template <typename T>
370  static bool areLinesOverlapping(const FiniteLineT2<T>& lineGroundTruth, const FiniteLineT2<T>& lineEvaluation, const T angleThresholdCos, const T distanceThresholdPixels, const DistanceMeasure distanceMeasure, T* projectedLength = nullptr, T* outOfBorderDistance0 = nullptr, T* outOfBorderDistance1 = nullptr, T* locationOnLine0 = nullptr, T* locationOnLine1 = nullptr);
371 
372  /**
373  * Determines the overlapping metrics between two lines that are known to overlapping (and to be similar) already.
374  * This function can determine out-of-border distances for the projected end points of the evaluation line, see FiniteLineT2::nearestPointOnInfiniteLine() for more details.
375  * The paris of resulting distance values are sorted so that the following holds: outOfBorderDistance0 <= outOfBorderDistance1, locationOnLine0 <= locationOnLine1.
376  * @param lineGroundTruth The ground truth line (actually one of both lines) while all optional resulting location values are determined in relation to this line, must be valid
377  * @param lineEvaluation The evaluation line (actually the second line), must be valid
378  * @param projectedLength Optional resulting length of the projected evaluation line on the ground truth line, with range [0, infinity)
379  * @param outOfBorderDistance0 Optional resulting distance between the projected point (of the first end point of the evaluation line) on the infinite ground truth line and the closest end point of the (finite) ground truth line, with range (-infinity, 0]
380  * @param outOfBorderDistance1 Optional resulting distance between the projected point (of the second end point of the evaluation line) on the infinite ground truth line and the closest end point of the (finite) ground truth line, with range [0, infinity)
381  * @param locationOnLine0 Optional resulting location of the projected point (of the first end point of the evaluation line) on the infinite ground truth line in relation to the finite ground truth line so that the following holds lineGroundTruth.point0() + lineGroundTruth.direction() * locationOnLine0, with range (-infinity, infinity)
382  * @param locationOnLine1 Optional resulting location of the projected point (of the second end point of the evaluation line) on the infinite ground truth line in relation to the finite ground truth line so that the following holds lineGroundTruth.point1() + lineGroundTruth.direction() * locationOnLine1, with range (-infinity, infinity)
383  * @tparam T The data type of a scalar, either 'float' or 'double'
384  * @see areLinesOverlapping(), FiniteLineT2::nearestPointOnInfiniteLine().
385  */
386  template <typename T>
387  static void determineOverlappingAmount(const FiniteLineT2<T>& lineGroundTruth, const FiniteLineT2<T>& lineEvaluation, T* projectedLength = nullptr, T* outOfBorderDistance0 = nullptr, T* outOfBorderDistance1 = nullptr, T* locationOnLine0 = nullptr, T* locationOnLine1 = nullptr);
388 
389  /**
390  * Determines the similarity between two lines known to be overlapping.
391  * @param lineGroundTruth The ground truth line (actually one of both lines) while all optional resulting location values are determined in relation to this line, must be valid
392  * @param lineEvaluation The evaluation line (actually the second line), must be valid
393  * @param distanceMeasure The distance measure to be used to determine the distance between both lines
394  * @param angle The resulting angle between both lines in radian, with range [0, infinity)
395  * @param distance The distance between both lines based on the specified distance measure
396  * @tparam T The data type of a scalar, either 'float' or 'double'
397  * @see areLinesOverlapping().
398  */
399  template <typename T>
400  static void determineSimilarity(const FiniteLineT2<T>& lineGroundTruth, const FiniteLineT2<T>& lineEvaluation, const DistanceMeasure distanceMeasure, T& angle, T& distance);
401 
402  /**
403  * Evaluates two sets of finite lines.
404  * The given ground truth lines should be accurate and should contain lines that e.g., can be detected by a line detector
405  * The lines to evaluate are match to the set of ground truth lines.
406  * @param linesGroundTruth The ground truth lines, at least one
407  * @param linesEvaluation The lines to be evaluated, at least one
408  * @param perfectMatchAngleThreshold The maximal angle between lines have a perfect match, in radian, with range [0, PI/2]
409  * @param perfectMatchPixelThreshold The maximal distance between corresponding end points of a perfect match, in pixels (defined in the domain of the line coordinates), with range [0, infinity)
410  * @param matchAngleThreshold The maximal angle between lines having a general match, in radian, with range [0, PI/2]
411  * @param matchCloseToLinePixelThreshold The maximal distance between two lines having a general match, in pixels (defined in the domain of the line coordinates), with range [0, infinity)
412  * @param partialMatchNonOverlappingPixelThreshold The maximal amount an evaluation line may exceed a ground truth line in a partial match, in pixels (defined in the domain of the line coordinates), with range [0, infinity)
413  * @param complexMatchMaximalGapPixelThreshold The maximal gap between valid evaluation lines for a complex match, in pixels (defined in the domain of the line coordinates), with range [0, infinity)
414  * @return The resulting set of matches
415  * @tparam T The data type of a scalar, either 'float' or 'double'
416  */
417  template <typename T>
418  static LineMatchMap evaluateLineSegments(const std::unordered_map<Id, FiniteLineT2<T>>& linesGroundTruth, const std::unordered_map<Id, FiniteLineT2<T>>& linesEvaluation, const T perfectMatchAngleThreshold = NumericT<T>::deg2rad(2), const T perfectMatchPixelThreshold = T(2), const T matchAngleThreshold = NumericT<T>::deg2rad(5), const T matchCloseToLinePixelThreshold = T(3), const T partialMatchNonOverlappingPixelThreshold = T(25), const T complexMatchMaximalGapPixelThreshold = T(15));
419 
420  /**
421  * Evaluates the overall quality of line matches.
422  * @param linesGroundTruth The ground truth lines, at least one
423  * @param linesEvaluation The lines to be evaluated, at least one
424  * @param lineMatches The line matches that are found between the ground truth lines and evaluation lines
425  * @param coverage The resulting overall match coverage for all ground truth lines, with range [0, 1]
426  * @param medianAngle The resulting median angle of all matches, in radian, with range [0, PI/2]
427  * @param medianDistance The resulting median distance of all matches, with range [0, infinity)
428  * @param countPerfectMatches The resulting number of provided perfect matches, with range [0, lineMatches.size()]
429  * @param countPartialMatches The resulting number of provided partial matches, with range [0, lineMatches.size()]
430  * @param countComplexMatches The resulting number of provided complex matches, with range [0, lineMatches.size()]
431  * @param notCoveredGroundTruthLines The resulting number of ground truth lines not part of the matches, with range [0, linesGroundTruth.size() - 1]
432  * @param notCoveredEvaluationLines The resulting number of evaluation lines not part of the matches, with range [0, linesEvaluation.size() - 1]
433  * @param notCoveredGroundTruthLineIds Optional resulting ids of all ground truth lines not covered by a corresponding evaluation line
434  * @param notCoveredEvaluationLineIds Optional resulting ids of all evaluation lines not covered by a corresponding ground truth line
435  * @return True, if succeeded
436  * @tparam T The data type of a scalar, either 'float' or 'double'
437  */
438  template <typename T>
439  static bool evaluateLineMatches(const std::unordered_map<Id, FiniteLineT2<T>>& linesGroundTruth, const std::unordered_map<Id, FiniteLineT2<T>>& linesEvaluation, const LineMatchMap& lineMatches, double& coverage, double& medianAngle, double& medianDistance, size_t& countPerfectMatches, size_t& countPartialMatches, size_t& countComplexMatches, size_t& notCoveredGroundTruthLines, size_t& notCoveredEvaluationLines, IdSet* notCoveredGroundTruthLineIds = nullptr, IdSet* notCoveredEvaluationLineIds = nullptr);
440 
441  protected:
442 
443  /**
444  * Determines the union of segments resulting from projecting several finite lines onto a unique finite line of interest.
445  * The segments are specified by the location of the projected points on the unique finite line of interest.
446  * @param lineOfInterest The line of interest on which the segments will be determined, must be valid
447  * @param lineIdsToProject The ids of all lines for which the projected segments will be determined
448  * @param lines The map mapping the ids given in 'lineIdsToProject' to finite lines
449  * @return The resulting union of segments, may be an empty union
450  */
451  template <typename T>
452  static SegmentUnion<T> determineProjectedSegmentUnion(const FiniteLineT2<T>& lineOfInterest, const IdSet& lineIdsToProject, const std::unordered_map<Id, FiniteLineT2<T>>& lines);
453 
454  /**
455  * Determines a complex match for a given ground truth line.
456  * @param linesGroundTruth The ground truth lines, at least one
457  * @param linesEvaluation The lines to be evaluated, at least one
458  * @param groundTruthToEvaluationMap The map mapping ids of ground truth lines to similar/overlapping evaluation lines
459  * @param evaluationToGroundTruthMap The map mapping ids of evaluation lines to to similar/overlapping ground truth lines, actually the reverse mapping of 'groundTruthToEvaluationMap'
460  * @param groundTruthId The id of the ground truth line for which the complex match will be determined
461  * @param groundTruthEvaluationSet The set of pairs of line ids for which a mapping in 'groundTruthToEvaluationMap' exists, this set is used to speed up the process
462  * @param complexMatchMaximalGapPixelThreshold The maximal gap between valid evaluation lines, in pixels (defined in the domain of the line coordinates), with range [0, infinity)
463  */
464  template <typename T>
465  static LineMatchRef determineComplexMatch(const std::unordered_map<Id, FiniteLineT2<T>>& linesGroundTruth, const std::unordered_map<Id, FiniteLineT2<T>>& linesEvaluation, const IdToIdSetMap& groundTruthToEvaluationMap, const IdToIdSetMap evaluationToGroundTruthMap, const Id groundTruthId, const Id64Set& groundTruthEvaluationSet, const T complexMatchMaximalGapPixelThreshold);
466 
467  /**
468  * Combines two (32 bit) ids to one 64 bit value.
469  * @param firstId The first id to combine
470  * @param secondId The second id to combine
471  * @return The resulting 64 bit value
472  */
473  static inline unsigned long long combineIds(const Id& firstId, const Id& secondId);
474 };
475 
476 inline LineEvaluator::LineMatch::LineMatch(const Id sourceId) :
477  sourceId_(sourceId)
478 {
479  // nothing to do here
480 }
481 
483 {
484  // nothing to do here
485 }
486 
488 {
489  return sourceId_;
490 }
491 
492 inline LineEvaluator::PerfectLineMatch::PerfectLineMatch(const Id sourceId, const Id targetId, const double angle, const double maximalDistance) :
493  LineMatch(sourceId),
494  targetId_(targetId),
495  angle_(angle),
496  maximalDistance_(maximalDistance)
497 {
498  ocean_assert(NumericD::isInsideRange(0, angle, NumericD::pi_2()));
499 }
500 
502 {
503  return MT_PERFECT;
504 }
505 
507 {
508  return targetId_;
509 }
510 
512 {
513  return angle_;
514 }
515 
517 {
518  return maximalDistance_;
519 }
520 
521 inline LineEvaluator::PartialLineMatch::PartialLineMatch(const Id sourceId, const IdSet& targetIds, const double coverage, const double medianAngle, const double medianDistance) :
522  LineMatch(sourceId),
523  targetIds_(targetIds),
524  coverage_(coverage),
525  medianAngle_(medianAngle),
526  medianDistance_(medianDistance)
527 {
528  ocean_assert(coverage >= 0.0);
529  ocean_assert(medianAngle >= 0.0 && medianAngle <= NumericD::pi_2());
530  ocean_assert(medianDistance >= 0.0);
531 }
532 
533 inline LineEvaluator::PartialLineMatch::PartialLineMatch(const Id sourceId, IdSet&& targetIds, const double coverage, const double medianAngle, const double medianDistance) :
534  LineMatch(sourceId),
535  targetIds_(std::move(targetIds)),
536  coverage_(coverage),
537  medianAngle_(medianAngle),
538  medianDistance_(medianDistance)
539 {
540  ocean_assert(coverage >= 0.0);
541  ocean_assert(medianAngle >= 0.0 && medianAngle <= NumericD::pi_2());
542  ocean_assert(medianDistance >= 0.0);
543 }
544 
546 {
547  return MT_PARTIAL;
548 }
549 
551 {
552  return targetIds_;
553 }
554 
556 {
557  return coverage_;
558 }
559 
561 {
562  return medianAngle_;
563 }
564 
566 {
567  return medianDistance_;
568 }
569 
570 inline LineEvaluator::ComplexLineMatch::ComplexLineMatch(const Id sourceId, const IdSet& targetIds, const double coverage, const double medianAngle, const double medianDistance, const IdSet& connectedSourceIds, const IdSet& connectedTargetIds) :
571  PartialLineMatch(sourceId, targetIds, coverage, medianAngle, medianDistance),
572  connectedSourceIds_(connectedSourceIds),
573  connectedTargetIds_(connectedTargetIds)
574 {
575  // nothing to do here
576 }
577 
579 {
580  return MT_COMPLEX;
581 }
582 
584 {
585  return connectedSourceIds_;
586 }
587 
589 {
590  return connectedTargetIds_;
591 }
592 
593 template <typename T>
594 bool LineEvaluator::areLinesOverlapping(const FiniteLineT2<T>& lineGroundTruth, const FiniteLineT2<T>& lineEvaluation, const T angleThresholdCos, const T distanceThresholdPixels, DistanceMeasure distanceMeasure, T* projectedLength, T* outOfBorderDistance0, T* outOfBorderDistance1, T* locationOnLine0, T* locationOnLine1)
595 {
596  ocean_assert(lineGroundTruth.isValid() && lineEvaluation.isValid());
597 
598  ocean_assert(angleThresholdCos >= T(0) && angleThresholdCos <= T(1)); // angleThresholdCos = cos(angleThreshold)
599  ocean_assert(distanceThresholdPixels >= T(0));
600 
601  // let's check whether both lines are almost parallel
602 
603  const T absCosineValue = NumericT<T>::abs(lineGroundTruth.direction() * lineEvaluation.direction());
604 
605  if (absCosineValue < angleThresholdCos)
606  {
607  return false;
608  }
609 
610  const T sqrDistanceThresholdPixels = distanceThresholdPixels * distanceThresholdPixels;
611 
612  T internalSqrDistance = NumericT<T>::maxValue();
613 
614  if (distanceMeasure == DM_PROJECTED_ONTO_EVALUATION_LINE || distanceMeasure == DM_PROJECTED_ONTO_EACH_OTHER)
615  {
616  const VectorT2<T> pointOnInfiniteEvaluation0 = lineEvaluation.nearestPointOnInfiniteLine(lineGroundTruth.point0());
617  const VectorT2<T> pointOnInfiniteEvaluation1 = lineEvaluation.nearestPointOnInfiniteLine(lineGroundTruth.point1());
618 
619  // the maximum of the distance between end points and projected end points
620  internalSqrDistance = std::max(pointOnInfiniteEvaluation0.sqrDistance(lineGroundTruth.point0()), pointOnInfiniteEvaluation1.sqrDistance(lineGroundTruth.point1()));
621 
622  if (distanceMeasure == DM_PROJECTED_ONTO_EVALUATION_LINE)
623  {
624  // we may stop already if the distance is outside the threshold
625 
626  if (internalSqrDistance > sqrDistanceThresholdPixels)
627  {
628  return false;
629  }
630  }
631  }
632 
633  T internalOutOfBoundaryDistanceOnGroundTruth0, internalLocationOnGroundTruth0;
634  const VectorT2<T> pointOnInfiniteGroundTruth0 = lineGroundTruth.nearestPointOnInfiniteLine(lineEvaluation.point0(), &internalOutOfBoundaryDistanceOnGroundTruth0, &internalLocationOnGroundTruth0);
635 
636  T internalOutOfBoundaryDistanceOnGroundTruth1, internalLocationOnGroundTruth1;
637  const VectorT2<T> pointOnInfiniteGroundTruth1 = lineGroundTruth.nearestPointOnInfiniteLine(lineEvaluation.point1(), &internalOutOfBoundaryDistanceOnGroundTruth1, &internalLocationOnGroundTruth1);
638 
639  if (distanceMeasure != DM_PROJECTED_ONTO_EVALUATION_LINE)
640  {
641  ocean_assert(distanceMeasure == DM_PROJECTED_ONTO_GROUND_TRUTH || distanceMeasure == DM_PROJECTED_ONTO_EACH_OTHER);
642 
643  const T sqrDistanceOnEvaluationLine = std::max(pointOnInfiniteGroundTruth0.sqrDistance(lineEvaluation.point0()), pointOnInfiniteGroundTruth1.sqrDistance(lineEvaluation.point1()));
644 
645  if (distanceMeasure == DM_PROJECTED_ONTO_GROUND_TRUTH)
646  {
647  // we may stop already if the distance is outside the threshold
648 
649  if (sqrDistanceOnEvaluationLine > sqrDistanceThresholdPixels)
650  {
651  return false;
652  }
653 
654  internalSqrDistance = sqrDistanceOnEvaluationLine;
655  }
656  else
657  {
658  // we take the smallest of both distances
659 
660  if (sqrDistanceOnEvaluationLine < internalSqrDistance)
661  {
662  internalSqrDistance = sqrDistanceOnEvaluationLine;
663  }
664 
665  // we may stop already if the distance is outside the threshold
666 
667  if (internalSqrDistance > sqrDistanceThresholdPixels)
668  {
669  return false;
670  }
671  }
672  }
673 
674  ocean_assert(internalSqrDistance != NumericT<T>::maxValue());
675 
676  // both lines are close to each other, further the angle is almost similar
677  // now let's find out whether the evaluation line is within the boundaries of the finite ground truth line
678 
679  Utilities::sortLowestToFront2(internalOutOfBoundaryDistanceOnGroundTruth0, internalOutOfBoundaryDistanceOnGroundTruth1);
680  ocean_assert(internalOutOfBoundaryDistanceOnGroundTruth0 <= internalOutOfBoundaryDistanceOnGroundTruth1);
681 
682  if (internalOutOfBoundaryDistanceOnGroundTruth1 < 0 || internalOutOfBoundaryDistanceOnGroundTruth0 > 0)
683  {
684  // the line is completely outside of the boundary of the ground truth line
685  return false;
686  }
687 
688  Utilities::sortLowestToFront2(internalLocationOnGroundTruth0, internalLocationOnGroundTruth1);
689  ocean_assert(internalLocationOnGroundTruth0 <= internalLocationOnGroundTruth1);
690 
691  if (projectedLength)
692  {
693  *projectedLength = internalLocationOnGroundTruth1 - internalLocationOnGroundTruth0;
694 
695  ocean_assert(NumericT<T>::isWeakEqual(pointOnInfiniteGroundTruth0.distance(pointOnInfiniteGroundTruth1), *projectedLength));
696  }
697 
698  if (outOfBorderDistance0)
699  {
700  *outOfBorderDistance0 = internalOutOfBoundaryDistanceOnGroundTruth0;
701  }
702 
703  if (outOfBorderDistance1)
704  {
705  *outOfBorderDistance1 = internalOutOfBoundaryDistanceOnGroundTruth1;
706  }
707 
708  if (locationOnLine0)
709  {
710  *locationOnLine0 = internalLocationOnGroundTruth0;
711  }
712 
713  if (locationOnLine1)
714  {
715  *locationOnLine1 = internalLocationOnGroundTruth1;
716  }
717 
718  return true;
719 }
720 
721 template <typename T>
722 void LineEvaluator::determineOverlappingAmount(const FiniteLineT2<T>& lineGroundTruth, const FiniteLineT2<T>& lineEvaluation, T* projectedLength, T* outOfBorderDistance0, T* outOfBorderDistance1, T* locationOnLine0, T* locationOnLine1)
723 {
724  ocean_assert(lineGroundTruth.isValid() && lineEvaluation.isValid());
725 
726  T internalOutOfBoundaryDistanceOnGroundTruth0, internalLocationOnGroundTruth0;
727  lineGroundTruth.nearestPointOnInfiniteLine(lineEvaluation.point0(), &internalOutOfBoundaryDistanceOnGroundTruth0, &internalLocationOnGroundTruth0);
728 
729  T internalOutOfBoundaryDistanceOnGroundTruth1, internalLocationOnGroundTruth1;
730  lineGroundTruth.nearestPointOnInfiniteLine(lineEvaluation.point1(), &internalOutOfBoundaryDistanceOnGroundTruth1, &internalLocationOnGroundTruth1);
731 
732  Utilities::sortLowestToFront2(internalOutOfBoundaryDistanceOnGroundTruth0, internalOutOfBoundaryDistanceOnGroundTruth1);
733  Utilities::sortLowestToFront2(internalLocationOnGroundTruth0, internalLocationOnGroundTruth1);
734 
735  if (projectedLength)
736  {
737  *projectedLength = internalLocationOnGroundTruth1 - internalLocationOnGroundTruth0;
738  }
739 
740  if (outOfBorderDistance0)
741  {
742  *outOfBorderDistance0 = internalOutOfBoundaryDistanceOnGroundTruth0;
743  }
744 
745  if (outOfBorderDistance1)
746  {
747  *outOfBorderDistance1 = internalOutOfBoundaryDistanceOnGroundTruth1;
748  }
749 
750  if (locationOnLine0)
751  {
752  *locationOnLine0 = internalLocationOnGroundTruth0;
753  }
754 
755  if (locationOnLine1)
756  {
757  *locationOnLine1 = internalLocationOnGroundTruth1;
758  }
759 }
760 
761 template <typename T>
762 void LineEvaluator::determineSimilarity(const FiniteLineT2<T>& lineGroundTruth, const FiniteLineT2<T>& lineEvaluation, const DistanceMeasure distanceMeasure, T& angle, T& distance)
763 {
764  ocean_assert(lineGroundTruth.isValid() && lineEvaluation.isValid());
765 
766  const T absCosineValue = NumericT<T>::abs(lineGroundTruth.direction() * lineEvaluation.direction());
767  angle = NumericT<T>::acos(absCosineValue);
768 
770 
771  if (distanceMeasure == DM_PROJECTED_ONTO_EVALUATION_LINE || distanceMeasure == DM_PROJECTED_ONTO_EACH_OTHER)
772  {
773  const VectorT2<T> pointOnInfiniteEvaluation0 = lineEvaluation.nearestPointOnInfiniteLine(lineGroundTruth.point0());
774  const VectorT2<T> pointOnInfiniteEvaluation1 = lineEvaluation.nearestPointOnInfiniteLine(lineGroundTruth.point1());
775 
776  // the maximum of the distance between end points and projected end points
777  sqrDistance = std::max(pointOnInfiniteEvaluation0.sqrDistance(lineGroundTruth.point0()), pointOnInfiniteEvaluation1.sqrDistance(lineGroundTruth.point1()));
778 
779  if (distanceMeasure == DM_PROJECTED_ONTO_EVALUATION_LINE)
780  {
781  distance = NumericT<T>::sqrt(sqrDistance);
782  return;
783  }
784  }
785 
786  ocean_assert(distanceMeasure == DM_PROJECTED_ONTO_GROUND_TRUTH || distanceMeasure == DM_PROJECTED_ONTO_EACH_OTHER);
787 
788  const VectorT2<T> pointOnInfiniteGroundTruth0 = lineGroundTruth.nearestPointOnInfiniteLine(lineEvaluation.point0());
789  const VectorT2<T> pointOnInfiniteGroundTruth1 = lineGroundTruth.nearestPointOnInfiniteLine(lineEvaluation.point1());
790 
791  const T sqrDistanceOnEvaluationLine = std::max(pointOnInfiniteGroundTruth0.sqrDistance(lineEvaluation.point0()), pointOnInfiniteGroundTruth1.sqrDistance(lineEvaluation.point1()));
792 
793  if (sqrDistanceOnEvaluationLine < sqrDistance)
794  {
795  sqrDistance = sqrDistanceOnEvaluationLine;
796  }
797 
798  distance = NumericT<T>::sqrt(sqrDistance);
799 }
800 
801 template <typename T>
802 LineEvaluator::LineMatchMap LineEvaluator::evaluateLineSegments(const std::unordered_map<Id, FiniteLineT2<T>>& linesGroundTruth, const std::unordered_map<Id, FiniteLineT2<T>>& linesEvaluation, const T perfectMatchAngleThreshold, const T perfectMatchPixelThreshold, const T matchAngleThreshold, const T matchCloseToLinePixelThreshold, const T partialMatchNonOverlappingPixelThreshold, const T complexMatchMaximalGapPixelThreshold)
803 {
804  ocean_assert(!linesGroundTruth.empty() && !linesEvaluation.empty());
805 
806  ocean_assert(perfectMatchAngleThreshold >= 0 && perfectMatchAngleThreshold <= NumericT<T>::pi_2());
807  ocean_assert(perfectMatchPixelThreshold >= 0);
808 
809  ocean_assert(matchAngleThreshold >= 0 && matchAngleThreshold <= NumericT<T>::pi_2());
810  ocean_assert(matchCloseToLinePixelThreshold >= 0);
811 
812  ocean_assert(partialMatchNonOverlappingPixelThreshold >= 0);
813  ocean_assert(complexMatchMaximalGapPixelThreshold >= 0);
814 
815  typedef std::unordered_map<Id, FiniteLineT2<T>> LineMap;
816 
817  // first, we determine a mapping from ground truth lines to connected evaluation lines (all lines which are almost similar and partially overlapping)
818  // we can use this map to check for valid matching candidates
819 
820  IdToIdSetMap groundTruthToEvaluationMap;
821  IdToIdSetMap evaluationToGroundTruthMap;
822  Id64Set groundTruthEvaluationSet;
823 
824  const T perfectMatchAngleThresholdCos = NumericT<T>::cos(perfectMatchAngleThreshold);
825  const T matchAngleThresholdCos = NumericT<T>::cos(matchAngleThreshold);
826 
827  for (typename LineMap::const_iterator iG = linesGroundTruth.cbegin(); iG != linesGroundTruth.cend(); ++iG)
828  {
829  const Id& groundTruthId = iG->first;
830  const FiniteLineT2<T>& lineGroundTruth = iG->second;
831  ocean_assert(lineGroundTruth.isValid());
832 
833  for (typename LineMap::const_iterator iE = linesEvaluation.cbegin(); iE != linesEvaluation.cend(); ++iE)
834  {
835  const Id& evaluationId = iE->first;
836  const FiniteLineT2<T>& lineEvaluation = iE->second;
837  ocean_assert(lineEvaluation.isValid());
838 
839  if (areLinesOverlapping(lineGroundTruth, lineEvaluation, matchAngleThresholdCos, matchCloseToLinePixelThreshold, DM_PROJECTED_ONTO_EACH_OTHER))
840  {
841  // for each ground truth line we store the corresponding overlapping/similar evaluation line
842 
843  groundTruthToEvaluationMap[groundTruthId].insert(evaluationId);
844 
845  // just a set storing that the ground truth line is connected with the evaluation line
846  groundTruthEvaluationSet.insert(combineIds(groundTruthId, evaluationId));
847 
848  // also, we store the reverse mapping
849  evaluationToGroundTruthMap[evaluationId].insert(groundTruthId);
850  }
851  }
852  }
853 
854  // now we try to find a corresponding match for each ground truth line
855 
856  LineMatchMap lineMatches;
857 
858  for (typename LineMap::const_iterator iG = linesGroundTruth.cbegin(); iG != linesGroundTruth.cend(); ++iG)
859  {
860  const Id& groundTruthId = iG->first;
861  const FiniteLineT2<T>& lineGroundTruth = iG->second;
862 
863  // whenever we have a valid complex match, we do not need to investage any remaining evaluation line, as they are covered within the complex match already
864  LineMatchRef complexMatch;
865 
866  SegmentUnion<T> partialMatchUnion;
867  IdSet targetIds;
868 
869  for (typename LineMap::const_iterator iE = linesEvaluation.cbegin(); !complexMatch && iE != linesEvaluation.cend(); ++iE)
870  {
871  const Id& evaluationId = iE->first;
872  const FiniteLineT2<T>& lineEvaluation = iE->second;
873 
874  if (groundTruthEvaluationSet.find(combineIds(groundTruthId, evaluationId)) != groundTruthEvaluationSet.cend())
875  {
876  T projectedLengthEvaluationLine;
877  T outOfBoundaryDistance0, outOfBoundaryDistance1;
878  T locationOnLine0, locationOnLine1;
879 
880  ocean_assert(areLinesOverlapping(lineGroundTruth, lineEvaluation, matchAngleThresholdCos, matchCloseToLinePixelThreshold, DM_PROJECTED_ONTO_EACH_OTHER));
881  determineOverlappingAmount(lineGroundTruth, lineEvaluation, &projectedLengthEvaluationLine, &outOfBoundaryDistance0, &outOfBoundaryDistance1, &locationOnLine0, &locationOnLine1);
882 
883  const T projectedNonOverlappingLength = NumericT<T>::abs(outOfBoundaryDistance0) + NumericT<T>::abs(outOfBoundaryDistance1);
884 
885  if (projectedNonOverlappingLength <= partialMatchNonOverlappingPixelThreshold)
886  {
887  const T projectedOverlappingLength = projectedLengthEvaluationLine - projectedNonOverlappingLength;
888 
889  // the projected overlapping length cannot be longer than the length of the ground truth line
890  ocean_assert_and_suppress_unused(projectedOverlappingLength <= lineGroundTruth.length() + NumericT<T>::weakEps(), projectedOverlappingLength);
891 
892  ocean_assert(locationOnLine0 <= locationOnLine1);
893  partialMatchUnion.addSegment(locationOnLine0, locationOnLine1);
894 
895  targetIds.insert(evaluationId);
896  }
897  else
898  {
899  // the line segment is similar to the ground truth line but extends the ground truth line significantly
900  // thus, we need to check whether other ground truth lines support the line segment
901 
902  complexMatch = determineComplexMatch(linesGroundTruth, linesEvaluation, groundTruthToEvaluationMap, evaluationToGroundTruthMap, groundTruthId, groundTruthEvaluationSet, complexMatchMaximalGapPixelThreshold);
903 
904  // the for loop will stop immediately
905  }
906  }
907  }
908 
909  if (complexMatch)
910  {
911  // **TODO** a complex match may also be a perfect match or a partial match
912 
913  lineMatches.insert(std::make_pair(groundTruthId, complexMatch));
914  }
915  else
916  {
917  if (partialMatchUnion)
918  {
919  ocean_assert(!targetIds.empty());
920 
921  LineMatchRef lineMatch;
922 
923  if (targetIds.size() == 1)
924  {
925  // we may have found a perfect match
926 
927  const Id evaluationId = *targetIds.begin();
928  const FiniteLineT2<T>& lineEvaluation = linesEvaluation.find(evaluationId)->second;
929 
930  // let's check whether both lines are almost parallel
931  const T absCosineValue = NumericT<T>::abs(lineGroundTruth.direction() * lineEvaluation.direction());
932 
933  if (absCosineValue >= perfectMatchAngleThresholdCos)
934  {
935  if (lineGroundTruth.isEqual(lineEvaluation, perfectMatchPixelThreshold))
936  {
937  const T projectedDistancePoint0 = lineGroundTruth.nearestPointOnInfiniteLine(lineEvaluation.point0()).distance(lineEvaluation.point0());
938  const T projectedDistancePoint1 = lineGroundTruth.nearestPointOnInfiniteLine(lineEvaluation.point1()).distance(lineEvaluation.point1());
939 
940  const T maximalProjectedDistance = std::max(projectedDistancePoint0, projectedDistancePoint1);
941 
942  lineMatch = std::make_shared<PerfectLineMatch>(groundTruthId, evaluationId, NumericT<T>::acos(absCosineValue), maximalProjectedDistance);
943  }
944  }
945  }
946 
947  if (!lineMatch)
948  {
949  // we do not have a perfect match, so just a partial match
950 
951  const T lengthGroundTruthLine = lineGroundTruth.length();
952  const T lengthMatch = partialMatchUnion.unionSize();
953 
954  ocean_assert(NumericT<T>::isNotEqualEps(lengthGroundTruthLine) && NumericT<T>::isNotEqualEps(lengthMatch));
955 
956  const T matchCoverage = lengthMatch / lengthGroundTruthLine;
957 
958  std::vector<T> angles(targetIds.size());
959  std::vector<T> distances(targetIds.size());
960 
961  const FiniteLineT2<T>& groundTruthLine = linesGroundTruth.find(groundTruthId)->second;
962 
963  size_t n = 0;
964  for (const Id& targetId : targetIds)
965  {
966  const FiniteLineT2<T>& evaluationLine = linesEvaluation.find(targetId)->second;
967 
968  determineSimilarity(groundTruthLine, evaluationLine, DM_PROJECTED_ONTO_EACH_OTHER, angles[n], distances[n]);
969  ++n;
970  }
971 
972  const double medianAngle = double(Median::median<T>(angles.data(), angles.size()));
973  const double medianDistance = double(Median::median<T>(distances.data(), distances.size()));
974 
975  lineMatch = std::make_shared<PartialLineMatch>(groundTruthId, std::move(targetIds), matchCoverage, medianAngle, medianDistance);
976  }
977 
978  lineMatches.insert(std::make_pair(groundTruthId, lineMatch));
979  }
980  else
981  {
982  ocean_assert(targetIds.empty());
983  }
984  }
985  }
986 
987  ocean_assert(lineMatches.size() <= linesGroundTruth.size());
988 
989  return lineMatches;
990 }
991 
992 template <typename T>
993 bool LineEvaluator::evaluateLineMatches(const std::unordered_map<Id, FiniteLineT2<T>>& linesGroundTruth, const std::unordered_map<Id, FiniteLineT2<T>>& linesEvaluation, const LineMatchMap& lineMatches, double& coverage, double& medianAngle, double& medianDistance, size_t& countPerfectMatches, size_t& countPartialMatches, size_t& countComplexMatches, size_t& notCoveredGroundTruthLines, size_t& notCoveredEvaluationLines, IdSet* notCoveredGroundTruthLineIds, IdSet* notCoveredEvaluationLineIds)
994 {
995  if (lineMatches.empty())
996  {
997  return false;
998  }
999 
1000  ocean_assert(lineMatches.size() <= linesGroundTruth.size());
1001  if (lineMatches.size() > linesGroundTruth.size())
1002  {
1003  return false;
1004  }
1005 
1006  double sumLengthGroundTruth = 0.0;
1007 
1008  for (typename std::unordered_map<Id, FiniteLineT2<T>>::const_iterator iG = linesGroundTruth.cbegin(); iG != linesGroundTruth.cend(); ++iG)
1009  {
1010  sumLengthGroundTruth += double(iG->second.length());
1011  }
1012 
1013  ocean_assert(sumLengthGroundTruth > 0.0);
1014  if (sumLengthGroundTruth <= 0.0)
1015  {
1016  return false;
1017  }
1018 
1019  countPerfectMatches = 0;
1020  countPartialMatches = 0;
1021  countComplexMatches = 0;
1022 
1023  double sumLengthMatches = 0.0;
1024 
1025  std::vector<double> angles;
1026  angles.reserve(lineMatches.size());
1027 
1028  std::vector<double> distances;
1029  distances.reserve(lineMatches.size());
1030 
1031  IdSet coveredEvaluationLineIds;
1032 
1033  for (typename LineMatchMap::const_iterator iMatch = lineMatches.cbegin(); iMatch != lineMatches.cend(); ++iMatch)
1034  {
1035  const LineMatchRef& match = iMatch->second;
1036  ocean_assert(match);
1037 
1038  const typename std::unordered_map<Id, FiniteLineT2<T>>::const_iterator iG = linesGroundTruth.find(match->sourceId());
1039 
1040  ocean_assert(iG != linesGroundTruth.cend());
1041  if (iG == linesGroundTruth.cend())
1042  {
1043  return false;
1044  }
1045 
1046  const FiniteLineT2<T>& groundTruthLine = iG->second;
1047  ocean_assert(groundTruthLine.isValid());
1048 
1049  const double lengthGroundTruth = double(groundTruthLine.length());
1050 
1051  switch (match->matchType())
1052  {
1053  case LineMatch::MT_PERFECT:
1054  {
1055  const PerfectLineMatch& perfectMatch = dynamic_cast<const PerfectLineMatch&>(*match);
1056 
1057  sumLengthMatches += double(lengthGroundTruth);
1058  angles.push_back(perfectMatch.angle());
1059  distances.push_back(perfectMatch.maximalDistance());
1060 
1061  coveredEvaluationLineIds.insert(perfectMatch.targetId());
1062 
1063  countPerfectMatches++;
1064 
1065  break;
1066  }
1067 
1068  case LineMatch::MT_PARTIAL:
1069  {
1070  const PartialLineMatch& partialMatch = dynamic_cast<const PartialLineMatch&>(*match);
1071 
1072  sumLengthMatches += partialMatch.coverage() * lengthGroundTruth;
1073  angles.push_back(partialMatch.medianAngle());
1074  distances.push_back(partialMatch.medianDistance());
1075 
1076  coveredEvaluationLineIds.insert(partialMatch.targetIds().cbegin(), partialMatch.targetIds().cend());
1077 
1078  countPartialMatches++;
1079 
1080  break;
1081  }
1082 
1083  case LineMatch::MT_COMPLEX:
1084  {
1085  const ComplexLineMatch& complexMatch = dynamic_cast<const ComplexLineMatch&>(*match);
1086 
1087  sumLengthMatches += complexMatch.coverage() * lengthGroundTruth;
1088  angles.push_back(complexMatch.medianAngle());
1089  distances.push_back(complexMatch.medianDistance());
1090 
1091  coveredEvaluationLineIds.insert(complexMatch.targetIds().cbegin(), complexMatch.targetIds().cend());
1092 
1093  countComplexMatches++;
1094 
1095  break;
1096  }
1097 
1098  default:
1099  ocean_assert(false && "Invalid type!");
1100  return false;
1101  }
1102  }
1103 
1104  ocean_assert(NumericT<T>::isNotEqualEps(sumLengthGroundTruth));
1105  coverage = sumLengthMatches / sumLengthGroundTruth;
1106 
1107  medianAngle = Median::median(angles.data(), angles.size());
1108  medianDistance = Median::median(distances.data(), distances.size());
1109 
1110  ocean_assert(coveredEvaluationLineIds.size() <= linesEvaluation.size());
1111  if (coveredEvaluationLineIds.size() > linesEvaluation.size())
1112  {
1113  return false;
1114  }
1115 
1116  notCoveredGroundTruthLines = linesGroundTruth.size() - lineMatches.size();
1117 
1118  notCoveredEvaluationLines = linesEvaluation.size() - coveredEvaluationLineIds.size();
1119 
1120  if (notCoveredGroundTruthLineIds)
1121  {
1122  notCoveredGroundTruthLineIds->clear();
1123 
1124  for (typename std::unordered_map<Id, FiniteLineT2<T>>::const_iterator iG = linesGroundTruth.cbegin(); iG != linesGroundTruth.cend(); ++iG)
1125  {
1126  if (lineMatches.find(iG->first) == lineMatches.cend())
1127  {
1128  notCoveredGroundTruthLineIds->insert(iG->first);
1129  }
1130  }
1131  }
1132 
1133  if (notCoveredEvaluationLineIds)
1134  {
1135  notCoveredEvaluationLineIds->clear();
1136 
1137  for (typename std::unordered_map<Id, FiniteLineT2<T>>::const_iterator iE = linesEvaluation.cbegin(); iE != linesEvaluation.cend(); ++iE)
1138  {
1139  if (coveredEvaluationLineIds.find(iE->first) == coveredEvaluationLineIds.cend())
1140  {
1141  notCoveredEvaluationLineIds->insert(iE->first);
1142  }
1143  }
1144  }
1145 
1146  return true;
1147 }
1148 
1149 template <typename T>
1150 SegmentUnion<T> LineEvaluator::determineProjectedSegmentUnion(const FiniteLineT2<T>& lineOfInterest, const IdSet& lineIdsToProject, const std::unordered_map<Id, FiniteLineT2<T>>& lines)
1151 {
1152  ocean_assert(lineOfInterest.isValid());
1153  ocean_assert(!lineIdsToProject.empty());
1154 
1155  SegmentUnion<T> segmentUnion;
1156 
1157  for (const Id& lineIdToProject : lineIdsToProject)
1158  {
1159  ocean_assert(lines.find(lineIdToProject) != lines.cend());
1160 
1161  const FiniteLineT2<T>& lineToProject = lines.find(lineIdToProject)->second;
1162  ocean_assert(lineToProject.isValid());
1163 
1164  T locationOnLineOfInterest0;
1165  lineOfInterest.nearestPointOnInfiniteLine(lineToProject.point0(), nullptr, &locationOnLineOfInterest0);
1166 
1167  T locationOnLineOfInterest1;
1168  lineOfInterest.nearestPointOnInfiniteLine(lineToProject.point1(), nullptr, &locationOnLineOfInterest1);
1169 
1170  Utilities::sortLowestToFront2(locationOnLineOfInterest0, locationOnLineOfInterest1);
1171 
1172  segmentUnion.addSegment(locationOnLineOfInterest0, locationOnLineOfInterest1);
1173  }
1174 
1175  return segmentUnion;
1176 }
1177 
1178 template <typename T>
1179 LineEvaluator::LineMatchRef LineEvaluator::determineComplexMatch(const std::unordered_map<Id, FiniteLineT2<T>>& linesGroundTruth, const std::unordered_map<Id, FiniteLineT2<T>>& linesEvaluation, const IdToIdSetMap& groundTruthToEvaluationMap, const IdToIdSetMap evaluationToGroundTruthMap, const Id groundTruthId, const Id64Set& groundTruthEvaluationSet, const T complexMatchMaximalGapPixelThreshold)
1180 {
1181  ocean_assert(complexMatchMaximalGapPixelThreshold >= 0);
1182 
1183  /*
1184  * A complex match between lines is given whenever we do not have one or several evaluation line(s) for a ground truth line:
1185  *
1186  * A valid complex match:
1187  * ground truth lines: +++++++++++ ++++++++++++++++++++++++++++++ ++++++++++++
1188  * evaluation lines: ------------------------ -----------------------------
1189  *
1190  * An invalid complex match
1191  * ground truth lines: +++++++++++ ++++++++++++++++++++++++++++++ ++++++++++++
1192  * evaluation lines: ------------------------------ -----------------------------
1193  * ^^^^^^^^^
1194  * (out-of-boundary to large)
1195  *
1196  * Thus, we have to determine all sibling ground truth lines (connected via evaluation lines).
1197  * Afterwards, we can determine the coverage of ground truth line (based on individual evaluation lines)
1198  */
1199 
1200  // first we gather all ground truth lines and all evaluation lines which are connected (almost similar and partially overlapping)
1201  // we start at the ground truth line of interest, determine all connected evaluation lines
1202  // for each evaluation line, we determine connected ground truth line and restart the process for (new) ground truth lines
1203 
1204  IdSet connectedGroundTruthIds;
1205  IdSet connectedEvaluationIds;
1206 
1207  std::vector<Id> groundTruthIdStack(1, groundTruthId);
1208 
1209  while (!groundTruthIdStack.empty())
1210  {
1211  const Id currentGroundTruthId = groundTruthIdStack.back();
1212  groundTruthIdStack.pop_back();
1213 
1214  connectedGroundTruthIds.insert(currentGroundTruthId);
1215 
1216  const IdToIdSetMap::const_iterator iMappingsG2E = groundTruthToEvaluationMap.find(currentGroundTruthId);
1217 
1218  if (iMappingsG2E != groundTruthToEvaluationMap.cend())
1219  {
1220  for (const Id& connectedEvaluationId : iMappingsG2E->second)
1221  {
1222  const IdSet::const_iterator iConnectedEvaluation = connectedEvaluationIds.find(connectedEvaluationId);
1223 
1224  if (iConnectedEvaluation == connectedEvaluationIds.cend())
1225  {
1226  connectedEvaluationIds.insert(connectedEvaluationId);
1227 
1228  const IdToIdSetMap::const_iterator iMappingsE2G = evaluationToGroundTruthMap.find(connectedEvaluationId);
1229 
1230  if (iMappingsE2G != evaluationToGroundTruthMap.cend())
1231  {
1232  for (const Id& connectedGroundTruthId : iMappingsE2G->second)
1233  {
1234  if (connectedGroundTruthIds.find(connectedGroundTruthId) == connectedGroundTruthIds.cend())
1235  {
1236  groundTruthIdStack.push_back(connectedGroundTruthId);
1237  }
1238  }
1239  }
1240  }
1241  }
1242  }
1243  }
1244 
1245  // no we have all lines that are connected with each other (sibling ground truth lines, and sibling evaluation lines)
1246  // we need to determine all evaluation lines which are invalid: evaluation lines not fully covered by a corresponding ground truth line
1247 
1248  IdSet validEvaluationIds;
1249 
1250  for (const Id& connectedEvaluationId : connectedEvaluationIds)
1251  {
1252  const FiniteLineT2<T>& connectedEvaluationLine = linesEvaluation.find(connectedEvaluationId)->second;
1253 
1254  const SegmentUnion<T> connectedEvaluationUnion = determineProjectedSegmentUnion<T>(connectedEvaluationLine, connectedGroundTruthIds, linesGroundTruth);
1255 
1256  const T lengthConnectedEvaluationLine = connectedEvaluationLine.length();
1257  const SegmentUnion<T> clampedConnectedEvaluationUnion = connectedEvaluationUnion.intersection(0, lengthConnectedEvaluationLine);
1258 
1259  if (clampedConnectedEvaluationUnion)
1260  {
1261  const T frontGap = clampedConnectedEvaluationUnion.segments().begin()->first;
1262  ocean_assert(frontGap >= T(0));
1263 
1264  const T backGap = lengthConnectedEvaluationLine - clampedConnectedEvaluationUnion.segments().rbegin()->second;
1265  ocean_assert(backGap >= T(0));
1266 
1267  const T maximalGap = std::max(frontGap, std::max(backGap, clampedConnectedEvaluationUnion.maximalGap()));
1268 
1269  if (maximalGap < complexMatchMaximalGapPixelThreshold)
1270  {
1271  ocean_assert(validEvaluationIds.find(connectedEvaluationId) == validEvaluationIds.end());
1272  validEvaluationIds.insert(connectedEvaluationId);
1273  }
1274  }
1275  }
1276 
1277  // now we can handle our given ground truth line
1278  // we simply determine the coverage based on all valid evaluation ids
1279 
1280  // NOTE: we do not use the gathered information for other ground truth lines; however, this could improve performance
1281 
1282  const Id& connectedGroundTruthId = groundTruthId;
1283 
1284  const FiniteLineT2<T>& connectedGroundTruthLine = linesGroundTruth.find(connectedGroundTruthId)->second;
1285  const T lengthConnectedGroundTruthLine = connectedGroundTruthLine.length();
1286 
1287  SegmentUnion<T> connectedPartialMatch;
1288  IdSet connectedTargetIds;
1289 
1290  std::vector<T> angles;
1291  angles.reserve(validEvaluationIds.size());
1292 
1293  std::vector<T> distances;
1294  distances.reserve(validEvaluationIds.size());
1295 
1296  for (const Id& validEvaluationId : validEvaluationIds)
1297  {
1298  const FiniteLineT2<T>& validEvaluationLine = linesEvaluation.find(validEvaluationId)->second;
1299 
1300  if (groundTruthEvaluationSet.find(combineIds(connectedGroundTruthId, validEvaluationId)) != groundTruthEvaluationSet.cend())
1301  {
1302  // both lines are connected, so we need to determine the coverage
1303 
1304  T locationOnLine0;
1305  connectedGroundTruthLine.nearestPointOnInfiniteLine(validEvaluationLine.point0(), nullptr, &locationOnLine0);
1306 
1307  T locationOnLine1;
1308  connectedGroundTruthLine.nearestPointOnInfiniteLine(validEvaluationLine.point1(), nullptr, &locationOnLine1);
1309 
1310  Utilities::sortLowestToFront2(locationOnLine0, locationOnLine1);
1311 
1312  // as we have a complex matching, we do not consider any coverage outside the ground truth line
1313 
1314  locationOnLine0 = minmax(T(0), locationOnLine0, lengthConnectedGroundTruthLine);
1315  locationOnLine1 = minmax(T(0), locationOnLine1, lengthConnectedGroundTruthLine);
1316 
1317  ocean_assert(locationOnLine0 <= locationOnLine1);
1318  if (locationOnLine0 < locationOnLine1)
1319  {
1320  connectedPartialMatch.addSegment(locationOnLine0, locationOnLine1);
1321  connectedTargetIds.insert(validEvaluationId);
1322 
1323  T angle, distance;
1324  determineSimilarity(connectedGroundTruthLine, validEvaluationLine, DM_PROJECTED_ONTO_EACH_OTHER, angle, distance);
1325 
1326  angles.push_back(angle);
1327  distances.push_back(distance);
1328  }
1329  }
1330  }
1331 
1332  if (connectedPartialMatch)
1333  {
1334  ocean_assert(!connectedTargetIds.empty());
1335 
1336  const T lengthMatch = connectedPartialMatch.unionSize();
1337 
1338  ocean_assert(NumericT<T>::isNotEqualEps(lengthConnectedGroundTruthLine) && NumericT<T>::isNotEqualEps(lengthMatch));
1339 
1340  const T matchCoverage = lengthMatch / lengthConnectedGroundTruthLine;
1341 
1342  const double medianAngle = double(Median::median<T>(angles.data(), angles.size()));
1343  const double medianDistance = double(Median::median<T>(distances.data(), distances.size()));
1344 
1345  return std::make_shared<ComplexLineMatch>(connectedGroundTruthId, connectedTargetIds, matchCoverage, medianAngle, medianDistance, connectedGroundTruthIds, validEvaluationIds);
1346  }
1347 
1348  return LineMatchRef();
1349 }
1350 
1351 inline unsigned long long LineEvaluator::combineIds(const Id& firstId, const Id& secondId)
1352 {
1353  static_assert(sizeof(Id) * 2 == sizeof(unsigned long long), "Invalid data type!");
1354 
1355  return (unsigned long long)firstId | ((unsigned long long)secondId << 32ull);
1356 }
1357 
1358 }
1359 
1360 }
1361 
1362 }
1363 
1364 #endif // META_OCEAN_CV_DETECTOR_LINE_EVALUATOR_H
This class implements a complex match between one source line and several target lines.
Definition: LineEvaluator.h:271
const IdSet & connectedTargetIds() const
Returns the ids of all sibling/connected target lines which have been investigated during the match c...
Definition: LineEvaluator.h:588
const IdSet & connectedSourceIds() const
Returns the ids of all sibling/connected source lines which have been investigated during the match c...
Definition: LineEvaluator.h:583
IdSet connectedTargetIds_
The ids of all sibling/connected target lines which have been investigated during the match creation.
Definition: LineEvaluator.h:310
ComplexLineMatch(const Id sourceId, const IdSet &targetIds, const double coverage, const double medianAngle, const double medianDistance, const IdSet &connectedSourceIds, const IdSet &connectedTargetIds)
Creates a new match object.
Definition: LineEvaluator.h:570
IdSet connectedSourceIds_
The ids of all sibling/connected source lines which have been investigated during the match creation.
Definition: LineEvaluator.h:307
MatchType matchType() const override
Returns the type of the match.
Definition: LineEvaluator.h:578
This class is the base class for all line matches.
Definition: LineEvaluator.h:58
MatchType
Definition of individual match types.
Definition: LineEvaluator.h:65
@ MT_INVALID
An invalid type.
Definition: LineEvaluator.h:67
@ MT_PARTIAL
A partial match,.
Definition: LineEvaluator.h:71
@ MT_PERFECT
A perfect match,.
Definition: LineEvaluator.h:69
@ MT_COMPLEX
A complex match,.
Definition: LineEvaluator.h:73
Id sourceId_
The id of the source line.
Definition: LineEvaluator.h:106
virtual MatchType matchType() const =0
Returns the type of the match.
LineMatch(const Id sourceId)
Creates a new match object.
Definition: LineEvaluator.h:476
Id sourceId() const
Returns the id of the source line.
Definition: LineEvaluator.h:487
virtual ~LineMatch()
Destructs a match object.
Definition: LineEvaluator.h:482
This class implements a partial match between one source line and several target lines.
Definition: LineEvaluator.h:191
double medianAngle_
The median angle between the source line and all target lines, in radian, with range [0,...
Definition: LineEvaluator.h:253
double coverage() const
The amout the target lines cover the source line.
Definition: LineEvaluator.h:555
double medianDistance() const
Returns the median distance between the source line and all target lines.
Definition: LineEvaluator.h:565
const IdSet & targetIds() const
Returns the ids of all target lines belonging to the partial match.
Definition: LineEvaluator.h:550
PartialLineMatch(const Id sourceId, const IdSet &targetIds, const double coverage, const double medianAngle, const double medianDistance)
Creates a new partial match object.
Definition: LineEvaluator.h:521
double coverage_
The coverage of the source line (the amout the target lines cover the source line),...
Definition: LineEvaluator.h:250
MatchType matchType() const override
Returns the type of the match.
Definition: LineEvaluator.h:545
double medianDistance_
The median distance between the source line and all target lines, with range [0, infinity)
Definition: LineEvaluator.h:256
IdSet targetIds_
The ids of all target lines.
Definition: LineEvaluator.h:247
double medianAngle() const
Returns the median angle between the source line and all target lines.
Definition: LineEvaluator.h:560
This class implements a perfect match between a source line and a target line.
Definition: LineEvaluator.h:132
Id targetId() const
Returns the id of the target line.
Definition: LineEvaluator.h:506
PerfectLineMatch(const Id sourceId, const Id targetId, const double angle, const double maximalDistance)
Creates a new match object.
Definition: LineEvaluator.h:492
unsigned int targetId_
The id of the target line.
Definition: LineEvaluator.h:171
double angle_
The absolute angle between both lines, with range [0, PI/2].
Definition: LineEvaluator.h:174
double maximalDistance_
The maximal distance between infinite source line and target line, with range [0, infinity)
Definition: LineEvaluator.h:177
double angle() const
Returns the angle between the source and the target line.
Definition: LineEvaluator.h:511
MatchType matchType() const override
Returns the type of the match.
Definition: LineEvaluator.h:501
double maximalDistance() const
Returns maximal distance between infinite source line and target line.
Definition: LineEvaluator.h:516
This class implements an evalutator for line segments.
Definition: LineEvaluator.h:35
std::unordered_map< Id, IdSet > IdToIdSetMap
Definition of an unordered map mapping ids to sets of ids.
Definition: LineEvaluator.h:51
static bool evaluateLineMatches(const std::unordered_map< Id, FiniteLineT2< T >> &linesGroundTruth, const std::unordered_map< Id, FiniteLineT2< T >> &linesEvaluation, const LineMatchMap &lineMatches, double &coverage, double &medianAngle, double &medianDistance, size_t &countPerfectMatches, size_t &countPartialMatches, size_t &countComplexMatches, size_t &notCoveredGroundTruthLines, size_t &notCoveredEvaluationLines, IdSet *notCoveredGroundTruthLineIds=nullptr, IdSet *notCoveredEvaluationLineIds=nullptr)
Evaluates the overall quality of line matches.
Definition: LineEvaluator.h:993
DistanceMeasure
Definition of individual strategies to determine the distance between two line segments.
Definition: LineEvaluator.h:317
@ DM_PROJECTED_ONTO_GROUND_TRUTH
The end points of the evaluation line are projected onto the infinite ground truth line,...
Definition: LineEvaluator.h:324
@ DM_PROJECTED_ONTO_EVALUATION_LINE
The end points of the ground truth lines are projected onto the infinite evaluation line,...
Definition: LineEvaluator.h:332
@ DM_PROJECTED_ONTO_EACH_OTHER
This measure combines 'DM_PROJECTED_ONTO_GROUND_TRUTH' and 'DM_PROJECTED_ONTO_EVALUATION_LINE'.
Definition: LineEvaluator.h:339
std::unordered_set< Id > IdSet
Definition of an unordered set of ids.
Definition: LineEvaluator.h:46
unsigned int Id
Definition of an id identifying e.g., a specific line.
Definition: LineEvaluator.h:41
static unsigned long long combineIds(const Id &firstId, const Id &secondId)
Combines two (32 bit) ids to one 64 bit value.
Definition: LineEvaluator.h:1351
std::shared_ptr< LineMatch > LineMatchRef
Definition of a shared pointer for a LineMatch object.
Definition: LineEvaluator.h:113
static void determineSimilarity(const FiniteLineT2< T > &lineGroundTruth, const FiniteLineT2< T > &lineEvaluation, const DistanceMeasure distanceMeasure, T &angle, T &distance)
Determines the similarity between two lines known to be overlapping.
Definition: LineEvaluator.h:762
static SegmentUnion< T > determineProjectedSegmentUnion(const FiniteLineT2< T > &lineOfInterest, const IdSet &lineIdsToProject, const std::unordered_map< Id, FiniteLineT2< T >> &lines)
Determines the union of segments resulting from projecting several finite lines onto a unique finite ...
Definition: LineEvaluator.h:1150
static LineMatchRef determineComplexMatch(const std::unordered_map< Id, FiniteLineT2< T >> &linesGroundTruth, const std::unordered_map< Id, FiniteLineT2< T >> &linesEvaluation, const IdToIdSetMap &groundTruthToEvaluationMap, const IdToIdSetMap evaluationToGroundTruthMap, const Id groundTruthId, const Id64Set &groundTruthEvaluationSet, const T complexMatchMaximalGapPixelThreshold)
Determines a complex match for a given ground truth line.
Definition: LineEvaluator.h:1179
static void determineOverlappingAmount(const FiniteLineT2< T > &lineGroundTruth, const FiniteLineT2< T > &lineEvaluation, T *projectedLength=nullptr, T *outOfBorderDistance0=nullptr, T *outOfBorderDistance1=nullptr, T *locationOnLine0=nullptr, T *locationOnLine1=nullptr)
Determines the overlapping metrics between two lines that are known to overlapping (and to be similar...
Definition: LineEvaluator.h:722
std::unordered_multimap< Id, LineMatchRef > LineMatchMap
Definition of an unordered multi map mapping ids to match objects.
Definition: LineEvaluator.h:118
static bool areLinesOverlapping(const FiniteLineT2< T > &lineGroundTruth, const FiniteLineT2< T > &lineEvaluation, const T angleThresholdCos, const T distanceThresholdPixels, const DistanceMeasure distanceMeasure, T *projectedLength=nullptr, T *outOfBorderDistance0=nullptr, T *outOfBorderDistance1=nullptr, T *locationOnLine0=nullptr, T *locationOnLine1=nullptr)
Checks whether two given lines are overlapping up to some extend and determines some overlapping metr...
Definition: LineEvaluator.h:594
static LineMatchMap evaluateLineSegments(const std::unordered_map< Id, FiniteLineT2< T >> &linesGroundTruth, const std::unordered_map< Id, FiniteLineT2< T >> &linesEvaluation, const T perfectMatchAngleThreshold=NumericT< T >::deg2rad(2), const T perfectMatchPixelThreshold=T(2), const T matchAngleThreshold=NumericT< T >::deg2rad(5), const T matchCloseToLinePixelThreshold=T(3), const T partialMatchNonOverlappingPixelThreshold=T(25), const T complexMatchMaximalGapPixelThreshold=T(15))
Evaluates two sets of finite lines.
Definition: LineEvaluator.h:802
std::unordered_set< unsigned long long > Id64Set
Definition of a set holding a pair of ids.
Definition: LineEvaluator.h:347
This class implements an finite line in 2D space.
Definition: FiniteLine2.h:82
bool isValid() const
Returns whether this line has valid parameters.
Definition: FiniteLine2.h:622
bool isEqual(const FiniteLineT2< T > &line, const T &epsilon) const
Returns whether two lines are equal up to a specified epsilon.
Definition: FiniteLine2.h:628
T length() const
Returns the length of the finite line.
Definition: FiniteLine2.h:403
const VectorT2< T > & direction() const
Returns the direction of the line: normalized(point1() - point0())
Definition: FiniteLine2.h:381
const VectorT2< T > & point0() const
Returns the first end point of the line.
Definition: FiniteLine2.h:348
VectorT2< T > nearestPointOnInfiniteLine(const VectorT2< T > &point, T *outOfBoundaryDistance=nullptr, T *finiteLineLocation=nullptr) const
Returns the point on the infinite line (defined by this finite line) to an arbitrary given point.
Definition: FiniteLine2.h:475
const VectorT2< T > & point1() const
Returns the second end point of the line.
Definition: FiniteLine2.h:354
static T median(T *values, const size_t number)
Returns the median value in a given data array.
Definition: Median.h:1144
This class provides basic numeric functionalities.
Definition: Numeric.h:57
static constexpr bool isInsideRange(const T lower, const T value, const T upper, const T epsilon=NumericT< T >::eps())
Returns whether a value lies between a given range up to a provided epsilon border.
Definition: Numeric.h:2872
static constexpr T pi_2()
Returns PI/2 which is equivalent to 90 degree.
Definition: Numeric.h:938
static T abs(const T value)
Returns the absolute value of a given value.
Definition: Numeric.h:1220
static T sqrt(const T value)
Returns the square root of a given value.
Definition: Numeric.h:1533
static T cos(const T value)
Returns the cosine of a given value.
Definition: Numeric.h:1584
static T acos(const T value)
Returns the arccosine of a given value.
Definition: Numeric.h:2907
static constexpr T maxValue()
Returns the max scalar value.
Definition: Numeric.h:3244
This class implements a functionality to determine the union of individual segments.
Definition: SegmentUnion.h:39
const SegmentMap & segments() const
Returns the segments of this object.
Definition: SegmentUnion.h:570
T maximalGap() const
Returns the maximal gap between all successive segments.
Definition: SegmentUnion.h:537
SegmentUnion< T > intersection(const T &startPosition, const T &stopPosition) const
Returns the intersection of this union with a given range (an additional segment).
Definition: SegmentUnion.h:379
void addSegment(const T &startPosition, const T &stopPosition)
Adds a new segment to this union object.
Definition: SegmentUnion.h:109
T unionSize() const
Returns the sum of all segment sizes.
Definition: SegmentUnion.h:578
static void sortLowestToFront2(T &value0, T &value1)
Sorts two values so that the lowest value will finally be the first value.
Definition: base/Utilities.h:619
This class implements a vector with two elements.
Definition: Vector2.h:96
T distance(const VectorT2< T > &right) const
Returns the distance between this 2D position and a second 2D position.
Definition: Vector2.h:627
T sqrDistance(const VectorT2< T > &right) const
Returns the square distance between this 2D position and a second 2D position.
Definition: Vector2.h:633
T minmax(const T &lowerBoundary, const T &value, const T &upperBoundary)
This function fits a given parameter into a specified value range.
Definition: base/Utilities.h:903
unsigned int sqrDistance(const char first, const char second)
Returns the square distance between two values.
Definition: base/Utilities.h:1089
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15