Ocean
PixelContour.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_SEGMENTATION_PIXEL_CONTOUR_H
9 #define META_OCEAN_CV_SEGMENTATION_PIXEL_CONTOUR_H
10 
12 
13 #include "ocean/cv/Bresenham.h"
15 #include "ocean/cv/PixelPosition.h"
16 
17 #include "ocean/math/Vector2.h"
18 
19 namespace Ocean
20 {
21 
22 namespace CV
23 {
24 
25 namespace Segmentation
26 {
27 
28 // Forward declaration.
29 template <typename T> class PixelContourT;
30 
31 /**
32  * Definition of the default PixelContour object with a data type allowing only positive coordinate values.
33  * @see PixelCountourT
34  * @ingroup cvsegmentation
35  */
37 
38 /**
39  * Definition of a PixelContour object with a data type allowing positive and negative coordinate values.
40  * @see PixelCountourT
41  * @ingroup cvsegmentation
42  */
44 
45 /**
46  * Definition of a vector holding pixel contours (with positive coordinate values).
47  * @see PixelContour
48  * @ingroup cvsegmentation
49  */
50 typedef std::vector<PixelContour> PixelContours;
51 
52 /**
53  * Definition of a vector holding pixel contours (with positive and negative coordinate values).
54  * @see PixelContourI
55  * @ingroup cvsegmentation
56  */
57 typedef std::vector<PixelContourI> PixelContoursI;
58 
59 /**
60  * This class implements a contour with pixel accuracy.
61  * A valid contour is composed of several consecutive contour locations.<br>
62  * A contour is dense if all consecutive contour pixel locations are connected via a 8-neighborhood.<br>
63  * Otherwise, a contour is sparse.
64  * @tparam T The data type of the elements of the PixelPositions
65  * @see PixelContour, PixelContourI
66  * @ingroup cvsegmentation
67  */
68 template <typename T>
70 {
71  public:
72 
73  /**
74  * Definition of a pixel position.
75  */
77 
78  /**
79  * Definition of a vector holding pixel positions.
80  */
81  typedef std::vector<PixelPosition> PixelPositions;
82 
83  /**
84  * Definition of a pixel bounding box.
85  */
87 
88  public:
89 
90  /**
91  * Creates a new pixel contour object.
92  */
93  inline PixelContourT();
94 
95  /**
96  * Copy constructor.
97  * @param contour Contour to be copied
98  */
99  inline PixelContourT(const PixelContourT<T>& contour);
100 
101  /**
102  * Move constructor.
103  * @param contour Contour to be moved
104  */
105  inline PixelContourT(PixelContourT<T>&& contour) noexcept;
106 
107  /**
108  * Creates a new pixel contour object by a given set of pixel positions that represent the pixel locations of the contour in a ring order.
109  * @param pixelPositions Pixel positions defining the new contour
110  * @param pixelBoundingBox Optional bounding box of the provided pixel position, must be correct if provided
111  */
112  inline explicit PixelContourT(const PixelPositions& pixelPositions, const PixelBoundingBox& pixelBoundingBox = PixelBoundingBox());
113 
114  /**
115  * Creates a new pixel contour object by moving set of pixel positions that represent the pixel locations of the contour in a ring order.
116  * @param pixelPositions Pixel positions defining the new contour that will be moved
117  * @param pixelBoundingBox Optional bounding box of the provided pixel position, must be correct if provided
118  */
119  inline explicit PixelContourT(PixelPositions&& pixelPositions, const PixelBoundingBox& pixelBoundingBox = PixelBoundingBox());
120 
121  /**
122  * Creates a new pixel contour object by a given set of pixel positions that represent the pixel locations of the contour in a ring order.
123  * @param pixelPositions Pixel positions defining the new contour
124  * @param indexMostLeftPosition Index of the left most pixel position
125  * @param isCounterClockwise True, if the given pixel positions define a contour in counter clockwise order
126  * @param pixelBoundingBox Optional bounding box of the provided pixel position, must be correct if provided
127  */
128  inline PixelContourT(const PixelPositions& pixelPositions, const size_t indexMostLeftPosition, const bool isCounterClockwise, const PixelBoundingBox& pixelBoundingBox = PixelBoundingBox());
129 
130  /**
131  * Creates a new pixel contour object by moving a set of pixel positions that represent the pixel locations of the contour in a ring order.
132  * @param pixelPositions Pixel positions defining the new contour that will be moved
133  * @param indexMostLeftPosition Index of the left most pixel position
134  * @param isCounterClockwise True, if the given pixel positions define a contour in counter clockwise order
135  * @param pixelBoundingBox Optional bounding box of the provided pixel position, must be correct if provided
136  */
137  inline PixelContourT(PixelPositions&& pixelPositions, const size_t indexMostLeftPosition, const bool isCounterClockwise, const PixelBoundingBox& pixelBoundingBox = PixelBoundingBox());
138 
139  /**
140  * Creates a new pixel contour object by a given set of pixel positions that represent the pixel locations of the contour in a ring order and ensures that the contour will have specific properties afterwards.
141  * @param createDistinct True, to create a distinct pixel contour; False, to ignore this property
142  * @param createSimplified True, to create a simplified pixel contour; False, to ignore this property; a simplified contour is always distinct
143  * @param pixelPositions Pixel positions defining the new contour
144  * @param pixelBoundingBox Optional bounding box of the provided pixel position, must be correct if provided
145  */
146  inline PixelContourT(const bool createDistinct, const bool createSimplified, const PixelPositions& pixelPositions, const PixelBoundingBox& pixelBoundingBox = PixelBoundingBox());
147 
148  /**
149  * Creates a new sparse pixel contour object by a given set of pixel positions that represent the pixel locations of the contour in a ring order.
150  * In addition, the new contour will be a sparse contour by ensuring that the minimal distance between consecutive contour pixels has at least a specific distance.
151  * @param pixelPositions Pixel positions out of which the new contour will be created (by skipping pixels too close to eachother), at least one
152  * @param minimalSqrDistance The minimal square distance between consecutive locations of the new contour, with range [1, infinity)
153  * @param startIndex The index of the pixel which will be preserved in any case, with range [0, size())
154  */
155  PixelContourT(const PixelPositions& pixelPositions, const unsigned int minimalSqrDistance, const size_t startIndex = 0);
156 
157  /**
158  * Returns the pixels of this contour.
159  * @return Pixel positions
160  */
161  inline const PixelPositions& pixels() const;
162 
163  /**
164  * Returns the bounding box of this contour.
165  * @return Bounding box
166  */
168 
169  /**
170  * Computes the area of a contour
171  * Uses the Shoelace formula to determine the area of a contour. The contour must not self-intersect.
172  * @return The area of the contour, range: [0, infinity)
173  * @sa Geometry::Utilities::computePolygonArea()
174  */
175  inline unsigned int area() const;
176 
177  /**
178  * Computes the signed area of a contour
179  * Uses the Shoelace formula to determine the area of a contour. The contour must not self-intersect.
180  * @return The signed area of the contour; this value will be positive if the contour pixels are in counter-clockwise order and negative if they are in clock-wise order, range: (-infinity, infinity)
181  * @sa Geometry::Utilities::computePolygonAreaSigned()
182  */
183  int areaSigned() const;
184 
185  /**
186  * Returns the index of a left most position of this contour with following pixel right to this position.
187  * @return Index of the left most position, -1 if invalid
188  */
189  size_t indexLeftPosition() const;
190 
191  /**
192  * Returns whether this contour is defined in a counter clockwise order, clockwise otherwise.<br>
193  * A contour with contour clockwise order has a negative 2D edge cross product at the most left position.<br>
194  * If this contour is degenerated the result is arbitrary.
195  * @return True, if so
196  */
197  bool isCounterClockwise() const;
198 
199  /**
200  * Returns whether all consecutive pixels of this contour are different.
201  * @return True, if so
202  * @see makeDistinct().
203  */
204  bool isDistinct() const;
205 
206  /**
207  * Returns whether this contour is dense.
208  * Successive pixel positions in a dense contour are part of a direct 8-neighborhood.<br>
209  * An empty set of pixel positions is dense.
210  * @return True, if so
211  */
212  bool isDense() const;
213 
214  /**
215  * Returns whether this contour is dense according to a 4-neighborhood.
216  * Successive pixel positions in a dense contour are part of a direct 4-neighborhood.<br>
217  * An empty set of pixel positions is dense.
218  * @return True, if so
219  */
220  bool isDense4() const;
221 
222  /**
223  * Returns whether this contour is simplified.
224  * A simplified contour is the sparsest contour possible. e.g., the contour does not have successive pixels building a line.<br>
225  * An empty contour is always simplified.
226  * @return True, if so
227  * @see simplify().
228  */
229  bool isSimplified() const;
230 
231  /**
232  * Removes non distinct pixels from this contour.
233  * The resulting contour will not have identical consecutive pixels.
234  * @see isDistinct().
235  */
236  void makeDistinct();
237 
238  /**
239  * Makes this pixel contour dense.<br>
240  */
241  void makeDense();
242 
243  /**
244  * Returns the simplified contour of this contour which will be a sparse but identical contour.
245  * @return The simplified contour
246  * @see simplify(), isSimplified().
247  */
249 
250  /**
251  * Simplifies this (dense) contour to a sparse but identical contour.
252  * @see simplified(), isSimplified().
253  */
254  void simplify();
255 
256  /**
257  * Creates a sparse contour out of this contour by ensuring that the minimal distance between consecutive contour pixels has at least a specific distance.
258  * @param minimalSqrDistance The minimal square distance between consecutive locations of the resulting sparse contour, with range [1, infinity)
259  * @param startIndex The index of the contour pixel which will be preserved in any case, with range [0, size())
260  * @return The sparse contour
261  */
262  PixelContourT<T> sparseContour(const unsigned int minimalSqrDistance, const size_t startIndex = 0) const;
263 
264  /**
265  * Returns the smallest square distance between consecutive contour pixels.
266  * This contour must not be empty.
267  * @return The smallest square distance, with range [0, infinity)
268  */
269  unsigned int smallestSqrDistanceBetweenPixels() const;
270 
271  /**
272  * Returns the largest square distance between consecutive contour pixels.
273  * This contour must not be empty.
274  * @return The largest square distance, with range [0, infinity)
275  */
276  unsigned int largestSqrDistanceBetweenPixels() const;
277 
278  /**
279  * Returns the number of pixel positions of this contour.
280  * @return Number of pixel positions
281  */
282  inline size_t size() const;
283 
284  /**
285  * Returns whether this contour does not hold any pixel position.
286  * @return True, if so
287  */
288  inline bool isEmpty() const;
289 
290  /**
291  * Returns the pixel position of this pixel contour.
292  * @param index Index of the pixel position that is requested, with range [0, size())
293  * @return Requested pixel position
294  */
295  inline const PixelPosition& operator[](const size_t index) const;
296 
297  /**
298  * Assign operator.
299  * @param contour Contour object to be assigned
300  * @return Reference to this object
301  */
302  inline PixelContourT<T>& operator=(const PixelContourT<T>& contour);
303 
304  /**
305  * Move operator.
306  * @param contour Contour object to be moved
307  * @return Reference to this object
308  */
309  inline PixelContourT<T>& operator=(PixelContourT<T>&& contour) noexcept;
310 
311  /**
312  * Returns whether this contour holds at least one pixel position.
313  * @return True, if so
314  */
315  explicit inline operator bool() const;
316 
317  private:
318 
319  /**
320  * Returns whether two given vectors are parallel and point into the same direction.
321  * @param first First vector
322  * @param second Second vector
323  * @return True, if so
324  */
325  static inline bool similar(const VectorI2& first, const VectorI2& second);
326 
327  protected:
328 
329  /// Pixel positions of the contour.
331 
332  /// Index of the most left pixel.
333  mutable size_t contourMostLeftIndex;
334 
335  /// State whether this contour is counter clockwise: -1 undefined, 0 false, 1 true.
336  mutable unsigned int contourCounterClockwise;
337 
338  /// Bounding box of the contour.
340 };
341 
342 template <typename T>
344  contourMostLeftIndex(size_t(-1)),
345  contourCounterClockwise((unsigned int)(-1))
346 {
347  // nothing to do here
348 }
349 
350 template <typename T>
352  contourPixels(contour.contourPixels),
353  contourMostLeftIndex(contour.contourMostLeftIndex),
354  contourCounterClockwise(contour.contourCounterClockwise),
355  contourBoundingBox(contour.contourBoundingBox)
356 {
357  // nothing to do here
358 }
359 
360 template <typename T>
362  contourPixels(std::move(contour.contourPixels)),
363  contourMostLeftIndex(contour.contourMostLeftIndex),
364  contourCounterClockwise(contour.contourCounterClockwise),
365  contourBoundingBox(contour.contourBoundingBox)
366 {
367  contour.contourMostLeftIndex = (size_t)(-1);
368  contour.contourCounterClockwise = (unsigned int)(-1);
369  contour.contourBoundingBox = PixelBoundingBox();
370 }
371 
372 template <typename T>
373 inline PixelContourT<T>::PixelContourT(const PixelPositions& pixelPositions, const PixelBoundingBox& pixelBoundingBox) :
374  contourPixels(pixelPositions),
375  contourMostLeftIndex((size_t)(-1)),
376  contourCounterClockwise((unsigned int)(-1)),
377  contourBoundingBox(pixelBoundingBox)
378 {
380 }
381 
382 template <typename T>
383 inline PixelContourT<T>::PixelContourT(PixelPositions&& pixelPositions, const PixelBoundingBox& pixelBoundingBox) :
384  contourPixels(std::move(pixelPositions)),
385  contourMostLeftIndex((size_t)(-1)),
386  contourCounterClockwise((unsigned int)(-1)),
387  contourBoundingBox(pixelBoundingBox)
388 {
390 }
391 
392 template <typename T>
393 inline PixelContourT<T>::PixelContourT(const PixelPositions& pixelPositions, const size_t indexMostLeftPosition, const bool isCounterClockwise, const PixelBoundingBox& pixelBoundingBox) :
394  contourPixels(pixelPositions),
395  contourMostLeftIndex(indexMostLeftPosition),
396  contourCounterClockwise(isCounterClockwise),
397  contourBoundingBox(pixelBoundingBox)
398 {
400 }
401 
402 template <typename T>
403 inline PixelContourT<T>::PixelContourT(PixelPositions&& pixelPositions, const size_t indexMostLeftPosition, const bool isCounterClockwise, const PixelBoundingBox& pixelBoundingBox) :
404  contourPixels(std::move(pixelPositions)),
405  contourMostLeftIndex(indexMostLeftPosition),
406  contourCounterClockwise(isCounterClockwise),
407  contourBoundingBox(pixelBoundingBox)
408 {
410 }
411 
412 template <typename T>
413 inline PixelContourT<T>::PixelContourT(const bool createDistinct, const bool createSimplified, const PixelPositions& pixelPositions, const PixelBoundingBox& pixelBoundingBox) :
414  contourPixels(pixelPositions),
415  contourMostLeftIndex((size_t)(-1)),
416  contourCounterClockwise((unsigned int)(-1)),
417  contourBoundingBox(pixelBoundingBox)
418 {
419  if (createSimplified)
420  simplify();
421  else if (createDistinct)
422  makeDistinct();
423 
425 }
426 
427 template <typename T>
428 PixelContourT<T>::PixelContourT(const PixelPositions& pixelPositions, const unsigned int minimalSqrDistance, const size_t startIndex) :
429  contourMostLeftIndex((size_t)(-1)),
430  contourCounterClockwise((unsigned int)(-1))
431 {
432  ocean_assert(pixelPositions.size() >= 1);
433  ocean_assert(minimalSqrDistance >= 1u);
434  ocean_assert(startIndex < pixelPositions.size());
435 
436  ocean_assert(contourPixels.empty());
437  contourPixels.reserve(pixelPositions.size());
438 
439  // our first sparse
440  contourPixels.push_back(pixelPositions[startIndex]);
441 
442  for (size_t n = startIndex + 1; n <= startIndex + pixelPositions.size(); ++n)
443  {
444  const size_t nModulo = (size_t)modulo((int)n, (int)pixelPositions.size());
445 
446  if (contourPixels.back().sqrDistance(pixelPositions[nModulo]) >= minimalSqrDistance)
447  contourPixels.push_back(pixelPositions[nModulo]);
448  }
449 
450  if (contourPixels.size() > 1 && contourPixels.front().sqrDistance(contourPixels.back()) < minimalSqrDistance)
451  contourPixels.pop_back();
452 
453  ocean_assert(contourPixels.size() == 1 || PixelContourT<T>(contourPixels).smallestSqrDistanceBetweenPixels() >= minimalSqrDistance);
454 }
455 
456 template <typename T>
458 {
459  return contourPixels;
460 }
461 
462 template <typename T>
463 inline size_t PixelContourT<T>::size() const
464 {
465  return contourPixels.size();
466 }
467 
468 template <typename T>
469 inline bool PixelContourT<T>::isEmpty() const
470 {
471  return contourPixels.empty();
472 }
473 
474 template <typename T>
475 inline const typename PixelContourT<T>::PixelPosition& PixelContourT<T>::operator[](const size_t index) const
476 {
477  ocean_assert(index < contourPixels.size());
478  return contourPixels[index];
479 }
480 
481 template <typename T>
483 {
484  contourPixels = contour.contourPixels;
485  contourMostLeftIndex = contour.contourMostLeftIndex;
486  contourCounterClockwise = contour.contourCounterClockwise;
487  contourBoundingBox = contour.contourBoundingBox;
488 
489  return *this;
490 }
491 
492 template <typename T>
494 {
495  if (this != &contour)
496  {
497  contourPixels = std::move(contour.contourPixels);
498  contourMostLeftIndex = contour.contourMostLeftIndex;
499  contourCounterClockwise = contour.contourCounterClockwise;
500  contourBoundingBox = contour.contourBoundingBox;
501 
502  contour.contourMostLeftIndex = (size_t)(-1);
503  contour.contourCounterClockwise = (unsigned int)(-1);
504  contour.contourBoundingBox = PixelBoundingBox();
505  }
506 
507  return *this;
508 }
509 
510 template <typename T>
512 {
513  if (!contourBoundingBox)
514  contourBoundingBox = PixelBoundingBox(contourPixels);
515 
516  return contourBoundingBox;
517 }
518 
519 template <typename T>
520 inline unsigned int PixelContourT<T>::area() const
521 {
522  return (unsigned int)std::abs(areaSigned());
523 }
524 
525 template <typename T>
527 {
528  if (contourPixels.size() < 3)
529  {
530  return 0;
531  }
532 
533  int area = 0;
534 
535  for (size_t i = 0; i < (contourPixels.size() - 1); ++i)
536  {
537  const int partialArea = contourPixels[i].x() * contourPixels[i + 1].y() - contourPixels[i].y() * contourPixels[i + 1].x();
538  ocean_assert(partialArea <= 0 || area <= NumericT<int>::maxValue() - partialArea && "Integer overflow");
539  ocean_assert(partialArea >= 0 || area >= NumericT<int>::minValue() - partialArea && "Integer underflow");
540  area += partialArea;
541  }
542 
543  return (area + NumericT<int>::copySign(1, area)) / 2;
544 }
545 
546 template <typename T>
548 {
549  if (contourMostLeftIndex != (size_t)(-1))
550  return contourMostLeftIndex;
551 
552  if (contourPixels.empty())
553  return (size_t)(-1);
554 
555  if (contourPixels.size() == 1)
556  return 0;
557 
558  // finding the most left pixel with following pixel right to this position
559 
560  T left = NumericT<T>::maxValue();
561  T bottom = NumericT<T>::minValue();
562 
563  size_t index = size_t(-1);
564 
565  for (size_t n = 0u; n < contourPixels.size(); ++n)
566  {
567  if (contourPixels[n].x() < left || (contourPixels[n].x() == left && contourPixels[n].y() > bottom))
568  {
569  left = contourPixels[n].x();
570  bottom = contourPixels[n].y();
571  index = n;
572  }
573  }
574 
575  ocean_assert(index != size_t(-1));
576  ocean_assert(!contourBoundingBox || left == contourBoundingBox.left());
577 
578  contourMostLeftIndex = index;
579  return index;
580 }
581 
582 template <typename T>
584 {
585  if (contourCounterClockwise != (unsigned int)(-1))
586  return contourCounterClockwise == 1u;
587 
588  const size_t index0 = size_t(indexLeftPosition());
589  ocean_assert(index0 != size_t(-1));
590 
591  const size_t index2 = modulo(int(index0) - 1, int(contourPixels.size()));
592 
593  const PixelPosition& position0 = contourPixels[index0];
594  const PixelPosition& position2 = contourPixels[index2];
595 
596  const int dx02 = int(position2.x()) - int(position0.x());
597  const int dy02 = int(position2.y()) - int(position0.y());
598 
599  size_t index1 = size_t(-1);
600  size_t offset = 1;
601 
602  while (true)
603  {
604  index1 = modulo(int(index0 + offset), int(contourPixels.size()));
605 
606  // the contour is degenerated and thus the result is arbitrary
607  if (index1 == index2 || index1 == index0)
608  return true;
609 
610  const PixelPosition& position1 = contourPixels[index1];
611 
612  const int dx01 = int(position1.x()) - int(position0.x());
613  const int dy01 = int(position1.y()) - int(position0.y());
614 
615  // cross (dx01, dy01) x (dx02, dy02) = dx01 * dy02 - dx02 * dy01
616  const int crossProduct = dx01 * dy02 - dx02 * dy01;
617 
618  if (crossProduct != 0)
619  {
620  contourCounterClockwise = crossProduct < 0;
621  return contourCounterClockwise == 1u;
622  }
623 
624  offset++;
625  }
626 
627  ocean_assert(false && "This should never happen!");
628  contourCounterClockwise = 1u;
629  return true;
630 }
631 
632 template <typename T>
634 {
635  if (contourPixels.size() <= 1)
636  return true;
637 
638  for (size_t n = 0; n < contourPixels.size() - 1; ++n)
639  if (contourPixels[n] == contourPixels[n + 1u])
640  return false;
641 
642  return contourPixels.front() != contourPixels.back();
643 }
644 
645 template <typename T>
647 {
648  if (contourPixels.size() <= 1)
649  return true;
650 
651  for (size_t n = 1; n < contourPixels.size(); ++n)
652  if (!contourPixels[n - 1].isNeighbor8(contourPixels[n]))
653  return false;
654 
655  return contourPixels.back().isNeighbor8(contourPixels.front());
656 }
657 
658 template <typename T>
660 {
661  if (contourPixels.size() <= 1)
662  return true;
663 
664  for (size_t n = 1; n < contourPixels.size(); ++n)
665  if (!contourPixels[n - 1].isNeighbor4(contourPixels[n]))
666  return false;
667 
668  return contourPixels.back().isNeighbor4(contourPixels.front());
669 }
670 
671 template <typename T>
673 {
674  if (contourPixels.size() <= 2)
675  return true;
676 
677  PixelPosition previousOffset(contourPixels[1] - contourPixels[0]);
678 
679  for (size_t n = 2; n < contourPixels.size(); ++n)
680  {
681  const PixelPosition currentOffset(contourPixels[n] - contourPixels[n - 1]);
682 
683  if (currentOffset == previousOffset)
684  return false;
685 
686  previousOffset = currentOffset;
687  }
688 
689  // now the remaining two pixels
690  PixelPosition currentOffset(contourPixels[0] - contourPixels[contourPixels.size() - 1]);
691  if (previousOffset == currentOffset)
692  return false;
693 
694  // we avoid: previousOffset = currentOffset;
695  if (PixelPosition(contourPixels[1] - contourPixels[0]) == currentOffset)
696  return false;
697 
698  return true;
699 }
700 
701 template <typename T>
703 {
704  if (contourPixels.size() > 1)
705  {
706  PixelPositions distinctPixels;
707  distinctPixels.reserve(contourPixels.size());
708 
709  distinctPixels.push_back(contourPixels.front());
710 
711  for (size_t n = 1; n < contourPixels.size(); ++n)
712  if (contourPixels[n - 1] != contourPixels[n])
713  distinctPixels.push_back(contourPixels[n]);
714 
715  if (distinctPixels.size() > 1 && distinctPixels.front() == distinctPixels.back())
716  distinctPixels.pop_back();
717 
718  ocean_assert(distinctPixels.size() <= 1 || distinctPixels.front() != distinctPixels.back());
719 
720  // the bounding box should not have changed
721  ocean_assert(!contourBoundingBox || contourBoundingBox == PixelBoundingBox(distinctPixels));
722 
723  contourMostLeftIndex = size_t(-1);
724  contourCounterClockwise = (unsigned int)(-1);
725 
726  contourPixels = std::move(distinctPixels);
727  }
728 }
729 
730 template <typename T>
732 {
733  if (contourPixels.size() > 1)
734  {
735  PixelPositions newPositions;
736  newPositions.reserve(contourPixels.size() * 20);
737 
738  for (size_t n = 0; n < contourPixels.size(); ++n)
739  {
740  const PixelPosition& start = contourPixels[n];
741  const PixelPosition& end = contourPixels[modulo(int(n + 1), int(contourPixels.size()))];
742 
743  int x = int(start.x());
744  int y = int(start.y());
745  const int xEnd = int(end.x());
746  const int yEnd = int(end.y());
747 
748  CV::Bresenham bresenham(x, y, xEnd, yEnd);
749 
750  while (x != xEnd || y != yEnd)
751  {
752  newPositions.push_back(PixelPosition(T(x), T(y)));
753  bresenham.findNext(x, y);
754  }
755  }
756 
757  contourPixels = std::move(newPositions);
758  ocean_assert(!contourBoundingBox || contourBoundingBox == PixelBoundingBox(contourPixels));
759 
760  contourMostLeftIndex = size_t(-1);
761  contourCounterClockwise = (unsigned int)(-1);
762  }
763 }
764 
765 template <typename T>
767 {
768  if (contourPixels.size() <= 1)
769  return PixelContourT<T>(*this);
770 
771  PixelPositions newPixelPositions;
772  newPixelPositions.reserve(contourPixels.size());
773 
774  VectorI2 currentDirection = VectorI2(int(contourPixels.front().x() - contourPixels.back().x()), int(contourPixels.front().y() - contourPixels.back().y()));
775 
776  for (size_t n = 1; n < contourPixels.size(); ++n)
777  {
778  const VectorI2 newDirection = VectorI2(int(contourPixels[n].x() - contourPixels[n - 1].x()), int(contourPixels[n].y() - contourPixels[n - 1].y()));
779 
780  if (!newDirection.isNull())
781  {
782  if (!similar(currentDirection, newDirection))
783  {
784  currentDirection = newDirection;
785  newPixelPositions.push_back(contourPixels[n - 1]);
786  }
787  }
788  }
789 
790  const VectorI2 newDirection = VectorI2(int(contourPixels.front().x() - contourPixels.back().x()), int(contourPixels.front().y() - contourPixels.back().y()));
791 
792  if (currentDirection != newDirection)
793  newPixelPositions.push_back(contourPixels.back());
794 
795 #ifdef OCEAN_DEBUG
796  {
797  ocean_assert(!newPixelPositions.empty());
798 
799  const PixelContourT<T> debugContour(newPixelPositions);
800  ocean_assert(debugContour.boundingBox() == boundingBox());
801  ocean_assert(debugContour.isCounterClockwise() == isCounterClockwise());
802 
803  ocean_assert(debugContour.isSimplified());
804  ocean_assert(debugContour.isDistinct());
805  }
806 #endif
807 
808  return PixelContourT<T>(std::move(newPixelPositions), contourBoundingBox);
809 }
810 
811 template <typename T>
813 {
814  *this = simplified();
815 }
816 
817 template <typename T>
818 PixelContourT<T> PixelContourT<T>::sparseContour(const unsigned int minimalSqrDistance, const size_t startIndex) const
819 {
820  ocean_assert(contourPixels.size() >= 1);
821 
822  if (contourPixels.empty())
823  return PixelContourT<T>();
824 
825  ocean_assert(minimalSqrDistance >= 1u);
826  ocean_assert(startIndex < contourPixels.size());
827 
828  return PixelContourT<T>(contourPixels, minimalSqrDistance, startIndex);
829 }
830 
831 template <typename T>
833 {
834  ocean_assert(!contourPixels.empty());
835 
836  unsigned int sqrDistance = contourPixels.front().sqrDistance(contourPixels.back());
837 
838  for (size_t n = 1; n < contourPixels.size(); ++n)
839  {
840  const unsigned int localSqrDistance = contourPixels[n - 1].sqrDistance(contourPixels[n]);
841 
842  if (localSqrDistance < sqrDistance)
843  sqrDistance = localSqrDistance;
844  }
845 
846  return sqrDistance;
847 }
848 
849 template <typename T>
851 {
852  ocean_assert(!contourPixels.empty());
853 
854  unsigned int sqrDistance = contourPixels.front().sqrDistance(contourPixels.back());
855 
856  for (size_t n = 1; n < contourPixels.size(); ++n)
857  {
858  const unsigned int localSqrDistance = contourPixels[n - 1].sqrDistance(contourPixels[n]);
859 
860  if (localSqrDistance > sqrDistance)
861  sqrDistance = localSqrDistance;
862  }
863 
864  return sqrDistance;
865 }
866 
867 template <typename T>
868 inline PixelContourT<T>::operator bool() const
869 {
870  return !contourPixels.empty();
871 }
872 
873 template <typename T>
874 inline bool PixelContourT<T>::similar(const VectorI2& first, const VectorI2& second)
875 {
876  ocean_assert(first.x() != 0 || first.y() != 0);
877  ocean_assert(second.x() != 0 || second.y() != 0);
878 
879 #ifdef OCEAN_DEBUG
880 
881  bool fastResult = first.x() * second.y() == second.x() * first.y()
882  && (0x80000000 & first.x()) == (0x80000000 & second.x())
883  && (0x80000000 & first.y()) == (0x80000000 & second.y());
884 
885  Vector2 vf(Scalar(first.x()), Scalar(first.y()));
886  Vector2 vs(Scalar(second.x()), Scalar(second.y()));
887 
888  vf.normalize();
889  vs.normalize();
890 
891  ocean_assert(fastResult == (vf == vs));
892 
893 #endif
894 
895  return first.x() * second.y() == second.x() * first.y()
896  && (0x80000000 & first.x()) == (0x80000000 & second.x())
897  && (0x80000000 & first.y()) == (0x80000000 & second.y());
898 }
899 
900 }
901 
902 }
903 
904 }
905 
906 #endif // META_OCEAN_CV_SEGMENTATION_PIXEL_CONTOUR_H
This class implements bresenham line algorithms.
Definition: Bresenham.h:27
void findNext(int &x, int &y)
Applies one Bresenham step to find the next pixel.
This class implements a 2D bounding box with pixel precision.
Definition: PixelBoundingBox.h:57
This class implements a 2D pixel position with pixel precision.
Definition: PixelPosition.h:65
void simplify()
Simplifies this (dense) contour to a sparse but identical contour.
Definition: PixelContour.h:812
unsigned int smallestSqrDistanceBetweenPixels() const
Returns the smallest square distance between consecutive contour pixels.
Definition: PixelContour.h:832
size_t size() const
Returns the number of pixel positions of this contour.
Definition: PixelContour.h:463
PixelContourT< T > & operator=(PixelContourT< T > &&contour) noexcept
Move operator.
Definition: PixelContour.h:493
void makeDense()
Makes this pixel contour dense.
Definition: PixelContour.h:731
unsigned int largestSqrDistanceBetweenPixels() const
Returns the largest square distance between consecutive contour pixels.
Definition: PixelContour.h:850
PixelPositionT< T > PixelPosition
Definition of a pixel position.
Definition: PixelContour.h:76
PixelPositions contourPixels
Pixel positions of the contour.
Definition: PixelContour.h:330
size_t indexLeftPosition() const
Returns the index of a left most position of this contour with following pixel right to this position...
Definition: PixelContour.h:547
unsigned int contourCounterClockwise
State whether this contour is counter clockwise: -1 undefined, 0 false, 1 true.
Definition: PixelContour.h:336
PixelBoundingBoxT< T > PixelBoundingBox
Definition of a pixel bounding box.
Definition: PixelContour.h:86
void makeDistinct()
Removes non distinct pixels from this contour.
Definition: PixelContour.h:702
bool isEmpty() const
Returns whether this contour does not hold any pixel position.
Definition: PixelContour.h:469
PixelContourT< T > & operator=(const PixelContourT< T > &contour)
Assign operator.
Definition: PixelContour.h:482
size_t contourMostLeftIndex
Index of the most left pixel.
Definition: PixelContour.h:333
const PixelPositions & pixels() const
Returns the pixels of this contour.
Definition: PixelContour.h:457
PixelContourT(PixelPositions &&pixelPositions, const PixelBoundingBox &pixelBoundingBox=PixelBoundingBox())
Creates a new pixel contour object by moving set of pixel positions that represent the pixel location...
Definition: PixelContour.h:383
PixelContourT(PixelContourT< T > &&contour) noexcept
Move constructor.
Definition: PixelContour.h:361
bool isDense() const
Returns whether this contour is dense.
Definition: PixelContour.h:646
bool isDense4() const
Returns whether this contour is dense according to a 4-neighborhood.
Definition: PixelContour.h:659
PixelContourT(const PixelPositions &pixelPositions, const PixelBoundingBox &pixelBoundingBox=PixelBoundingBox())
Creates a new pixel contour object by a given set of pixel positions that represent the pixel locatio...
Definition: PixelContour.h:373
const PixelBoundingBox & boundingBox() const
Returns the bounding box of this contour.
Definition: PixelContour.h:511
bool isDistinct() const
Returns whether all consecutive pixels of this contour are different.
Definition: PixelContour.h:633
PixelContourT(const PixelContourT< T > &contour)
Copy constructor.
Definition: PixelContour.h:351
PixelContourT< T > simplified() const
Returns the simplified contour of this contour which will be a sparse but identical contour.
Definition: PixelContour.h:766
unsigned int area() const
Computes the area of a contour Uses the Shoelace formula to determine the area of a contour.
Definition: PixelContour.h:520
PixelContourT(const PixelPositions &pixelPositions, const size_t indexMostLeftPosition, const bool isCounterClockwise, const PixelBoundingBox &pixelBoundingBox=PixelBoundingBox())
Creates a new pixel contour object by a given set of pixel positions that represent the pixel locatio...
Definition: PixelContour.h:393
PixelContourT(const PixelPositions &pixelPositions, const unsigned int minimalSqrDistance, const size_t startIndex=0)
Creates a new sparse pixel contour object by a given set of pixel positions that represent the pixel ...
Definition: PixelContour.h:428
PixelContourT()
Creates a new pixel contour object.
Definition: PixelContour.h:343
PixelContourT(const bool createDistinct, const bool createSimplified, const PixelPositions &pixelPositions, const PixelBoundingBox &pixelBoundingBox=PixelBoundingBox())
Creates a new pixel contour object by a given set of pixel positions that represent the pixel locatio...
Definition: PixelContour.h:413
std::vector< PixelPosition > PixelPositions
Definition of a vector holding pixel positions.
Definition: PixelContour.h:81
static bool similar(const VectorI2 &first, const VectorI2 &second)
Returns whether two given vectors are parallel and point into the same direction.
Definition: PixelContour.h:874
bool isSimplified() const
Returns whether this contour is simplified.
Definition: PixelContour.h:672
PixelBoundingBox contourBoundingBox
Bounding box of the contour.
Definition: PixelContour.h:339
const PixelPosition & operator[](const size_t index) const
Returns the pixel position of this pixel contour.
Definition: PixelContour.h:475
bool isCounterClockwise() const
Returns whether this contour is defined in a counter clockwise order, clockwise otherwise.
Definition: PixelContour.h:583
PixelContourT(PixelPositions &&pixelPositions, const size_t indexMostLeftPosition, const bool isCounterClockwise, const PixelBoundingBox &pixelBoundingBox=PixelBoundingBox())
Creates a new pixel contour object by moving a set of pixel positions that represent the pixel locati...
Definition: PixelContour.h:403
PixelContourT< T > sparseContour(const unsigned int minimalSqrDistance, const size_t startIndex=0) const
Creates a sparse contour out of this contour by ensuring that the minimal distance between consecutiv...
Definition: PixelContour.h:818
int areaSigned() const
Computes the signed area of a contour Uses the Shoelace formula to determine the area of a contour.
Definition: PixelContour.h:526
This class provides basic numeric functionalities.
Definition: Numeric.h:57
static constexpr T minValue()
Returns the min scalar value.
Definition: Numeric.h:3250
static constexpr T maxValue()
Returns the max scalar value.
Definition: Numeric.h:3244
const T & x() const noexcept
Returns the x value.
Definition: Vector2.h:698
const T & y() const noexcept
Returns the y value.
Definition: Vector2.h:710
bool isNull() const
Returns whether this vector is a null vector up to a small epsilon.
Definition: Vector2.h:734
bool normalize()
Normalizes this vector.
Definition: Vector2.h:600
unsigned int sqrDistance(const char first, const char second)
Returns the square distance between two values.
Definition: base/Utilities.h:1089
T modulo(const T &value, const T &ring)
Returns the modulo value of a given parameter within a ring allowing positive and negative parameters...
Definition: base/Utilities.h:924
PixelPositionT< unsigned int > PixelPosition
Definition of the default PixelPosition object with a data type allowing only positive coordinate val...
Definition: PixelPosition.h:27
PixelBoundingBoxT< unsigned int > PixelBoundingBox
Definition of the default PixelBoundingBox object with data type allowing only positive coordinate va...
Definition: PixelBoundingBox.h:21
std::vector< PixelContourI > PixelContoursI
Definition of a vector holding pixel contours (with positive and negative coordinate values).
Definition: PixelContour.h:57
PixelContourT< unsigned int > PixelContour
Definition of the default PixelContour object with a data type allowing only positive coordinate valu...
Definition: PixelContour.h:29
std::vector< PixelContour > PixelContours
Definition of a vector holding pixel contours (with positive coordinate values).
Definition: PixelContour.h:50
PixelContourT< int > PixelContourI
Definition of a PixelContour object with a data type allowing positive and negative coordinate values...
Definition: PixelContour.h:43
VectorT2< int > VectorI2
Definition of a 2D vector with integer values.
Definition: Vector2.h:49
float Scalar
Definition of a scalar type.
Definition: Math.h:128
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15