Ocean
Loading...
Searching...
No Matches
InitializerShrinkingPatchMatchingI1.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_SYNTHESIS_INITIALIZER_SHRINKING_PATCH_MATCHING_I_1_H
9#define META_OCEAN_CV_SYNTHESIS_INITIALIZER_SHRINKING_PATCH_MATCHING_I_1_H
10
16
19
21
23
24#include "ocean/math/Random.h"
25
26#include <list>
27
28namespace Ocean
29{
30
31namespace CV
32{
33
34namespace Synthesis
35{
36
37/**
38 * This class implements an initializer that shrinks the inpainting mask by the application of images patches.
39 * @ingroup cvsynthesis
40 */
41class OCEAN_CV_SYNTHESIS_EXPORT InitializerShrinkingPatchMatchingI1 :
42 virtual public InitializerI,
43 virtual public InitializerRandomized,
44 virtual public Initializer1
45{
46 private:
47
48 /**
49 * Definition of a wrapper class for ssd functions.
50 */
52 {
53 public:
54
55 /**
56 * Wrapper function for AdvancedSumSquareDifferences::patchWithRejectingMask8BitPerChannel().
57 * @see AdvancedSumSquareDifferences::patchWithRejectingMask8BitPerChannel().
58 */
59 template <unsigned int tChannels>
60 static inline unsigned int determine5x5(const uint8_t* frame, const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const CV::PixelPosition& inPosition, const CV::PixelPosition& outPosition);
61 };
62
63 /**
64 * Definition of a wrapper class for ssd functions.
65 */
67 {
68 public:
69
70 /**
71 * Wrapper function for SumSquareDifferences::patchAtBorder8BitPerChannel.
72 * @see SumSquareDifferences::patchAtBorder8BitPerChannel().
73 */
74 template <unsigned int tChannels>
75 static inline unsigned int determine5x5(const uint8_t* frame, const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const CV::PixelPosition& inPosition, const CV::PixelPosition& outPosition);
76 };
77
78 /**
79 * This class implements a single inpainting pixel.
80 */
82 {
83 public:
84
85 /**
86 * Creates a new inpainting pixel by a given border direction and image orientation.
87 * @param position Inpainting pixel position
88 * @param borderDirection Direction of the inpainting border
89 * @param imageOrientation Orientation of the surrounding image
90 */
91 inline InpaintingPixel(const PixelPosition& position, const VectorI2& borderDirection, const VectorI2& imageOrientation);
92
93 /**
94 * Returns the border direction of this inpainting pixel.
95 * @return Border direction
96 */
97 inline const VectorI2& borderDirection() const;
98
99 /**
100 * Returns the image orientation of this inpainting pixel.
101 * @return Image orientation
102 */
103 inline const VectorI2& imageOrientation() const;
104
105 /**
106 * Returns the priority of this inpainting pixel.
107 * @return Pixel priority
108 */
109 inline unsigned int priority() const;
110
111 /**
112 * Sets the border direction of this inpainting pixel.
113 * @param direction Border direction to be set
114 */
115 inline void setBorderDirection(const VectorI2& direction);
116
117 /**
118 * Sets the image orientation of this inpainting pixel.
119 * @param orientation Image orientation to be set
120 */
121 inline void setImageOrientation(const VectorI2& orientation);
122
123 /**
124 * Determines the border direction of a border pixel by a 5x5 area.
125 * @param mask The mask frame, must be valid
126 * @param width The width of the mask in pixel, with range [1, infinity)
127 * @param height The height of the mask in pixel, with range [1, infinity)
128 * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
129 * @param position The position to determine the direction for, this pixel must be a mask-border pixel, with range [0, with-1]x[0, height-1]
130 * @return Resulting direction
131 */
132 static VectorI2 determineBorderDirection5x5(const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition& position);
133
134 /**
135 * Determines the image orientation at a given position.
136 * The template parameter given the depth of the given response
137 * @param sobelResponse The Sobel filter response used to determine the image orientation, must be valid
138 * @param mask The mask frame with same frame dimension as the response frame, must be valid
139 * @param width The width of the frame in pixel, with range [1, infinity)
140 * @param height The height of the frame in pixel, with range [1, infinity)
141 * @param sobelStrideElements The number of elements between two consecutive Sobel response rows, in elements, with range [width*2, infinity)
142 * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
143 * @param position The position to determine the image orientation for, this pixel must be a mask-border pixel, with range [0, with-1]x[0, height-1]
144 * @tparam tChannels Number of channels of the frame for which the sobel response is provided, with range [1, infinity)
145 */
146 template <unsigned int tChannels>
147 static VectorI2 determineImageOrientation5x5(const int16_t* sobelResponse, const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int sobelStrideElements, const unsigned int maskPaddingElements, const CV::PixelPosition& position);
148
149 /**
150 * Returns whether this inpainting pixel has a lower priority than a second one.
151 * @param right Second inpainting pixel to be compared
152 * @return True, if so
153 */
154 inline bool operator<(const InpaintingPixel& right) const;
155
156 /**
157 * Returns whether two inpainting pixels are identical.
158 * @param right Second inpainting pixel
159 * @return True, if so
160 */
161 inline bool operator==(const InpaintingPixel& right) const;
162
163 protected:
164
165 /// Image orientation.
166 VectorI2 imageOrientation_ = VectorI2(0, 0);
167
168 /// Border direction.
169 VectorI2 borderDirection_ = VectorI2(0, 0);
170
171 /// Inpainting priority.
172 unsigned int priority_ = 0u;
173 };
174
175 /**
176 * Definition of a list holding inpainting pixels.
177 */
178 typedef std::list<InpaintingPixel> InpaintingPixelList;
179
180 public:
181
182 /**
183 * Creates a new initializer object.
184 * @param layer The layer for that the initial mapping has to be provided
185 * @param randomGenerator Random number generator
186 * @param iterations Number of shrinking iterations
187 * @param heuristic True, to apply a heuristic optimization, faster but more inaccurate
188 * @param maximalBoundingBoxOffset Maximal search offset around an inpainting pixel to be used in the non-heuristic mode
189 */
190 inline InitializerShrinkingPatchMatchingI1(LayerI1& layer, RandomGenerator& randomGenerator, const unsigned int iterations = 2u, const bool heuristic = false, const unsigned int maximalBoundingBoxOffset = 0xFFFFFFFF);
191
192 /**
193 * Invokes the initialization process.
194 * @see Initializer::invoke().
195 */
196 bool invoke(Worker* worker = nullptr) const override;
197
198 private:
199
200 /**
201 * Invokes the initialization process.
202 * @tparam tChannels The number of frame channels, with range [1, infinity)
203 */
204 template <unsigned int tChannels>
205 bool invoke(Worker* worker) const;
206
207 /**
208 * Applies the first iterations of the patch initialization for a frame using 5x5 areas.
209 * @param sobelResponse Horizontal and vertical sobel response for the given frame, must be valid
210 * @param staticMask Static inpainting mask to be filled with 0xFF for non-mask pixels, must be valid
211 * @param randomGenerator The random generator to be used
212 * @return True, if succeeded
213 * @tparam tChannels Number of channels of the frame
214 * @tparam SSD SSD wrapper class that is used to determine the patch similarity
215 */
216 template<unsigned int tChannels, typename SSD>
217 bool patchInitializationIteration5x5(Frame& sobelResponse, const Frame& staticMask, RandomGenerator& randomGenerator) const;
218
219 private:
220
221 /// Number of initialization iterations.
222 const unsigned int iterations_;
223
224 /// Heuristic execution statement of the initializer.
225 const bool heuristic_;
226
227 /// Maximal search offset that is applied during the initialization for each inpainting pixel.
228 const unsigned int maximalBoundingBoxOffset_;
229};
230
231inline InitializerShrinkingPatchMatchingI1::InpaintingPixel::InpaintingPixel(const PixelPosition& position, const VectorI2& borderDirection, const VectorI2& imageOrientation) :
232 PixelPosition(position),
233 imageOrientation_(imageOrientation),
234 borderDirection_(borderDirection),
235 priority_(abs(imageOrientation.perpendicular() * borderDirection))
236{
237 // nothing to do here
238}
239
241{
242 return borderDirection_;
243}
244
246{
247 return imageOrientation_;
248}
249
251{
252 return priority_;
253}
254
256{
257 borderDirection_ = direction;
258 priority_ = abs(imageOrientation_.perpendicular() * borderDirection_);
259}
260
262{
263 imageOrientation_ = orientation;
264 priority_ = abs(imageOrientation_.perpendicular() * borderDirection_);
265}
266
267template <unsigned int tChannels>
268VectorI2 InitializerShrinkingPatchMatchingI1::InpaintingPixel::determineImageOrientation5x5(const int16_t* sobelResponse, const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int sobelStrideElements, const unsigned int maskPaddingElements, const CV::PixelPosition& position)
269{
270 static_assert(tChannels >= 1u, "Invalid channel number!");
271
272 ocean_assert(sobelResponse != nullptr && mask != nullptr);
273
274 ocean_assert(sobelStrideElements >= width * 2u);
275 ocean_assert(position.x() < width && position.y() < height);
276
277 VectorI2 orientation(0, 0);
278
279 // as the response values will be zero at the image borders border pixels does not need to be investigated
280
281 for (unsigned int y = max(1, int(position.y()) - 2); y < min(position.y() + 3u, height - 1u); ++y)
282 {
283 for (unsigned int x = max(1, int(position.x()) - 2); x < min(position.x() + 3u, width - 1u); ++x)
284 {
285 constexpr uint8_t nonMaskPixelValue = 0xFFu;
286
287 if (!Segmentation::MaskAnalyzer::hasMaskNeighbor9<false>(mask, width, height, maskPaddingElements, PixelPosition(x, y), nonMaskPixelValue))
288 {
289 const int16_t* const sobelPixel = sobelResponse + y * sobelStrideElements + x * tChannels * 2u;
290
291 for (unsigned int n = 0u; n < tChannels; ++n)
292 {
293 const int16_t responseX = sobelPixel[n * 2u + 0u];
294 const int16_t responseY = sobelPixel[n * 2u + 1u];
295
296 if (responseX >= 0)
297 {
298 orientation += VectorI2(responseX, responseY);
299 }
300 else
301 {
302 orientation -= VectorI2(responseX, responseY);
303 }
304 }
305 }
306 }
307 }
308
309 return orientation;
310}
311
313{
314 //return pixelPriority < right.pixelPriority;
315 if(priority_ < right.priority_)
316 {
317 return true;
318 }
319
320 if (priority_ == right.priority_)
321 {
322 return index(1000) < right.index(1000);
323 }
324
325 return false;
326}
327
329{
330 return (PixelPosition&)*this == (PixelPosition&)right && borderDirection_ == right.borderDirection_ && imageOrientation_ == right.imageOrientation_;
331}
332
333inline InitializerShrinkingPatchMatchingI1::InitializerShrinkingPatchMatchingI1(LayerI1& layer, RandomGenerator& randomGenerator, const unsigned int initializations, const bool heuristic, const unsigned int maximalBoundingBoxOffset) :
334 Initializer(layer),
335 InitializerI(layer),
336 InitializerRandomized(layer, randomGenerator),
337 Initializer1(layer),
338 iterations_(initializations),
339 heuristic_(heuristic),
340 maximalBoundingBoxOffset_(maximalBoundingBoxOffset)
341{
342 // nothing to do here
343}
344
345template <unsigned int tChannels>
346inline unsigned int InitializerShrinkingPatchMatchingI1::SSDWrapperMask::determine5x5(const uint8_t* frame, const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const CV::PixelPosition& inPosition, const CV::PixelPosition& outPosition)
347{
348 constexpr uint8_t maskValue = 0x00u;
349
350 return Advanced::AdvancedSumSquareDifferences::patchWithRejectingMask8BitPerChannel<tChannels>(frame, frame, mask, mask, 5u, width, height, width, height, inPosition.x(), inPosition.y(), outPosition.x(), outPosition.y(), framePaddingElements, framePaddingElements, maskPaddingElements, maskPaddingElements, maskValue).first;
351}
352
353template <unsigned int tChannels>
354inline unsigned int InitializerShrinkingPatchMatchingI1::SSDWrapper::determine5x5(const uint8_t* frame, const uint8_t* /*mask*/, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int /*maskPaddingElements*/, const CV::PixelPosition& inPosition, const CV::PixelPosition& outPosition)
355{
356 return SumSquareDifferences::patchAtBorder8BitPerChannel<tChannels, 5u>(frame, frame, width, height, width, height, inPosition.x(), inPosition.y(), outPosition.x(), outPosition.y(), framePaddingElements, framePaddingElements).first;
357}
358
359template<unsigned int tChannels, typename SSD>
360bool InitializerShrinkingPatchMatchingI1::patchInitializationIteration5x5(Frame& sobelResponse, const Frame& staticMask, RandomGenerator& randomGenerator) const
361{
362 static_assert(tChannels >= 1u, "Invalid number of frame channels!");
363
364 Frame& frame = layer_.frame();
365
366 MappingI& mapping = layerI_.mapping();
367
368 const unsigned int width = layer_.width();
369 const unsigned int height = layer_.height();
370
371 ocean_assert(frame.isValid());
372 ocean_assert(sobelResponse.isValid() && staticMask.isValid());
373
374 const CV::PixelBoundingBox& boundingBox = layerI_.boundingBox();
375
376#ifdef OCEAN_DEBUG
377 for (unsigned int y = 0; y < height; ++y)
378 {
379 for (unsigned int x = 0; x < width; ++x)
380 {
381 const CV::PixelPosition& position = mapping.position(x, y);
382
383 ocean_assert(!position.isValid() || (position.x() < width && position.y() < height));
384 }
385 }
386#endif
387
388 Frame dynamicMask(staticMask, Frame::ACM_COPY_REMOVE_PADDING_LAYOUT);
389
390 uint8_t* const frameData = frame.data<uint8_t>();
391 const unsigned int framePaddingElements = frame.paddingElements();
392 const unsigned int frameStrideElements = frame.strideElements();
393
394 const uint8_t* const staticMaskData = staticMask.constdata<uint8_t>();
395 const unsigned int staticMaskStrideElements = staticMask.strideElements();
396
397 uint8_t* const dynamicMaskData = dynamicMask.data<uint8_t>();
398 const unsigned int dynamicMaskPaddingElements = dynamicMask.paddingElements();
399 const unsigned int dynamicMaskStrideElements = dynamicMask.strideElements();
400
401 int16_t* const sobelResponseData = sobelResponse.data<int16_t>();
402 const unsigned int sobelResponseStrideElements = sobelResponse.strideElements();
403
404 CV::PixelPositions borderPixels;
405 borderPixels.reserve(1024);
406
407 Segmentation::MaskAnalyzer::findBorderPixels4(dynamicMask.constdata<uint8_t>(), width, height, dynamicMask.paddingElements(), borderPixels, boundingBox);
408
409 InpaintingPixelList inpaintingPixels;
410 for (const CV::PixelPosition& borderPixel : borderPixels)
411 {
412 const VectorI2 borderDirection(InpaintingPixel::determineBorderDirection5x5(dynamicMaskData, width, height, dynamicMaskPaddingElements, borderPixel));
413 const VectorI2 imageOrientation(InpaintingPixel::determineImageOrientation5x5<tChannels>(sobelResponseData, dynamicMaskData, width, height, sobelResponseStrideElements, dynamicMaskPaddingElements, borderPixel));
414
415 inpaintingPixels.emplace_back(borderPixel, borderDirection, imageOrientation);
416 }
417
418 inpaintingPixels.sort();
419
420 while (!inpaintingPixels.empty())
421 {
422 const InpaintingPixel position = inpaintingPixels.back();
423 ocean_assert(dynamicMask.constpixel<uint8_t>(position.x(), position.y())[0] != 0xFF);
424
425 inpaintingPixels.pop_back();
426
427 unsigned int ssdBest = (unsigned int)(-1);
428
429 unsigned int xBest = (unsigned int)(-1);
430 unsigned int yBest = (unsigned int)(-1);
431
432 if (heuristic_)
433 {
434 const CV::PixelPosition& center = mapping.position(position);
435
436 // propagation itself
437 if (center.isValid())
438 {
439 for (unsigned int y = max(0, int(center.y()) - 3); y < min(center.y() + 4u, height); ++y)
440 {
441 for (unsigned int x = max(0, int(center.x()) - 3); x < min(center.x() + 4u, width); ++x)
442 {
443 if ((y != position.y() || x != position.x()) && staticMaskData[y * staticMaskStrideElements + x] == 0xFF)
444 {
445 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, PixelPosition(x, y));
446
447 if (ssd < ssdBest)
448 {
449 ssdBest = ssd;
450 xBest = x;
451 yBest = y;
452 }
453 }
454 }
455 }
456 }
457
458 // propagation left
459 if (position.x() != 0u && mapping.position(position.west()).isValid())
460 {
461 const PixelPosition east(mapping.position(position.west()).east());
462
463 for (unsigned int y = max(0, int(east.y()) - 3); y < min(east.y() + 4u, height); ++y)
464 {
465 for (unsigned int x = max(0, int(east.x()) - 3); x < min(east.x() + 4u, width); ++x)
466 {
467 if ((y != position.y() || x != position.x()) && staticMaskData[y * staticMaskStrideElements + x] == 0xFF)
468 {
469 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, PixelPosition(x, y));
470
471 if (ssd < ssdBest)
472 {
473 ssdBest = ssd;
474 xBest = x;
475 yBest = y;
476 }
477 }
478 }
479 }
480 }
481
482 // propagation top left
483 if (position.x() != 0u && position.y() != 0u && mapping.position(position.northWest()).isValid())
484 {
485 const PixelPosition southEast(mapping.position(position.northWest()).southEast());
486
487 for (unsigned int y = max(0, int(southEast.y()) - 3); y < min(southEast.y() + 4u, height); ++y)
488 {
489 for (unsigned int x = max(0, int(southEast.x()) - 3); x < min(southEast.x() + 4u, width); ++x)
490 {
491 if ((y != position.y() || x != position.x()) && staticMaskData[y * staticMaskStrideElements + x] == 0xFF)
492 {
493 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, PixelPosition(x, y));
494
495 if (ssd < ssdBest)
496 {
497 ssdBest = ssd;
498 xBest = x;
499 yBest = y;
500 }
501 }
502 }
503 }
504 }
505
506 // propagation top
507 if (position.y() != 0u && mapping.position(position.north()).isValid())
508 {
509 const PixelPosition south(mapping.position(position.north()).south());
510
511 for (unsigned int y = max(0, int(south.y()) - 3); y < min(south.y() + 4u, height); ++y)
512 {
513 for (unsigned int x = max(0, int(south.x()) - 3); x < min(south.x() + 4u, width); ++x)
514 {
515 if ((y != position.y() || x != position.x()) && staticMaskData[y * staticMaskStrideElements + x] == 0xFF)
516 {
517 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, PixelPosition(x, y));
518
519 if (ssd < ssdBest)
520 {
521 ssdBest = ssd;
522 xBest = x;
523 yBest = y;
524 }
525 }
526 }
527 }
528 }
529
530 // propagation top right
531 if (position.y() != 0u && position.x() + 1u < width && mapping.position(position.northEast()).isValid())
532 {
533 const PixelPosition southWest(mapping.position(position.northEast()).southWest());
534
535 for (unsigned int y = max(0, int(southWest.y()) - 3); y < min(southWest.y() + 4u, height); ++y)
536 {
537 for (unsigned int x = max(0, int(southWest.x()) - 3); x < min(southWest.x() + 4u, width); ++x)
538 {
539 if ((y != position.y() || x != position.x()) && staticMaskData[y * staticMaskStrideElements + x] == 0xFF)
540 {
541 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, PixelPosition(x, y));
542
543 if (ssd < ssdBest)
544 {
545 ssdBest = ssd;
546 xBest = x;
547 yBest = y;
548 }
549 }
550 }
551 }
552 }
553
554 // propagation right
555 if (position.x() + 1u < width && mapping.position(position.east()).isValid())
556 {
557 const PixelPosition west(mapping.position(position.east()).west());
558
559 for (unsigned int y = max(0, int(west.y()) - 3); y < min(west.y() + 4u, height); ++y)
560 {
561 for (unsigned int x = max(0, int(west.x()) - 3); x < min(west.x() + 4u, width); ++x)
562 {
563 if ((y != position.y() || x != position.x()) && staticMaskData[y * staticMaskStrideElements + x] == 0xFF)
564 {
565 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, PixelPosition(x, y));
566
567 if (ssd < ssdBest)
568 {
569 ssdBest = ssd;
570 xBest = x;
571 yBest = y;
572 }
573 }
574 }
575 }
576 }
577
578 // propagation bottom right
579 if (position.y() + 1u < height && position.x() + 1u < width && mapping.position(position.southEast()).isValid())
580 {
581 const PixelPosition northWest(mapping.position(position.southEast()).northWest());
582
583 for (unsigned int y = max(0, int(northWest.y()) - 3); y < min(northWest.y() + 4u, height); ++y)
584 {
585 for (unsigned int x = max(0, int(northWest.x()) - 3); x < min(northWest.x() + 4u, width); ++x)
586 {
587 if ((y != position.y() || x != position.x()) && staticMaskData[y * staticMaskStrideElements + x] == 0xFF)
588 {
589 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, PixelPosition(x, y));
590
591 if (ssd < ssdBest)
592 {
593 ssdBest = ssd;
594 xBest = x;
595 yBest = y;
596 }
597 }
598 }
599 }
600 }
601
602 // propagation bottom
603 if (position.y() + 1u < height && mapping.position(position.south()).isValid())
604 {
605 const PixelPosition north(mapping.position(position.south()).north());
606
607 for (unsigned int y = max(0, int(north.y()) - 3); y < min(north.y() + 4u, height); ++y)
608 {
609 for (unsigned int x = max(0, int(north.x()) - 3); x < min(north.x() + 4u, width); ++x)
610 {
611 if ((y != position.y() || x != position.x()) && staticMaskData[y * staticMaskStrideElements + x] == 0xFF)
612 {
613 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, PixelPosition(x, y));
614
615 if (ssd < ssdBest)
616 {
617 ssdBest = ssd;
618 xBest = x;
619 yBest = y;
620 }
621 }
622 }
623 }
624 }
625
626 // propagation bottom left
627 if (position.x() != 0u && position.y() + 1u < height && mapping.position(position.southWest()).isValid())
628 {
629 const PixelPosition northEast(mapping.position(position.southWest()).northEast());
630
631 for (unsigned int y = max(0, int(northEast.y()) - 3); y < min(northEast.y() + 4u, height); ++y)
632 {
633 for (unsigned int x = max(0, int(northEast.x()) - 3); x < min(northEast.x() + 4u, width); ++x)
634 {
635 if ((y != position.y() || x != position.x()) && staticMaskData[y * staticMaskStrideElements + x] == 0xFF)
636 {
637 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, PixelPosition(x, y));
638
639 if (ssd < ssdBest)
640 {
641 ssdBest = ssd;
642 xBest = x;
643 yBest = y;
644 }
645 }
646 }
647 }
648 }
649
650 Scalar diagonal_4 = min(Vector2(Scalar(width), Scalar(height)).length() * Scalar(0.25), Scalar(10));
651 Vector3 normal(Scalar(position.borderDirection().x()), Scalar(position.borderDirection().y()), 0);
652
653 if (normal.normalize())
654 {
655 for (unsigned int n = 0u; n < 100u; ++n)
656 {
657 Rotation rotation(0, 0, 1, Random::scalar(randomGenerator, -Numeric::deg2rad(90), Numeric::deg2rad(90)));
658 const Scalar length = Random::scalar(randomGenerator, 1, diagonal_4);
659 const Vector3 offset = rotation * normal * length;
660
661 const PixelPosition first(position.x() + Numeric::round32(offset.x()), position.y() + Numeric::round32(offset.y()));
662 if (first.x() < width && first.y() < height && staticMaskData[first.y() * staticMaskStrideElements + first.x()] == 0xFF)
663 {
664 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, first);
665
666 if (ssd < ssdBest)
667 {
668 ssdBest = ssd;
669
670 xBest = first.x();
671 yBest = first.y();
672 }
673 }
674
675 const PixelPosition second(position.x() - Numeric::round32(offset.x()), position.y() - Numeric::round32(offset.y()));
676 if (second.x() < width && second.y() < height && staticMaskData[second.y() * staticMaskStrideElements + second.x()] == 0xFF)
677 {
678 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, second);
679
680 if (ssd < ssdBest)
681 {
682 ssdBest = ssd;
683
684 xBest = second.x();
685 yBest = second.y();
686 }
687 }
688 }
689 }
690
691 if (ssdBest != (unsigned int)(-1))
692 {
693 const unsigned int iterations = 200u;
694 for (unsigned int n = 0u; n < iterations; ++n)
695 {
696 const int xRadius = max(1, int(width - (width - 1u) * n / iterations) >> 1);
697 const int yRadius = max(1, int(height - (height - 1u) * n / iterations) >> 1);
698
699 const int offsetX = RandomI::random(randomGenerator, -xRadius, xRadius);
700 const int offsetY = RandomI::random(randomGenerator, -yRadius, yRadius);
701
702 const unsigned int randomX = xBest + offsetX;
703 const unsigned int randomY = yBest + offsetY;
704
705 if (randomX < width && randomY < height && (randomY != position.y() || randomX != position.x()) && staticMaskData[randomY * staticMaskStrideElements + randomX] == 0xFF)
706 {
707 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, CV::PixelPosition(randomX, randomY));
708
709 if (ssd < ssdBest)
710 {
711 ssdBest = ssd;
712
713 xBest = randomX;
714 yBest = randomY;
715 }
716 }
717 }
718 }
719 }
720
721 if (ssdBest == (unsigned int)(-1))
722 {
723 if (maximalBoundingBoxOffset_ == (unsigned int)(-1))
724 {
725 // find the best matching patch with brute force
726 for (unsigned int y = 0u; y < height; ++y)
727 {
728 const uint8_t* const staticMaskRow = staticMask.constrow<uint8_t>(y);
729
730 for (unsigned int x = 0u; x < width; ++x)
731 {
732 if ((y != position.y() || x != position.x()) && staticMaskRow[x] == 0xFF)
733 {
734 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, CV::PixelPosition(x, y));
735
736 if (ssd < ssdBest)
737 {
738 ssdBest = ssd;
739
740 xBest = x;
741 yBest = y;
742 }
743 }
744 }
745 }
746 }
747 else
748 {
749 const unsigned int left = max(0, int(position.x() - maximalBoundingBoxOffset_));
750 const unsigned int top = max(0, int(position.y() - maximalBoundingBoxOffset_));
751
752 const unsigned int rightEnd = min(position.x() + maximalBoundingBoxOffset_ + 1u, width);
753 const unsigned int bottomEnd = min(position.y() + maximalBoundingBoxOffset_ + 1u, height);
754
755 // find the best matching patch with brute force
756 for (unsigned int y = top; y < bottomEnd; ++y)
757 {
758 for (unsigned int x = left; x < rightEnd; ++x)
759 {
760 if ((y != position.y() || x != position.x()) && staticMaskData[y * staticMaskStrideElements + x] == 0xFF)
761 {
762 const unsigned int ssd = SSD::template determine5x5<tChannels>(frameData, dynamicMaskData, width, height, framePaddingElements, dynamicMaskPaddingElements, position, CV::PixelPosition(x, y));
763
764 if (ssd < ssdBest)
765 {
766 ssdBest = ssd;
767
768 xBest = x;
769 yBest = y;
770 }
771 }
772 }
773 }
774 }
775 }
776
777 if (ssdBest == (unsigned int)(-1))
778 {
779 for (unsigned int y = 0u; ssdBest == (unsigned int)(-1) && y < height; ++y)
780 {
781 const uint8_t* dynamicMaskRow = dynamicMask.constrow<uint8_t>(y);
782
783 for (unsigned int x = 0u; x < width; ++x)
784 {
785 if (dynamicMaskRow[x] == 0xFFu)
786 {
787 ssdBest = (unsigned int)(-2);
788
789 xBest = x;
790 yBest = y;
791
792 break;
793 }
794 }
795 }
796 }
797
798 if (ssdBest == (unsigned int)(-1))
799 {
800 return false;
801 }
802
803 ocean_assert(xBest < width && yBest < height);
804
805 // update the color frame border pixel itself
806 CVUtilities::copyPixel<tChannels>(frameData + position.y() * frameStrideElements + position.x() * tChannels, frameData + yBest * frameStrideElements + xBest * tChannels);
807
808 for (unsigned int y = max(0, int(position.y()) - 1); y < min(position.y() + 2u, height); ++y)
809 {
810 for (unsigned int x = max(0, int(position.x()) - 1); x < min(position.x() + 2u, width); ++x)
811 {
812 CV::FrameFilterSobel::filterPixelHorizontalVertical8BitPerChannel<int16_t, tChannels>(frameData, width, height, x, y, sobelResponseData + y * sobelResponseStrideElements + x * tChannels * 2u, framePaddingElements);
813 }
814 }
815
816 // find new border pixel
817 PixelPositions newBorderPixels;
818 newBorderPixels.reserve(4u);
819
820 constexpr uint8_t nonMaskPixelValue = 0xFF;
821
822 // check all pixels in the 4-neighborhood without frame border pixels as frame border pixels are mask-border pixels by definition already
823 if (position.x() - 1u < width - 2u)
824 {
825 // north
826 if (position.y() > 1u && !Segmentation::MaskAnalyzer::hasMaskNeighbor5<true>(dynamicMaskData, width, height, dynamicMaskPaddingElements, position.north(), nonMaskPixelValue))
827 {
828 newBorderPixels.emplace_back(position.north());
829 }
830
831 // south
832 if (position.y() + 2u < height && !Segmentation::MaskAnalyzer::hasMaskNeighbor5<true>(dynamicMaskData, width, height, dynamicMaskPaddingElements, position.south(), nonMaskPixelValue))
833 {
834 newBorderPixels.emplace_back(position.south());
835 }
836 }
837
838 if (position.y() - 1u < height - 2u)
839 {
840 // west
841 if (position.x() > 1u && !Segmentation::MaskAnalyzer::hasMaskNeighbor5<true>(dynamicMaskData, width, height, dynamicMaskPaddingElements, position.west(), nonMaskPixelValue))
842 {
843 newBorderPixels.emplace_back(position.west());
844 }
845
846 // east
847 if (position.x() + 2u < width && !Segmentation::MaskAnalyzer::hasMaskNeighbor5<true>(dynamicMaskData, width, height, dynamicMaskPaddingElements, position.east(), nonMaskPixelValue))
848 {
849 newBorderPixels.emplace_back(position.east());
850 }
851 }
852
853#ifdef OCEAN_DEBUG
854
855 for (const PixelPosition& newBorerPixel : newBorderPixels)
856 {
857 for (const InpaintingPixel& inpaintingPixel : inpaintingPixels)
858 {
859 ocean_assert(newBorerPixel != inpaintingPixel);
860 }
861 }
862
863#endif // OCEAN_DEBUG
864
865 // update the border mask frame
866 ocean_assert(dynamicMask.constpixel<uint8_t>(position.x(), position.y())[0] != 0xFFu);
867 ocean_assert(staticMask.constpixel<uint8_t>(xBest, yBest)[0] == 0xFFu);
868
869 dynamicMaskData[position.y() * dynamicMaskStrideElements + position.x()] = 0xFFu;
870
871 mapping.setPosition(position.x(), position.y(), PixelPosition(xBest, yBest));
872
873 InpaintingPixelList changedInpaintingPixels;
874
875 // update all inpainting positions inside the direction neighborhood, stop if all pixels have been updated
876 for (InpaintingPixelList::iterator i = inpaintingPixels.begin(); i != inpaintingPixels.end(); /* noop */)
877 {
878 ocean_assert(*i != position);
879
880 // check whether the point is inside the small neighborhood
881 const int dx = abs(int(i->x()) - int(position.x()));
882 const int dy = abs(int(i->y()) - int(position.y()));
883
884 if (dx <= 3 && dy <= 3)
885 {
886 // the border direction might have changed
887 changedInpaintingPixels.emplace_back(*i, InpaintingPixel::determineBorderDirection5x5(dynamicMaskData, width, height, dynamicMaskPaddingElements, *i), InpaintingPixel::determineImageOrientation5x5<tChannels>(sobelResponseData, dynamicMaskData, width, height, sobelResponseStrideElements, dynamicMaskPaddingElements, *i));
888
889 i = inpaintingPixels.erase(i);
890 }
891 else
892 {
893 ++i;
894 }
895 }
896
897 // add new border pixels
898 for (const PixelPosition& newBorderPixel : newBorderPixels)
899 {
900 changedInpaintingPixels.emplace_back(newBorderPixel, InpaintingPixel::determineBorderDirection5x5(dynamicMaskData, width, height, dynamicMaskPaddingElements, newBorderPixel), InpaintingPixel::determineImageOrientation5x5<tChannels>(sobelResponseData, dynamicMaskData, width, height, sobelResponseStrideElements, dynamicMaskPaddingElements, newBorderPixel));
901 }
902
903 changedInpaintingPixels.sort();
904 inpaintingPixels.merge(changedInpaintingPixels);
905
906#ifdef OCEAN_DEBUG
907 for (const InpaintingPixel& inpaintingPixel : inpaintingPixels)
908 {
909 ocean_assert(inpaintingPixel == InpaintingPixel(inpaintingPixel, InpaintingPixel::determineBorderDirection5x5(dynamicMaskData, width, height, dynamicMaskPaddingElements, inpaintingPixel), InpaintingPixel::determineImageOrientation5x5<tChannels>(sobelResponseData, dynamicMaskData, width, height, sobelResponseStrideElements, dynamicMaskPaddingElements, inpaintingPixel)));
910 }
911#endif // OCEAN_DEBUG
912 }
913
914#ifdef OCEAN_DEBUG
915 for (unsigned int y = 0; y < height; ++y)
916 {
917 for (unsigned int x = 0; x < width; ++x)
918 {
919 ocean_assert(dynamicMask.constpixel<uint8_t>(x, y)[0] == 0xFFu);
920 const CV::PixelPosition& position = mapping.position(x, y);
921
922 if (staticMask.constpixel<uint8_t>(x, y)[0] != 0xFFu)
923 {
924 ocean_assert(position.isValid() && position.x() < width && position.y() < height);
925 }
926 else
927 {
928 ocean_assert(!position.isValid());
929 }
930 }
931 }
932#endif
933
934 return true;
935}
936
937}
938
939}
940
941}
942
943#endif // META_OCEAN_CV_SYNTHESIS_INITIALIZER_SHRINKING_PATCH_MATCHING_I_1_H
PixelPositionT< T > west() const
Returns the pixel position west to this position.
Definition PixelPosition.h:561
T index(const unsigned int width) const
Returns the index of this position inside a frame with given width.
Definition PixelPosition.h:676
PixelPositionT< T > southWest() const
Returns the pixel position south west to this position.
Definition PixelPosition.h:567
PixelPositionT< T > north() const
Returns the pixel position north to this position.
Definition PixelPosition.h:549
Vector2 vector() const
Returns a sub-pixel accuracy vector of this pixel position.
Definition PixelPosition.h:609
bool isValid() const
Returns whether this pixel position object holds two valid parameters.
PixelPositionT< T > east() const
Returns the pixel position east to this position.
Definition PixelPosition.h:585
T y() const
Returns the vertical coordinate position of this object.
Definition PixelPosition.h:470
PixelPositionT< T > southEast() const
Returns the pixel position south east to this position.
Definition PixelPosition.h:579
T x() const
Returns the horizontal coordinate position of this object.
Definition PixelPosition.h:458
PixelPositionT< T > northWest() const
Returns the pixel position north west to this position.
Definition PixelPosition.h:555
PixelPositionT< T > south() const
Returns the pixel position south to this position.
Definition PixelPosition.h:573
PixelPositionT< T > northEast() const
Returns the pixel position north east to this position.
Definition PixelPosition.h:591
static void findBorderPixels4(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, PixelPositions &borderPixels, const PixelBoundingBox &boundingBox=PixelBoundingBox(), Worker *worker=nullptr, const uint8_t nonMaskValue=255u)
Determines all border pixels in an 8 bit mask frame for a 4-neighborhood.
This class is the base class for all initializers that are provided for a single frame only.
Definition Initializer1.h:29
This class implements the base class for all synthesis initializers.
Definition Initializer.h:34
Layer & layer_
Synthesis layer that has to be initialized.
Definition Initializer.h:55
This class implements the base class for all initializer objects that are applied for mappings with i...
Definition InitializerI.h:30
LayerI & layerI_
Specialized layer reference.
Definition InitializerI.h:42
This class is the base class for all initializers that mainly initialize the synthesis mapping by a h...
Definition InitializerRandomized.h:30
This class implements a single inpainting pixel.
Definition InitializerShrinkingPatchMatchingI1.h:82
InpaintingPixel(const PixelPosition &position, const VectorI2 &borderDirection, const VectorI2 &imageOrientation)
Creates a new inpainting pixel by a given border direction and image orientation.
Definition InitializerShrinkingPatchMatchingI1.h:231
static VectorI2 determineImageOrientation5x5(const int16_t *sobelResponse, const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int sobelStrideElements, const unsigned int maskPaddingElements, const CV::PixelPosition &position)
Determines the image orientation at a given position.
Definition InitializerShrinkingPatchMatchingI1.h:268
void setImageOrientation(const VectorI2 &orientation)
Sets the image orientation of this inpainting pixel.
Definition InitializerShrinkingPatchMatchingI1.h:261
bool operator<(const InpaintingPixel &right) const
Returns whether this inpainting pixel has a lower priority than a second one.
Definition InitializerShrinkingPatchMatchingI1.h:312
unsigned int priority_
Inpainting priority.
Definition InitializerShrinkingPatchMatchingI1.h:172
void setBorderDirection(const VectorI2 &direction)
Sets the border direction of this inpainting pixel.
Definition InitializerShrinkingPatchMatchingI1.h:255
static VectorI2 determineBorderDirection5x5(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, const PixelPosition &position)
Determines the border direction of a border pixel by a 5x5 area.
const VectorI2 & imageOrientation() const
Returns the image orientation of this inpainting pixel.
Definition InitializerShrinkingPatchMatchingI1.h:245
bool operator==(const InpaintingPixel &right) const
Returns whether two inpainting pixels are identical.
Definition InitializerShrinkingPatchMatchingI1.h:328
const VectorI2 & borderDirection() const
Returns the border direction of this inpainting pixel.
Definition InitializerShrinkingPatchMatchingI1.h:240
VectorI2 imageOrientation_
Image orientation.
Definition InitializerShrinkingPatchMatchingI1.h:166
VectorI2 borderDirection_
Border direction.
Definition InitializerShrinkingPatchMatchingI1.h:169
unsigned int priority() const
Returns the priority of this inpainting pixel.
Definition InitializerShrinkingPatchMatchingI1.h:250
Definition of a wrapper class for ssd functions.
Definition InitializerShrinkingPatchMatchingI1.h:67
static unsigned int determine5x5(const uint8_t *frame, const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const CV::PixelPosition &inPosition, const CV::PixelPosition &outPosition)
Wrapper function for SumSquareDifferences::patchAtBorder8BitPerChannel.
Definition InitializerShrinkingPatchMatchingI1.h:354
Definition of a wrapper class for ssd functions.
Definition InitializerShrinkingPatchMatchingI1.h:52
static unsigned int determine5x5(const uint8_t *frame, const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int maskPaddingElements, const CV::PixelPosition &inPosition, const CV::PixelPosition &outPosition)
Wrapper function for AdvancedSumSquareDifferences::patchWithRejectingMask8BitPerChannel().
Definition InitializerShrinkingPatchMatchingI1.h:346
This class implements an initializer that shrinks the inpainting mask by the application of images pa...
Definition InitializerShrinkingPatchMatchingI1.h:45
const unsigned int maximalBoundingBoxOffset_
Maximal search offset that is applied during the initialization for each inpainting pixel.
Definition InitializerShrinkingPatchMatchingI1.h:228
std::list< InpaintingPixel > InpaintingPixelList
Definition of a list holding inpainting pixels.
Definition InitializerShrinkingPatchMatchingI1.h:178
InitializerShrinkingPatchMatchingI1(LayerI1 &layer, RandomGenerator &randomGenerator, const unsigned int iterations=2u, const bool heuristic=false, const unsigned int maximalBoundingBoxOffset=0xFFFFFFFF)
Creates a new initializer object.
Definition InitializerShrinkingPatchMatchingI1.h:333
bool invoke(Worker *worker=nullptr) const override
Invokes the initialization process.
const bool heuristic_
Heuristic execution statement of the initializer.
Definition InitializerShrinkingPatchMatchingI1.h:225
bool invoke(Worker *worker) const
Invokes the initialization process.
bool patchInitializationIteration5x5(Frame &sobelResponse, const Frame &staticMask, RandomGenerator &randomGenerator) const
Applies the first iterations of the patch initialization for a frame using 5x5 areas.
Definition InitializerShrinkingPatchMatchingI1.h:360
const unsigned int iterations_
Number of initialization iterations.
Definition InitializerShrinkingPatchMatchingI1.h:222
unsigned int height() const
Returns the height of this layer.
Definition Layer.h:169
unsigned int width() const
Returns the width of this layer.
Definition Layer.h:164
const Frame & frame() const
Returns the frame of this layer.
Definition Layer.h:174
const PixelBoundingBox & boundingBox() const
Returns the optional bounding box of this layer.
Definition Layer.h:194
This class implements a single layer for pixel synthesis within one frame and pixel accuracy.
Definition LayerI1.h:41
virtual const MappingI & mapping() const =0
Returns the mapping of this synthesis layer.
This class implements a mapping with integer accuracy.
Definition MappingI.h:30
const PixelPosition & position(const unsigned int x, const unsigned int y) const
Returns the mapping for a given position.
Definition MappingI.h:215
void setPosition(const unsigned int x, const unsigned int y, const PixelPosition &pixelPosition)
Sets a new mapping for a specified position.
Definition MappingI.h:237
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 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
bool isValid() const
Returns whether this frame is valid.
Definition Frame.h:4528
const T * constpixel(const unsigned int x, const unsigned int y, const unsigned int planeIndex=0u) const
Returns the pointer to the constant data of a specific pixel.
Definition Frame.h:4330
@ ACM_COPY_REMOVE_PADDING_LAYOUT
Same as CM_COPY_REMOVE_PADDING_LAYOUT.
Definition Frame.h:1834
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
static constexpr T deg2rad(const T deg)
Converts deg to rad.
Definition Numeric.h:3232
static constexpr int32_t round32(const T value)
Returns the rounded 32 bit integer value of a given value.
Definition Numeric.h:2064
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.
static T scalar(const T lower, const T upper)
Returns a random number between two borders.
Definition Random.h:388
This class implements a axis-angle rotation using floating point values.
Definition Rotation.h:79
const T & x() const noexcept
Returns the x value.
Definition Vector2.h:710
const T & y() const noexcept
Returns the y value.
Definition Vector2.h:722
const T & y() const noexcept
Returns the y value.
Definition Vector3.h:824
bool normalize()
Normalizes this vector.
Definition Vector3.h:659
const T & x() const noexcept
Returns the x value.
Definition Vector3.h:812
This class implements a worker able to distribute function calls over different threads.
Definition Worker.h:33
std::vector< PixelPosition > PixelPositions
Definition of a vector holding pixel positions (with positive coordinate values).
Definition PixelPosition.h:48
PixelPositionT< unsigned int > PixelPosition
Definition of the default PixelPosition object with a data type allowing only positive coordinate val...
Definition PixelPosition.h:34
VectorT2< int > VectorI2
Definition of a 2D vector with integer values.
Definition Vector2.h:49
float Scalar
Definition of a scalar type.
Definition Math.h:129
VectorT2< Scalar > Vector2
Definition of a 2D vector.
Definition Vector2.h:28
The namespace covering the entire Ocean framework.
Definition Accessor.h:15