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 */
50typedef 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 */
57typedef 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 */
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 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 */
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 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
342template <typename T>
344 contourMostLeftIndex(size_t(-1)),
345 contourCounterClockwise((unsigned int)(-1))
346{
347 // nothing to do here
348}
349
350template <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
360template <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
372template <typename T>
373inline 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
382template <typename T>
383inline 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
392template <typename T>
393inline 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
402template <typename T>
403inline 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
412template <typename T>
413inline 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
427template <typename T>
428PixelContourT<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
456template <typename T>
458{
459 return contourPixels;
460}
461
462template <typename T>
463inline size_t PixelContourT<T>::size() const
464{
465 return contourPixels.size();
466}
467
468template <typename T>
469inline bool PixelContourT<T>::isEmpty() const
470{
471 return contourPixels.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 < contourPixels.size());
478 return contourPixels[index];
479}
480
481template <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
492template <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
510template <typename T>
512{
513 if (!contourBoundingBox)
514 contourBoundingBox = PixelBoundingBox(contourPixels);
515
516 return contourBoundingBox;
517}
518
519template <typename T>
520inline unsigned int PixelContourT<T>::area() const
521{
522 return (unsigned int)std::abs(areaSigned());
523}
524
525template <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
546template <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
582template <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
632template <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
645template <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
658template <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
671template <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
701template <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
730template <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
765template <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
811template <typename T>
813{
814 *this = simplified();
815}
816
817template <typename T>
818PixelContourT<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
831template <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
849template <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
867template <typename T>
868inline PixelContourT<T>::operator bool() const
869{
870 return !contourPixels.empty();
871}
872
873template <typename T>
874inline 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
This class implements a contour with pixel accuracy.
Definition PixelContour.h:70
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:710
const T & y() const noexcept
Returns the y value.
Definition Vector2.h:722
bool isNull() const
Returns whether this vector is a null vector up to a small epsilon.
Definition Vector2.h:746
bool normalize()
Normalizes this vector.
Definition Vector2.h:612
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:34
PixelBoundingBoxT< unsigned int > PixelBoundingBox
Definition of the default PixelBoundingBox object with data type allowing only positive coordinate va...
Definition PixelBoundingBox.h:28
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:36
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:129
The namespace covering the entire Ocean framework.
Definition Accessor.h:15