Ocean
SeedSegmentation.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_SEED_SEGMENTATION_H
9 #define META_OCEAN_CV_SEGMENTATION_SEED_SEGMENTATION_H
10 
12 
13 #include "ocean/base/DataType.h"
14 #include "ocean/base/Frame.h"
15 #include "ocean/base/Worker.h"
16 
18 #include "ocean/cv/PixelPosition.h"
19 
21 
22 namespace Ocean
23 {
24 
25 namespace CV
26 {
27 
28 namespace Segmentation
29 {
30 
31 /**
32  * This class implements basic seed-based segmentation functions.
33  * @ingroup cvsegmentation
34  */
35 class OCEAN_CV_SEGMENTATION_EXPORT SeedSegmentation
36 {
37  public:
38 
39  /**
40  * The following comfort class provides comfortable functions simplifying prototyping applications but also increasing binary size of the resulting applications.
41  * Best practice is to avoid using these functions if binary size matters,<br>
42  * as for every comfort function a corresponding function exists with specialized functionality not increasing binary size significantly.<br>
43  */
44  class OCEAN_CV_SEGMENTATION_EXPORT Comfort
45  {
46  public:
47 
48  /**
49  * Determines the seed segmentation in a frame.<br>
50  * Neighboring area costs must not exceed the local threshold.<br>
51  * Optional a global threshold can be defined ensuring that the cost between seed location and any candidate location does not exceed a (second) threshold.<br>
52  * The thresholds will be applied to each color channel separately.
53  * @param frame The frame holding the frame data in which the seed segmentation is determined, must be valid
54  * @param mask Resulting 8 bit binary mask defining the segmentation, a value of 0x00 defines a mask pixel, 0xFF defines a non-mask pixel, see setMaskFrameType
55  * @param seed The seed position in the frame, with range ([0, width)x[0, height))
56  * @param localThreshold The local threshold for neighboring pixels, with range [0, infinity)
57  * @param globalThreshold Optional global threshold for the seed pixel and any candidate pixel, with range [0, infinity), 0 to disable the global threshold
58  * @param boundingBox Optional resulting bounding box covering the entire mask area
59  * @param setMaskFrameType True, to change/modify the mask frame type internally if necessary
60  * @return Number of selected mask pixels defining the segmentation, with range [0, frame.pixels()]
61  */
62  static unsigned int seedSegmentation(const Frame& frame, Frame& mask, const PixelPosition& seed, const uint8_t localThreshold, const uint8_t globalThreshold = 0, PixelBoundingBox* boundingBox = nullptr, const bool setMaskFrameType = false);
63 
64  /**
65  * Determines the seed segmentation in a frame within several iterations.<br>
66  * Neighboring area costs must not exceed the local threshold.<br>
67  * A global threshold ensures that the cost between seed location and any candidate location does not exceed a (second) threshold.<br>
68  * In addition, the global threshold will be increased within a specified value range resulting in different corresponding masks.<br>
69  * The mask determination stops in the moment the number of mask pixels (the size of the mask) exceed the previous number of mask pixel by a specified factor.<br>
70  * The thresholds will be applied to each color channel separately.
71  * @param frame The frame holding the frame data in which the seed segmentation is determined, must be valid
72  * @param mask Resulting 8 bit binary mask defining the segmentation, a value of 0x00 defines a mask pixel, 0xFF defines a non-mask pixel, see setMaskFrameType
73  * @param seed The seed position in the frame, with range ([0, width)x[0, height))
74  * @param localThreshold The local threshold for neighboring pixels, with range [0, infinity)
75  * @param minimalGlobalThreshold The initial global threshold for the seed pixel and any candidate pixel, with range [0, infinity)
76  * @param maximalGlobalThreshold The maximal global threshold for the seed pixel and any candidate pixel, with range [minimalGlobalThreshold, infinity)
77  * @param maximalIncreaseFactor The maximal increase factor between the size of two successive mask so that the iterative process goes on
78  * @param boundingBox Optional resulting bounding box covering the entire mask area
79  * @param setMaskFrameType True, to change/modify the mask frame type internally if necessary
80  * @param worker Optional worker object to distribute the computation
81  * @return Number of selected mask pixels defining the segmentation, with range [0, width * height]
82  */
83  static unsigned int iterativeSeedSegmentation(const Frame& frame, Frame& mask, const PixelPosition& seed, const unsigned char localThreshold, const unsigned char minimalGlobalThreshold, const unsigned char maximalGlobalThreshold, const unsigned int maximalIncreaseFactor, PixelBoundingBox* boundingBox = nullptr, const bool setMaskFrameType = false, Worker* worker = nullptr);
84  };
85 
86  protected:
87 
88  /**
89  * This class extends the pixel position by another parameter holding the pixel index of the reference pixel.
90  * The index of the reference pixel is defined by the absolute pixel position of the reference pixel in a frame with a row aligned buffer.
91  */
93  {
94  public:
95 
96  /**
97  * Creates a new pixel candidate object.
98  * @param x Horizontal position
99  * @param y Vertical position
100  * @param reference Index of the reference pixel
101  */
102  inline PixelCandidate(const unsigned int x, const unsigned int y, const unsigned int reference);
103 
104  /**
105  * Returns the absolute pixel position of the reference pixel.
106  * @return Reference pixel
107  */
108  inline unsigned int reference() const;
109 
110  private:
111 
112  /// Holds the position of the reference pixel.
113  unsigned int reference_;
114  };
115 
116  /**
117  * Definition of a vector holding pixel candidate objects (will be used as stack).
118  */
119  typedef std::vector<PixelCandidate> PixelCandidates;
120 
121  /// Mask value for unvisited mask pixels.
122  static constexpr uint8_t unvisitedMaskValue_ = 0xFFu;
123 
124  /// Mask value for visited mask pixels.
125  static constexpr uint8_t visitedMaskValue_ = 0x00u;
126 
127  public:
128 
129  /**
130  * Determines the seed segmentation in a frame.<br>
131  * Neighboring area costs must not exceed the local threshold.<br>
132  * Optional a global threshold can be defined ensuring that the cost between seed location and any candidate location does not exceed a (second) threshold.<br>
133  * The thresholds will be applied to each color channel separately.
134  * @param frame The frame holding the frame data in which the seed segmentation is determined, must be valid
135  * @param width The width of the frame in pixel, with range [1, infinity)
136  * @param height The height of the frame in pixel, with range [1, infinity)
137  * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
138  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
139  * @param seed The seed position in the frame, with range ([0, width)x[0, height))
140  * @param localThreshold The local threshold for neighboring pixels, with range [0, infinity)
141  * @param globalThreshold Optional global threshold for the seed pixel and any candidate pixel, with range [0, infinity), 0 to disable the global threshold
142  * @param mask Resulting 8 bit binary mask defining the segmentation, a value of 0x00 defines a mask pixel, 0xFF defines a non-mask pixel
143  * @param boundingBox Optional resulting bounding box covering the entire mask area
144  * @return Number of selected mask pixels defining the segmentation, with range [0, width * height]
145  * @tparam T Data type of each channel pixel value
146  * @tparam tChannels The number of data channels of the given frame, with range [1, infinity)
147  */
148  template <typename T, unsigned int tChannels>
149  static unsigned int seedSegmentation(const T* frame, uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const PixelPosition& seed, const T localThreshold, const T globalThreshold = T(0), PixelBoundingBox* boundingBox = nullptr);
150 
151  /**
152  * Determines the seed segmentation in a frame within several iterations.<br>
153  * Neighboring area costs must not exceed the local threshold.<br>
154  * A global threshold ensures that the cost between seed location and any candidate location does not exceed a (second) threshold.<br>
155  * In addition, the global threshold will be increased within a specified value range resulting in different corresponding masks.<br>
156  * The mask determination stops in the moment the number of mask pixels (the size of the mask) exceed the previous number of mask pixel by a specified factor.<br>
157  * The thresholds will be applied to each color channel separately.
158  * @param frame The frame holding the frame data in which the seed segmentation is determined, must be valid
159  * @param mask Resulting 8 bit binary mask defining the segmentation, a value of 0x00 defines a mask pixel, 0xFF defines a non-mask pixel
160  * @param width The width of the frame in pixel, with range [1, infinity)
161  * @param height The height of the frame in pixel, with range [1, infinity)
162  * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
163  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
164  * @param seed The seed position in the frame, with range ([0, width)x[0, height))
165  * @param localThreshold The local threshold for neighboring pixels, with range [0, infinity)
166  * @param minimalGlobalThreshold The initial global threshold for the seed pixel and any candidate pixel, with range [0, infinity)
167  * @param maximalGlobalThreshold The maximal global threshold for the seed pixel and any candidate pixel, with range [minimalGlobalThreshold, infinity)
168  * @param maximalIncreaseFactor The maximal increase factor between the size of two successive mask so that the iterative process goes on
169  * @param boundingBox Optional resulting bounding box covering the entire mask area
170  * @param worker Optional worker object to distribute the computation
171  * @return Number of selected mask pixels defining the segmentation, with range [0, width * height]
172  * @tparam T Data type of each channel pixel value
173  * @tparam tChannels The number of data channels of the given frame, with range [1, infinity)
174  */
175  template <typename T, unsigned int tChannels>
176  static unsigned int iterativeSeedSegmentation(const T* frame, uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const PixelPosition& seed, const T localThreshold, const T minimalGlobalThreshold, const T maximalGlobalThreshold, const unsigned int maximalIncreaseFactor, PixelBoundingBox* boundingBox = nullptr, Worker* worker = nullptr);
177 
178  /**
179  * Determines the seed segmentation in a frame while using a mean-area around each pixel to increase the robustness.<br>
180  * Neighboring area costs must not exceed the local threshold.<br>
181  * The cost between the seed area and any other area must not exceed the global threshold.<br>
182  * The thresholds will be applied to each color channel separately.
183  * @param borderedIntegral Bordered integral image of the frame to find an area segmentation for, with 'tChannels' channels, must be valid
184  * @param width The width of the original frame (not the integral frame) in pixel, with range [1, infinity)
185  * @param height The height of the original frame (not the integral frame) in pixel, with range [1, infinity)
186  * @param integralBorder Border size of the integral frame in pixel, while the size of the mean area will be (integralBorder * 2 + 1), with range [1, infinity)
187  * @param areaSize The size of the surrounding area around each pixel, with range [1, integralBorder * 2 + 1], must be odd
188  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
189  * @param seed The seed Position in the original frame, with range ([0, width), [0, height))
190  * @param localThreshold The local threshold for neighboring pixels, with range [0, infinity)
191  * @param globalThreshold Optional global threshold for the seed pixel and any candidate pixel, with range [0, infinity), 0 to disable the global threshold
192  * @param mask Resulting 8 bit binary mask defining the segmentation, a value of 0x00 defines a mask pixel, 0xFF defines a non-mask pixel
193  * @param boundingBox Optional resulting bounding box covering the entire mask area
194  * @return Number of selected mask pixels defining the segmentation
195  * @tparam tChannels The number of data channel the information has, with range [1, infinity)
196  */
197  template <unsigned int tChannels>
198  static unsigned int seedSegmentationArea8BitPerChannel(const uint32_t* borderedIntegral, const unsigned int width, const unsigned int height, const unsigned int integralBorder, const unsigned int areaSize, const unsigned int maskPaddingElements, const PixelPosition& seed, const unsigned char localThreshold, const unsigned char globalThreshold, uint8_t* mask, PixelBoundingBox* boundingBox = nullptr);
199 
200  private:
201 
202  /**
203  * Tests whether all channel-wise SSD values between two pixels are below a given threshold.
204  * Each channel is tested separately, this function fails if one channel exceeds the threshold.
205  * @param image0 Position of the first pixel
206  * @param image1 Position of the second pixel
207  * @param sqrThreshold The maximal value a channel-wise SSD value can have, with range [0, 255 * 255)
208  * @return True, if so
209  * @tparam T The data type of each pixel channel value
210  * @tparam tChannels The number of data channel the information has, with range [1, infinity)
211  */
212  template <typename T, unsigned int tChannels>
213  static inline bool ssdBelowThreshold(const T* image0, const T* image1, const typename SquareValueTyper<T>::Type sqrThreshold);
214 
215  /**
216  * Tests whether the SSD between two areas is below a given threshold.
217  * Each channel is tested separately, this function fails if one channel exceeds the threshold.
218  * @param borderedIntegral0 Top left position in the bordered integral image for the first area
219  * @param borderedIntegral1 Top left position in the bordered integral image for the second area
220  * @param integralWidth Width of the integral frame (including the two borders and one extra pixel for the zero-column) in pixel with, range [1, infinity)
221  * @param size The size of the area in the integral image, with range [1, integralWidth)
222  * @param sqrThreshold The maximal value a channel-wise SSD value can have, with range [0, 255 * 255)
223  * @return True, if succeeded
224  * @tparam tChannels The number of data channel the information has, with range [1, infinity)
225  */
226  template <unsigned int tChannels>
227  static inline bool ssdBelowThreshold8BitPerChannel(const unsigned int* borderedIntegral0, const unsigned int* borderedIntegral1, const unsigned int integralWidth, const unsigned int size, const unsigned int sqrThreshold);
228 };
229 
230 inline SeedSegmentation::PixelCandidate::PixelCandidate(const unsigned int x, const unsigned int y, const unsigned int reference) :
231  PixelPosition(x, y),
232  reference_(reference)
233 {
234  // nothing to do here
235 }
236 
238 {
239  return reference_;
240 }
241 
242 template <typename T, unsigned int tChannels>
243 unsigned int SeedSegmentation::seedSegmentation(const T* frame, uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const PixelPosition& seed, const T localThreshold, const T globalThreshold, PixelBoundingBox* boundingBox)
244 {
245  static_assert(tChannels != 0u, "Invalid channel number!");
246 
247  ocean_assert(frame != nullptr && mask != nullptr);
248 
249  if (seed.x() >= width || seed.y() >= height)
250  {
251  return 0u;
252  }
253 
254  const unsigned int frameStrideElements = width * tChannels + framePaddingElements;
255  const unsigned int maskStrideElements = width + maskPaddingElements;
256 
257  // setting all mask values to unvisited
258 
260 
261  // we use a vector and not a std::stack as the stack implementation is significant slower
262  PixelCandidates stack;
263  stack.reserve(width * height / 32u);
264 
265  unsigned int counter = 1u;
266 
267  if (boundingBox)
268  {
269  *boundingBox = PixelBoundingBox(seed);
270  }
271 
272  const unsigned int frameSeedOffset = seed.y() * frameStrideElements + seed.x() * tChannels;
273 
274  stack.emplace_back(seed.x(), seed.y(), frameSeedOffset);
275 
276  typedef typename SquareValueTyper<T>::Type SqrType;
277 
278  const SqrType sqrLocalThreshold = localThreshold * localThreshold;
279  const SqrType sqrGlobalThreshold = globalThreshold * globalThreshold;
280 
281  while (!stack.empty())
282  {
283  const PixelCandidate pixel = stack.back();
284  stack.pop_back();
285 
286  const unsigned int maskTestOffset = pixel.y() * maskStrideElements + pixel.x();
287  const unsigned int frameTestOffset = pixel.y() * frameStrideElements + pixel.x() * tChannels;
288 
289  // test whether the new pixel can be accepted
290  if (mask[maskTestOffset] == unvisitedMaskValue_ // checking unvisited state again (as we may have visited this state already from a different path
291  && ssdBelowThreshold<T, tChannels>(frame + frameTestOffset, frame + pixel.reference(), sqrLocalThreshold)
292  && (sqrGlobalThreshold == SqrType(0) || ssdBelowThreshold<T, tChannels>(frame + frameTestOffset, frame + frameSeedOffset, sqrGlobalThreshold)))
293  {
294  if (boundingBox)
295  {
296  *boundingBox += pixel;
297  }
298 
299  mask[maskTestOffset] = visitedMaskValue_;
300 
301  // top
302  if (pixel.y() > 0u && mask[maskTestOffset - maskStrideElements] != visitedMaskValue_)
303  {
304  stack.emplace_back(pixel.x(), pixel.y() - 1u, frameTestOffset);
305  }
306 
307  // bottom
308  if (pixel.y() + 1u < height && mask[maskTestOffset + maskStrideElements] != visitedMaskValue_)
309  {
310  stack.emplace_back(pixel.x(), pixel.y() + 1u, frameTestOffset);
311  }
312 
313  // left
314  if (pixel.x() > 0u && mask[maskTestOffset - 1u] != visitedMaskValue_)
315  {
316  stack.emplace_back(pixel.x() - 1u, pixel.y(), frameTestOffset);
317  }
318 
319  // right
320  if (pixel.x() + 1u < width && mask[maskTestOffset + 1u] != visitedMaskValue_)
321  {
322  stack.emplace_back(pixel.x() + 1u, pixel.y(), frameTestOffset);
323  }
324 
325  ++counter;
326  }
327  }
328 
329  return counter;
330 }
331 
332 template <typename T, unsigned int tChannels>
333 unsigned int SeedSegmentation::iterativeSeedSegmentation(const T* frame, uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const PixelPosition& seed, const T localThreshold, const T minimalGlobalThreshold, const T maximalGlobalThreshold, const unsigned int maximalIncreaseFactor, PixelBoundingBox* boundingBox, Worker* worker)
334 {
335  static_assert(tChannels != 0u, "Invalid channel number!");
336 
337  ocean_assert(frame != nullptr && mask != nullptr);
338  ocean_assert(width >= 1u && height >= 1u);
339  ocean_assert(seed.x() < width && seed.y() < height);
340 
341  ocean_assert(minimalGlobalThreshold != T(0));
342  ocean_assert(minimalGlobalThreshold < maximalGlobalThreshold);
343 
344  if (seed.x() >= width || seed.y() >= height || minimalGlobalThreshold > maximalGlobalThreshold)
345  {
346  return 0u;
347  }
348 
349  // we use a vector and not a std::stack as the stack implementation is significant slower
350  PixelCandidates stack;
351  stack.reserve((width * height) / 16u);
352 
353  PixelBoundingBox tmpBoundingBox;
354  if (boundingBox == nullptr)
355  {
356  boundingBox = &tmpBoundingBox;
357  }
358 
359  const typename SquareValueTyper<T>::Type sqrLocalThreshold = localThreshold * localThreshold;
360 
361  // first iteration with local threshold and global threshold
362 
363  unsigned int maskPixelCounter = seedSegmentation<T, tChannels>(frame, mask, width, height, framePaddingElements, maskPaddingElements, seed, localThreshold, maximalGlobalThreshold, boundingBox);
364 
365  // in the following iterations we increase the global threshold and stop if the number of mask pixels increase too much between two iterations
366 
367  Frame secondMaskFrame(FrameType(width, height, FrameType::FORMAT_Y8, FrameType::ORIGIN_UPPER_LEFT));
368  uint8_t* const secondMask = secondMaskFrame.data<uint8_t>();
369 
370  const unsigned int secondMaskPaddingElements = secondMaskFrame.paddingElements();
371 
372  Frame maskFrame(secondMaskFrame.frameType(), mask, Frame::CM_USE_KEEP_LAYOUT, maskPaddingElements);
373 
374  const unsigned int frameStrideElements = width * tChannels + framePaddingElements;
375  const unsigned int secondMaskStrideElements = secondMaskFrame.strideElements();
376 
377  const unsigned int frameSeedOffset = seed.y() * frameStrideElements + seed.x() * tChannels;
378 
379  PixelPositions borderPixels8;
380  unsigned int maximalIterationMaskPixelCounter = (unsigned int)(-1);
381 
382  for (T t = minimalGlobalThreshold + 1u; t <= maximalGlobalThreshold; ++t)
383  {
384  const typename SquareValueTyper<T>::Type sqrIterationGlobalThreshold = t * t;
385 
386  secondMaskFrame.copy(0, 0, maskFrame);
387 
388  PixelBoundingBox iterationBoundingBox(*boundingBox);
389  unsigned int iterationMaskPixelCounter = maskPixelCounter;
390 
391  borderPixels8.clear();
392  MaskAnalyzer::findBorderPixels8(secondMask, width, height, secondMaskPaddingElements, borderPixels8, iterationBoundingBox, worker);
393 
394  for (PixelPositions::const_iterator i = borderPixels8.begin(); i != borderPixels8.end(); ++i)
395  {
396  const unsigned int frameBorderOffset = i->y() * frameStrideElements + i->x() * tChannels;
397  const unsigned int secondMaskBorderOffset = i->y() * secondMaskStrideElements + i->x();
398 
399  ocean_assert(secondMask[secondMaskBorderOffset] == visitedMaskValue_);
400 
401  // top
402  if (i->y() > 0u && secondMask[secondMaskBorderOffset - secondMaskStrideElements] != visitedMaskValue_)
403  {
404  stack.emplace_back(i->x(), i->y() - 1u, frameBorderOffset);
405  }
406 
407  // bottom
408  if (i->y() + 1u < height && secondMask[secondMaskBorderOffset + secondMaskStrideElements] != visitedMaskValue_)
409  {
410  stack.emplace_back(i->x(), i->y() + 1u, frameBorderOffset);
411  }
412 
413  // left
414  if (i->x() > 0u && secondMask[secondMaskBorderOffset - 1u] != visitedMaskValue_)
415  {
416  stack.emplace_back(i->x() - 1u, i->y(), frameBorderOffset);
417  }
418 
419  // right
420  if (i->x() + 1u < width && secondMask[secondMaskBorderOffset + 1u] != visitedMaskValue_)
421  {
422  stack.emplace_back(i->x() + 1u, i->y(), frameBorderOffset);
423  }
424  }
425 
426  while (!stack.empty())
427  {
428  const PixelCandidate pixel = stack.back();
429  stack.pop_back();
430 
431  const unsigned int frameTestOffset = pixel.y() * frameStrideElements + pixel.x() * tChannels;
432  const unsigned int secondMaskTestOffset = pixel.y() * secondMaskStrideElements + pixel.x();
433 
434  // test whether the new pixel can be accepted
435  if (secondMask[secondMaskTestOffset] == unvisitedMaskValue_ // checking unvisited state again (as we may have visited this state already from a different path
436  && ssdBelowThreshold<T, tChannels>(frame + frameTestOffset, frame + pixel.reference(), sqrLocalThreshold)
437  && ssdBelowThreshold<T, tChannels>(frame + frameTestOffset, frame + frameSeedOffset, sqrIterationGlobalThreshold))
438  {
439  iterationBoundingBox += pixel;
440  secondMask[secondMaskTestOffset] = visitedMaskValue_;
441 
442  // top
443  if (pixel.y() > 0 && secondMask[secondMaskTestOffset - secondMaskStrideElements] != visitedMaskValue_)
444  {
445  stack.emplace_back(pixel.x(), pixel.y() - 1u, frameTestOffset);
446  }
447 
448  // bottom
449  if (pixel.y() + 1u < height && secondMask[secondMaskTestOffset + secondMaskStrideElements] != visitedMaskValue_)
450  {
451  stack.emplace_back(pixel.x(), pixel.y() + 1u, frameTestOffset);
452  }
453 
454  // left
455  if (pixel.x() > 0 && secondMask[secondMaskTestOffset - 1u] != visitedMaskValue_)
456  {
457  stack.emplace_back(pixel.x() - 1u, pixel.y(), frameTestOffset);
458  }
459 
460  // right
461  if (pixel.x() + 1u < width && secondMask[secondMaskTestOffset + 1u] != visitedMaskValue_)
462  {
463  stack.emplace_back(pixel.x() + 1u, pixel.y(), frameTestOffset);
464  }
465 
466  ++iterationMaskPixelCounter;
467  }
468  }
469 
470  if (iterationMaskPixelCounter <= maximalIterationMaskPixelCounter)
471  {
472  ocean_assert(iterationMaskPixelCounter >= maskPixelCounter);
473  maximalIterationMaskPixelCounter = max(iterationMaskPixelCounter + maximalIncreaseFactor * (iterationMaskPixelCounter - maskPixelCounter), iterationMaskPixelCounter * 105u / 100u);
474 
475  maskPixelCounter = iterationMaskPixelCounter;
476  *boundingBox = iterationBoundingBox;
477 
478  maskFrame.copy(0, 0, secondMaskFrame);
479  }
480  else
481  {
482  break;
483  }
484  }
485 
486  return maskPixelCounter;
487 }
488 
489 template <unsigned int tChannels>
490 unsigned int SeedSegmentation::seedSegmentationArea8BitPerChannel(const uint32_t* borderedIntegral, const unsigned int width, const unsigned int height, const unsigned int integralBorder, const unsigned int areaSize, const unsigned int maskPaddingElements, const PixelPosition& seed, const unsigned char localThreshold, const unsigned char globalThreshold, uint8_t* mask, PixelBoundingBox* boundingBox)
491 {
492  static_assert(tChannels != 0u, "Invalid channel number!");
493 
494  ocean_assert(borderedIntegral != nullptr && mask != nullptr);
495  ocean_assert(integralBorder >= areaSize / 2u);
496  ocean_assert(areaSize % 2u == 1u);
497 
498  if (seed.x() >= width || seed.y() >= height)
499  {
500  return 0u;
501  }
502 
503  const unsigned int maskStrideElements = width + maskPaddingElements;
504 
505  const unsigned int areaHalf = areaSize;
506  const unsigned int integralWidth = width + integralBorder * 2u + 1u;
507 
508  // setting all mask values to unvisited
509 
511 
512  // we use a vector and not a std::stack as the stack implementation is significant slower
513  PixelCandidates stack;
514  stack.reserve((width * height) / 16u);
515 
516  unsigned int counter = 1u;
517 
518  PixelBoundingBox tmpBoundingBox;
519  if (boundingBox == nullptr)
520  {
521  boundingBox = &tmpBoundingBox;
522  }
523 
524  *boundingBox = PixelBoundingBox(seed);
525 
526  const unsigned int integralIndex = ((seed.y() + integralBorder - areaHalf) * integralWidth + seed.x() + integralBorder - areaHalf) * tChannels;
527 
528  stack.emplace_back(seed.x(), seed.y(), integralIndex);
529 
530  const unsigned int sqrLocalThreshold = localThreshold * localThreshold;
531  const unsigned int sqrGlobalThreshold = globalThreshold * globalThreshold;
532 
533  while (!stack.empty())
534  {
535  const PixelCandidate pixel = stack.back();
536  stack.pop_back();
537 
538  const unsigned int maskTestOffset = pixel.y() * maskStrideElements + pixel.x();
539  const unsigned int testIntegralIndex = ((pixel.y() + integralBorder - areaHalf) * integralWidth + pixel.x() + integralBorder - areaHalf) * tChannels;
540 
541  // test whether the new pixel can be accepted
542  if (mask[maskTestOffset] == unvisitedMaskValue_ // checking unvisited state again (as we may have visited this state already from a different path
543  && ssdBelowThreshold8BitPerChannel<tChannels>(borderedIntegral + testIntegralIndex, borderedIntegral + pixel.reference(), integralWidth, areaSize, sqrLocalThreshold)
544  && (sqrGlobalThreshold == 0u || ssdBelowThreshold8BitPerChannel<tChannels>(borderedIntegral + testIntegralIndex, borderedIntegral + integralIndex, integralWidth, areaSize, sqrGlobalThreshold)))
545  {
546  *boundingBox += pixel;
547 
548  mask[maskTestOffset] = visitedMaskValue_;
549 
550  // top
551  if (pixel.y() > 0u && mask[maskTestOffset - width] != unvisitedMaskValue_)
552  {
553  stack.emplace_back(pixel.x(), pixel.y() - 1u, testIntegralIndex);
554  }
555 
556  // bottom
557  if (pixel.y() + 1u < height && mask[maskTestOffset + width] != unvisitedMaskValue_)
558  {
559  stack.emplace_back(pixel.x(), pixel.y() + 1u, testIntegralIndex);
560  }
561 
562  // left
563  if (pixel.x() > 0u && mask[maskTestOffset - 1u] != unvisitedMaskValue_)
564  {
565  stack.emplace_back(pixel.x() - 1u, pixel.y(), testIntegralIndex);
566  }
567 
568  // right
569  if (pixel.x() + 1u < width && mask[maskTestOffset + 1u] != unvisitedMaskValue_)
570  {
571  stack.emplace_back(pixel.x() + 1u, pixel.y(), testIntegralIndex);
572  }
573 
574  ++counter;
575  }
576  }
577 
578  return counter;
579 }
580 
581 template <typename T, unsigned int tChannels>
582 inline bool SeedSegmentation::ssdBelowThreshold(const T* image0, const T* image1, const typename SquareValueTyper<T>::Type sqrThreshold)
583 {
584  static_assert(tChannels != 0u, "Invalid channel number!");
585 
586  ocean_assert(image0 && image1);
587  ocean_assert(sqrThreshold <= 255u * 255u);
588 
589  typename DifferenceValueTyper<T>::Type value;
590 
591  for (unsigned int n = 0u; n < tChannels; ++n)
592  {
593  value = image0[n] - image1[n];
594 
595  if ((typename SquareValueTyper<T>::Type)(value * value) > sqrThreshold)
596  {
597  return false;
598  }
599  }
600 
601  return true;
602 }
603 
604 template <unsigned int tChannels>
605 inline bool SeedSegmentation::ssdBelowThreshold8BitPerChannel(const unsigned int* borderedIntegral0, const unsigned int* borderedIntegral1, const unsigned int integralWidth, const unsigned int size, const unsigned int sqrThreshold)
606 {
607  static_assert(tChannels != 0u, "Invalid channel number!");
608 
609  ocean_assert(borderedIntegral0 && borderedIntegral1);
610  ocean_assert(integralWidth >= 1u);
611  ocean_assert(size < integralWidth);
612  ocean_assert(sqrThreshold <= 255u * 255u);
613 
614  const unsigned int sqrThresholdArea = sqrThreshold * size * size;
615 
616  for (unsigned int n = 0u; n < tChannels; ++n)
617  {
618  const int value0 = *(borderedIntegral0 + n) - *(borderedIntegral0 + tChannels * size + n) - *(borderedIntegral0 + tChannels * size * integralWidth + n) + *(borderedIntegral0 + tChannels * (size * integralWidth + size) + n);
619  const int value1 = *(borderedIntegral1 + n) - *(borderedIntegral1 + tChannels * size + n) - *(borderedIntegral1 + tChannels * size * integralWidth + n) + *(borderedIntegral1 + tChannels * (size * integralWidth + size) + n);
620 
621  const int value = value0 - value1;
622 
623  if ((unsigned int)(value * value) > sqrThresholdArea)
624  {
625  return false;
626  }
627  }
628 
629  return true;
630 }
631 
632 }
633 
634 }
635 
636 }
637 
638 #endif // META_OCEAN_CV_SEGMENTATION_SEED_SEGMENTATION_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
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.
The following comfort class provides comfortable functions simplifying prototyping applications but a...
Definition: SeedSegmentation.h:45
static unsigned int seedSegmentation(const Frame &frame, Frame &mask, const PixelPosition &seed, const uint8_t localThreshold, const uint8_t globalThreshold=0, PixelBoundingBox *boundingBox=nullptr, const bool setMaskFrameType=false)
Determines the seed segmentation in a frame.
static unsigned int iterativeSeedSegmentation(const Frame &frame, Frame &mask, const PixelPosition &seed, const unsigned char localThreshold, const unsigned char minimalGlobalThreshold, const unsigned char maximalGlobalThreshold, const unsigned int maximalIncreaseFactor, PixelBoundingBox *boundingBox=nullptr, const bool setMaskFrameType=false, Worker *worker=nullptr)
Determines the seed segmentation in a frame within several iterations.
This class extends the pixel position by another parameter holding the pixel index of the reference p...
Definition: SeedSegmentation.h:93
unsigned int reference() const
Returns the absolute pixel position of the reference pixel.
Definition: SeedSegmentation.h:237
unsigned int reference_
Holds the position of the reference pixel.
Definition: SeedSegmentation.h:113
PixelCandidate(const unsigned int x, const unsigned int y, const unsigned int reference)
Creates a new pixel candidate object.
Definition: SeedSegmentation.h:230
This class implements basic seed-based segmentation functions.
Definition: SeedSegmentation.h:36
static unsigned int iterativeSeedSegmentation(const T *frame, uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const PixelPosition &seed, const T localThreshold, const T minimalGlobalThreshold, const T maximalGlobalThreshold, const unsigned int maximalIncreaseFactor, PixelBoundingBox *boundingBox=nullptr, Worker *worker=nullptr)
Determines the seed segmentation in a frame within several iterations.
Definition: SeedSegmentation.h:333
static unsigned int seedSegmentation(const T *frame, uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const PixelPosition &seed, const T localThreshold, const T globalThreshold=T(0), PixelBoundingBox *boundingBox=nullptr)
Determines the seed segmentation in a frame.
Definition: SeedSegmentation.h:243
static constexpr uint8_t unvisitedMaskValue_
Mask value for unvisited mask pixels.
Definition: SeedSegmentation.h:122
static bool ssdBelowThreshold8BitPerChannel(const unsigned int *borderedIntegral0, const unsigned int *borderedIntegral1, const unsigned int integralWidth, const unsigned int size, const unsigned int sqrThreshold)
Tests whether the SSD between two areas is below a given threshold.
Definition: SeedSegmentation.h:605
static constexpr uint8_t visitedMaskValue_
Mask value for visited mask pixels.
Definition: SeedSegmentation.h:125
static unsigned int seedSegmentationArea8BitPerChannel(const uint32_t *borderedIntegral, const unsigned int width, const unsigned int height, const unsigned int integralBorder, const unsigned int areaSize, const unsigned int maskPaddingElements, const PixelPosition &seed, const unsigned char localThreshold, const unsigned char globalThreshold, uint8_t *mask, PixelBoundingBox *boundingBox=nullptr)
Determines the seed segmentation in a frame while using a mean-area around each pixel to increase the...
Definition: SeedSegmentation.h:490
std::vector< PixelCandidate > PixelCandidates
Definition of a vector holding pixel candidate objects (will be used as stack).
Definition: SeedSegmentation.h:119
static bool ssdBelowThreshold(const T *image0, const T *image1, const typename SquareValueTyper< T >::Type sqrThreshold)
Tests whether all channel-wise SSD values between two pixels are below a given threshold.
Definition: SeedSegmentation.h:582
T Type
Definition of the data type for the signed difference value.
Definition: DataType.h:176
This class implements Ocean's image class.
Definition: Frame.h:1792
unsigned int strideElements(const unsigned int planeIndex=0u) const
Returns the number of elements within one row, including optional padding at the end of a row for a s...
Definition: Frame.h:4058
const FrameType & frameType() const
Returns the frame type of this frame.
Definition: Frame.h:3775
T * data(const unsigned int planeIndex=0u)
Returns a pointer to the pixel data of a specific plane.
Definition: Frame.h:4159
bool copy(const Frame &source, const bool copyTimestamp=true)
Deprecated.
@ CM_USE_KEEP_LAYOUT
The source memory is used only, no copy is created, the padding layout is preserved.
Definition: Frame.h:1801
bool setValue(const uint8_t value, const unsigned int planeIndex=0u, const bool skipPaddingData=true)
Sets the memory of the frame to a specified byte value (the memory of one plane).
unsigned int paddingElements(const unsigned int planeIndex=0u) const
Returns the optional number of padding elements at the end of each row for a specific plane.
Definition: Frame.h:4042
Definition of a frame type composed by the frame dimension, pixel format and pixel origin.
Definition: Frame.h:30
@ FORMAT_Y8
Pixel format for grayscale images with byte order Y and 8 bits per pixel.
Definition: Frame.h:594
@ ORIGIN_UPPER_LEFT
The first pixel lies in the upper left corner, the last pixel in the lower right corner.
Definition: Frame.h:1050
T Type
Definition of the data type for the square value.
Definition: DataType.h:132
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
PixelBoundingBoxT< unsigned int > PixelBoundingBox
Definition of the default PixelBoundingBox object with data type allowing only positive coordinate va...
Definition: PixelBoundingBox.h:21
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15