Ocean
Loading...
Searching...
No Matches
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
19
21
22namespace Ocean
23{
24
25namespace CV
26{
27
28namespace Segmentation
29{
30
31/**
32 * This class implements basic seed-based segmentation functions.
33 * @ingroup cvsegmentation
34 */
35class 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
230inline 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
242template <typename T, unsigned int tChannels>
243unsigned 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
332template <typename T, unsigned int tChannels>
333unsigned 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
489template <unsigned int tChannels>
490unsigned 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
581template <typename T, unsigned int tChannels>
582inline 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
604template <unsigned int tChannels>
605inline 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:1808
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:4138
const FrameType & frameType() const
Returns the frame type of this frame.
Definition Frame.h:3855
T * data(const unsigned int planeIndex=0u)
Returns a pointer to the pixel data of a specific plane.
Definition Frame.h:4239
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:1817
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:4122
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:28
The namespace covering the entire Ocean framework.
Definition Accessor.h:15