Ocean
Loading...
Searching...
No Matches
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"
16
17#include "ocean/math/Vector2.h"
18
19namespace Ocean
20{
21
22namespace CV
23{
24
25namespace Segmentation
26{
27
28// Forward declaration.
29template <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 */
50using PixelContours = std::vector<PixelContour>;
51
52/**
53 * Definition of a vector holding pixel contours (with positive and negative coordinate values).
54 * @see PixelContourI
55 * @ingroup cvsegmentation
56 */
57using PixelContoursI = std::vector<PixelContourI>;
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 */
68template <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 using PixelPositions = std::vector<PixelPosition>;
82
83 /**
84 * Definition of a pixel bounding box.
85 */
87
88 public:
89
90 /**
91 * Creates a new pixel contour object.
92 */
93 PixelContourT() = default;
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 */
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 */
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 */
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 */
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 mostLeftIndex_ = size_t(-1);
334
335 /// State whether this contour is counter clockwise: -1 undefined, 0 false, 1 true.
336 mutable unsigned int counterClockwise_ = (unsigned int)(-1);
337
338 /// Bounding box of the contour.
340};
341
342template <typename T>
344 pixels_(contour.pixels_),
345 mostLeftIndex_(contour.mostLeftIndex_),
346 counterClockwise_(contour.counterClockwise_),
347 boundingBox_(contour.boundingBox_)
348{
349 // nothing to do here
350}
351
352template <typename T>
354 pixels_(std::move(contour.pixels_)),
355 mostLeftIndex_(contour.mostLeftIndex_),
356 counterClockwise_(contour.counterClockwise_),
357 boundingBox_(contour.boundingBox_)
358{
359 contour.mostLeftIndex_ = (size_t)(-1);
360 contour.counterClockwise_ = (unsigned int)(-1);
361 contour.boundingBox_ = PixelBoundingBox();
362}
363
364template <typename T>
365inline PixelContourT<T>::PixelContourT(const PixelPositions& pixelPositions, const PixelBoundingBox& pixelBoundingBox) :
366 pixels_(pixelPositions),
367 mostLeftIndex_((size_t)(-1)),
368 counterClockwise_((unsigned int)(-1)),
369 boundingBox_(pixelBoundingBox)
370{
372}
373
374template <typename T>
375inline PixelContourT<T>::PixelContourT(PixelPositions&& pixelPositions, const PixelBoundingBox& pixelBoundingBox) :
376 pixels_(std::move(pixelPositions)),
377 mostLeftIndex_((size_t)(-1)),
378 counterClockwise_((unsigned int)(-1)),
379 boundingBox_(pixelBoundingBox)
380{
382}
383
384template <typename T>
385inline PixelContourT<T>::PixelContourT(const PixelPositions& pixelPositions, const size_t indexMostLeftPosition, const bool isCounterClockwise, const PixelBoundingBox& pixelBoundingBox) :
386 pixels_(pixelPositions),
387 mostLeftIndex_(indexMostLeftPosition),
388 counterClockwise_(isCounterClockwise),
389 boundingBox_(pixelBoundingBox)
390{
392}
393
394template <typename T>
395inline PixelContourT<T>::PixelContourT(PixelPositions&& pixelPositions, const size_t indexMostLeftPosition, const bool isCounterClockwise, const PixelBoundingBox& pixelBoundingBox) :
396 pixels_(std::move(pixelPositions)),
397 mostLeftIndex_(indexMostLeftPosition),
398 counterClockwise_(isCounterClockwise),
399 boundingBox_(pixelBoundingBox)
400{
402}
403
404template <typename T>
405inline PixelContourT<T>::PixelContourT(const bool createDistinct, const bool createSimplified, const PixelPositions& pixelPositions, const PixelBoundingBox& pixelBoundingBox) :
406 pixels_(pixelPositions),
407 mostLeftIndex_((size_t)(-1)),
408 counterClockwise_((unsigned int)(-1)),
409 boundingBox_(pixelBoundingBox)
410{
411 if (createSimplified)
412 {
413 simplify();
414 }
415 else if (createDistinct)
416 {
417 makeDistinct();
418 }
419
421}
422
423template <typename T>
424PixelContourT<T>::PixelContourT(const PixelPositions& pixelPositions, const unsigned int minimalSqrDistance, const size_t startIndex) :
425 mostLeftIndex_((size_t)(-1)),
426 counterClockwise_((unsigned int)(-1))
427{
428 ocean_assert(pixelPositions.size() >= 1);
429 ocean_assert(minimalSqrDistance >= 1u);
430 ocean_assert(startIndex < pixelPositions.size());
431
432 ocean_assert(pixels_.empty());
433 pixels_.reserve(pixelPositions.size());
434
435 // our first sparse
436 pixels_.push_back(pixelPositions[startIndex]);
437
438 for (size_t n = startIndex + 1; n <= startIndex + pixelPositions.size(); ++n)
439 {
440 const size_t nModulo = (size_t)modulo((int)n, (int)pixelPositions.size());
441
442 if (pixels_.back().sqrDistance(pixelPositions[nModulo]) >= minimalSqrDistance)
443 {
444 pixels_.push_back(pixelPositions[nModulo]);
445 }
446 }
447
448 if (pixels_.size() > 1 && pixels_.front().sqrDistance(pixels_.back()) < minimalSqrDistance)
449 {
450 pixels_.pop_back();
451 }
452
453 ocean_assert(pixels_.size() == 1 || PixelContourT<T>(pixels_).smallestSqrDistanceBetweenPixels() >= minimalSqrDistance);
454}
455
456template <typename T>
458{
459 return pixels_;
460}
461
462template <typename T>
463inline size_t PixelContourT<T>::size() const
464{
465 return pixels_.size();
466}
467
468template <typename T>
469inline bool PixelContourT<T>::isEmpty() const
470{
471 return pixels_.empty();
472}
473
474template <typename T>
475inline const typename PixelContourT<T>::PixelPosition& PixelContourT<T>::operator[](const size_t index) const
476{
477 ocean_assert(index < pixels_.size());
478 return pixels_[index];
479}
480
481template <typename T>
483{
484 pixels_ = contour.pixels_;
485 mostLeftIndex_ = contour.mostLeftIndex_;
486 counterClockwise_ = contour.counterClockwise_;
487 boundingBox_ = contour.boundingBox_;
488
489 return *this;
490}
491
492template <typename T>
494{
495 if (this != &contour)
496 {
497 pixels_ = std::move(contour.pixels_);
498 mostLeftIndex_ = contour.mostLeftIndex_;
499 counterClockwise_ = contour.counterClockwise_;
500 boundingBox_ = contour.boundingBox_;
501
502 contour.mostLeftIndex_ = (size_t)(-1);
503 contour.counterClockwise_ = (unsigned int)(-1);
504 contour.boundingBox_ = PixelBoundingBox();
505 }
506
507 return *this;
508}
509
510template <typename T>
512{
513 if (!boundingBox_)
514 {
515 boundingBox_ = PixelBoundingBox(pixels_);
516 }
517
518 return boundingBox_;
519}
520
521template <typename T>
522inline unsigned int PixelContourT<T>::area() const
523{
524 return (unsigned int)std::abs(areaSigned());
525}
526
527template <typename T>
529{
530 if (pixels_.size() < 3)
531 {
532 return 0;
533 }
534
535 int area = 0;
536
537 for (size_t i = 0; i < (pixels_.size() - 1); ++i)
538 {
539 const int partialArea = pixels_[i].x() * pixels_[i + 1].y() - pixels_[i].y() * pixels_[i + 1].x();
540
541 ocean_assert(partialArea <= 0 || area <= NumericT<int>::maxValue() - partialArea && "Integer overflow");
542 ocean_assert(partialArea >= 0 || area >= NumericT<int>::minValue() - partialArea && "Integer underflow");
543
544 area += partialArea;
545 }
546
547 const int partialArea = pixels_.back().x() * pixels_.front().y() - pixels_.back().y() * pixels_.front().x();
548
549 ocean_assert(partialArea <= 0 || area <= NumericT<int>::maxValue() - partialArea && "Integer overflow");
550 ocean_assert(partialArea >= 0 || area >= NumericT<int>::minValue() - partialArea && "Integer underflow");
551
552 area += partialArea;
553
554 return (area + NumericT<int>::copySign(1, area)) / 2;
555}
556
557template <typename T>
559{
560 if (mostLeftIndex_ != (size_t)(-1))
561 {
562 return mostLeftIndex_;
563 }
564
565 if (pixels_.empty())
566 {
567 return (size_t)(-1);
568 }
569
570 if (pixels_.size() == 1)
571 {
572 return 0;
573 }
574
575 // finding the most left pixel with following pixel right to this position
576
577 T left = NumericT<T>::maxValue();
578 T bottom = NumericT<T>::minValue();
579
580 size_t index = size_t(-1);
581
582 for (size_t n = 0u; n < pixels_.size(); ++n)
583 {
584 if (pixels_[n].x() < left || (pixels_[n].x() == left && pixels_[n].y() > bottom))
585 {
586 left = pixels_[n].x();
587 bottom = pixels_[n].y();
588 index = n;
589 }
590 }
591
592 ocean_assert(index != size_t(-1));
593 ocean_assert(!boundingBox_ || left == boundingBox_.left());
594
595 mostLeftIndex_ = index;
596 return index;
597}
598
599template <typename T>
601{
602 if (counterClockwise_ != (unsigned int)(-1))
603 {
604 return counterClockwise_ == 1u;
605 }
606
607 const size_t index0 = size_t(indexLeftPosition());
608 ocean_assert(index0 != size_t(-1));
609
610 const size_t index2 = modulo(int(index0) - 1, int(pixels_.size()));
611
612 const PixelPosition& position0 = pixels_[index0];
613 const PixelPosition& position2 = pixels_[index2];
614
615 const int dx02 = int(position2.x()) - int(position0.x());
616 const int dy02 = int(position2.y()) - int(position0.y());
617
618 size_t index1 = size_t(-1);
619 size_t offset = 1;
620
621 while (true)
622 {
623 index1 = modulo(int(index0 + offset), int(pixels_.size()));
624
625 // the contour is degenerated and thus the result is arbitrary
626 if (index1 == index2 || index1 == index0)
627 {
628 return true;
629 }
630
631 const PixelPosition& position1 = pixels_[index1];
632
633 const int dx01 = int(position1.x()) - int(position0.x());
634 const int dy01 = int(position1.y()) - int(position0.y());
635
636 // cross (dx01, dy01) x (dx02, dy02) = dx01 * dy02 - dx02 * dy01
637 const int crossProduct = dx01 * dy02 - dx02 * dy01;
638
639 if (crossProduct != 0)
640 {
641 counterClockwise_ = crossProduct < 0;
642 return counterClockwise_ == 1u;
643 }
644
645 offset++;
646 }
647
648 ocean_assert(false && "This should never happen!");
649 counterClockwise_ = 1u;
650 return true;
651}
652
653template <typename T>
655{
656 if (pixels_.size() <= 1)
657 {
658 return true;
659 }
660
661 for (size_t n = 0; n < pixels_.size() - 1; ++n)
662 {
663 if (pixels_[n] == pixels_[n + 1u])
664 {
665 return false;
666 }
667 }
668
669 return pixels_.front() != pixels_.back();
670}
671
672template <typename T>
674{
675 if (pixels_.size() <= 1)
676 {
677 return true;
678 }
679
680 for (size_t n = 1; n < pixels_.size(); ++n)
681 {
682 if (!pixels_[n - 1].isNeighbor8(pixels_[n]))
683 {
684 return false;
685 }
686 }
687
688 return pixels_.back().isNeighbor8(pixels_.front());
689}
690
691template <typename T>
693{
694 if (pixels_.size() <= 1)
695 {
696 return true;
697 }
698
699 for (size_t n = 1; n < pixels_.size(); ++n)
700 {
701 if (!pixels_[n - 1].isNeighbor4(pixels_[n]))
702 {
703 return false;
704 }
705 }
706
707 return pixels_.back().isNeighbor4(pixels_.front());
708}
709
710template <typename T>
712{
713 if (pixels_.size() <= 2)
714 {
715 return true;
716 }
717
718 PixelPosition previousOffset(pixels_[1] - pixels_[0]);
719
720 for (size_t n = 2; n < pixels_.size(); ++n)
721 {
722 const PixelPosition currentOffset(pixels_[n] - pixels_[n - 1]);
723
724 if (currentOffset == previousOffset)
725 {
726 return false;
727 }
728
729 previousOffset = currentOffset;
730 }
731
732 // now the remaining two pixels
733 PixelPosition currentOffset(pixels_[0] - pixels_[pixels_.size() - 1]);
734 if (previousOffset == currentOffset)
735 {
736 return false;
737 }
738
739 // we avoid: previousOffset = currentOffset;
740 if (PixelPosition(pixels_[1] - pixels_[0]) == currentOffset)
741 {
742 return false;
743 }
744
745 return true;
746}
747
748template <typename T>
750{
751 if (pixels_.size() > 1)
752 {
753 PixelPositions distinctPixels;
754 distinctPixels.reserve(pixels_.size());
755
756 distinctPixels.push_back(pixels_.front());
757
758 for (size_t n = 1; n < pixels_.size(); ++n)
759 {
760 if (pixels_[n - 1] != pixels_[n])
761 {
762 distinctPixels.push_back(pixels_[n]);
763 }
764 }
765
766 if (distinctPixels.size() > 1 && distinctPixels.front() == distinctPixels.back())
767 {
768 distinctPixels.pop_back();
769 }
770
771 ocean_assert(distinctPixels.size() <= 1 || distinctPixels.front() != distinctPixels.back());
772
773 // the bounding box should not have changed
774 ocean_assert(!boundingBox_ || boundingBox_ == PixelBoundingBox(distinctPixels));
775
776 mostLeftIndex_ = size_t(-1);
777 counterClockwise_ = (unsigned int)(-1);
778
779 pixels_ = std::move(distinctPixels);
780 }
781}
782
783template <typename T>
785{
786 if (pixels_.size() > 1)
787 {
788 PixelPositions newPositions;
789 newPositions.reserve(pixels_.size() * 20);
790
791 for (size_t n = 0; n < pixels_.size(); ++n)
792 {
793 const PixelPosition& start = pixels_[n];
794 const PixelPosition& end = pixels_[modulo(int(n + 1), int(pixels_.size()))];
795
796 int x = int(start.x());
797 int y = int(start.y());
798 const int xEnd = int(end.x());
799 const int yEnd = int(end.y());
800
801 CV::Bresenham bresenham(x, y, xEnd, yEnd);
802
803 while (x != xEnd || y != yEnd)
804 {
805 newPositions.push_back(PixelPosition(T(x), T(y)));
806 bresenham.findNext(x, y);
807 }
808 }
809
810 pixels_ = std::move(newPositions);
811 ocean_assert(!boundingBox_ || boundingBox_ == PixelBoundingBox(pixels_));
812
813 mostLeftIndex_ = size_t(-1);
814 counterClockwise_ = (unsigned int)(-1);
815 }
816}
817
818template <typename T>
820{
821 if (pixels_.size() <= 1)
822 {
823 return PixelContourT<T>(*this);
824 }
825
826 PixelPositions newPixelPositions;
827 newPixelPositions.reserve(pixels_.size());
828
829 VectorI2 currentDirection = VectorI2(int(pixels_.front().x() - pixels_.back().x()), int(pixels_.front().y() - pixels_.back().y()));
830
831 for (size_t n = 1; n < pixels_.size(); ++n)
832 {
833 const VectorI2 newDirection = VectorI2(int(pixels_[n].x() - pixels_[n - 1].x()), int(pixels_[n].y() - pixels_[n - 1].y()));
834
835 if (!newDirection.isNull())
836 {
837 if (!similar(currentDirection, newDirection))
838 {
839 currentDirection = newDirection;
840 newPixelPositions.push_back(pixels_[n - 1]);
841 }
842 }
843 }
844
845 const VectorI2 newDirection = VectorI2(int(pixels_.front().x() - pixels_.back().x()), int(pixels_.front().y() - pixels_.back().y()));
846
847 if (currentDirection != newDirection)
848 {
849 newPixelPositions.push_back(pixels_.back());
850 }
851
852#ifdef OCEAN_DEBUG
853 {
854 ocean_assert(!newPixelPositions.empty());
855
856 const PixelContourT<T> debugContour(newPixelPositions);
857 ocean_assert(debugContour.boundingBox() == boundingBox());
858 ocean_assert(debugContour.isCounterClockwise() == isCounterClockwise());
859
860 ocean_assert(debugContour.isSimplified());
861 ocean_assert(debugContour.isDistinct());
862 }
863#endif
864
865 return PixelContourT<T>(std::move(newPixelPositions), boundingBox_);
866}
867
868template <typename T>
870{
871 *this = simplified();
872}
873
874template <typename T>
875PixelContourT<T> PixelContourT<T>::sparseContour(const unsigned int minimalSqrDistance, const size_t startIndex) const
876{
877 ocean_assert(pixels_.size() >= 1);
878
879 if (pixels_.empty())
880 {
881 return PixelContourT<T>();
882 }
883
884 ocean_assert(minimalSqrDistance >= 1u);
885 ocean_assert(startIndex < pixels_.size());
886
887 return PixelContourT<T>(pixels_, minimalSqrDistance, startIndex);
888}
889
890template <typename T>
892{
893 ocean_assert(!pixels_.empty());
894
895 unsigned int sqrDistance = pixels_.front().sqrDistance(pixels_.back());
896
897 for (size_t n = 1; n < pixels_.size(); ++n)
898 {
899 const unsigned int localSqrDistance = pixels_[n - 1].sqrDistance(pixels_[n]);
900
901 if (localSqrDistance < sqrDistance)
902 {
903 sqrDistance = localSqrDistance;
904 }
905 }
906
907 return sqrDistance;
908}
909
910template <typename T>
912{
913 ocean_assert(!pixels_.empty());
914
915 unsigned int sqrDistance = pixels_.front().sqrDistance(pixels_.back());
916
917 for (size_t n = 1; n < pixels_.size(); ++n)
918 {
919 const unsigned int localSqrDistance = pixels_[n - 1].sqrDistance(pixels_[n]);
920
921 if (localSqrDistance > sqrDistance)
922 {
923 sqrDistance = localSqrDistance;
924 }
925 }
926
927 return sqrDistance;
928}
929
930template <typename T>
931inline PixelContourT<T>::operator bool() const
932{
933 return !pixels_.empty();
934}
935
936template <typename T>
937inline bool PixelContourT<T>::similar(const VectorI2& first, const VectorI2& second)
938{
939 ocean_assert(first.x() != 0 || first.y() != 0);
940 ocean_assert(second.x() != 0 || second.y() != 0);
941
942#ifdef OCEAN_DEBUG
943
944 {
945 const bool fastResult = first.x() * second.y() == second.x() * first.y()
946 && (0x80000000 & first.x()) == (0x80000000 & second.x())
947 && (0x80000000 & first.y()) == (0x80000000 & second.y());
948
949 Vector2 vf(Scalar(first.x()), Scalar(first.y()));
950 Vector2 vs(Scalar(second.x()), Scalar(second.y()));
951
952 vf.normalize();
953 vs.normalize();
954
955 ocean_assert(fastResult == (vf == vs));
956 }
957
958#endif
959
960 return first.x() * second.y() == second.x() * first.y()
961 && (0x80000000 & first.x()) == (0x80000000 & second.x())
962 && (0x80000000 & first.y()) == (0x80000000 & second.y());
963}
964
965}
966
967}
968
969}
970
971#endif // META_OCEAN_CV_SEGMENTATION_PIXEL_CONTOUR_H
This class implements Bresenham's 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:63
unsigned int counterClockwise_
State whether this contour is counter clockwise: -1 undefined, 0 false, 1 true.
Definition PixelContour.h:336
void simplify()
Simplifies this (dense) contour to a sparse but identical contour.
Definition PixelContour.h:869
unsigned int smallestSqrDistanceBetweenPixels() const
Returns the smallest square distance between consecutive contour pixels.
Definition PixelContour.h:891
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:784
unsigned int largestSqrDistanceBetweenPixels() const
Returns the largest square distance between consecutive contour pixels.
Definition PixelContour.h:911
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:558
void makeDistinct()
Removes non distinct pixels from this contour.
Definition PixelContour.h:749
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
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:375
PixelPositionT< T > PixelPosition
Definition of a pixel position.
Definition PixelContour.h:76
PixelContourT()=default
Creates a new pixel contour object.
PixelContourT(PixelContourT< T > &&contour) noexcept
Move constructor.
Definition PixelContour.h:353
bool isDense() const
Returns whether this contour is dense.
Definition PixelContour.h:673
bool isDense4() const
Returns whether this contour is dense according to a 4-neighborhood.
Definition PixelContour.h:692
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:365
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:654
PixelContourT(const PixelContourT< T > &contour)
Copy constructor.
Definition PixelContour.h:343
PixelContourT< T > simplified() const
Returns the simplified contour of this contour which will be a sparse but identical contour.
Definition PixelContour.h:819
PixelBoundingBox boundingBox_
Bounding box of the contour.
Definition PixelContour.h:339
unsigned int area() const
Computes the area of a contour Uses the Shoelace formula to determine the area of a contour.
Definition PixelContour.h:522
PixelPositions pixels_
Pixel positions of the contour.
Definition PixelContour.h:330
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:385
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:424
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:405
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:937
bool isSimplified() const
Returns whether this contour is simplified.
Definition PixelContour.h:711
PixelBoundingBoxT< T > PixelBoundingBox
Definition of a pixel bounding box.
Definition PixelContour.h:86
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:600
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:395
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:875
size_t mostLeftIndex_
Index of the most left pixel.
Definition PixelContour.h:333
int areaSigned() const
Computes the signed area of a contour Uses the Shoelace formula to determine the area of a contour.
Definition PixelContour.h:528
This class provides basic numeric functionalities.
Definition Numeric.h:57
static constexpr T minValue()
Returns the min scalar value.
Definition Numeric.h:3259
static constexpr T maxValue()
Returns the max scalar value.
Definition Numeric.h:3253
const T & x() const noexcept
Returns the x value.
Definition Vector2.h:703
const T & y() const noexcept
Returns the y value.
Definition Vector2.h:715
bool isNull() const
Returns whether this vector is a null vector up to a small epsilon.
Definition Vector2.h:739
bool normalize()
Normalizes this vector.
Definition Vector2.h:605
unsigned int sqrDistance(const char first, const char second)
Returns the square distance between two values.
Definition base/Utilities.h:1159
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:994
PixelBoundingBoxT< unsigned int > PixelBoundingBox
Definition of the default PixelBoundingBox object with data type allowing only positive coordinate va...
Definition PixelBoundingBox.h:28
PixelPositionT< unsigned int > PixelPosition
Definition of the default PixelPosition object with a data type allowing only positive coordinate val...
Definition PixelPosition.h:32
std::vector< PixelContour > PixelContours
Definition of a vector holding pixel contours (with positive coordinate values).
Definition PixelContour.h:50
std::vector< PixelContourI > PixelContoursI
Definition of a vector holding pixel contours (with positive and negative coordinate values).
Definition PixelContour.h:57
float Scalar
Definition of a scalar type.
Definition Math.h:129
VectorT2< int > VectorI2
Definition of a 2D vector with integer values.
Definition Vector2.h:49
The namespace covering the entire Ocean framework.
Definition Accessor.h:15