Ocean
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 
17 #include "ocean/cv/CVUtilities.h"
19 
21 
23 
24 #include "ocean/math/Random.h"
25 
26 #include <list>
27 
28 namespace Ocean
29 {
30 
31 namespace CV
32 {
33 
34 namespace 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  */
41 class 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  */
66  class SSDWrapper
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 
231 inline 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 
267 template <unsigned int tChannels>
268 VectorI2 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 
333 inline 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 
345 template <unsigned int tChannels>
346 inline 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 
353 template <unsigned int tChannels>
354 inline 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 
359 template<unsigned int tChannels, typename SSD>
360 bool 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
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:1760
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:4026
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:4136
T * data(const unsigned int planeIndex=0u)
Returns a pointer to the pixel data of a specific plane.
Definition: Frame.h:4127
bool isValid() const
Returns whether this frame is valid.
Definition: Frame.h:4416
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:4218
@ ACM_COPY_REMOVE_PADDING_LAYOUT
Same as CM_COPY_REMOVE_PADDING_LAYOUT.
Definition: Frame.h:1786
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:4161
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:4010
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:698
const T & y() const noexcept
Returns the y value.
Definition: Vector2.h:710
const T & y() const noexcept
Returns the y value.
Definition: Vector3.h:812
bool normalize()
Normalizes this vector.
Definition: Vector3.h:647
const T & x() const noexcept
Returns the x value.
Definition: Vector3.h:800
This class implements a worker able to distribute function calls over different threads.
Definition: Worker.h:33
std::vector< PixelPosition > PixelPositions
Definition of a vector holding pixel positions (with positive coordinate values).
Definition: PixelPosition.h:48
PixelPositionT< unsigned int > PixelPosition
Definition of the default PixelPosition object with a data type allowing only positive coordinate val...
Definition: PixelPosition.h:27
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:128
VectorT2< Scalar > Vector2
Definition of a 2D vector.
Definition: Vector2.h:21
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15