Ocean
segmentation/MaskAnalyzer.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_MASK_ANALYZER_H
9 #define META_OCEAN_CV_SEGMENTATION_MASK_ANALYZER_H
10 
12 
13 #include "ocean/base/Memory.h"
14 #include "ocean/base/Worker.h"
15 
17 #include "ocean/cv/PixelPosition.h"
18 
20 
21 #include <map>
22 #include <set>
23 #include <vector>
24 
25 namespace Ocean
26 {
27 
28 namespace CV
29 {
30 
31 namespace Segmentation
32 {
33 
34 /**
35  * This class implements functions analyzing masks, determining specific pixels in relation to masks and allowing to convert pixels to contours.
36  * @ingroup cvsegmentation
37  */
38 class OCEAN_CV_SEGMENTATION_EXPORT MaskAnalyzer
39 {
40  public:
41 
42  /**
43  * This class implements a simple information for a block/area of mask pixels.
44  */
45  class OCEAN_CV_SEGMENTATION_EXPORT MaskBlock
46  {
47  public:
48 
49  /**
50  * Creates an invalid block object.
51  */
52  inline MaskBlock();
53 
54  /**
55  * Creates a new block object.
56  * @param position A seed point inside the mask block
57  * @param id The id of the block
58  * @param size The size of the mask block in pixels, with range [0, infinity)
59  * @param border Image border block
60  */
61  inline MaskBlock(const PixelPosition& position, const unsigned int id, const unsigned int size, const bool border = true);
62 
63  /**
64  * Returns the one position of this block.
65  * @return Block position
66  */
67  inline const PixelPosition& position() const;
68 
69  /**
70  * Returns the id of this block.
71  * @return Block id
72  */
73  inline unsigned int id() const;
74 
75  /**
76  * Returns the size of this block in pixel.
77  * @return Block size
78  */
79  inline unsigned int size() const;
80 
81  /**
82  * Returns whether this block intersects with the image border.
83  * @return True, if so
84  */
85  inline bool border() const;
86 
87  /**
88  * Returns whether the size of this block is smaller than the size of the given block.
89  * @param block Second block to compare
90  * @return True, if so
91  */
92  inline bool operator<(const MaskBlock& block) const;
93 
94  private:
95 
96  /// One position inside the mask block.
98 
99  /// Id of the mask block.
100  unsigned int blockId;
101 
102  /// Size of the mask block in pixel.
103  unsigned int blockSize;
104 
105  /// True, if image border block.
107  };
108 
109  /**
110  * Definition of a vector holding mask block objects.
111  */
112  typedef std::vector<MaskBlock> MaskBlocks;
113 
114  protected:
115 
116  /**
117  * Definition of process directions.
118  */
120  {
121  /// North.
123  /// North west.
125  /// West.
127  /// South west.
129  /// South.
131  /// South east.
133  /// East.
135  /// North east.
136  PD_NE
137  };
138 
139  /**
140  * Definition of a set holding pixel positions.
141  */
142  typedef std::set<PixelPosition> PixelPositionSet;
143 
144  /**
145  * Definition of a vector holding index pair vectors.
146  */
147  typedef std::vector<IndexPairs32> IndexPairGroups;
148 
149  /// Chessboard distance for vertical and horizontal steps
150  static constexpr uint32_t distanceVerticalHorizontalC = 1u;
151  /// Chessboard distance for diagonal steps
152  static constexpr uint32_t distanceDiagonalC = 1u;
153 
154  /// L1 distance for vertical and horizontal steps
155  static constexpr uint32_t distanceVerticalHorizontalL1 = 1u;
156  /// L1 distance for diagonal steps
157  static constexpr uint32_t distanceDiagonalL1 = 2u;
158 
159  /// L2 distance for vertical and horizontal steps
160  static constexpr float distanceVerticalHorizontalL2 = 0.95509f;
161  /// L2 distance for diagonal steps
162  static constexpr float distanceDiagonalL2 = 1.3693f;
163 
164  /**
165  * This class implements a mask island used in a sweep algorithm.
166  */
168  {
169  protected:
170 
171  /**
172  * Definition of a pair combining the horizontal start position (inclusive), and the horizontal end position (exclusive).
173  */
174  typedef std::pair<unsigned int, unsigned int> RowSegment;
175 
176  /**
177  * Definition of a vector holding row segments.
178  */
179  typedef std::vector<RowSegment> RowSegments;
180 
181  public:
182 
183  /**
184  * Default constructor.
185  */
186  inline SweepMaskIsland() = default;
187 
188  /**
189  * Creates a new island object starting at a given row with given mask segment.
190  * @param currentRow The current row at which the segment appears for the first time, with range [0, infinity)
191  * @param start The horizontal start location (inclusive) of the segment within the row, with range [0, infinity)
192  * @param end The horizontal end location (exclusive) of the island within the row, with range [start + 1, infinity)
193  */
194  inline SweepMaskIsland(const unsigned int currentRow, const unsigned int start, const unsigned int end);
195 
196  /**
197  * Joins a given sweep mask island with this island.
198  * The function mainly copies the row segments and updates the bounding box of this island with the union bounding box.
199  * @param sweepMask The sweep mask to join with this mask
200  */
201  inline void join(const SweepMaskIsland& sweepMask);
202 
203  /**
204  * Checks whether this island intersects with a given row segment.
205  * @param start The horizontal start location (inclusive) of the segment within the row, with range [0, infinity)
206  * @param end The horizontal end location (exclusive) of the island within the row, with range [start + 1, infinity)
207  * @param useNeighborhood4 True, to use a 4-connected neighborhood when determining the mask islands; False, to use a 8-connected neighborhood
208  */
209  inline bool hasIntersection(const unsigned int start, const unsigned int end, const bool useNeighborhood4) const;
210 
211  /**
212  * Adds a new row segment to this island.
213  * @param currentRow The current row at which the segment appears for the first time, with range [0, infinity)
214  * @param start The horizontal start location (inclusive) of the segment within the row, with range [0, infinity)
215  * @param end The horizontal end location (exclusive) of the island within the row, with range [start + 1, infinity)
216  */
217  inline void addSegment(const unsigned int currentRow, const unsigned int start, const unsigned int end);
218 
219  /**
220  * Ends segment handling for the current row and prepares the mask for the next row.
221  * This function must be called after each row is handled.
222  */
223  inline void nextRow();
224 
225  /**
226  * Returns the bounding box of this mask island.
227  * @return The mask's bounding box
228  */
229  inline const PixelBoundingBox& boundingBox() const;
230 
231  protected:
232 
233  /// The segments located in the previous row.
235 
236  /// The segments located in the current row.
238 
239  /// The bounding box of this mask.
241  };
242 
243  /**
244  * Definition of a vector holding SweepMaskIsland objects.
245  */
246  typedef std::vector<SweepMaskIsland> SweepMaskIslands;
247 
248  public:
249 
250  /**
251  * Determines whether at least one neighbor pixel in the 4-neighborhood is or is not a mask pixel.
252  * The position itself is not tested.
253  * @param mask The mask frame, must be valid
254  * @param width The width of the mask in pixel, with range [1, infinity)
255  * @param height The height of the mask in pixel, with range [1, infinity)
256  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
257  * @param position The position to be checked, with range [0, width)x[0, height)
258  * @param testValue The mask value to be used for testing
259  * @return True, if 'tMaskValueIsEqual && anyMaskPixel == testValue' or '!tMaskValueIsEqual && anyMaskPixel != testValue'
260  * @tparam tMaskValueIsEqual True, to check for mask values equal to 'testValue'; False, to check for mask values not equal to 'testValue'
261  * @tparam T The data type of each mask pixel
262  */
263  template <bool tMaskValueIsEqual, typename T>
264  static inline bool hasMaskNeighbor4(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue);
265 
266  /**
267  * Determines whether at least one neighbor pixel in the 4-neighborhood is a mask pixel with the given test position has all 8 neighbors.
268  * The position itself is not tested.
269  * @param mask The mask frame, must be valid
270  * @param width The width of the mask in pixel, with range [3, infinity)
271  * @param height The height of the mask in pixel, with range [3, infinity)
272  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
273  * @param position The position to be checked inside the frame, with range [1, width - 1)x[1, height - 1)
274  * @param testValue The mask value of a non-mask pixel
275  * @return True, if 'tMaskValueIsEqual && anyMaskPixel == testValue' or '!tMaskValueIsEqual && anyMaskPixel != testValue'
276  * @tparam T The data type of each mask pixel
277  * @tparam tMaskValueIsEqual True, to check for mask values equal to 'testValue'; False, to check for mask values not equal to 'testValue'
278  */
279  template <bool tMaskValueIsEqual, typename T>
280  static inline bool hasMaskNeighbor4Center(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue);
281 
282  /**
283  * Determines whether at least one neighbor pixel in the 4-neighborhood (+ center pixel) is or is not a mask pixel.
284  * The position itself is not tested.
285  * @param mask The mask frame, must be valid
286  * @param width The width of the mask in pixel, with range [1, infinity)
287  * @param height The height of the mask in pixel, with range [1, infinity)
288  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
289  * @param position The position to be checked, with range [0, width)x[0, height)
290  * @param testValue The mask value to be used for testing
291  * @return True, if 'tMaskValueIsEqual && anyMaskPixel == testValue' or '!tMaskValueIsEqual && anyMaskPixel != testValue'
292  * @tparam T The data type of each mask pixel
293  * @tparam tMaskValueIsEqual True, to check for mask values equal to 'testValue'; False, to check for mask values not equal to 'testValue'
294  */
295  template <bool tMaskValueIsEqual, typename T>
296  static inline bool hasMaskNeighbor5(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue);
297 
298  /**
299  * Determines whether at least one neighbor pixel in the 4-neighborhood (+ center pixel) is a mask pixel with the given test position has all 8 neighbors.
300  * The position itself is not tested.
301  * @param mask The mask frame, must be valid
302  * @param width The width of the mask in pixel, with range [3, infinity)
303  * @param height The height of the mask in pixel, with range [3, infinity)
304  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
305  * @param position The position to be checked inside the frame, with range [1, width - 1)x[1, height - 1)
306  * @param testValue The mask value of a non-mask pixel
307  * @return True, if 'tMaskValueIsEqual && anyMaskPixel == testValue' or '!tMaskValueIsEqual && anyMaskPixel != testValue'
308  * @tparam T The data type of each mask pixel
309  * @tparam tMaskValueIsEqual True, to check for mask values equal to 'testValue'; False, to check for mask values not equal to 'testValue'
310  */
311  template <bool tMaskValueIsEqual, typename T>
312  static inline bool hasMaskNeighbor5Center(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue);
313 
314  /**
315  * Determines whether at least one neighbor pixel in the 8-neighborhood is or is not a mask pixel.
316  * The position itself is not tested.
317  * @param mask The mask frame, must be valid
318  * @param width The width of the mask in pixel, with range [1, infinity)
319  * @param height The height of the mask in pixel, with range [1, infinity)
320  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
321  * @param position The position to be checked, with range [0, width)x[0, height)
322  * @param testValue The mask value to be used for testing
323  * @return True, if 'tMaskValueIsEqual && anyMaskPixel == testValue' or '!tMaskValueIsEqual && anyMaskPixel != testValue'
324  * @tparam T The data type of each mask pixel
325  * @tparam tMaskValueIsEqual True, to check for mask values equal to 'testValue'; False, to check for mask values not equal to 'testValue'
326  */
327  template <bool tMaskValueIsEqual, typename T>
328  static inline bool hasMaskNeighbor8(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue);
329 
330  /**
331  * Determines whether at least one neighbor pixel in the 8-neighborhood is a mask pixel with the given test position has all 8 neighbors.
332  * The position itself is not tested.
333  * @param mask The mask frame, must be valid
334  * @param width The width of the mask in pixel, with range [3, infinity)
335  * @param height The height of the mask in pixel, with range [3, infinity)
336  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
337  * @param position The position to be checked inside the frame, with range [1, width - 1)x[1, height - 1)
338  * @param testValue The mask value of a non-mask pixel
339  * @return True, if 'tMaskValueIsEqual && anyMaskPixel == testValue' or '!tMaskValueIsEqual && anyMaskPixel != testValue'
340  * @tparam T The data type of each mask pixel
341  * @tparam tMaskValueIsEqual True, to check for mask values equal to 'testValue'; False, to check for mask values not equal to 'testValue'
342  */
343  template <bool tMaskValueIsEqual, typename T>
344  static inline bool hasMaskNeighbor8Center(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue);
345 
346  /**
347  * Determines whether at least one neighbor pixel in the 8-neighborhood (+ center pixel) is or is not a mask pixel.
348  * The position itself is not tested.
349  * @param mask The mask frame, must be valid
350  * @param width The width of the mask in pixel, with range [1, infinity)
351  * @param height The height of the mask in pixel, with range [1, infinity)
352  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
353  * @param position The position to be checked, with range [0, width)x[0, height)
354  * @param testValue The mask value to be used for testing
355  * @return True, if 'tMaskValueIsEqual && anyMaskPixel == testValue' or '!tMaskValueIsEqual && anyMaskPixel != testValue'
356  * @tparam T The data type of each mask pixel
357  * @tparam tMaskValueIsEqual True, to check for mask values equal to 'testValue'; False, to check for mask values not equal to 'testValue'
358  */
359  template <bool tMaskValueIsEqual, typename T>
360  static inline bool hasMaskNeighbor9(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue);
361 
362  /**
363  * Determines whether at least one neighbor pixel in the 8-neighborhood (+ center pixel) is a mask pixel with the given test position has all 8 neighbors.
364  * The position itself is not tested.
365  * @param mask The mask frame, must be valid
366  * @param width The width of the mask in pixel, with range [3, infinity)
367  * @param height The height of the mask in pixel, with range [3, infinity)
368  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
369  * @param position The position to be checked inside the frame, with range [1, width - 1)x[1, height - 1)
370  * @param testValue The mask value of a non-mask pixel
371  * @return True, if 'tMaskValueIsEqual && anyMaskPixel == testValue' or '!tMaskValueIsEqual && anyMaskPixel != testValue'
372  * @tparam T The data type of each mask pixel
373  * @tparam tMaskValueIsEqual True, to check for mask values equal to 'testValue'; False, to check for mask values not equal to 'testValue'
374  */
375  template <bool tMaskValueIsEqual, typename T>
376  static inline bool hasMaskNeighbor9Center(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue);
377 
378  /**
379  * Computes a per pixel Chessboard distance to the nearest pixel with a reference value using 3-by-3 neighborhood
380  * @note This function is similar to `cv::distanceTransform()` from OpenCV
381  * @param source The frame for which a distance map will be computed, must be valid
382  * @param width The width of the source frame, range: [1, infinity)
383  * @param height The height of the source frame, range: [1, infinity)
384  * @param target The location where the computed distance map will be stored, must be valid and have the same size the source frame, this data will only be valid if the return status is `Success`
385  * @param buffer An optional buffer of the size `(width + 2) * (height + 2)`. If not provided, memory will be allocated internally (which is more expensive!)
386  * @param referenceValue Distance values pixels will be computed to the closest pixel with this reference value, range: [0, 255]
387  * @param sourcePaddingElements Optional number of padding elements in the source frame, range: [0, infinity)
388  * @param targetPaddingElements Optional number of padding elements in the target frame, range: [0, infinity)
389  * @return True on success, otherwise false
390  */
391  static inline bool computeChessboardDistanceTransform8Bit(const uint8_t* source, const uint32_t width, const uint32_t height, uint32_t* target, uint32_t* buffer = nullptr, const uint8_t referenceValue = 0u, const uint32_t sourcePaddingElements = 0u, const uint32_t targetPaddingElements = 0u);
392 
393  /**
394  * Computes a per pixel L1 distance to the nearest pixel with a reference value using 3-by-3 neighborhood
395  * @note This function is similar to `cv::distanceTransform()` from OpenCV
396  * @param source The frame for which a distance map will be computed, must be valid
397  * @param width The width of the source frame, range: [1, infinity)
398  * @param height The height of the source frame, range: [1, infinity)
399  * @param target The location where the computed distance map will be stored, must be valid and have the same size the source frame, this data will only be valid if the return status is `Success`
400  * @param buffer An optional buffer of the size `(width + 2) * (height + 2)`. If not provided, memory will be allocated internally (which is more expensive!)
401  * @param referenceValue Distance values pixels will be computed to the closest pixel with this reference value, range: [0, 255]
402  * @param sourcePaddingElements Optional number of padding elements in the source frame, range: [0, infinity)
403  * @param targetPaddingElements Optional number of padding elements in the target frame, range: [0, infinity)
404  * @return True on success, otherwise false
405  */
406  static inline bool computeL1DistanceTransform8Bit(const uint8_t* source, const uint32_t width, const uint32_t height, uint32_t* target, uint32_t* buffer = nullptr, const uint8_t referenceValue = 0u, const uint32_t sourcePaddingElements = 0u, const uint32_t targetPaddingElements = 0u);
407 
408  /**
409  * Computes a per pixel (approximated) L2 distance to the nearest pixel with a reference value using 3-by-3 neighborhood
410  * @note This function is similar to `cv::distanceTransform()` from OpenCV
411  * @param source The frame for which a distance map will be computed, must be valid
412  * @param width The width of the source frame, range: [1, infinity)
413  * @param height The height of the source frame, range: [1, infinity)
414  * @param target The location where the computed distance map will be stored, must be valid and have the same size the source frame, this data will only be valid if the return status is `Success`
415  * @param buffer An optional buffer of the size `(width + 2) * (height + 2)`. If not provided, memory will be allocated internally (which is more expensive!)
416  * @param referenceValue Distance values pixels will be computed to the closest pixel with this reference value, range: [0, 255]
417  * @param sourcePaddingElements Optional number of padding elements in the source frame, range: [0, infinity)
418  * @param targetPaddingElements Optional number of padding elements in the target frame, range: [0, infinity)
419  * @return True on success, otherwise false
420  */
421  static inline bool computeL2DistanceTransform8Bit(const uint8_t* source, const uint32_t width, const uint32_t height, float* target, uint32_t* buffer = nullptr, const uint8_t referenceValue = 0u, const uint32_t sourcePaddingElements = 0u, const uint32_t targetPaddingElements = 0u);
422 
423  /**
424  * Analyzes an 8 bit binary mask frame and separates the pixels into individual blocks of joined sub-masks.
425  * Two neighboring mask blocks count as two separate sub-masks if both mask do not share a common mask pixel in a 4-neighborhood.<br>
426  * Beware: This function does not seek for individual mask values but for not joined sub-masks.
427  * @param mask Given binary 8 bit mask frame, pixel values not equal to 0xFF count as mask pixels, must be valid
428  * @param width The width of the entire mask frame in pixel, with range [1, infinity)
429  * @param height The height of the entire mask frame in pixel, with range [1, infinity)
430  * @param maskPaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
431  * @param separation Resulting separation frame (1 channel 32 bit) holding the mask block ids, with same dimension and pixel origin as the mask frame
432  * @param separationPaddingElements The number of padding elements at the end of each separation row, in elements, with range [0, infinity)
433  * @param blocks Resulting separation block information, one object for each found mask block
434  * @see analyzeNonMaskSeparation8Bit().
435  */
436  static void analyzeMaskSeparation8Bit(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, uint32_t* separation, const unsigned int separationPaddingElements, MaskBlocks& blocks);
437 
438  /**
439  * Analyzes an 8 bit binary mask frame and separates the pixels into individual blocks of not joined sub-areas covering non-masks.
440  * Two neighboring non-mask blocks count as two separate sub-masks if both areas do not share a common non-mask pixel in a 4-neighborhood.<br>
441  * Beware: This function does not seek for individual non-mask values but for not joined sub-areas.
442  * @param mask Given binary 8 bit mask frame, pixel values not equal to 0xFF count as mask pixels, must be valid
443  * @param width The width of the entire mask frame in pixel, with range [1, infinity)
444  * @param height The height of the entire mask frame in pixel, with range [1, infinity)
445  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
446  * @param separation Resulting separation frame (1 channel 32 bit) holding the mask block ids, with same dimension and pixel origin as the mask frame
447  * @param separationPaddingElements The number of padding elements at the end of each separation row, in elements, with range [0, infinity)
448  * @param blocks Resulting separation block information, one object for each found non-mask block
449  * @see analyzeMaskSeparation8Bit().
450  */
451  static void analyzeNonMaskSeparation8Bit(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, uint32_t* separation, const unsigned int separationPaddingElements, MaskBlocks& blocks);
452 
453  /**
454  * Determines all outline-4 pixels in an 8 bit mask frame.
455  * @param mask The binary 8 bit mask frame in which the outline-4 pixels will be determined, pixel values not equal to 0xFF count as mask pixels, must be valid
456  * @param width The width of the mask frame in pixel, with range [1, infinity)
457  * @param height The height of the mask frame in pixel, with range [1, infinity)
458  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
459  * @param outlinePixels4 Resulting outline-4 pixels, the pixel itself is not a mask pixel but has at least one neighbor mask pixel in the four-neighborhood
460  * @param boundingBox Optional bounding box object shrinking the operation area, outline pixels will be detected inside the given bounding box and one pixel outside the given bounding box
461  * @param nonMaskValue The pixel value of a mask pixel not belonging to the mask
462  */
463  static void findOutline4(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions& outlinePixels4, const PixelBoundingBox& boundingBox = PixelBoundingBox(), const uint8_t nonMaskValue = 0xFF);
464 
465  /**
466  * Determines all border pixels in an 8 bit mask frame for a 4-neighborhood.
467  * A border pixel itself is a mask pixel but not all 4-neighborhood pixels are mask pixels; a mask pixel at the frame's border is a mask-border pixel.
468  * @param mask Given binary 8 bit mask frame, pixel values not equal to `nonMaskValue` count as mask pixels, must be valid
469  * @param width The width of the mask frame in pixel, with range [1, infinity)
470  * @param height The height of the mask frame in pixel, with range [1, infinity)
471  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
472  * @param borderPixels Resulting border pixels
473  * @param boundingBox Optional bounding box object shrinking the operation area
474  * @param worker Optional worker object to distribute the computation
475  * @param nonMaskValue Optional pixel value that is to be interpreted as not belonging to the mask and which will not be enclosed by the border pixels, range: [0, 255], default: 255
476  */
477  static void findBorderPixels4(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions& borderPixels, const PixelBoundingBox& boundingBox = PixelBoundingBox(), Worker* worker = nullptr, const uint8_t nonMaskValue = 255u);
478 
479  /**
480  * Determines all border pixels in an 8 bit mask frame for a 8-neighborhood.
481  * A border pixel itself is a mask pixel but not all 8-neighborhood pixels are mask pixels; a mask pixel at the frame's border is a mask-border pixel.
482  * @param mask Given binary 8 bit mask frame, pixel values not equal to `nonMaskValue` count as mask pixels, must be valid
483  * @param width The width of the mask frame in pixel, with range [1, infinity)
484  * @param height The height of the mask frame in pixel, with range [1, infinity)
485  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
486  * @param borderPixels Resulting border pixels
487  * @param boundingBox Optional bounding box object shrinking the operation area
488  * @param worker Optional worker object to distribute the computation
489  * @param nonMaskValue Optional pixel value that is to be interpreted as not belonging to the mask and which will not be enclosed by the border pixels, range: [0, 255], default: 255
490  */
491  static void findBorderPixels8(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions& borderPixels, const PixelBoundingBox& boundingBox = PixelBoundingBox(), Worker* worker = nullptr, const uint8_t nonMaskValue = 255u);
492 
493  /**
494  * Determines the pixels in an 8 bit mask frame not having the identical pixel values in an 4-neighborhood.
495  * @param mask Given binary 8 bit mask frame, must be valid
496  * @param width The width of the entire mask frame in pixel, with range [2, infinity)
497  * @param height The height of the entire mask frame in pixel, with range [2, infinity)
498  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
499  * @param nonUniquePixels The resulting non unique pixels
500  * @param boundingBox Optional bounding box object shrinking the operation area
501  */
502  static void findNonUniquePixels4(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions& nonUniquePixels, const PixelBoundingBox& boundingBox = PixelBoundingBox());
503 
504  /**
505  * Determines the pixels in an 8 bit mask frame not having the identical pixel values in an 8-neighborhood.
506  * @param mask Given binary 8 bit mask frame, must be valid
507  * @param width The width of the entire mask frame in pixel, with range [2, infinity)
508  * @param height The height of the entire mask frame in pixel, with range [2, infinity)
509  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
510  * @param nonUniquePixels The resulting non unique pixels
511  * @param boundingBox Optional bounding box object shrinking the operation area
512  */
513  static void findNonUniquePixels8(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions& nonUniquePixels, const PixelBoundingBox& boundingBox = PixelBoundingBox());
514 
515  /**
516  * Converts an unordered set of pixel positions (providing all pixels at the mask border either inside the mask or outside the mask) to one inner our one outer mask contours.
517  * The given pixel positions can either be border pixels (mask pixels in the 4-neighborhood not being a mask pixel) or can be outline-4 pixels of several binary masks.<br>
518  * The resulting contour will include the most left pixel of the provided pixel positions.<br>
519  * Beware: If the mask is connected/intersects the boundary of a frame, then provided pixels must also include the pixel position exactly lying on the boundary of the frame.
520  * However, boundary contour pixels will not be included in the final contour.<br>
521  * Nevertheless, potential pixels lying on the frame boundary will not be part of the resulting contour, there will be a gap for all of these pixel positions.
522  * @param pixels The unordered set of pixel positions for which the inner or outer contour will be determined, with range [-1, width]x[-1, height]
523  * @param width The width of the frame in which the pixel positions (and the resulting contour) is defined, in pixel, with range [1, infinity)
524  * @param height The height of the frame in which the pixel positions (and the resulting contour) is defined, in pixel with range [1, infinity)
525  * @param contour Resulting mask contour including the most left pixel position
526  * @param remainingPixels Optional resulting set of pixels that are not part of the resulting contour and that may be part of another contour
527  * @return True, if succeeded
528  * @see pixels2contours().
529  */
530  static bool pixels2contour(const PixelPositions& pixels, const unsigned int width, const unsigned int height, PixelPositions& contour, PixelPositions* remainingPixels = nullptr);
531 
532  /**
533  * Converts an unordered set of pixel positions (providing all pixels at the mask border either inside the mask or outside the mask) to inner and outer mask contours.
534  * The given pixel positions can either be border pixels (mask pixels in the 4-neighborhood not being a mask pixel) or can be outline-4 pixels of several binary masks.<br>
535  * The mask can include holds with non-mask pixels/areas.<br>
536  * Beware: If the mask is connected/intersects the boundary of a frame, then provided pixels must also include the pixel position exactly lying on the boundary of the frame.<br>
537  * Nevertheless, potential pixels lying on the frame boundary will not be part of the resulting contour, there will be a gap for all of these pixel positions.
538  * @param mask The mask frame for which the contours will be determined, must be valid
539  * @param width The width of the mask frame in pixel, with range [1, infinity)
540  * @param height The height of the mask frame in pixel, with range [1, infinity)
541  * @param pixels The unordered set of pixel positions for which the inner and outer contours will be determined
542  * @param outerContours The resulting set of outer mask contours, outer contours enclose a mask and are not defined within a mask
543  * @param innerContours The resulting set of inner mask contours, inner contours are defined within a mask and cut a hole (positive or negative into the outer mask)
544  * @param maskValue Optional pixel value that is to be interpreted as belonging to the mask and which will be enclosed by the border pixels, range: [0, 255], default: 0
545  * @return True, if succeeded
546  * @see pixels2contour(), MaskCreator::denseContour2inclusiveMaskXOR(), MaskCreator::denseContour2exclusiveMaskXOR().
547  */
548  static bool pixels2contours(const uint8_t* mask, const unsigned int width, const unsigned int height, const PixelPositions& pixels, PixelContours& outerContours, PixelContours& innerContours, const uint8_t maskValue = 0u);
549 
550  /**
551  * This functions checks whether a given contour is an outer contour or an inner contour.
552  * A contour is an outer contour if the left start position is at the left frame border, or if the west pixel of the most left pixel is a non-mask pixel.
553  * @param mask The mask frame where pixels with value `maskValue` define mask pixels, must be valid
554  * @param width The width of the mask frame in pixel, with range [1, infinity)
555  * @param contour Pixel contour that will be checked
556  * @param maskValue Optional pixel value that is to be interpreted as belonging to the mask and which will not enclosed by the border pixels, range: [0, 255], default: 0
557  * @return True, if so
558  */
559  static inline bool isOuterContour(const uint8_t* mask, const unsigned int width, const PixelContour& contour, const uint8_t maskValue = 0u);
560 
561  /**
562  * Counts the number of mask pixels.
563  * @param mask The mask frame in which the mask pixels will be counted, pixels with 0xFF value are non-mask pixels, must be valid
564  * @param width The width of the mask frame in pixel, with range [1, infinity)
565  * @param height The height of the mask frame in pixel, with range [1, infinity)
566  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
567  * @param boundingBox Optional bounding box to speedup the computation
568  * @param nonMaskValue The mask value of a non-mask pixel, with range [0, 255]
569  * @return Number of mask pixels, with range [0, width * height]
570  */
571  static unsigned int countMaskPixels(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelBoundingBox& boundingBox = PixelBoundingBox(), const uint8_t nonMaskValue = 0xFFu);
572 
573  /**
574  * Determines the distance to the mask border in an 8 bit mask frame.<br>
575  * The 8 bit mask frame can hold two different pixel values 0x00 and 0xFF. A mask pixel is defined by 0x00.<br>
576  * The minimal distance for all mask pixels (with value 0x00) to their nearest non-mask pixels (with value 0xFF) is determined.<br>
577  * All mask pixels will receive their distance values: 1 means that the nearest mask border is one pixel off, 2 means two pixels, ...<br>
578  * Distances larger than the specified iteration number are not determined.<br>
579  * Thus, resulting mask pixels with value 0x00 have a larger distance to the mask border than the specified iteration number.
580  * @param mask The 8 bit mask frame the distances are calculated for (and assigned), must be valid
581  * @param width The width of the mask frame in pixel, with range [3, infinity)
582  * @param height The height of the mask frame in pixel, with range [3, infinity)
583  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
584  * @param iterations Number of maximal border distance to be calculated, with range [0, 254]
585  * @param assignFinal True, to assign each mask pixel a minimal distance after the final iteration
586  * @param boundingBox Optional bounding box to speed up the computation, invalid to process the entire mask
587  * @param worker Optional worker object to distribute the computation
588  */
589  static void determineDistancesToBorder8Bit(uint8_t* mask, const unsigned int width, const unsigned int height, unsigned int maskPaddingElements, const unsigned int iterations, const bool assignFinal, const PixelBoundingBox& boundingBox, Worker* worker = nullptr);
590 
591  /**
592  * Determines the axis-aligned bounding boxes of all isolated mask islands in a binary (but 8-bit) mask frame.
593  * The function determines the individual bounding boxes of connected components (mask pixels).
594  * This function applies a row-sweep approach to determine the individual mask blocks in individual rows and joins the results from the current row with previous rows.
595  * @param mask The binary mask in which the mask islands are located, must be valid
596  * @param width The width of the mask frame, in pixel, with range [1, infinity)
597  * @param height The height of the mask frame, in pixel, with range [1, infinity)
598  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
599  * @param maskValue The value of a mask pixel, with range [0, 255]
600  * @param useNeighborhood4 True, to use a 4-connected neighborhood when determining the mask islands; False, to use a 8-connected neighborhood
601  * @return The resulting bounding boxes of all isolated mask islands
602  * @see CV::MaskAnalyzer::detectBoundingBox().
603  */
604  static PixelBoundingBoxes detectBoundingBoxes(const uint8_t* const mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const uint8_t maskValue, const bool useNeighborhood4 = true);
605 
606  protected:
607 
608  /**
609  * Determines the border pixels in a subset of a 8 bit mask frame for a 4-neighborhood.
610  * @param mask Given binary 8 bit mask frame, pixel values not equal `nonMaskValue` count as mask pixels
611  * @param width The width of the entire mask frame in pixel
612  * @param height The height of the entire mask frame in pixel, with range [1, infinity)
613  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
614  * @param borderPixelsArray Array of resulting border pixels, the pixel itself is a mask pixel but not all 4-neighborhood pixels are mask pixels
615  * @param nonMaskValue Pixel value that is to be interpreted as not belonging to the mask and which will not be enclosed by the border pixels, range: [0, 255]
616  * @param firstColumn First column to be handled, with range [0, width - 1]
617  * @param numberColumns Number of columns to be handled, with range [1u, width - firstColumn]
618  * @param firstRow First row to be handled, with range [0, height - 1]
619  * @param numberRows Number of rows to be handled, with range [1u, height - firstRow]
620  * @param threadId Id of the execution thread, 0 if no multi-threading is applied
621  */
622  static void findBorderPixels4Subset(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions* borderPixelsArray, const uint8_t nonMaskValue, const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, const unsigned int threadId);
623 
624  /**
625  * Determines the border pixels in a subset of a 8 bit mask frame for a 8-neighborhood.
626  * @param mask Given binary 8 bit mask frame, pixel values not equal `nonMaskValue` count as mask pixels
627  * @param width The width of the entire mask frame in pixel
628  * @param height The height of the entire mask frame in pixel, with range [1, infinity)
629  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
630  * @param borderPixelsArray Array of resulting border pixels, the pixel itself is a mask pixel but not all 8-neighborhood pixels are mask pixels
631  * @param nonMaskValue Pixel value that is to be interpreted as not belonging to the mask and which will not be enclosed by the border pixels, range: [0, 255]
632  * @param firstColumn First column to be handled, with range [0, width - 1]
633  * @param numberColumns Number of columns to be handled, with range [1u, width - firstColumn]
634  * @param firstRow First row to be handled, with range [0, height - 1]
635  * @param numberRows Number of rows to be handled, with range [1u, height - firstRow]
636  * @param threadId Id of the execution thread, 0 if no multi-threading is applied
637  */
638  static void findBorderPixels8Subset(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions* borderPixelsArray, const uint8_t nonMaskValue, const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, const unsigned int threadId);
639 
640  /**
641  * Determines the distance to the mask border in an 8 bit mask frame.<br>
642  * The 8 bit mask frame may hold two different pixel values 0x00 and 0xFF. A mask pixel is defined by 0x00.<br>
643  * The minimal distance for all mask pixels (with value 0x00) to their nearest non-mask pixels (with value 0xFF) are determined.<br>
644  * All mask pixels will receive their distance values: 1 means that the nearest mask border is one pixel off, 2 means two pixels, ...<br>
645  * Distances larger than the specified iteration number are not determined.<br>
646  * Thus, resulting mask pixels with value 0x00 have a larger distance to the mask border than the specified iteration number.
647  * @param mask The 8 bit mask frame the distances are calculated for (and assigned), must be valid
648  * @param width The width of the mask frame in pixel, with range [3, infinity)
649  * @param height The height of the mask frame in pixel, with range [3, infinity)
650  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
651  * @param searchValue Pixel value to be searched
652  * @param resultValue Resulting value for neighboring pixels of the search value
653  * @param firstColumn First column to be handled
654  * @param numberColumns Number of columns to be handled
655  * @param firstRow First row to be handled
656  * @param numberRows Number of rows to be handled
657  */
658  static void determineDistancesToBorder8BitSubset(uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const uint8_t searchValue, const uint8_t resultValue, const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows);
659 
660  /**
661  * Computes a per pixel distance to the nearest pixel with a reference value using 3-by-3 neighborhood
662  * @param source The frame for which a distance map will be computed, must be valid
663  * @param width The width of the source frame, range: [1, infinity)
664  * @param height The height of the source frame, range: [1, infinity)
665  * @param target The location where the computed distance map will be stored, must be valid and have the same size the source frame, this data will only be valid if the return status is `Success`
666  * @param buffer An optional buffer of the size `(width + 2) * (height + 2)`. If not provided, memory will be allocated internally (which is more expensive!)
667  * @param distanceVerticalHorizontal The cost of a step in horizontal/vertical direction, range: (1, infinity)
668  * @param distanceDiagonal The cost of a step in diagonal direction, range: (1, infinity)
669  * @param referenceValue Distance values pixels will be computed to the closest pixel with this reference value, range: [0, 255]
670  * @param sourcePaddingElements Optional number of padding elements in the source frame, range: [0, infinity)
671  * @param targetPaddingElements Optional number of padding elements in the target frame, range: [0, infinity)
672  * @return True on success, otherwise false
673  * @tparam TDistanceType The data type of the resulting distance map, must be integral or floating-point
674  */
675  template <typename TDistanceType>
676  static bool computeDistanceTransform8Bit(const uint8_t* source, const uint32_t width, const uint32_t height, TDistanceType* target, uint32_t* buffer, const TDistanceType distanceVerticalHorizontal, const TDistanceType distanceDiagonal, const uint8_t referenceValue = 0u, const uint32_t sourcePaddingElements = 0u, const uint32_t targetPaddingElements = 0u);
677 
678  /**
679  * Returns whether two values are identical or not identical.
680  * @param valueA The first value to compare
681  * @param valueB The second value to compare
682  * @return True, if so
683  * @tparam True, if both values need to be identical; False, if both values must not be identical
684  */
685  template <typename T, bool tMaskValueIsEqual>
686  static inline bool compareValues(const T valueA, const T valueB);
687 };
688 
690  blockPosition(),
691  blockId(0u),
692  blockSize(0u)
693 {
694  // nothing to do here
695 }
696 
697 inline MaskAnalyzer::MaskBlock::MaskBlock(const PixelPosition& position, const unsigned int id, const unsigned int size, const bool border) :
698  blockPosition(position),
699  blockId(id),
700  blockSize(size),
701  blockBorder(border)
702 {
703  // nothing to do here
704 }
705 
707 {
708  return blockPosition;
709 }
710 
711 inline unsigned int MaskAnalyzer::MaskBlock::id() const
712 {
713  return blockId;
714 }
715 
716 inline unsigned int MaskAnalyzer::MaskBlock::size() const
717 {
718  return blockSize;
719 }
720 
722 {
723  return blockBorder;
724 }
725 
726 inline bool MaskAnalyzer::MaskBlock::operator<(const MaskBlock& block) const
727 {
728  return blockSize < block.blockSize;
729 }
730 
731 inline MaskAnalyzer::SweepMaskIsland::SweepMaskIsland(const unsigned int currentRow, const unsigned int start, const unsigned int end)
732 {
733  ocean_assert(start < end);
734 
735  previousRowSegments_.reserve(8);
736  currentRowSegments_.reserve(8);
737 
738  addSegment(currentRow, start, end);
739 }
740 
741 inline void MaskAnalyzer::SweepMaskIsland::join(const SweepMaskIsland& sweepMaskIsland)
742 {
743  boundingBox_ = boundingBox_ || sweepMaskIsland.boundingBox_;
744 
745  previousRowSegments_.insert(previousRowSegments_.cend(), sweepMaskIsland.previousRowSegments_.cbegin(), sweepMaskIsland.previousRowSegments_.cend());
746  currentRowSegments_.insert(currentRowSegments_.cend(), sweepMaskIsland.currentRowSegments_.cbegin(), sweepMaskIsland.currentRowSegments_.cend());
747 }
748 
749 inline bool MaskAnalyzer::SweepMaskIsland::hasIntersection(const unsigned int start, const unsigned int end, const bool useNeighborhood4) const
750 {
751  ocean_assert(start < end);
752 
753  for (const RowSegment& previousRowSegment : previousRowSegments_)
754  {
755  const unsigned int& previousSegmentStart = previousRowSegment.first;
756  const unsigned int& previousSegmentEnd = previousRowSegment.second;
757 
758  if ((useNeighborhood4 && start < previousSegmentEnd && end > previousSegmentStart) || (!useNeighborhood4 && start <= previousSegmentEnd && end >= previousSegmentStart))
759  {
760  return true;
761  }
762  }
763 
764  return false;
765 }
766 
767 inline void MaskAnalyzer::SweepMaskIsland::addSegment(const unsigned int currentRow, const unsigned int start, const unsigned int end)
768 {
769  ocean_assert(start < end);
770 
771  currentRowSegments_.emplace_back(start, end);
772 
773  boundingBox_ += PixelPosition(start, currentRow);
774  boundingBox_ += PixelPosition(end - 1u, currentRow);
775 }
776 
778 {
779  std::swap(currentRowSegments_, previousRowSegments_);
780 
781  currentRowSegments_.clear();
782 }
783 
785 {
786  return boundingBox_;
787 }
788 
789 template <bool tMaskValueIsEqual, typename T>
790 inline bool MaskAnalyzer::hasMaskNeighbor4(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue)
791 {
792  ocean_assert(mask != nullptr && width != 0u && height != 0u);
793  ocean_assert(position.x() < width && position.y() < height);
794 
795  ocean_assert((position.x() - 1u < width - 2u && position.y() - 1u < height - 2u) == (position.x() >= 1u && position.x() + 1u < width && position.y() >= 1u && position.y() + 1u < height));
796  if (position.x() - 1u < width - 2u && position.y() - 1u < height - 2u)
797  {
798  return hasMaskNeighbor4Center<tMaskValueIsEqual, T>(mask, width, height, maskPaddingElements, position, testValue);
799  }
800 
801  const unsigned int maskStrideElements = width + maskPaddingElements;
802 
803  mask += position.y() * maskStrideElements + position.x();
804 
805  if (position.x() != 0u && compareValues<T, tMaskValueIsEqual>(*(mask - 1), testValue))
806  {
807  return true;
808  }
809 
810  if (position.x() + 1u < width && compareValues<T, tMaskValueIsEqual>(*(mask + 1), testValue))
811  {
812  return true;
813  }
814 
815  if (position.y() != 0u && compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements), testValue))
816  {
817  return true;
818  }
819 
820  if (position.y() + 1u < height && compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements), testValue))
821  {
822  return true;
823  }
824 
825  return false;
826 }
827 
828 template <bool tMaskValueIsEqual, typename T>
829 inline bool MaskAnalyzer::hasMaskNeighbor4Center(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue)
830 {
831  ocean_assert(mask != nullptr && height >= 3u && height >= 3u);
832  ocean_assert(position.x() >= 1u && position.x() + 1u < width);
833  ocean_assert_and_suppress_unused(position.y() >= 1u && position.y() + 1u < height, height);
834 
835  const unsigned int maskStrideElements = width + maskPaddingElements;
836 
837  mask += position.y() * maskStrideElements + position.x();
838 
839  return compareValues<T, tMaskValueIsEqual>(*(mask - 1), testValue)
840  || compareValues<T, tMaskValueIsEqual>(*(mask + 1), testValue)
841  || compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements), testValue)
842  || compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements), testValue);
843 }
844 
845 template <bool tMaskValueIsEqual, typename T>
846 inline bool MaskAnalyzer::hasMaskNeighbor5(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue)
847 {
848  ocean_assert(mask != nullptr && width != 0u && height != 0u);
849  ocean_assert(position.x() < width && position.y() < height);
850 
851  ocean_assert((position.x() - 1u < width - 2u && position.y() - 1u < height - 2u) == (position.x() >= 1u && position.x() + 1u < width && position.y() >= 1u && position.y() + 1u < height));
852  if (position.x() - 1u < width - 2u && position.y() - 1u < height - 2u)
853  {
854  return hasMaskNeighbor5Center<tMaskValueIsEqual, T>(mask, width, height, maskPaddingElements, position, testValue);
855  }
856 
857  const unsigned int maskStrideElements = width + maskPaddingElements;
858 
859  mask += position.y() * maskStrideElements + position.x();
860 
861  if (compareValues<T, tMaskValueIsEqual>(*mask, testValue))
862  {
863  return true;
864  }
865 
866  if (position.x() != 0u && compareValues<T, tMaskValueIsEqual>(*(mask - 1), testValue))
867  {
868  return true;
869  }
870 
871  if (position.x() + 1u < width && compareValues<T, tMaskValueIsEqual>(*(mask + 1), testValue))
872  {
873  return true;
874  }
875 
876  if (position.y() != 0u && compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements), testValue))
877  {
878  return true;
879  }
880 
881  if (position.y() + 1u < height && compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements), testValue))
882  {
883  return true;
884  }
885 
886  return false;
887 }
888 
889 template <bool tMaskValueIsEqual, typename T>
890 inline bool MaskAnalyzer::hasMaskNeighbor5Center(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue)
891 {
892  ocean_assert(mask != nullptr && height >= 3u && height >= 3u);
893  ocean_assert(position.x() >= 1u && position.x() + 1u < width);
894  ocean_assert_and_suppress_unused(position.y() >= 1u && position.y() + 1u < height, height);
895 
896  const unsigned int maskStrideElements = width + maskPaddingElements;
897 
898  mask += position.y() * maskStrideElements + position.x();
899 
900  return compareValues<T, tMaskValueIsEqual>(*mask, testValue)
901  || compareValues<T, tMaskValueIsEqual>(*(mask - 1), testValue)
902  || compareValues<T, tMaskValueIsEqual>(*(mask + 1), testValue)
903  || compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements), testValue)
904  || compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements), testValue);
905 }
906 
907 template <bool tMaskValueIsEqual, typename T>
908 inline bool MaskAnalyzer::hasMaskNeighbor8(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue)
909 {
910  ocean_assert(mask != nullptr && width != 0u && height != 0u);
911  ocean_assert(position.x() < width && position.y() < height);
912 
913  ocean_assert((position.x() - 1u < width - 2u && position.y() - 1u < height - 2u) == (position.x() >= 1u && position.x() + 1u < width && position.y() >= 1u && position.y() + 1u < height));
914  if (position.x() - 1u < width - 2u && position.y() - 1u < height - 2u)
915  {
916  return hasMaskNeighbor8Center<tMaskValueIsEqual, T>(mask, width, height, maskPaddingElements, position, testValue);
917  }
918 
919  const unsigned int maskStrideElements = width + maskPaddingElements;
920 
921  mask += position.y() * maskStrideElements + position.x();
922 
923  if (position.x() != 0u)
924  {
925  if (compareValues<T, tMaskValueIsEqual>(*(mask - 1), testValue))
926  {
927  return true;
928  }
929 
930  if (position.y() != 0u && compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements - 1u), testValue))
931  {
932  return true;
933  }
934 
935  if (position.y() + 1u < height && compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements - 1u), testValue))
936  {
937  return true;
938  }
939  }
940 
941  if (position.x() + 1u < width)
942  {
943  if (compareValues<T, tMaskValueIsEqual>(*(mask + 1), testValue))
944  {
945  return true;
946  }
947 
948  if (position.y() != 0u && compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements + 1u), testValue))
949  {
950  return true;
951  }
952 
953  if (position.y() + 1u < height && compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements + 1u), testValue))
954  {
955  return true;
956  }
957  }
958 
959  if (position.y() != 0u && compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements), testValue))
960  {
961  return true;
962  }
963 
964  if (position.y() + 1u < height && compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements), testValue))
965  {
966  return true;
967  }
968 
969  return false;
970 }
971 
972 template <bool tMaskValueIsEqual, typename T>
973 inline bool MaskAnalyzer::hasMaskNeighbor8Center(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue)
974 {
975  ocean_assert(mask != nullptr && height >= 3u && height >= 3u);
976  ocean_assert(position.x() >= 1u && position.x() + 1u < width);
977  ocean_assert_and_suppress_unused(position.y() >= 1u && position.y() + 1u < height, height);
978 
979  const unsigned int maskStrideElements = width + maskPaddingElements;
980 
981  mask += position.y() * maskStrideElements + position.x();
982 
983  return compareValues<T, tMaskValueIsEqual>(*(mask - 1), testValue) || compareValues<T, tMaskValueIsEqual>(*(mask + 1), testValue)
984  || compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements - 1), testValue) || compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements), testValue) || compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements + 1), testValue)
985  || compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements - 1), testValue) || compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements), testValue) || compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements + 1), testValue);
986 }
987 
988 template <bool tMaskValueIsEqual, typename T>
989 inline bool MaskAnalyzer::hasMaskNeighbor9(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue)
990 {
991  ocean_assert(mask != nullptr && width != 0u && height != 0u);
992  ocean_assert(position.x() < width && position.y() < height);
993 
994  ocean_assert((position.x() - 1u < width - 2u && position.y() - 1u < height - 2u) == (position.x() >= 1u && position.x() + 1u < width && position.y() >= 1u && position.y() + 1u < height));
995  if (position.x() - 1u < width - 2u && position.y() - 1u < height - 2u)
996  {
997  return hasMaskNeighbor9Center<tMaskValueIsEqual, T>(mask, width, height, maskPaddingElements, position, testValue);
998  }
999 
1000  const unsigned int maskStrideElements = width + maskPaddingElements;
1001 
1002  mask += position.y() * maskStrideElements + position.x();
1003 
1004  if (compareValues<T, tMaskValueIsEqual>(*mask, testValue))
1005  {
1006  return true;
1007  }
1008 
1009  if (position.x() != 0u)
1010  {
1011  if (compareValues<T, tMaskValueIsEqual>(*(mask - 1), testValue))
1012  {
1013  return true;
1014  }
1015 
1016  if (position.y() != 0u && compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements - 1u), testValue))
1017  {
1018  return true;
1019  }
1020 
1021  if (position.y() + 1u < height && compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements - 1u), testValue))
1022  {
1023  return true;
1024  }
1025  }
1026 
1027  if (position.x() + 1u < width)
1028  {
1029  if (compareValues<T, tMaskValueIsEqual>(*(mask + 1), testValue))
1030  {
1031  return true;
1032  }
1033 
1034  if (position.y() != 0u && compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements + 1u), testValue))
1035  {
1036  return true;
1037  }
1038 
1039  if (position.y() + 1u < height && compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements + 1u), testValue))
1040  {
1041  return true;
1042  }
1043  }
1044 
1045  if (position.y() != 0u && compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements), testValue))
1046  {
1047  return true;
1048  }
1049 
1050  if (position.y() + 1u < height && compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements), testValue))
1051  {
1052  return true;
1053  }
1054 
1055  return false;
1056 }
1057 
1058 template <bool tMaskValueIsEqual, typename T>
1059 inline bool MaskAnalyzer::hasMaskNeighbor9Center(const T* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position, const T testValue)
1060 {
1061  ocean_assert(mask != nullptr && height >= 3u && height >= 3u);
1062  ocean_assert(position.x() >= 1u && position.x() + 1u < width);
1063  ocean_assert_and_suppress_unused(position.y() >= 1u && position.y() + 1u < height, height);
1064 
1065  const unsigned int maskStrideElements = width + maskPaddingElements;
1066 
1067  mask += position.y() * maskStrideElements + position.x();
1068 
1069  return compareValues<T, tMaskValueIsEqual>(*(mask - 1), testValue) || compareValues<T, tMaskValueIsEqual>(*mask, testValue) || compareValues<T, tMaskValueIsEqual>(*(mask + 1), testValue)
1070  || compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements - 1), testValue) || compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements), testValue) || compareValues<T, tMaskValueIsEqual>(*(mask - maskStrideElements + 1), testValue)
1071  || compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements - 1), testValue) || compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements), testValue) || compareValues<T, tMaskValueIsEqual>(*(mask + maskStrideElements + 1), testValue);
1072 }
1073 
1074 inline bool MaskAnalyzer::isOuterContour(const uint8_t* mask, const unsigned int width, const PixelContour& contour, const uint8_t maskValue)
1075 {
1076  ocean_assert(mask && width >= 1u);
1077  ocean_assert(contour);
1078 
1079  const PixelPosition& mostLeftPixel = contour[contour.indexLeftPosition()];
1080 
1081  ocean_assert(mostLeftPixel.x() < width);
1082  return mostLeftPixel.x() == 0u || (mask[mostLeftPixel.y() * width + mostLeftPixel.x() - 1u] != maskValue);
1083 }
1084 
1085 inline bool MaskAnalyzer::computeChessboardDistanceTransform8Bit(const uint8_t* source, const uint32_t width, const uint32_t height, uint32_t* target, uint32_t* buffer, const uint8_t referenceValue, const uint32_t sourcePaddingElements, const uint32_t targetPaddingElements)
1086 {
1087  return computeDistanceTransform8Bit(source, width, height, target, buffer, distanceVerticalHorizontalC, distanceDiagonalC, referenceValue, sourcePaddingElements, targetPaddingElements);
1088 }
1089 
1090 inline bool MaskAnalyzer::computeL1DistanceTransform8Bit(const uint8_t* source, const uint32_t width, const uint32_t height, uint32_t* target, uint32_t* buffer, const uint8_t referenceValue, const uint32_t sourcePaddingElements, const uint32_t targetPaddingElements)
1091 {
1092  return computeDistanceTransform8Bit(source, width, height, target, buffer, distanceVerticalHorizontalL1, distanceDiagonalL1, referenceValue, sourcePaddingElements, targetPaddingElements);
1093 }
1094 
1095 inline bool MaskAnalyzer::computeL2DistanceTransform8Bit(const uint8_t* source, const uint32_t width, const uint32_t height, float* target, uint32_t* buffer, const uint8_t referenceValue, const uint32_t sourcePaddingElements, const uint32_t targetPaddingElements)
1096 {
1097  return computeDistanceTransform8Bit(source, width, height, target, buffer, distanceVerticalHorizontalL2, distanceDiagonalL2, referenceValue, sourcePaddingElements, targetPaddingElements);
1098 }
1099 
1100 template <typename TDistanceType>
1101 bool MaskAnalyzer::computeDistanceTransform8Bit(const uint8_t* source, const uint32_t width, const uint32_t height, TDistanceType* target, uint32_t* buffer, const TDistanceType distanceVerticalHorizontal, const TDistanceType distanceDiagonal, const uint8_t referenceValue, const uint32_t sourcePaddingElements, const uint32_t targetPaddingElements)
1102 {
1103  if (source == nullptr || width == 0u || height == 0u || target == nullptr)
1104  {
1105  ocean_assert(false && "Invalid input data");
1106  return false;
1107  }
1108 
1109  const uint32_t bufferWidth = width + 2u;
1110  const uint32_t bufferHeight = height + 2u;
1111 
1112  Memory memory;
1113 
1114  if (buffer == nullptr)
1115  {
1116  memory = Memory(bufferWidth * bufferHeight * sizeof(uint32_t));
1117  buffer = memory.data<uint32_t>();
1118  }
1119 
1120  if (distanceVerticalHorizontal <= 0 || distanceDiagonal <= 0)
1121  {
1122  ocean_assert(false && "Distance values must be positive");
1123  return false;
1124  }
1125 
1126  const uint32_t sourceStrideElements = width + sourcePaddingElements;
1127  const uint32_t targetStrideElements = width + targetPaddingElements;
1128 
1129  const uint8_t* const sourceEnd = source + (height - 1u) * sourceStrideElements + width;
1130  const TDistanceType* const targetEnd = target + (height - 1u) * targetStrideElements + width;
1131 
1132  const uint32_t* bufferEnd = buffer + bufferHeight * bufferWidth;
1133 
1134  // In case of floating-point numbers, all intermediate values are be stored as fixed-point values with 16-bit precision. Integral types remain unchanged.
1135  constexpr TDistanceType fixedPointScalingFactor = TDistanceType(std::is_floating_point<TDistanceType>::value ? 1u << 16u : 1u);
1136  constexpr TDistanceType fixedPointUnscalingFactor = TDistanceType(1) / fixedPointScalingFactor;
1137  static_assert(fixedPointScalingFactor > 0 && fixedPointUnscalingFactor > 0, "Fixed-point scaling factors must be positive");
1138  static_assert(std::is_floating_point<TDistanceType>::value || (fixedPointScalingFactor == TDistanceType(1u) && fixedPointUnscalingFactor == TDistanceType(1)), "For integral types scaling factor must be 1");
1139 
1140  const uint32_t distanceVerticalHorizontal_q = uint32_t((distanceVerticalHorizontal * fixedPointScalingFactor));
1141  const uint32_t distanceDiagonal_q = uint32_t(distanceDiagonal * fixedPointScalingFactor);
1142 
1143  constexpr uint32_t boundaryValue = NumericT<uint32_t>::maxValue() / 2u;
1144 
1145  // Initialize the top and bottom row of the buffer memory (the left-most and right-most columns will be initialized
1146  // during the forward pass)
1147  {
1148  uint32_t* bufferTopRow = buffer;
1149  uint32_t* bufferBottomRow = buffer + (bufferHeight - 1u) * bufferWidth;
1150  for (uint32_t x = 0u; x < bufferWidth; ++x)
1151  {
1152  *bufferTopRow = boundaryValue;
1153  *bufferBottomRow = boundaryValue;
1154 
1155  ++bufferTopRow;
1156  ++bufferBottomRow;
1157  }
1158  }
1159 
1160  // Forward and backward passes using a 3x3 neighborhood:
1161  //
1162  // 0 1 2
1163  // 3 X 4
1164  // 5 6 7
1165  //
1166  // The forward pass uses the neighbors 0-3 and iterates through the image from the top-left corner to the
1167  // bottom-right corner; the backward pass uses the neighbors 4-7 and iterates from the bottom-right corner back to
1168  // top-left corner.
1169  //
1170  // Example for distanceDiagonal = distanceVerticalHorizontal = 1 (<=> distance metric C):
1171  //
1172  // Input: Forward pass: Backward pass:
1173  // - - - - - - - - - - - - - - 3 3 3 3 3 3 3
1174  // - - - - - - - - - - - - - - 3 2 2 2 2 2 3
1175  // - - - - - - - - - - - - - - 3 2 1 1 1 2 3
1176  // - - - 0 - - - - - - 0 1 2 3 3 2 1 0 1 2 3
1177  // - - - - - - - - - 1 1 1 2 3 3 2 1 1 1 2 3
1178  // - - - - - - - - 2 2 2 2 2 3 3 2 2 2 2 2 3
1179  // - - - - - - - 3 3 3 3 3 3 3 3 3 3 3 3 3 3
1180 
1181  bool foundReferenceValue = false;
1182 
1183  // Forward pass
1184  for (uint32_t y = 0u; y < height; ++y)
1185  {
1186  const uint8_t* sourceRow = source + y * sourceStrideElements;
1187  uint32_t* bufferRow = buffer + (y + 1u) * bufferWidth;
1188 
1189  // Initialize the left-most and right-most columns of the current row of the buffer memory
1190  *bufferRow = boundaryValue;
1191  *(bufferRow + bufferWidth - 1u) = boundaryValue;
1192 
1193  ++bufferRow;
1194 
1195  for (uint32_t x = 0u; x < width; ++x)
1196  {
1197  ocean_assert_and_suppress_unused(sourceRow >= source && sourceRow < sourceEnd, sourceEnd);
1198  ocean_assert_and_suppress_unused(bufferRow - bufferWidth - 1u >= buffer && bufferRow < bufferEnd, bufferEnd);
1199 
1200  if (*sourceRow == referenceValue)
1201  {
1202  *bufferRow = 0u;
1203  foundReferenceValue = true;
1204  }
1205  else
1206  {
1207  const uint32_t neighbor0_q = *(bufferRow - bufferWidth - 1u) + distanceDiagonal_q;
1208  const uint32_t neighbor1_q = *(bufferRow - bufferWidth) + distanceVerticalHorizontal_q;
1209  const uint32_t neighbor2_q = *(bufferRow - bufferWidth + 1u) + distanceDiagonal_q;
1210  const uint32_t neighbor3_q = *(bufferRow - 1u) + distanceVerticalHorizontal_q;
1211 
1212  *bufferRow = std::min(std::min(neighbor0_q, neighbor1_q), std::min(neighbor2_q, neighbor3_q));
1213  }
1214 
1215  ++sourceRow;
1216  ++bufferRow;
1217  }
1218  }
1219 
1220  if (foundReferenceValue == false)
1221  {
1222  return false;
1223  }
1224 
1225  // Backward pass
1226  for (uint32_t y = height - 1u; y < height; --y)
1227  {
1228  TDistanceType* targetRow = target + y * targetStrideElements + width - 1u;
1229  uint32_t* bufferRow = buffer + (y + 1u) * bufferWidth + width;
1230 
1231  for (uint32_t x = width - 1u; x < width; --x)
1232  {
1233  ocean_assert_and_suppress_unused(targetRow >= target && targetRow < targetEnd, targetEnd);
1234  ocean_assert(bufferRow >= buffer && bufferRow + bufferWidth + 1u < bufferEnd);
1235 
1236  const uint32_t neighbor4_q = *(bufferRow + 1u) + distanceVerticalHorizontal_q;
1237  const uint32_t neighbor5_q = *(bufferRow + bufferWidth - 1u) + distanceDiagonal_q;
1238  const uint32_t neighbor6_q = *(bufferRow + bufferWidth) + distanceVerticalHorizontal_q;
1239  const uint32_t neighbor7_q = *(bufferRow + bufferWidth + 1u) + distanceDiagonal_q;
1240 
1241  *bufferRow = std::min(*bufferRow, std::min(std::min(neighbor4_q, neighbor5_q), std::min(neighbor6_q, neighbor7_q)));
1242 
1243  if (std::is_floating_point<TDistanceType>::value)
1244  {
1245  *targetRow = TDistanceType(*bufferRow) * fixedPointUnscalingFactor;
1246  }
1247  else
1248  {
1249  *targetRow = TDistanceType(*bufferRow);
1250  }
1251 
1252 
1253  --targetRow;
1254  --bufferRow;
1255  }
1256  }
1257 
1258  return true;
1259 }
1260 
1261 template <typename T, bool tMaskValueIsEqual>
1262 inline bool MaskAnalyzer::compareValues(const T valueA, const T valueB)
1263 {
1264  if constexpr (tMaskValueIsEqual)
1265  {
1266  return valueA == valueB;
1267  }
1268  else
1269  {
1270  return valueA != valueB;
1271  }
1272 }
1273 
1274 } // namespace Segmentation
1275 
1276 } // namespace CV
1277 
1278 } // namespace Ocean
1279 
1280 #endif // META_OCEAN_CV_SEGMENTATION_MASK_ANALYZER_H
T y() const
Returns the vertical coordinate position of this object.
Definition: PixelPosition.h:470
T x() const
Returns the horizontal coordinate position of this object.
Definition: PixelPosition.h:458
This class implements a simple information for a block/area of mask pixels.
Definition: segmentation/MaskAnalyzer.h:46
unsigned int id() const
Returns the id of this block.
Definition: segmentation/MaskAnalyzer.h:711
PixelPosition blockPosition
One position inside the mask block.
Definition: segmentation/MaskAnalyzer.h:97
unsigned int size() const
Returns the size of this block in pixel.
Definition: segmentation/MaskAnalyzer.h:716
MaskBlock()
Creates an invalid block object.
Definition: segmentation/MaskAnalyzer.h:689
unsigned int blockSize
Size of the mask block in pixel.
Definition: segmentation/MaskAnalyzer.h:103
bool operator<(const MaskBlock &block) const
Returns whether the size of this block is smaller than the size of the given block.
Definition: segmentation/MaskAnalyzer.h:726
bool border() const
Returns whether this block intersects with the image border.
Definition: segmentation/MaskAnalyzer.h:721
bool blockBorder
True, if image border block.
Definition: segmentation/MaskAnalyzer.h:106
unsigned int blockId
Id of the mask block.
Definition: segmentation/MaskAnalyzer.h:100
const PixelPosition & position() const
Returns the one position of this block.
Definition: segmentation/MaskAnalyzer.h:706
This class implements a mask island used in a sweep algorithm.
Definition: segmentation/MaskAnalyzer.h:168
void join(const SweepMaskIsland &sweepMask)
Joins a given sweep mask island with this island.
Definition: segmentation/MaskAnalyzer.h:741
RowSegments previousRowSegments_
The segments located in the previous row.
Definition: segmentation/MaskAnalyzer.h:234
RowSegments currentRowSegments_
The segments located in the current row.
Definition: segmentation/MaskAnalyzer.h:237
const PixelBoundingBox & boundingBox() const
Returns the bounding box of this mask island.
Definition: segmentation/MaskAnalyzer.h:784
PixelBoundingBox boundingBox_
The bounding box of this mask.
Definition: segmentation/MaskAnalyzer.h:240
void addSegment(const unsigned int currentRow, const unsigned int start, const unsigned int end)
Adds a new row segment to this island.
Definition: segmentation/MaskAnalyzer.h:767
bool hasIntersection(const unsigned int start, const unsigned int end, const bool useNeighborhood4) const
Checks whether this island intersects with a given row segment.
Definition: segmentation/MaskAnalyzer.h:749
void nextRow()
Ends segment handling for the current row and prepares the mask for the next row.
Definition: segmentation/MaskAnalyzer.h:777
std::pair< unsigned int, unsigned int > RowSegment
Definition of a pair combining the horizontal start position (inclusive), and the horizontal end posi...
Definition: segmentation/MaskAnalyzer.h:174
std::vector< RowSegment > RowSegments
Definition of a vector holding row segments.
Definition: segmentation/MaskAnalyzer.h:179
This class implements functions analyzing masks, determining specific pixels in relation to masks and...
Definition: segmentation/MaskAnalyzer.h:39
static void analyzeNonMaskSeparation8Bit(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, uint32_t *separation, const unsigned int separationPaddingElements, MaskBlocks &blocks)
Analyzes an 8 bit binary mask frame and separates the pixels into individual blocks of not joined sub...
static constexpr uint32_t distanceDiagonalC
Chessboard distance for diagonal steps.
Definition: segmentation/MaskAnalyzer.h:152
static constexpr float distanceDiagonalL2
L2 distance for diagonal steps.
Definition: segmentation/MaskAnalyzer.h:162
static bool computeL1DistanceTransform8Bit(const uint8_t *source, const uint32_t width, const uint32_t height, uint32_t *target, uint32_t *buffer=nullptr, const uint8_t referenceValue=0u, const uint32_t sourcePaddingElements=0u, const uint32_t targetPaddingElements=0u)
Computes a per pixel L1 distance to the nearest pixel with a reference value using 3-by-3 neighborhoo...
Definition: segmentation/MaskAnalyzer.h:1090
static unsigned int countMaskPixels(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelBoundingBox &boundingBox=PixelBoundingBox(), const uint8_t nonMaskValue=0xFFu)
Counts the number of mask pixels.
static void findOutline4(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions &outlinePixels4, const PixelBoundingBox &boundingBox=PixelBoundingBox(), const uint8_t nonMaskValue=0xFF)
Determines all outline-4 pixels in an 8 bit mask frame.
static bool hasMaskNeighbor8(const T *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition &position, const T testValue)
Determines whether at least one neighbor pixel in the 8-neighborhood is or is not a mask pixel.
Definition: segmentation/MaskAnalyzer.h:908
static bool hasMaskNeighbor4Center(const T *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition &position, const T testValue)
Determines whether at least one neighbor pixel in the 4-neighborhood is a mask pixel with the given t...
Definition: segmentation/MaskAnalyzer.h:829
static void analyzeMaskSeparation8Bit(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, uint32_t *separation, const unsigned int separationPaddingElements, MaskBlocks &blocks)
Analyzes an 8 bit binary mask frame and separates the pixels into individual blocks of joined sub-mas...
static bool hasMaskNeighbor8Center(const T *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition &position, const T testValue)
Determines whether at least one neighbor pixel in the 8-neighborhood is a mask pixel with the given t...
Definition: segmentation/MaskAnalyzer.h:973
static bool pixels2contour(const PixelPositions &pixels, const unsigned int width, const unsigned int height, PixelPositions &contour, PixelPositions *remainingPixels=nullptr)
Converts an unordered set of pixel positions (providing all pixels at the mask border either inside t...
static void findBorderPixels8(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions &borderPixels, const PixelBoundingBox &boundingBox=PixelBoundingBox(), Worker *worker=nullptr, const uint8_t nonMaskValue=255u)
Determines all border pixels in an 8 bit mask frame for a 8-neighborhood.
static bool compareValues(const T valueA, const T valueB)
Returns whether two values are identical or not identical.
Definition: segmentation/MaskAnalyzer.h:1262
static bool isOuterContour(const uint8_t *mask, const unsigned int width, const PixelContour &contour, const uint8_t maskValue=0u)
This functions checks whether a given contour is an outer contour or an inner contour.
Definition: segmentation/MaskAnalyzer.h:1074
static void findBorderPixels8Subset(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions *borderPixelsArray, const uint8_t nonMaskValue, const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, const unsigned int threadId)
Determines the border pixels in a subset of a 8 bit mask frame for a 8-neighborhood.
static bool pixels2contours(const uint8_t *mask, const unsigned int width, const unsigned int height, const PixelPositions &pixels, PixelContours &outerContours, PixelContours &innerContours, const uint8_t maskValue=0u)
Converts an unordered set of pixel positions (providing all pixels at the mask border either inside t...
static void findBorderPixels4Subset(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions *borderPixelsArray, const uint8_t nonMaskValue, const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows, const unsigned int threadId)
Determines the border pixels in a subset of a 8 bit mask frame for a 4-neighborhood.
static bool computeL2DistanceTransform8Bit(const uint8_t *source, const uint32_t width, const uint32_t height, float *target, uint32_t *buffer=nullptr, const uint8_t referenceValue=0u, const uint32_t sourcePaddingElements=0u, const uint32_t targetPaddingElements=0u)
Computes a per pixel (approximated) L2 distance to the nearest pixel with a reference value using 3-b...
Definition: segmentation/MaskAnalyzer.h:1095
std::vector< MaskBlock > MaskBlocks
Definition of a vector holding mask block objects.
Definition: segmentation/MaskAnalyzer.h:112
static void findNonUniquePixels4(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions &nonUniquePixels, const PixelBoundingBox &boundingBox=PixelBoundingBox())
Determines the pixels in an 8 bit mask frame not having the identical pixel values in an 4-neighborho...
std::vector< SweepMaskIsland > SweepMaskIslands
Definition of a vector holding SweepMaskIsland objects.
Definition: segmentation/MaskAnalyzer.h:246
static void findNonUniquePixels8(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions &nonUniquePixels, const PixelBoundingBox &boundingBox=PixelBoundingBox())
Determines the pixels in an 8 bit mask frame not having the identical pixel values in an 8-neighborho...
static bool hasMaskNeighbor5(const T *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition &position, const T testValue)
Determines whether at least one neighbor pixel in the 4-neighborhood (+ center pixel) is or is not a ...
Definition: segmentation/MaskAnalyzer.h:846
static constexpr uint32_t distanceVerticalHorizontalC
Chessboard distance for vertical and horizontal steps.
Definition: segmentation/MaskAnalyzer.h:150
static constexpr uint32_t distanceVerticalHorizontalL1
L1 distance for vertical and horizontal steps.
Definition: segmentation/MaskAnalyzer.h:155
static bool computeChessboardDistanceTransform8Bit(const uint8_t *source, const uint32_t width, const uint32_t height, uint32_t *target, uint32_t *buffer=nullptr, const uint8_t referenceValue=0u, const uint32_t sourcePaddingElements=0u, const uint32_t targetPaddingElements=0u)
Computes a per pixel Chessboard distance to the nearest pixel with a reference value using 3-by-3 nei...
Definition: segmentation/MaskAnalyzer.h:1085
static void determineDistancesToBorder8Bit(uint8_t *mask, const unsigned int width, const unsigned int height, unsigned int maskPaddingElements, const unsigned int iterations, const bool assignFinal, const PixelBoundingBox &boundingBox, Worker *worker=nullptr)
Determines the distance to the mask border in an 8 bit mask frame.
static bool computeDistanceTransform8Bit(const uint8_t *source, const uint32_t width, const uint32_t height, TDistanceType *target, uint32_t *buffer, const TDistanceType distanceVerticalHorizontal, const TDistanceType distanceDiagonal, const uint8_t referenceValue=0u, const uint32_t sourcePaddingElements=0u, const uint32_t targetPaddingElements=0u)
Computes a per pixel distance to the nearest pixel with a reference value using 3-by-3 neighborhood.
Definition: segmentation/MaskAnalyzer.h:1101
static bool hasMaskNeighbor9(const T *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition &position, const T testValue)
Determines whether at least one neighbor pixel in the 8-neighborhood (+ center pixel) is or is not a ...
Definition: segmentation/MaskAnalyzer.h:989
static constexpr uint32_t distanceDiagonalL1
L1 distance for diagonal steps.
Definition: segmentation/MaskAnalyzer.h:157
std::vector< IndexPairs32 > IndexPairGroups
Definition of a vector holding index pair vectors.
Definition: segmentation/MaskAnalyzer.h:147
static void determineDistancesToBorder8BitSubset(uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const uint8_t searchValue, const uint8_t resultValue, const unsigned int firstColumn, const unsigned int numberColumns, const unsigned int firstRow, const unsigned int numberRows)
Determines the distance to the mask border in an 8 bit mask frame.
std::set< PixelPosition > PixelPositionSet
Definition of a set holding pixel positions.
Definition: segmentation/MaskAnalyzer.h:142
static bool hasMaskNeighbor5Center(const T *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition &position, const T testValue)
Determines whether at least one neighbor pixel in the 4-neighborhood (+ center pixel) is a mask pixel...
Definition: segmentation/MaskAnalyzer.h:890
static bool hasMaskNeighbor9Center(const T *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition &position, const T testValue)
Determines whether at least one neighbor pixel in the 8-neighborhood (+ center pixel) is a mask pixel...
Definition: segmentation/MaskAnalyzer.h:1059
static void findBorderPixels4(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions &borderPixels, const PixelBoundingBox &boundingBox=PixelBoundingBox(), Worker *worker=nullptr, const uint8_t nonMaskValue=255u)
Determines all border pixels in an 8 bit mask frame for a 4-neighborhood.
ProcessDirection
Definition of process directions.
Definition: segmentation/MaskAnalyzer.h:120
@ PD_SE
South east.
Definition: segmentation/MaskAnalyzer.h:132
@ PD_NW
North west.
Definition: segmentation/MaskAnalyzer.h:124
@ PD_W
West.
Definition: segmentation/MaskAnalyzer.h:126
@ PD_N
North.
Definition: segmentation/MaskAnalyzer.h:122
@ PD_E
East.
Definition: segmentation/MaskAnalyzer.h:134
@ PD_S
South.
Definition: segmentation/MaskAnalyzer.h:130
@ PD_SW
South west.
Definition: segmentation/MaskAnalyzer.h:128
static bool hasMaskNeighbor4(const T *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition &position, const T testValue)
Determines whether at least one neighbor pixel in the 4-neighborhood is or is not a mask pixel.
Definition: segmentation/MaskAnalyzer.h:790
static PixelBoundingBoxes detectBoundingBoxes(const uint8_t *const mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const uint8_t maskValue, const bool useNeighborhood4=true)
Determines the axis-aligned bounding boxes of all isolated mask islands in a binary (but 8-bit) mask ...
static constexpr float distanceVerticalHorizontalL2
L2 distance for vertical and horizontal steps.
Definition: segmentation/MaskAnalyzer.h:160
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
This class implements an object able to allocate memory.
Definition: base/Memory.h:22
void * data()
Returns the pointer to the writable memory which is allocated by this object.
Definition: base/Memory.h:303
static constexpr T maxValue()
Returns the max scalar value.
Definition: Numeric.h:3244
This class implements a worker able to distribute function calls over different threads.
Definition: Worker.h:33
std::vector< PixelPosition > PixelPositions
Definition of a vector holding pixel positions (with positive coordinate values).
Definition: PixelPosition.h:48
PixelPositionT< unsigned int > PixelPosition
Definition of the default PixelPosition object with a data type allowing only positive coordinate val...
Definition: PixelPosition.h:27
std::vector< PixelBoundingBox > PixelBoundingBoxes
Definition of a vector holding bounding box objects with only positive coordinate values.
Definition: PixelBoundingBox.h:42
PixelBoundingBoxT< unsigned int > PixelBoundingBox
Definition of the default PixelBoundingBox object with data type allowing only positive coordinate va...
Definition: PixelBoundingBox.h:21
std::vector< PixelContour > PixelContours
Definition of a vector holding pixel contours (with positive coordinate values).
Definition: PixelContour.h:50
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15