Ocean
Loading...
Searching...
No Matches
FrameFilterErosion.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_FRAME_FILTER_EROSION_H
9#define META_OCEAN_CV_FRAME_FILTER_EROSION_H
10
11#include "ocean/cv/CV.h"
15
16#include "ocean/base/Frame.h"
18#include "ocean/base/RandomI.h"
19#include "ocean/base/Worker.h"
20
21namespace Ocean
22{
23
24namespace CV
25{
26
27/**
28 * This class implements an erosion filter.
29 * @ingroup cv
30 */
31class OCEAN_CV_EXPORT FrameFilterErosion : public FrameFilterMorphology
32{
33 protected:
34
35 /**
36 * Definition of an unordered set holding pixel positions.
37 */
38 typedef std::unordered_set<CV::PixelPosition, CV::PixelPosition> PixelPositionSet;
39
40 public:
41
42 /**
43 * The following comfort class provides comfortable functions simplifying prototyping applications but also increasing binary size of the resulting applications.
44 * Best practice is to avoid using these functions if binary size matters,<br>
45 * as for every comfort function a corresponding function exists with specialized functionality not increasing binary size significantly.<br>
46 */
47 class OCEAN_CV_EXPORT Comfort
48 {
49 public:
50
51 /**
52 * Closes holes inside a frame using a shrinking/in-bleeding approach based on either a 4-neighborhood or an 8-neighborhood.
53 * The frame and mask will be changed during the filtering process.<br>
54 * Hole pixels are defined by a 0x00 mask value while non-hole pixels have 0xFF as musk value.
55 * Beware: Frame and mask must have the same frame dimension and pixel origin.
56 * @param frame The frame to be handled, must be valid
57 * @param mask The 8 bit mask defining the hole(s), must be valid
58 * @param erosionFilter Defines the shape of the neighborhood
59 * @param randomNoise Optional the maximal random noise (+/-) that will be added to each channel and pixel, with range [0, 255]
60 * @param randomSeed The random seed value to be used when generating random values, with range [0, infinity)
61 * @return True, if succeeded
62 */
63 static bool shrinkMask(Frame& frame, Frame& mask, const MorphologyFilter erosionFilter, const unsigned int randomNoise = 3u, const unsigned int randomSeed = RandomI::random32());
64
65 /**
66 * Closes a hole inside an 8 bit grayscale frame using a randomized erosion filter.
67 * The frame and mask buffer will be changed during the filtering process.<br>
68 * Hole pixels must have 0x00 as grayscale value, all other pixels must have 0xFF as value.<br>
69 * Beware: Both frame must have the same frame dimension and pixel origin.
70 * @param frame The frame to be handled, must be valid
71 * @param mask The 8 bit mask defining the hole(s), must be valid
72 * @param erosionFilter Defines the shape of the erosion filter mask
73 * @param randomNoise Optional the maximal random noise (+/-) that will be added to each channel and pixel, with range [0, 255]
74 * @param randomSeed The random seed value to be used when generating random values, with range [0, infinity)
75 * @return True, if succeeded
76 */
77 static bool shrinkMaskRandom(Frame& frame, Frame& mask, const MorphologyFilter erosionFilter, const unsigned int randomNoise = 3u, const unsigned int randomSeed = RandomI::random32());
78 };
79
80 /**
81 * Closes a hole inside a frame by using a shrinking/in-bleeding approach based on a 4-neighborhood.
82 * The frame and mask buffer will be changed during the filtering process.<br>
83 * Hole pixels are defined by a 0x00 mask value while non-hole pixels have 0xFF as musk value.
84 * @param frame The frame to be filtered, must be valid
85 * @param mask The 8 bit mask defining the hole, must be valid
86 * @param width The width of all frames in pixel, with range [2, infinity)
87 * @param height The height of all frames in pixel, with range [2, infinity)
88 * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
89 * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
90 * @param randomNoise Optional the maximal random noise (+/-) that will be added to each channel and pixel, with range [0, 255]
91 * @param randomSeed The random seed value to be used when generating random values, with range [0, infinity)
92 * @return True, if succeeded
93 * @tparam tChannels The number of frame channels with range [1, infinity)
94 * @tparam tUseRandomNoise True, to add/apply random noise to each channel and pixel; False, to create a deterministic result
95 */
96 template <unsigned int tChannels, bool tUseRandomNoise>
97 static bool shrinkMask8BitPerChannel4Neighbor(uint8_t* frame, uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const unsigned int randomNoise = 0u, const unsigned int randomSeed = RandomI::random32());
98
99 /**
100 * Closes a hole inside a frame by using a shrinking/in-bleeding approach based on an 8-neighborhood.
101 * The frame and mask buffer will be changed during the filtering process.<br>
102 * Hole pixels must have 0x00 as grayscale value, all other pixels must have 0xFF as value.<br>
103 * Beware: All three frame buffers must have the same size.
104 * @param frame The frame to be filtered, must be valid
105 * @param mask The 8 bit mask defining the hole, must be valid
106 * @param width The width of all frames in pixel, with range [1, infinity)
107 * @param height The height of all frames in pixel, with range [1, infinity)
108 * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
109 * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
110 * @param randomNoise Optional the maximal random noise (+/-) that will be added to each channel and pixel, with range [0, 255]
111 * @param randomSeed The random seed value to be used when generating random values, with range [0, infinity)
112 * @return True, if succeeded
113 * @tparam tChannels The number of frame channels with range [1, infinity)
114 * @tparam tUseRandomNoise Optional random noise for each channel and pixel, with range [0, 255], 0 to create a deterministic result
115 */
116 template <unsigned int tChannels, bool tUseRandomNoise>
117 static bool shrinkMask8BitPerChannel8Neighbor(uint8_t* frame, uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const unsigned int randomNoise = 0u, const unsigned int randomSeed = RandomI::random32());
118
119 /**
120 * Closes a hole inside a frame by using a randomized shrinking/in-bleeding approach based on an 8-neighborhood.
121 * The frame and mask buffer will be changed during the filtering process.<br>
122 * Hole pixels must have 0x00 as grayscale value, all other pixels must have 0xFF as value.
123 * @param frame The frame to be filtered, must be valid
124 * @param mask The 8 bit mask defining the hole, must be valid
125 * @param width The width of all frames in pixel, with range [2, infinity)
126 * @param height The height of all frames in pixel, with range [2, infinity)
127 * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
128 * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
129 * @param randomNoise Optional the maximal random noise (+/-) that will be added to each channel and pixel, with range [0, 255]
130 * @param randomSeed The random seed value to be used when generating random values, with range [0, infinity)
131 * @tparam tChannels The number of frame channels with range [1, infinity)
132 */
133 template <unsigned int tChannels>
134 static void shrinkMaskRandom8BitPerChannel8Neighbor(uint8_t* frame, uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const unsigned int randomNoise = 0u, const unsigned int randomSeed = RandomI::random32());
135
136 /**
137 * Applies several erosion filter iterations for an 8 bit mask image.
138 * The value of a mask pixel (to be eroded) can be defined, every other pixel value is interpreted as a non-mask pixels.
139 * @param mask The mask frame to be filtered, must be valid
140 * @param width The width of the mask frame in pixel, with range [4, infinity)
141 * @param height The height of the mask frame in pixel, with range [4, infinity)
142 * @param iterations Number of iterations to be applied, best performance when number of iterations is even, with range [1, infinity)
143 * @param maskValue The value of a mask pixel to be eroded, pixels with other values will be untouched, with range [0, 255]
144 * @param maskPaddingElements Optional number of padding elements at the end of each mask row, in elements, with range [0, infinity)
145 * @param worker Optional worker object to distribute the computation
146 * @tparam tErosionFilter The type of the erosion filter to be applied
147 * @see filter1Channel8Bit4Neighbor(), filter1Channel8Bit8Neighbor().
148 */
149 template <MorphologyFilter tErosionFilter>
150 static void filter1Channel8Bit(uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int iterations, const uint8_t maskValue = 0x00, const unsigned int maskPaddingElements = 0u, Worker* worker = nullptr);
151
152 /**
153 * Applies one erosion filter iteration in an 8 bit mask image using a 4-neighborhood.
154 * The value of a mask pixel (to be eroded) can be defined, every other pixel value is interpreted as a non-mask pixels.
155 * @param mask The mask frame to be filtered, must be valid
156 * @param target The target frame receiving the filter response, must be valid
157 * @param width The width of the mask frame in pixel, with range [2, infinity)
158 * @param height The height of the mask frame in pixel, with range [2, infinity)
159 * @param maskValue The value of a mask pixel to be eroded, pixels with other values will be untouched, with range [0, 255]
160 * @param maskPaddingElements Optional number of padding elements at the end of each mask row, in elements, with range [0, infinity)
161 * @param targetPaddingElements Optional number of padding elements at the end of each target row, in elements, with range [0, infinity)
162 * @param worker Optional worker object to distribute the computation
163 */
164 static inline void filter1Channel8Bit4Neighbor(const uint8_t* mask, uint8_t* target, const unsigned int width, const unsigned int height, const uint8_t maskValue = 0x00, const unsigned int maskPaddingElements = 0u, const unsigned int targetPaddingElements = 0u, Worker* worker = nullptr);
165
166 /**
167 * Applies one erosion filter iteration for an 8 bit mask image using a 8-neighborhood, a 3x3 square kernel.
168 * The value of a mask pixel (to be eroded) can be defined, every other pixel value is interpreted as a non-mask pixels.
169 * @param mask The mask frame to be filtered, must be valid
170 * @param target The target frame receiving the filter response, must be valid
171 * @param width The width of the mask frame in pixel, with range [2, infinity)
172 * @param height The height of the mask frame in pixel, with range [2, infinity)
173 * @param maskValue The value of a mask pixel to be eroded, pixels with other values will be untouched, with range [0, 255]
174 * @param maskPaddingElements Optional number of padding elements at the end of each mask row, in elements, with range [0, infinity)
175 * @param targetPaddingElements Optional number of padding elements at the end of each target row, in elements, with range [0, infinity)
176 * @param worker Optional worker object to distribute the computation
177 */
178 static inline void filter1Channel8Bit8Neighbor(const uint8_t* mask, uint8_t* target, const unsigned int width, const unsigned int height, const uint8_t maskValue = 0x00, const unsigned int maskPaddingElements = 0u, const unsigned int targetPaddingElements = 0u, Worker* worker = nullptr);
179
180 /**
181 * Applies one erosion filter iteration for an 8 bit mask image using a 24-neighborhood, a 5x5 square kernel.
182 * The value of a mask pixel (to be eroded) can be defined, every other pixel value is interpreted as a non-mask pixels.
183 * @param mask The mask frame to be filtered, must be valid
184 * @param target The target frame receiving the filter response, must be valid
185 * @param width The width of the mask frame in pixel, with range [4, infinity)
186 * @param height The height of the mask frame in pixel, with range [4, infinity)
187 * @param maskValue The value of a mask pixel to be eroded, pixels with other values will be untouched, with range [0, 255]
188 * @param maskPaddingElements Optional number of padding elements at the end of each mask row, in elements, with range [0, infinity)
189 * @param targetPaddingElements Optional number of padding elements at the end of each target row, in elements, with range [0, infinity)
190 * @param worker Optional worker object to distribute the computation
191 */
192 static inline void filter1Channel8Bit24Neighbor(const uint8_t* mask, uint8_t* target, const unsigned int width, const unsigned int height, const uint8_t maskValue = 0x00, const unsigned int maskPaddingElements = 0u, const unsigned int targetPaddingElements = 0u, Worker* worker = nullptr);
193
194 private:
195
196 /**
197 * Applies one erosion filter iteration in a subset of an 8 bit mask image using a 4-neighborhood.
198 * @param mask The mask frame to be filtered, must be valid
199 * @param target The target frame receiving the filter response, must be valid
200 * @param width The width of the mask frame in pixel, with range [2, infinity)
201 * @param height The height of the mask frame in pixel, with range [2, infinity)
202 * @param maskValue The value of a mask pixel to be eroded, pixels with other values will be untouched, with range [0, 255]
203 * @param maskPaddingElements Optional number of padding elements at the end of each mask row, in elements, with range [0, infinity)
204 * @param targetPaddingElements Optional number of padding elements at the end of each target row, in elements, with range [0, infinity)
205 * @param firstRow First row to be handled, with range [0, height - 1]
206 * @param numberRows Number of rows to be handled, with range [1, height - firstRow]
207 */
208 static void filter1Channel8Bit4NeighborSubset(const uint8_t* mask, uint8_t* target, const unsigned int width, const unsigned int height, const uint8_t maskValue, const unsigned int maskPaddingElements, const unsigned int targetPaddingElements, const unsigned int firstRow, const unsigned int numberRows);
209
210 /**
211 * Applies one erosion filter iteration for an 8 bit mask image using a 8-neighborhood.
212 * @param mask The mask frame to be filtered, must be valid
213 * @param target The target frame receiving the filter response, must be valid
214 * @param width The width of the mask frame in pixel, with range [2, infinity)
215 * @param height The height of the mask frame in pixel, with range [2, infinity)
216 * @param maskValue The value of a mask pixel to be eroded, pixels with other values will be untouched, with range [0, 255]
217 * @param maskPaddingElements Optional number of padding elements at the end of each mask row, in elements, with range [0, infinity)
218 * @param targetPaddingElements Optional number of padding elements at the end of each target row, in elements, with range [0, infinity)
219 * @param firstRow First row to be handled, with range [0, height - 1]
220 * @param numberRows Number of rows to be handled, with range [1, height - firstRow]
221 */
222 static void filter1Channel8Bit8NeighborSubset(const uint8_t* mask, uint8_t* target, const unsigned int width, const unsigned int height, const uint8_t maskValue, const unsigned int maskPaddingElements, const unsigned int targetPaddingElements, const unsigned int firstRow, const unsigned int numberRows);
223
224 /**
225 * Applies one erosion filter iteration for an 8 bit mask image using a 24-neighborhood.
226 * @param mask The mask frame to be filtered, must be valid
227 * @param target The target frame receiving the filter response, must be valid
228 * @param width The width of the mask frame in pixel, with range [4, infinity)
229 * @param height The height of the mask frame in pixel, with range [4, infinity)
230 * @param maskValue The value of a mask pixel to be eroded, pixels with other values will be untouched, with range [0, 255]
231 * @param maskPaddingElements Optional number of padding elements at the end of each mask row, in elements, with range [0, infinity)
232 * @param targetPaddingElements Optional number of padding elements at the end of each target row, in elements, with range [0, infinity)
233 * @param firstRow First row to be handled, with range [0, height - 1]
234 * @param numberRows Number of rows to be handled, with range [1, height - firstRow]
235 */
236 static void filter1Channel8Bit24NeighborSubset(const uint8_t* mask, uint8_t* target, const unsigned int width, const unsigned int height, const uint8_t maskValue, const unsigned int maskPaddingElements, const unsigned int targetPaddingElements, const unsigned int firstRow, const unsigned int numberRows);
237
238 /**
239 * Returns whether at least one of several pixels in a row is not equal to a specified value.
240 * @param maskPixels The first pixel within the row to check, must be valid
241 * @param maskValue The mask value to check, with range [0, 255]
242 * @return True, if so
243 * @tparam tSize The number of pixels in a row to check, with range [1, infinity)
244 */
245 template <unsigned int tSize>
246 static OCEAN_FORCE_INLINE bool onePixelNotEqual(const uint8_t* const maskPixels, const uint8_t maskValue);
247};
248
249template <unsigned int tChannels, bool tUseRandomNoise>
250bool FrameFilterErosion::shrinkMask8BitPerChannel4Neighbor(uint8_t* frame, uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const unsigned int randomNoise, const unsigned int randomSeed)
251{
252 static_assert(tChannels >= 1u, "Invalid channel number!");
253
254 ocean_assert(frame != nullptr && mask != nullptr);
255 ocean_assert(width >= 2u && height >= 2u);
256 ocean_assert(randomNoise <= 255u);
257
258 const unsigned int frameStrideElements = width * tChannels + framePaddingElements;
259 const unsigned int maskStrideElements = width + maskPaddingElements;
260
261#ifdef OCEAN_DEBUG
262 for (unsigned int y = 0u; y < height; ++y)
263 {
264 const uint8_t* maskRow = mask + y * maskStrideElements;
265
266 for (unsigned int x = 0u; x < width; ++x)
267 {
268 ocean_assert(maskRow[x] == 0x00u || maskRow[x] == 0xFFu);
269 }
270 }
271#endif // OCEAN_DEBUG
272
273 RandomGenerator randomGenerator(randomSeed);
274
275 /**
276 * O
277 * O X O
278 * O
279 */
280
281 Frame intermediateMask(FrameType(width, height, FrameType::FORMAT_Y8, FrameType::ORIGIN_UPPER_LEFT), mask, Frame::CM_COPY_REMOVE_PADDING_LAYOUT, maskPaddingElements);
282 ocean_assert(intermediateMask.isContinuous());
283
284 bool atLeastOnePixel = true;
285
286 while (atLeastOnePixel)
287 {
288 atLeastOnePixel = false;
289
290 const uint8_t* maskMiddle = mask;
291 const uint8_t* maskLower = mask + maskStrideElements;
292
293 uint8_t* frameMiddle = frame;
294 const uint8_t* frameLower = frame + frameStrideElements;
295
296 uint8_t* intermediateMaskMiddle = intermediateMask.data<uint8_t>();
297
298 // upper left pixel, if the anchor pixel is inside the hole and at least one of the filter pixels is outside the hole
299 if (*maskMiddle == 0x00u && (*(maskMiddle + 1) != 0x00u || *maskLower != 0x00u))
300 {
301 const unsigned int weight = *(maskMiddle + 1) + *maskLower;
302 ocean_assert(weight > 0u && weight <= 0xFFu * 2u);
303
304 for (unsigned int n = 0u; n < tChannels; ++n)
305 {
306 if constexpr (tUseRandomNoise)
307 {
308 ocean_assert(randomNoise != 0u);
309 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskMiddle + 1) * *(frameMiddle + tChannels + n) + *maskLower * *(frameLower + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
310 }
311 else
312 {
313 *(frameMiddle + n) = uint8_t((*(maskMiddle + 1) * *(frameMiddle + tChannels + n) + *maskLower * *(frameLower + n)) / weight);
314 }
315 }
316
317 *intermediateMaskMiddle = 0xFFu;
318 atLeastOnePixel = true;
319 }
320
321 frameMiddle += tChannels;
322 frameLower += tChannels;
323 ++maskMiddle;
324 ++maskLower;
325 ++intermediateMaskMiddle;
326
327
328 // upper row
329 for (unsigned int i = 1u; i < width - 1u; ++i)
330 {
331 // if the anchor pixel is inside the hole and at least one of the filter pixels is outside the hole
332 if (*maskMiddle == 0x00u && (*(maskMiddle - 1) != 0x00u || *(maskMiddle + 1) != 0x00u || *maskLower != 0x00u))
333 {
334 const unsigned int weight = *(maskMiddle - 1) + *(maskMiddle + 1) + *maskLower;
335 ocean_assert(weight > 0u && weight <= 0xFFu * 3u);
336
337 for (unsigned int n = 0u; n < tChannels; ++n)
338 {
339 if constexpr (tUseRandomNoise)
340 {
341 ocean_assert(randomNoise != 0u);
342 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskMiddle - 1) * *(frameMiddle - tChannels + n) + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) + *maskLower * *(frameLower + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
343 }
344 else
345 {
346 *(frameMiddle + n) = uint8_t((*(maskMiddle - 1) * *(frameMiddle - tChannels + n) + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) + *maskLower * *(frameLower + n)) / weight);
347 }
348 }
349
350 *intermediateMaskMiddle = 0xFFu;
351 atLeastOnePixel = true;
352 }
353
354 frameMiddle += tChannels;
355 frameLower += tChannels;
356 ++maskMiddle;
357 ++maskLower;
358 ++intermediateMaskMiddle;
359 }
360
361
362 // upper right pixel, if the anchor pixel is inside the hole and at least one of the filter pixels is outside the hole
363 if (*maskMiddle == 0 && (*(maskMiddle - 1) != 0u || *maskLower != 0u))
364 {
365 const unsigned int weight = *(maskMiddle - 1) + *maskLower;
366 ocean_assert(weight > 0u && weight <= 0xFFu * 2u);
367
368 for (unsigned int n = 0u; n < tChannels; ++n)
369 {
370 if constexpr (tUseRandomNoise)
371 {
372 ocean_assert(randomNoise != 0u);
373 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskMiddle - 1) * *(frameMiddle - tChannels + n) + *maskLower * *(frameLower + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
374 }
375 else
376 {
377 *(frameMiddle + n) = uint8_t((*(maskMiddle - 1) * *(frameMiddle - tChannels + n) + *maskLower * *(frameLower + n)) / weight);
378 }
379 }
380
381 *intermediateMaskMiddle = 0xFFu;
382 atLeastOnePixel = true;
383 }
384
385 frameMiddle += tChannels + framePaddingElements;
386 frameLower += tChannels + framePaddingElements;
387 maskMiddle += 1u + maskPaddingElements;
388 maskLower += 1u + maskPaddingElements;
389 ++intermediateMaskMiddle; // intermediate mask has no padding
390
391 ocean_assert(frameMiddle == frame + frameStrideElements);
392 ocean_assert(frameLower == frame + frameStrideElements * 2u);
393 ocean_assert(maskMiddle == mask + maskStrideElements);
394 ocean_assert(maskLower == mask + maskStrideElements * 2u);
395 ocean_assert(intermediateMaskMiddle == intermediateMask.data<uint8_t>() + width);
396
397
398 // center rows
399
400 const uint8_t* maskUpper = maskMiddle - maskStrideElements;
401 const uint8_t* frameUpper = frameMiddle - frameStrideElements;
402
403 const uint8_t* const maskUpperEnd = mask + maskStrideElements * (height - 2u);
404
405 while (maskUpper != maskUpperEnd)
406 {
407 // left pixel
408 if (*maskMiddle == 0x00u && (*maskUpper != 0x00u || *(maskMiddle + 1) != 0x00u || *maskLower != 0x00u))
409 {
410 const unsigned int weight = *maskUpper + *(maskMiddle + 1) + *maskLower;
411 ocean_assert(weight > 0u && weight <= 0xFFu * 3u);
412
413 for (unsigned int n = 0u; n < tChannels; ++n)
414 {
415 if constexpr (tUseRandomNoise)
416 {
417 ocean_assert(randomNoise != 0u);
418 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*maskUpper * *(frameUpper + n) + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) + *maskLower * *(frameLower + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
419 }
420 else
421 {
422 *(frameMiddle + n) = uint8_t((*maskUpper * *(frameUpper + n) + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) + *maskLower * *(frameLower + n)) / weight);
423 }
424 }
425
426 *intermediateMaskMiddle = 0xFFu;
427 atLeastOnePixel = true;
428 }
429
430 frameUpper += tChannels;
431 frameMiddle += tChannels;
432 frameLower += tChannels;
433 ++maskUpper;
434 ++maskMiddle;
435 ++maskLower;
436 ++intermediateMaskMiddle;
437
438
439 // center pixels
440 for (unsigned int i = 1u; i < width - 1u; ++i)
441 {
442 if (*maskMiddle == 0x00u && (*maskUpper != 0x00u || *(maskMiddle - 1) != 0x00u || *(maskMiddle + 1) != 0x00u || *maskLower != 0x00u))
443 {
444 const unsigned int weight = *maskUpper + *(maskMiddle - 1) + *(maskMiddle + 1) + *maskLower;
445 ocean_assert(weight > 0u && weight <= 0xFFu * 4u);
446
447 for (unsigned int n = 0u; n < tChannels; ++n)
448 {
449 if constexpr (tUseRandomNoise)
450 {
451 ocean_assert(randomNoise != 0u);
452 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*maskUpper * *(frameUpper + n) + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) + *maskLower * *(frameLower + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
453 }
454 else
455 {
456 *(frameMiddle + n) = uint8_t((*maskUpper * *(frameUpper + n) + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) + *maskLower * *(frameLower + n)) / weight);
457 }
458 }
459
460 *intermediateMaskMiddle = 0xFFu;
461 atLeastOnePixel = true;
462 }
463
464 frameUpper += tChannels;
465 frameMiddle += tChannels;
466 frameLower += tChannels;
467 ++maskUpper;
468 ++maskMiddle;
469 ++maskLower;
470 ++intermediateMaskMiddle;
471 }
472
473
474 // right pixel
475 if (*maskMiddle == 0x00u && (*maskUpper != 0x00u || *(maskMiddle - 1) != 0x00u || *maskLower != 0x00u))
476 {
477 const unsigned int weight = *maskUpper + *(maskMiddle - 1) + *maskLower;
478 ocean_assert(weight > 0u && weight <= 0xFFu * 3u);
479
480 for (unsigned int n = 0u; n < tChannels; ++n)
481 {
482 if constexpr (tUseRandomNoise)
483 {
484 ocean_assert(randomNoise != 0u);
485 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*maskUpper * *(frameUpper + n) + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) + *maskLower * *(frameLower + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
486 }
487 else
488 {
489 *(frameMiddle + n) = uint8_t((*maskUpper * *(frameUpper + n) + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) + *maskLower * *(frameLower + n)) / weight);
490 }
491 }
492
493 *intermediateMaskMiddle = 0xFFu;
494 atLeastOnePixel = true;
495 }
496
497 frameUpper += tChannels + framePaddingElements;
498 frameMiddle += tChannels + framePaddingElements;
499 frameLower += tChannels + framePaddingElements;
500 maskUpper += 1u + maskPaddingElements;
501 maskMiddle += 1u + maskPaddingElements;
502 maskLower += 1u + maskPaddingElements;
503 ++intermediateMaskMiddle; // intermediate mask has no padding elements
504 }
505
506 ocean_assert(frameUpper == frame + frameStrideElements * (height - 2u));
507 ocean_assert(frameMiddle == frame + frameStrideElements * (height - 1u));
508 ocean_assert(maskUpper == mask + maskStrideElements * (height - 2u));
509 ocean_assert(maskMiddle == mask + maskStrideElements * (height - 1u));
510 ocean_assert(intermediateMaskMiddle == intermediateMask.data<uint8_t>() + width * (height - 1u));
511
512 // bottom left pixel
513 if (*maskMiddle == 0x00u && (*maskUpper != 0x00u || *(maskMiddle + 1) != 0x00u))
514 {
515 const unsigned int weight = *maskUpper + *(maskMiddle + 1);
516 ocean_assert(weight > 0u && weight <= 0xFFu * 2u);
517
518 for (unsigned int n = 0u; n < tChannels; ++n)
519 {
520 if constexpr (tUseRandomNoise)
521 {
522 ocean_assert(randomNoise != 0u);
523 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*maskUpper * *(frameUpper + n) + *(maskMiddle + 1) * *(frameMiddle + tChannels + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
524 }
525 else
526 {
527 *(frameMiddle + n) = uint8_t((*maskUpper * *(frameUpper + n) + *(maskMiddle + 1) * *(frameMiddle + tChannels + n)) / weight);
528 }
529 }
530
531 *intermediateMaskMiddle = 0xFFu;
532 atLeastOnePixel = true;
533 }
534
535 frameUpper += tChannels;
536 frameMiddle += tChannels;
537 ++maskUpper;
538 ++maskMiddle;
539 ++intermediateMaskMiddle;
540
541
542 // center pixels
543 for (unsigned int i = 1u; i < width - 1u; ++i)
544 {
545 if (*maskMiddle == 0x00u && (*maskUpper != 0x00u || *(maskMiddle - 1) != 0x00u || *(maskMiddle + 1) != 0x00u))
546 {
547 const unsigned int weight = *maskUpper + *(maskMiddle - 1) + *(maskMiddle + 1);
548 ocean_assert(weight > 0u && weight <= 0xFFu * 4u);
549
550 for (unsigned int n = 0u; n < tChannels; ++n)
551 {
552 if constexpr (tUseRandomNoise)
553 {
554 ocean_assert(randomNoise != 0u);
555 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*maskUpper * *(frameUpper + n) + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) + *(maskMiddle + 1) * *(frameMiddle + tChannels + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
556 }
557 else
558 {
559 *(frameMiddle + n) = uint8_t((*maskUpper * *(frameUpper + n) + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) + *(maskMiddle + 1) * *(frameMiddle + tChannels + n)) / weight);
560 }
561 }
562
563 *intermediateMaskMiddle = 0xFFu;
564 atLeastOnePixel = true;
565 }
566
567 frameUpper += tChannels;
568 frameMiddle += tChannels;
569 ++maskUpper;
570 ++maskMiddle;
571 ++intermediateMaskMiddle;
572 }
573
574
575 // right pixel
576 if (*maskMiddle == 0x00u && (*maskUpper != 0x00u || *(maskMiddle - 1) != 0x00u))
577 {
578 const unsigned int weight = *maskUpper + *(maskMiddle - 1);
579 ocean_assert(weight > 0u && weight <= 0xFFu * 3u);
580
581 for (unsigned int n = 0u; n < tChannels; ++n)
582 {
583 if constexpr (tUseRandomNoise)
584 {
585 ocean_assert(randomNoise != 0u);
586 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*maskUpper * *(frameUpper + n) + *(maskMiddle - 1) * *(frameMiddle - tChannels + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
587 }
588 else
589 {
590 *(frameMiddle + n) = uint8_t((*maskUpper * *(frameUpper + n) + *(maskMiddle - 1) * *(frameMiddle - tChannels + n)) / weight);
591 }
592 }
593
594 *intermediateMaskMiddle = 0xFFu;
595 atLeastOnePixel = true;
596 }
597
598 if (atLeastOnePixel)
599 {
600 if (maskPaddingElements == 0u)
601 {
602 memcpy(mask, intermediateMask.constdata<uint8_t>(), sizeof(uint8_t) * width * height);
603 }
604 else
605 {
606 for (unsigned int y = 0u; y < height; ++y)
607 {
608 memcpy(mask + y * maskStrideElements, intermediateMask.constrow<uint8_t>(y), sizeof(uint8_t) * width);
609 }
610 }
611 }
612 }
613
614 return true;
615}
616
617template <unsigned int tChannels, bool tUseRandomNoise>
618bool FrameFilterErosion::shrinkMask8BitPerChannel8Neighbor(uint8_t* frame, uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const unsigned int randomNoise, const unsigned int randomSeed)
619{
620 static_assert(tChannels >= 1u, "Invalid channel number!");
621
622 ocean_assert(frame != nullptr && mask != nullptr);
623 ocean_assert(width >= 2 && height >= 2u);
624 ocean_assert(randomNoise <= 255u);
625
626 const unsigned int frameStrideElements = width * tChannels + framePaddingElements;
627 const unsigned int maskStrideElements = width + maskPaddingElements;
628
629#ifdef OCEAN_DEBUG
630 for (unsigned int y = 0u; y < height; ++y)
631 {
632 const uint8_t* maskRow = mask + y * maskStrideElements;
633
634 for (unsigned int x = 0u; x < width; ++x)
635 {
636 ocean_assert(maskRow[x] == 0x00u || maskRow[x] == 0xFFu);
637 }
638 }
639#endif // OCEAN_DEBUG
640
641 RandomGenerator randomGenerator(randomSeed);
642
643 /**
644 * O O O
645 * O X O
646 * O O O
647 */
648
649 Frame intermediateMask(FrameType(width, height, FrameType::FORMAT_Y8, FrameType::ORIGIN_UPPER_LEFT), mask, Frame::CM_COPY_REMOVE_PADDING_LAYOUT, maskPaddingElements);
650 ocean_assert(intermediateMask.isContinuous());
651
652 bool atLeastOnePixel = true;
653
654 while (atLeastOnePixel)
655 {
656 atLeastOnePixel = false;
657
658 const uint8_t* maskMiddle = mask;
659 const uint8_t* maskLower = mask + maskStrideElements;
660
661 uint8_t* frameMiddle = frame;
662 const uint8_t* frameLower = frame + frameStrideElements;
663
664 uint8_t* intermediateMaskMiddle = intermediateMask.data<uint8_t>();
665
666 // upper left pixel, if the anchor pixel is inside the hole and at least one of the filter pixels is outside the hole
667 if (*maskMiddle == 0x00u && (*(maskMiddle + 1) != 0x00u || *(maskLower + 0) != 0x00u || *(maskLower + 1) != 0x00u))
668 {
669 const unsigned int weight = *(maskMiddle + 1) * 2u + *(maskLower + 0) * 2u + *(maskLower + 1);
670 ocean_assert(weight > 0u && weight <= 0xFF * 12u);
671
672 for (unsigned int n = 0u; n < tChannels; ++n)
673 {
674 if constexpr (tUseRandomNoise)
675 {
676 ocean_assert(randomNoise != 0u);
677
678 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u
679 + *(maskLower + 0) * *(frameLower + n) * 2u + *(maskLower + 1) * *(frameLower + tChannels + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
680 }
681 else
682 {
683 *(frameMiddle + n) = uint8_t((*(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u
684 + *(maskLower + 0) * *(frameLower + n) * 2u + *(maskLower + 1) * *(frameLower + tChannels + n)) / weight);
685 }
686 }
687
688 *intermediateMaskMiddle = 0xFFu;
689 atLeastOnePixel = true;
690 }
691
692 frameMiddle += tChannels;
693 frameLower += tChannels;
694 ++maskMiddle;
695 ++maskLower;
696 ++intermediateMaskMiddle;
697
698
699 // upper row
700 for (unsigned int i = 1u; i < width - 1u; ++i)
701 {
702 // if the anchor pixel is inside the hole and at least one of the filter pixels is outside the hole
703 if (*maskMiddle == 0x00u && (*(maskMiddle - 1) != 0x00u || *(maskMiddle + 1) != 0x00u || *(maskLower - 1) != 0x00u || *(maskLower + 0) != 0x00u || *(maskLower + 1) != 0x00u))
704 {
705 const unsigned int weight = *(maskMiddle - 1) * 2u + *(maskMiddle + 1) * 2u + *(maskLower - 1) + *(maskLower + 0) * 2u + *(maskLower + 1);
706 ocean_assert(weight > 0u && weight <= 0xFFu * 12u);
707
708 for (unsigned int n = 0u; n < tChannels; ++n)
709 {
710 if constexpr (tUseRandomNoise)
711 {
712 ocean_assert(randomNoise != 0u);
713
714 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u
715 + *(maskLower - 1) * *(frameLower - tChannels + n) + *(maskLower + 0) * *(frameLower + n) * 2u + *(maskLower + 1) * *(frameLower + tChannels + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
716 }
717 else
718 {
719 *(frameMiddle + n) = uint8_t((*(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u
720 + *(maskLower - 1) * *(frameLower - tChannels + n) + *(maskLower + 0) * *(frameLower + n) * 2u + *(maskLower + 1) * *(frameLower + tChannels + n)) / weight);
721 }
722 }
723
724 *intermediateMaskMiddle = 0xFFu;
725 atLeastOnePixel = true;
726 }
727
728 frameMiddle += tChannels;
729 frameLower += tChannels;
730 ++maskMiddle;
731 ++maskLower;
732 ++intermediateMaskMiddle;
733 }
734
735
736 // upper right pixel
737 if (*maskMiddle == 0x00u && (*(maskMiddle - 1) != 0x00u || *(maskLower - 1) != 0x00u || *(maskLower + 0) != 0x00u))
738 {
739 const unsigned int weight = *(maskMiddle - 1) * 2u + *(maskLower - 1) + *(maskLower + 0) * 2u;
740 ocean_assert(weight > 0u && weight <= 0xFFu * 12u);
741
742 for (unsigned int n = 0u; n < tChannels; ++n)
743 {
744 if constexpr (tUseRandomNoise)
745 {
746 ocean_assert(randomNoise != 0u);
747
748 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u
749 + *(maskLower - 1) * *(frameLower - tChannels + n) + *(maskLower + 0) * *(frameLower + n) * 2u) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
750 }
751 else
752 {
753 *(frameMiddle + n) = uint8_t((*(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u
754 + *(maskLower - 1) * *(frameLower - tChannels + n) + *(maskLower + 0) * *(frameLower + n) * 2u) / weight);
755 }
756 }
757
758 *intermediateMaskMiddle = 0xFF;
759 atLeastOnePixel = true;
760 }
761
762 frameMiddle += tChannels + framePaddingElements;
763 frameLower += tChannels + framePaddingElements;
764 maskMiddle += 1u + maskPaddingElements;
765 maskLower += 1u + maskPaddingElements;
766 ++intermediateMaskMiddle; // intermediate mask has no padding
767
768 ocean_assert(frameMiddle == frame + frameStrideElements);
769 ocean_assert(frameLower == frame + frameStrideElements * 2u);
770 ocean_assert(maskMiddle == mask + maskStrideElements);
771 ocean_assert(maskLower == mask + maskStrideElements * 2u);
772 ocean_assert(intermediateMaskMiddle == intermediateMask.data<uint8_t>() + width);
773
774
775 // center rows
776
777 const uint8_t* maskUpper = maskMiddle - maskStrideElements;
778 const uint8_t* frameUpper = frameMiddle - frameStrideElements;
779
780 const uint8_t* const maskUpperEnd = mask + maskStrideElements * (height - 2u);
781
782 while (maskUpper != maskUpperEnd)
783 {
784 // left pixel
785 if (*maskMiddle == 0x00u && (*(maskUpper + 0) != 0x00u || *(maskUpper + 1) != 0x00u || *(maskMiddle + 1) != 0x00u || *(maskLower + 0) != 0x00u || *(maskLower + 1) != 0x00u))
786 {
787 const unsigned int weight = *(maskUpper + 0) * 2u + *(maskUpper + 1) + *(maskMiddle + 1) * 2u + *(maskLower + 0) * 2u + *(maskLower + 1);
788 ocean_assert(weight > 0u && weight <= 0xFFu * 12u);
789
790 for (unsigned int n = 0u; n < tChannels; ++n)
791 {
792 if constexpr (tUseRandomNoise)
793 {
794 ocean_assert(randomNoise != 0u);
795
796 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskUpper + 0) * *(frameUpper + n) * 2u + *(maskUpper + 1) * *(frameUpper + tChannels + n)
797 + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u
798 + *(maskLower + 0) * *(frameLower + n) * 2u + *(maskLower + 1) * *(frameLower + tChannels + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
799 }
800 else
801 {
802 *(frameMiddle + n) = uint8_t((*(maskUpper + 0) * *(frameUpper + n) * 2u + *(maskUpper + 1) * *(frameUpper + tChannels + n)
803 + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u
804 + *(maskLower + 0) * *(frameLower + n) * 2u + *(maskLower + 1) * *(frameLower + tChannels + n)) / weight);
805 }
806 }
807
808 *intermediateMaskMiddle = 0xFFu;
809 atLeastOnePixel = true;
810 }
811
812 frameUpper += tChannels;
813 frameMiddle += tChannels;
814 frameLower += tChannels;
815 ++maskUpper;
816 ++maskMiddle;
817 ++maskLower;
818 ++intermediateMaskMiddle;
819
820
821 // center pixels
822 for (unsigned int i = 1u; i < width - 1u; ++i)
823 {
824 if (*maskMiddle == 0x00u && (*(maskUpper - 1) != 0x00u || *(maskUpper + 0) != 0x00u || *(maskUpper + 1) != 0x00u || *(maskMiddle - 1) != 0x00u || *(maskMiddle + 1) != 0x00u || *(maskLower - 1) != 0x00u || *(maskLower + 0) != 0x00u || *(maskLower + 1) != 0x00u))
825 {
826 const unsigned int weight = *(maskUpper - 1) + *(maskUpper + 0) * 2u + *(maskUpper + 1) + *(maskMiddle - 1) * 2u + *(maskMiddle + 1) * 2u + *(maskLower - 1) + *(maskLower + 0) * 2u + *(maskLower + 1);
827 ocean_assert(weight > 0u && weight <= 0xFFu * 12u);
828
829 for (unsigned int n = 0u; n < tChannels; ++n)
830 {
831 if constexpr (tUseRandomNoise)
832 {
833 ocean_assert(randomNoise != 0u);
834
835 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskUpper - 1) * *(frameUpper - tChannels + n) + *(maskUpper + 0) * *(frameUpper + n) * 2u + *(maskUpper + 1) * *(frameUpper + tChannels + n)
836 + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u
837 + *(maskLower - 1) * *(frameLower - tChannels + n) + *(maskLower + 0) * *(frameLower + n) * 2u + *(maskLower + 1) * *(frameLower + tChannels + n)) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
838 }
839 else
840 {
841 *(frameMiddle + n) = uint8_t((*(maskUpper - 1) * *(frameUpper - tChannels + n) + *(maskUpper + 0) * *(frameUpper + n) * 2u + *(maskUpper + 1) * *(frameUpper + tChannels + n)
842 + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u
843 + *(maskLower - 1) * *(frameLower - tChannels + n) + *(maskLower + 0) * *(frameLower + n) * 2u + *(maskLower + 1) * *(frameLower + tChannels + n)) / weight);
844 }
845 }
846
847 *intermediateMaskMiddle = 0xFFu;
848 atLeastOnePixel = true;
849 }
850
851 frameUpper += tChannels;
852 frameMiddle += tChannels;
853 frameLower += tChannels;
854 ++maskUpper;
855 ++maskMiddle;
856 ++maskLower;
857 ++intermediateMaskMiddle;
858 }
859
860
861 // right pixels
862 if (*maskMiddle == 0x00u && (*(maskUpper - 1) != 0x00u || *(maskUpper + 0) != 0x00u || *(maskMiddle - 1) != 0x00u || *(maskLower - 1) != 0x00u || *(maskLower + 0) != 0x00u))
863 {
864 const unsigned int weight = *(maskUpper - 1) + *(maskUpper + 0) * 2u + *(maskMiddle - 1) * 2u + *(maskLower - 1) + *(maskLower + 0) * 2u;
865 ocean_assert(weight > 0u && weight <= 0xFFu * 12u);
866
867 for (unsigned int n = 0u; n < tChannels; ++n)
868 {
869 if constexpr (tUseRandomNoise)
870 {
871 ocean_assert(randomNoise != 0u);
872
873 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskUpper - 1) * *(frameUpper - tChannels + n) + *(maskUpper + 0) * *(frameUpper + n) * 2u
874 + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u
875 + *(maskLower - 1) * *(frameLower - tChannels + n) + *(maskLower + 0) * *(frameLower + n) * 2u) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
876 }
877 else
878 {
879 *(frameMiddle + n) = uint8_t((*(maskUpper - 1) * *(frameUpper - tChannels + n) + *(maskUpper + 0) * *(frameUpper + n) * 2u
880 + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u
881 + *(maskLower - 1) * *(frameLower - tChannels + n) + *(maskLower + 0) * *(frameLower + n) * 2u) / weight);
882 }
883 }
884
885 *intermediateMaskMiddle = 0xFFu;
886 atLeastOnePixel = true;
887 }
888
889 frameUpper += tChannels + framePaddingElements;
890 frameMiddle += tChannels + framePaddingElements;
891 frameLower += tChannels + framePaddingElements;
892 maskUpper += 1u + maskPaddingElements;
893 maskMiddle += 1u + maskPaddingElements;
894 maskLower += 1u + maskPaddingElements;
895 ++intermediateMaskMiddle; // intermediate mask has no padding elements
896 }
897
898 ocean_assert(frameUpper == frame + frameStrideElements * (height - 2u));
899 ocean_assert(frameMiddle == frame + frameStrideElements * (height - 1u));
900 ocean_assert(maskUpper == mask + maskStrideElements * (height - 2u));
901 ocean_assert(maskMiddle == mask + maskStrideElements * (height - 1u));
902 ocean_assert(intermediateMaskMiddle == intermediateMask.data<uint8_t>() + width * (height - 1u));
903
904
905 // bottom left pixel
906 if (*maskMiddle == 0x00u && (*(maskUpper + 0) != 0x00u || *(maskUpper + 1) != 0x00u || *(maskMiddle + 1) != 0x00u))
907 {
908 const unsigned int weight = *(maskUpper + 0) * 2u + *(maskUpper + 1) + *(maskMiddle + 1) * 2u;
909 ocean_assert(weight > 0u && weight <= 0xFFu * 12u);
910
911 for (unsigned int n = 0u; n < tChannels; ++n)
912 {
913 if constexpr (tUseRandomNoise)
914 {
915 ocean_assert(randomNoise != 0u);
916
917 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskUpper + 0) * *(frameUpper + n) * 2u + *(maskUpper + 1) * *(frameUpper + tChannels + n)
918 + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
919 }
920 else
921 {
922 *(frameMiddle + n) = uint8_t((*(maskUpper + 0) * *(frameUpper + n) * 2u + *(maskUpper + 1) * *(frameUpper + tChannels + n)
923 + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u) / weight);
924 }
925 }
926
927 *intermediateMaskMiddle = 0xFFu;
928 atLeastOnePixel = true;
929 }
930
931 frameUpper += tChannels;
932 frameMiddle += tChannels;
933 ++maskUpper;
934 ++maskMiddle;
935 ++intermediateMaskMiddle;
936
937
938 // center pixels
939 for (unsigned int i = 1u; i < width - 1u; ++i)
940 {
941 if (*maskMiddle == 0x00u && (*(maskUpper - 1) != 0x00u || *(maskUpper + 0) != 0x00u || *(maskUpper + 1) != 0x00u || *(maskMiddle - 1) != 0x00u || *(maskMiddle + 1) != 0x00u))
942 {
943 const unsigned int weight = *(maskUpper - 1) + *(maskUpper + 0) * 2u + *(maskUpper + 1) + *(maskMiddle - 1) * 2u + *(maskMiddle + 1) * 2u;
944 ocean_assert(weight > 0u && weight <= 0xFFu * 12u);
945
946 for (unsigned int n = 0u; n < tChannels; ++n)
947 {
948 if constexpr (tUseRandomNoise)
949 {
950 ocean_assert(randomNoise != 0u);
951
952 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskUpper - 1) * *(frameUpper - tChannels + n) + *(maskUpper + 0) * *(frameUpper + n) * 2u + *(maskUpper + 1) * *(frameUpper + tChannels + n)
953 + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
954 }
955 else
956 {
957 *(frameMiddle + n) = uint8_t((*(maskUpper - 1) * *(frameUpper - tChannels + n) + *(maskUpper + 0) * *(frameUpper + n) * 2u + *(maskUpper + 1) * *(frameUpper + tChannels + n)
958 + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u + *(maskMiddle + 1) * *(frameMiddle + tChannels + n) * 2u) / weight);
959 }
960 }
961
962 *intermediateMaskMiddle = 0xFFu;
963 atLeastOnePixel = true;
964 }
965
966 frameUpper += tChannels;
967 frameMiddle += tChannels;
968 ++maskUpper;
969 ++maskMiddle;
970 ++intermediateMaskMiddle;
971 }
972
973
974 // right pixel
975 if (*maskMiddle == 0x00u && (*(maskUpper - 1) != 0x00u || *(maskUpper + 0) != 0x00u || *(maskMiddle - 1) != 0x00u))
976 {
977 const unsigned int weight = *(maskUpper - 1) + *(maskUpper + 0) * 2u + *(maskMiddle - 1) * 2u;
978 ocean_assert(weight > 0u && weight <= 0xFFu * 12u);
979
980 for (unsigned int n = 0u; n < tChannels; ++n)
981 {
982 if constexpr (tUseRandomNoise)
983 {
984 ocean_assert(randomNoise != 0u);
985
986 *(frameMiddle + n) = uint8_t(minmax<int>(0, int((*(maskUpper - 1) * *(frameUpper - tChannels + n) + *(maskUpper + 0) * *(frameUpper + n) * 2u
987 + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u) / weight) + RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise)), 255));
988 }
989 else
990 {
991 *(frameMiddle + n) = uint8_t((*(maskUpper - 1) * *(frameUpper - tChannels + n) + *(maskUpper + 0) * *(frameUpper + n) * 2u
992 + *(maskMiddle - 1) * *(frameMiddle - tChannels + n) * 2u) / weight);
993 }
994 }
995
996 *intermediateMaskMiddle = 0xFFu;
997 atLeastOnePixel = true;
998 }
999
1000 if (atLeastOnePixel)
1001 {
1002 if (maskPaddingElements == 0u)
1003 {
1004 memcpy(mask, intermediateMask.constdata<uint8_t>(), sizeof(uint8_t) * width * height);
1005 }
1006 else
1007 {
1008 for (unsigned int y = 0u; y < height; ++y)
1009 {
1010 memcpy(mask + y * maskStrideElements, intermediateMask.constrow<uint8_t>(y), sizeof(uint8_t) * width);
1011 }
1012 }
1013 }
1014 }
1015
1016 return true;
1017}
1018
1019template <unsigned int tChannels>
1020void FrameFilterErosion::shrinkMaskRandom8BitPerChannel8Neighbor(uint8_t* frame, uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const unsigned int randomNoise, const unsigned int randomSeed)
1021{
1022 static_assert(tChannels >= 1u, "Invalid channel number!");
1023
1024 ocean_assert(frame != nullptr && mask != nullptr);
1025 ocean_assert(width >= 2u && height >= 2u);
1026
1027 const unsigned int frameStrideElements = width * tChannels + framePaddingElements;
1028 const unsigned int maskStrideElements = width + maskPaddingElements;
1029
1030 const unsigned int width_1 = width - 1u;
1031 const unsigned int height_1 = height - 1u;
1032
1033 RandomGenerator randomGenerator(randomSeed);
1034
1035 /**
1036 * O O O
1037 * O X O
1038 * O O O
1039 */
1040
1041 PixelPositionSet borderPixelSet;
1042 CV::PixelPositions borderPixels;
1043
1044 borderPixelSet.reserve(1024);
1045 borderPixels.reserve(1024);
1046
1047 constexpr uint8_t nonMaskValue = 0xFFu;
1048
1049 {
1050 // find all border pixels
1051
1052 const uint8_t* maskUpper = mask - maskStrideElements;
1053 const uint8_t* maskMiddle = mask;
1054 const uint8_t* maskLower = mask + maskStrideElements;
1055
1056 for (unsigned int y = 0u; y < height; ++y)
1057 {
1058 unsigned int x = 0u;
1059
1060 if (y == 0u)
1061 {
1062 // top left pixel
1063 if (maskMiddle[x] != nonMaskValue && (maskMiddle[x + 1u] == nonMaskValue || maskLower[x] == nonMaskValue || maskLower[x + 1u] == nonMaskValue))
1064 {
1065 borderPixelSet.emplace(x, 0u);
1066 borderPixels.emplace_back(x, 0u);
1067 }
1068
1069 while (++x < width_1)
1070 {
1071 if (*(maskMiddle + x) != nonMaskValue && (*(maskMiddle + x - 1u) == nonMaskValue || *(maskMiddle + x + 1u) == nonMaskValue || *(maskLower + x - 1u) == nonMaskValue || *(maskLower + x) == nonMaskValue || *(maskLower + x + 1u) == nonMaskValue))
1072 {
1073 borderPixelSet.emplace(x, 0u);
1074 borderPixels.emplace_back(x, 0u);
1075 }
1076 }
1077
1078 ocean_assert(x == width_1);
1079
1080 // top right pixel
1081 if (*(maskMiddle + x) != nonMaskValue && (*(maskMiddle + x - 1u) == nonMaskValue || *(maskLower + x - 1u) == nonMaskValue || *(maskLower + x) == nonMaskValue))
1082 {
1083 borderPixelSet.emplace(x, 0u);
1084 borderPixels.emplace_back(x, 0u);
1085 }
1086 }
1087 else if (y == height_1)
1088 {
1089 // bottom left pixel
1090 if (maskMiddle[x] != nonMaskValue && (maskMiddle[x + 1u] == nonMaskValue || maskUpper[x] == nonMaskValue || maskUpper[x + 1u] == nonMaskValue))
1091 {
1092 borderPixelSet.emplace(x, height_1);
1093 borderPixels.emplace_back(x, height_1);
1094 }
1095
1096 while (++x < width_1)
1097 {
1098 if (*(maskMiddle + x) != nonMaskValue && (*(maskMiddle + x - 1u) == nonMaskValue || *(maskMiddle + x + 1u) == nonMaskValue || *(maskUpper + x - 1u) == nonMaskValue || *(maskUpper + x) == nonMaskValue || *(maskUpper + x + 1u) == nonMaskValue))
1099 {
1100 borderPixelSet.emplace(x, height_1);
1101 borderPixels.emplace_back(x, height_1);
1102 }
1103 }
1104
1105 ocean_assert(x == width_1);
1106
1107 // bottom right pixel
1108 if (*(maskMiddle + x) != nonMaskValue && (*(maskMiddle + x - 1u) == nonMaskValue || *(maskUpper + x - 1u) == nonMaskValue || *(maskUpper + x) == nonMaskValue))
1109 {
1110 borderPixelSet.emplace(x, height_1);
1111 borderPixels.emplace_back(x, height_1);
1112 }
1113 }
1114 else
1115 {
1116 ocean_assert(y >= 1u && y < height_1);
1117
1118 // left pixel
1119 if (maskMiddle[x] != nonMaskValue && (maskMiddle[x + 1u] == nonMaskValue || maskUpper[x] == nonMaskValue || maskUpper[x + 1u] == nonMaskValue || maskLower[x] == nonMaskValue || maskLower[x + 1u] == nonMaskValue))
1120 {
1121 borderPixelSet.emplace(x, y);
1122 borderPixels.emplace_back(x, y);
1123 }
1124
1125 while (++x < width_1)
1126 {
1127 if (*(maskMiddle + x) != nonMaskValue && (*(maskMiddle + x - 1u) == nonMaskValue || *(maskMiddle + x + 1u) == nonMaskValue || *(maskUpper + x - 1u) == nonMaskValue || *(maskUpper + x) == nonMaskValue || *(maskUpper + x + 1u) == nonMaskValue || *(maskLower + x - 1u) == nonMaskValue || *(maskLower + x) == nonMaskValue || *(maskLower + x + 1u) == nonMaskValue))
1128 {
1129 borderPixelSet.emplace(x, y);
1130 borderPixels.emplace_back(x, y);
1131 }
1132 }
1133
1134 ocean_assert(x == width_1);
1135
1136 // right pixel
1137 if (*(maskMiddle + x) != nonMaskValue && (*(maskMiddle + x - 1u) == nonMaskValue || *(maskUpper + x - 1u) == nonMaskValue || *(maskUpper + x) == nonMaskValue || *(maskLower + x - 1u) == nonMaskValue || *(maskLower + x) == nonMaskValue))
1138 {
1139 borderPixelSet.emplace(x, y);
1140 borderPixels.emplace_back(x, y);
1141 }
1142 }
1143
1144 maskUpper += maskStrideElements;
1145 maskMiddle += maskStrideElements;
1146 maskLower += maskStrideElements;
1147 }
1148 }
1149
1150 while (!borderPixels.empty())
1151 {
1152 const unsigned int index = Random::random(randomGenerator, (unsigned int)(borderPixels.size()) - 1u);
1153
1154 const CV::PixelPosition pixelPosition(borderPixels[index]);
1155
1156 borderPixels[index] = borderPixels.back();
1157 borderPixels.pop_back();
1158
1159 borderPixelSet.erase(pixelPosition);
1160
1161 ocean_assert(pixelPosition.x() < width && pixelPosition.y() < height);
1162
1163 uint8_t* maskMiddle = mask + pixelPosition.y() * maskStrideElements + pixelPosition.x();
1164 --maskMiddle; // left pixel of 3x3 square
1165 const uint8_t* maskUpper = maskMiddle - maskStrideElements;
1166 const uint8_t* maskLower = maskMiddle + maskStrideElements;
1167
1168 uint8_t* frameMiddle = frame + pixelPosition.y() * frameStrideElements + pixelPosition.x() * tChannels;
1169 frameMiddle -= tChannels; // left pixel of 3x3 square
1170 const uint8_t* const frameUpper = frameMiddle - frameStrideElements;
1171 const uint8_t* const frameLower = frameMiddle + frameStrideElements;
1172
1173 if (pixelPosition.x() >= 1u && pixelPosition.y() >= 1u && pixelPosition.x() < width_1 && pixelPosition.y() < height_1)
1174 {
1175 // we have a center pixel
1176
1177 const unsigned int weight = maskUpper[0] + maskUpper[1] * 2u + maskUpper[2] + maskMiddle[0] * 2u + maskMiddle[2] * 2u + maskLower[0] + maskLower[1] * 2u + maskLower[2];
1178
1179 ocean_assert(weight > 0u && weight <= 0xFF * 12u);
1180 const unsigned int weight_2 = weight / 2u;
1181
1182 for (unsigned int n = 0u; n < tChannels; ++n)
1183 {
1184 const unsigned int value = frameUpper[n] * maskUpper[0] + frameUpper[tChannels + n] * maskUpper[1] * 2u + frameUpper[tChannels * 2u + n] * maskUpper[2]
1185 + frameMiddle[n] * maskMiddle[0] * 2u + frameMiddle[tChannels * 2u + n] * maskMiddle[2] * 2u
1186 + frameLower[n] * maskLower[0] + frameLower[tChannels + n] * maskLower[1] * 2u + frameLower[tChannels * 2u + n] * maskLower[2];
1187
1188 const int noise = randomNoise == 0u ? 0 : RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise));
1189
1190 frameMiddle[tChannels + n] = uint8_t(minmax<int>(0, int((value + weight_2) / weight) + noise, 255));
1191 }
1192
1193 maskMiddle[1] = nonMaskValue;
1194 }
1195 else
1196 {
1197 // we have a frame border pixel
1198
1199 unsigned int weight = 0u;
1200 unsigned int values[tChannels] = {0u};
1201
1202 if (pixelPosition.y() > 0u)
1203 {
1204 if (pixelPosition.x() > 0u)
1205 {
1206 weight += maskUpper[0];
1207
1208 for (unsigned int n = 0u; n < tChannels; ++n)
1209 {
1210 values[n] += frameUpper[n] * maskUpper[0];
1211 }
1212 }
1213
1214 weight += maskUpper[1] * 2u;
1215
1216 for (unsigned int n = 0u; n < tChannels; ++n)
1217 {
1218 values[n] += frameUpper[tChannels + n] * maskUpper[1] * 2u;
1219 }
1220
1221 if (pixelPosition.x() < width_1)
1222 {
1223 weight += maskUpper[2];
1224
1225 for (unsigned int n = 0u; n < tChannels; ++n)
1226 {
1227 values[n] += frameUpper[tChannels * 2u + n] * maskUpper[2];
1228 }
1229 }
1230 }
1231
1232 if (pixelPosition.x() > 0u)
1233 {
1234 weight += maskMiddle[0] * 2u;
1235
1236 for (unsigned int n = 0u; n < tChannels; ++n)
1237 {
1238 values[n] += frameMiddle[n] * maskMiddle[0] * 2u;
1239 }
1240 }
1241
1242 if (pixelPosition.x() < width_1)
1243 {
1244 weight += maskMiddle[2] * 2u;
1245
1246 for (unsigned int n = 0u; n < tChannels; ++n)
1247 {
1248 values[n] += frameMiddle[tChannels * 2u + n] * maskMiddle[2] * 2u;
1249 }
1250 }
1251
1252 if (pixelPosition.y() < height_1)
1253 {
1254 if (pixelPosition.x() > 0u)
1255 {
1256 weight += maskLower[0];
1257
1258 for (unsigned int n = 0u; n < tChannels; ++n)
1259 {
1260 values[n] += frameLower[n] * maskLower[0];
1261 }
1262 }
1263
1264 weight += maskLower[1] * 2u;
1265
1266 for (unsigned int n = 0u; n < tChannels; ++n)
1267 {
1268 values[n] += frameLower[tChannels + n] * maskLower[1] * 2u;
1269 }
1270
1271 if (pixelPosition.x() < width_1)
1272 {
1273 weight += maskLower[2];
1274
1275 for (unsigned int n = 0u; n < tChannels; ++n)
1276 {
1277 values[n] += frameLower[tChannels * 2u + n] * maskLower[2];
1278 }
1279 }
1280 }
1281
1282 ocean_assert(weight > 0u && weight <= 0xFF * 12u);
1283 const unsigned int weight_2 = weight / 2u;
1284
1285 for (unsigned int n = 0u; n < tChannels; ++n)
1286 {
1287 const int noise = randomNoise == 0u ? 0 : RandomI::random(randomGenerator, -int(randomNoise), int(randomNoise));
1288
1289 frameMiddle[tChannels + n] = uint8_t(minmax<int>(0, int((values[n] + weight_2) / weight) + noise, 255));
1290 }
1291
1292 maskMiddle[1] = nonMaskValue;
1293 }
1294
1295 for (int yy = -1; yy <= 1; ++yy)
1296 {
1297 const unsigned int yCenter = (unsigned int)(int(pixelPosition.y()) + yy);
1298
1299 if (yCenter < height)
1300 {
1301 for (int xx = -1; xx <= 1; ++xx)
1302 {
1303 if (yy != 0 || xx != 0)
1304 {
1305 const unsigned int xCenter = (unsigned int)(int(pixelPosition.x()) + xx);
1306
1307 if (xCenter < width)
1308 {
1309 maskMiddle = mask + yCenter * maskStrideElements + xCenter;
1310 --maskMiddle; // left pixel of 3x3 square
1311
1312 if (maskMiddle[1] != nonMaskValue)
1313 {
1314 if (borderPixelSet.find(CV::PixelPosition(xCenter, yCenter)) == borderPixelSet.cend())
1315 {
1316 maskUpper = maskMiddle - maskStrideElements;
1317 maskLower = maskMiddle + maskStrideElements;
1318
1319 const bool newBorderPixel = (xCenter > 0u && yCenter > 0u && maskUpper[0] == nonMaskValue) // upper pixels
1320 || (yCenter > 0u && maskUpper[1] == nonMaskValue)
1321 || (xCenter < width_1 && yCenter > 0u && maskUpper[2] == nonMaskValue)
1322 || (xCenter > 0u && maskMiddle[0] == nonMaskValue) // middle pixels
1323 || (xCenter < width_1 && maskMiddle[2] == nonMaskValue)
1324 || (xCenter > 0u && yCenter < height_1 && maskLower[0] == nonMaskValue) // lower pixels
1325 || (yCenter < height_1 && maskLower[1] == nonMaskValue)
1326 || (xCenter < width_1 && yCenter < height_1 && maskLower[2] == nonMaskValue);
1327
1328 if (newBorderPixel)
1329 {
1330 borderPixelSet.emplace(xCenter, yCenter);
1331 borderPixels.emplace_back(xCenter, yCenter);
1332 }
1333 }
1334 }
1335 }
1336 }
1337 }
1338 }
1339 }
1340 }
1341}
1342
1343template <FrameFilterErosion::MorphologyFilter tErosionFilter>
1344void FrameFilterErosion::filter1Channel8Bit(uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int iterations, const uint8_t maskValue, const unsigned int maskPaddingElements, Worker* worker)
1345{
1346 ocean_assert(mask != nullptr);
1347 ocean_assert(width >= 4u && height >= 4u);
1348 ocean_assert(iterations >= 1u);
1349
1350 Frame intermediateTarget(FrameType(width, height, FrameType::FORMAT_Y8, FrameType::ORIGIN_UPPER_LEFT));
1351
1352 switch (tErosionFilter)
1353 {
1354 case MF_CROSS_3:
1355 {
1356 for (unsigned int n = 0u; n < iterations / 2u; ++n)
1357 {
1358 filter1Channel8Bit4Neighbor(mask, intermediateTarget.data<uint8_t>(), width, height, maskValue, maskPaddingElements, intermediateTarget.paddingElements(), worker);
1359 filter1Channel8Bit4Neighbor(intermediateTarget.constdata<uint8_t>(), mask, width, height, maskValue, intermediateTarget.paddingElements(), maskPaddingElements, worker);
1360 }
1361
1362 if (iterations % 2u == 1u)
1363 {
1364 filter1Channel8Bit4Neighbor(mask, intermediateTarget.data<uint8_t>(), width, height, maskValue, maskPaddingElements, intermediateTarget.paddingElements(), worker);
1365 }
1366
1367 break;
1368 }
1369
1370 case MF_SQUARE_3:
1371 {
1372 for (unsigned int n = 0u; n < iterations / 2u; ++n)
1373 {
1374 filter1Channel8Bit8Neighbor(mask, intermediateTarget.data<uint8_t>(), width, height, maskValue, maskPaddingElements, intermediateTarget.paddingElements(), worker);
1375 filter1Channel8Bit8Neighbor(intermediateTarget.constdata<uint8_t>(), mask, width, height, maskValue, intermediateTarget.paddingElements(), maskPaddingElements, worker);
1376 }
1377
1378 if (iterations % 2u == 1u)
1379 {
1380 filter1Channel8Bit8Neighbor(mask, intermediateTarget.data<uint8_t>(), width, height, maskValue, maskPaddingElements, intermediateTarget.paddingElements(), worker);
1381 }
1382
1383 break;
1384 }
1385
1386 case MF_SQUARE_5:
1387 {
1388 for (unsigned int n = 0u; n < iterations / 2u; ++n)
1389 {
1390 filter1Channel8Bit24Neighbor(mask, intermediateTarget.data<uint8_t>(), width, height, maskValue, maskPaddingElements, intermediateTarget.paddingElements(), worker);
1391 filter1Channel8Bit24Neighbor(intermediateTarget.constdata<uint8_t>(), mask, width, height, maskValue, intermediateTarget.paddingElements(), maskPaddingElements, worker);
1392 }
1393
1394 if (iterations % 2u == 1u)
1395 {
1396 filter1Channel8Bit24Neighbor(mask, intermediateTarget.data<uint8_t>(), width, height, maskValue, maskPaddingElements, intermediateTarget.paddingElements(), worker);
1397 }
1398
1399 break;
1400 }
1401
1402 default:
1403 ocean_assert(false && "Invalid erosion filter!");
1404 }
1405
1406 if (iterations % 2u == 1u)
1407 {
1408 CV::FrameConverter::subFrame<uint8_t>(intermediateTarget.constdata<uint8_t>(), mask, width, height, width, height, 1u, 0u, 0u, 0u, 0u, width, height, intermediateTarget.paddingElements(), maskPaddingElements);
1409 }
1410}
1411
1412inline void FrameFilterErosion::filter1Channel8Bit4Neighbor(const uint8_t* mask, uint8_t* target, const unsigned int width, const unsigned int height, const uint8_t maskValue, const unsigned int maskPaddingElements, const unsigned int targetPaddingElements, Worker* worker)
1413{
1414 ocean_assert(mask && target);
1415 ocean_assert(width >= 2u && height >= 2u);
1416
1417 if (worker)
1418 {
1419 worker->executeFunction(Worker::Function::createStatic(&FrameFilterErosion::filter1Channel8Bit4NeighborSubset, mask, target, width, height, maskValue, maskPaddingElements, targetPaddingElements, 0u, 0u), 0u, height, 7u, 8u, 20u);
1420 }
1421 else
1422 {
1423 filter1Channel8Bit4NeighborSubset(mask, target, width, height, maskValue, maskPaddingElements, targetPaddingElements, 0u, height);
1424 }
1425}
1426
1427inline void FrameFilterErosion::filter1Channel8Bit8Neighbor(const uint8_t* mask, uint8_t* target, const unsigned int width, const unsigned int height, const uint8_t maskValue, const unsigned int maskPaddingElements, const unsigned int targetPaddingElements, Worker* worker)
1428{
1429 ocean_assert(mask && target);
1430 ocean_assert(width >= 2u && height >= 2u);
1431
1432 if (worker)
1433 {
1434 worker->executeFunction(Worker::Function::createStatic(&FrameFilterErosion::filter1Channel8Bit8NeighborSubset, mask, target, width, height, maskValue, maskPaddingElements, targetPaddingElements, 0u, 0u), 0u, height, 7u, 8u, 20u);
1435 }
1436 else
1437 {
1438 filter1Channel8Bit8NeighborSubset(mask, target, width, height, maskValue, maskPaddingElements, targetPaddingElements, 0u, height);
1439 }
1440}
1441
1442inline void FrameFilterErosion::filter1Channel8Bit24Neighbor(const uint8_t* mask, uint8_t* target, const unsigned int width, const unsigned int height, const uint8_t maskValue, const unsigned int maskPaddingElements, const unsigned int targetPaddingElements, Worker* worker)
1443{
1444 ocean_assert(mask && target);
1445 ocean_assert(width >= 2u && height >= 2u);
1446
1447 if (worker)
1448 {
1449 worker->executeFunction(Worker::Function::createStatic(&FrameFilterErosion::filter1Channel8Bit24NeighborSubset, mask, target, width, height, maskValue, maskPaddingElements, targetPaddingElements, 0u, 0u), 0u, height, 7u, 8u, 20u);
1450 }
1451 else
1452 {
1453 filter1Channel8Bit24NeighborSubset(mask, target, width, height, maskValue, maskPaddingElements, targetPaddingElements, 0u, height);
1454 }
1455}
1456
1457template <>
1458OCEAN_FORCE_INLINE bool FrameFilterErosion::onePixelNotEqual<3u>(const uint8_t* const maskPixels, const uint8_t maskValue)
1459{
1460 ocean_assert(maskPixels != nullptr);
1461
1462 return maskPixels[0] != maskValue || maskPixels[1] != maskValue || maskPixels[2] != maskValue;
1463}
1464
1465template <>
1466OCEAN_FORCE_INLINE bool FrameFilterErosion::onePixelNotEqual<4u>(const uint8_t* const maskPixels, const uint8_t maskValue)
1467{
1468 ocean_assert(maskPixels != nullptr);
1469
1470 return maskPixels[0] != maskValue || maskPixels[1] != maskValue || maskPixels[2] != maskValue || maskPixels[3] != maskValue;
1471}
1472
1473template <>
1474OCEAN_FORCE_INLINE bool FrameFilterErosion::onePixelNotEqual<5u>(const uint8_t* const maskPixels, const uint8_t maskValue)
1475{
1476 ocean_assert(maskPixels != nullptr);
1477
1478 return maskPixels[0] != maskValue || maskPixels[1] != maskValue || maskPixels[2] != maskValue || maskPixels[3] != maskValue || maskPixels[4] != maskValue;
1479}
1480
1481}
1482
1483}
1484
1485#endif // META_OCEAN_CV_FRAME_FILTER_EROSION_H
The following comfort class provides comfortable functions simplifying prototyping applications but a...
Definition FrameFilterErosion.h:48
static bool shrinkMaskRandom(Frame &frame, Frame &mask, const MorphologyFilter erosionFilter, const unsigned int randomNoise=3u, const unsigned int randomSeed=RandomI::random32())
Closes a hole inside an 8 bit grayscale frame using a randomized erosion filter.
static bool shrinkMask(Frame &frame, Frame &mask, const MorphologyFilter erosionFilter, const unsigned int randomNoise=3u, const unsigned int randomSeed=RandomI::random32())
Closes holes inside a frame using a shrinking/in-bleeding approach based on either a 4-neighborhood o...
This class implements an erosion filter.
Definition FrameFilterErosion.h:32
static bool shrinkMask8BitPerChannel8Neighbor(uint8_t *frame, uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const unsigned int randomNoise=0u, const unsigned int randomSeed=RandomI::random32())
Closes a hole inside a frame by using a shrinking/in-bleeding approach based on an 8-neighborhood.
Definition FrameFilterErosion.h:618
static void filter1Channel8Bit24Neighbor(const uint8_t *mask, uint8_t *target, const unsigned int width, const unsigned int height, const uint8_t maskValue=0x00, const unsigned int maskPaddingElements=0u, const unsigned int targetPaddingElements=0u, Worker *worker=nullptr)
Applies one erosion filter iteration for an 8 bit mask image using a 24-neighborhood,...
Definition FrameFilterErosion.h:1442
static void filter1Channel8Bit24NeighborSubset(const uint8_t *mask, uint8_t *target, const unsigned int width, const unsigned int height, const uint8_t maskValue, const unsigned int maskPaddingElements, const unsigned int targetPaddingElements, const unsigned int firstRow, const unsigned int numberRows)
Applies one erosion filter iteration for an 8 bit mask image using a 24-neighborhood.
static void filter1Channel8Bit8NeighborSubset(const uint8_t *mask, uint8_t *target, const unsigned int width, const unsigned int height, const uint8_t maskValue, const unsigned int maskPaddingElements, const unsigned int targetPaddingElements, const unsigned int firstRow, const unsigned int numberRows)
Applies one erosion filter iteration for an 8 bit mask image using a 8-neighborhood.
static void filter1Channel8Bit8Neighbor(const uint8_t *mask, uint8_t *target, const unsigned int width, const unsigned int height, const uint8_t maskValue=0x00, const unsigned int maskPaddingElements=0u, const unsigned int targetPaddingElements=0u, Worker *worker=nullptr)
Applies one erosion filter iteration for an 8 bit mask image using a 8-neighborhood,...
Definition FrameFilterErosion.h:1427
std::unordered_set< CV::PixelPosition, CV::PixelPosition > PixelPositionSet
Definition of an unordered set holding pixel positions.
Definition FrameFilterErosion.h:38
static OCEAN_FORCE_INLINE bool onePixelNotEqual(const uint8_t *const maskPixels, const uint8_t maskValue)
Returns whether at least one of several pixels in a row is not equal to a specified value.
static void shrinkMaskRandom8BitPerChannel8Neighbor(uint8_t *frame, uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const unsigned int randomNoise=0u, const unsigned int randomSeed=RandomI::random32())
Closes a hole inside a frame by using a randomized shrinking/in-bleeding approach based on an 8-neigh...
Definition FrameFilterErosion.h:1020
static void filter1Channel8Bit(uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int iterations, const uint8_t maskValue=0x00, const unsigned int maskPaddingElements=0u, Worker *worker=nullptr)
Applies several erosion filter iterations for an 8 bit mask image.
Definition FrameFilterErosion.h:1344
static bool shrinkMask8BitPerChannel4Neighbor(uint8_t *frame, uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const unsigned int randomNoise=0u, const unsigned int randomSeed=RandomI::random32())
Closes a hole inside a frame by using a shrinking/in-bleeding approach based on a 4-neighborhood.
Definition FrameFilterErosion.h:250
static void filter1Channel8Bit4NeighborSubset(const uint8_t *mask, uint8_t *target, const unsigned int width, const unsigned int height, const uint8_t maskValue, const unsigned int maskPaddingElements, const unsigned int targetPaddingElements, const unsigned int firstRow, const unsigned int numberRows)
Applies one erosion filter iteration in a subset of an 8 bit mask image using a 4-neighborhood.
static void filter1Channel8Bit4Neighbor(const uint8_t *mask, uint8_t *target, const unsigned int width, const unsigned int height, const uint8_t maskValue=0x00, const unsigned int maskPaddingElements=0u, const unsigned int targetPaddingElements=0u, Worker *worker=nullptr)
Applies one erosion filter iteration in an 8 bit mask image using a 4-neighborhood.
Definition FrameFilterErosion.h:1412
This class implements the base class for all morphology frame filter.
Definition FrameFilterMorphology.h:26
MorphologyFilter
Definition of individual morphology masks.
Definition FrameFilterMorphology.h:33
@ MF_SQUARE_3
Square mask filter defined by one center point and 8 surrounded filter pixels with an area of 3x3.
Definition FrameFilterMorphology.h:37
@ MF_CROSS_3
Cross mask filter defined by one center point and 4 (north, south, west, east) filter pixels with an ...
Definition FrameFilterMorphology.h:35
@ MF_SQUARE_5
Square mask filter defined by one center point and 24 surrounded filter pixels with an area of 5x5.
Definition FrameFilterMorphology.h:39
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 Caller< void > createStatic(typename StaticFunctionPointerMaker< void, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass >::Type function)
Creates a new caller container for a static function with no function parameter.
Definition Caller.h:2876
This class implements Ocean's image class.
Definition Frame.h:1808
bool isContinuous() const
Returns whether all planes of this frame have continuous memory and thus do not contain any padding a...
Definition Frame.h:4356
const T * constdata(const unsigned int planeIndex=0u) const
Returns a pointer to the read-only pixel data of a specific plane.
Definition Frame.h:4248
T * data(const unsigned int planeIndex=0u)
Returns a pointer to the pixel data of a specific plane.
Definition Frame.h:4239
@ CM_COPY_REMOVE_PADDING_LAYOUT
Makes a copy of the source memory, but the new plane will not contain padding elements.
Definition Frame.h:1819
const T * constrow(const unsigned int y, const unsigned int planeIndex=0u) const
Returns the pointer to the constant data of a specific row.
Definition Frame.h:4273
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
This class implements a generator for random numbers.
Definition RandomGenerator.h:42
static unsigned int random(const unsigned int maxValue)
Returns one random integer value with specified maximum value.
This class implements a worker able to distribute function calls over different threads.
Definition Worker.h:33
bool executeFunction(const Function &function, const unsigned int first, const unsigned int size, const unsigned int firstIndex=(unsigned int)(-1), const unsigned int sizeIndex=(unsigned int)(-1), const unsigned int minimalIterations=1u, const unsigned int threadIndex=(unsigned int)(-1))
Executes a callback function separable by two function parameters.
std::vector< PixelPosition > PixelPositions
Definition of a vector holding pixel positions (with positive coordinate values).
Definition PixelPosition.h:48
The namespace covering the entire Ocean framework.
Definition Accessor.h:15