Ocean
FrameFilterBlur.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_CV_SEGMENTATION_FRAME_FILTER_BLUR_H
9 #define META_OCEAN_CV_SEGMENTATION_FRAME_FILTER_BLUR_H
10 
13 
14 #include "ocean/base/Frame.h"
15 #include "ocean/base/RandomI.h"
16 
17 #include "ocean/cv/FrameBlender.h"
19 
20 namespace Ocean
21 {
22 
23 namespace CV
24 {
25 
26 namespace Segmentation
27 {
28 
29 /**
30  * This class implements functions allowing to blur image content.
31  * @ingroup cvsegmentation
32  */
33 class OCEAN_CV_SEGMENTATION_EXPORT FrameFilterBlur
34 {
35  public:
36 
37  /**
38  * The following comfort class provides comfortable functions simplifying prototyping applications but also increasing binary size of the resulting applications.
39  * Best practice is to avoid using these functions if binary size matters,<br>
40  * as for every comfort function a corresponding function exists with specialized functionality not increasing binary size significantly.
41  */
42  class OCEAN_CV_SEGMENTATION_EXPORT Comfort
43  {
44  public:
45 
46  /**
47  * Blurs several masked regions in an image.
48  * The resulting image will be a blurred version of the original image, with the masked regions blurred and the remaining regions untouched.
49  * The image color of blurred regions is defined by the average color of the mask region.
50  * @param image The image to be blurred, must be valid
51  * @param mask The mask defining the regions to be blurred, with pixel format FORMAT_Y8, mask values 0x00 define regions to be blurred, 0xFF define regions to remain untouched, must be valid
52  * @param blurBorder Optional border in pixel defining a smooth transition between blurred and non-blurred regions (outside of the masked region), with range [0, infinity), must be odd, 0 to avoid a smooth transition
53  * @param randomGenerator Optional random generator object to add tiny random nose to each blurred region, nullptr to avoid random noise
54  * @return True, if succeeded
55  */
56  static bool blurMaskRegions(Frame& image, const Frame& mask, const unsigned int blurBorder = 5u, RandomGenerator* randomGenerator = nullptr);
57  };
58 
59  public:
60 
61  /**
62  * Blurs several masked regions in an image.
63  * The resulting image will be a blurred version of the original image, with the masked regions blurred and the remaining regions untouched.
64  * The image color of blurred regions is defined by the average color of the mask region.
65  * @param image The image to be blurred, must be valid
66  * @param mask The mask defining the regions to be blurred, mask values 0x00 define regions to be blurred, 0xFF define regions to remain untouched, must be valid
67  * @param width The width of the image and mask frame in pixel, with range [1, infinity)
68  * @param height The height of the image and mask frame in pixel, with range [1, infinity)
69  * @param imagePaddingElements The number of padding elements at the end of each image row, in elements, with range [0, infinity)
70  * @param maskPaddingElements The number of padding elements at the end of each mask row, in elements, with range [0, infinity)
71  * @param blurBorder Optional border in pixel defining a smooth transition between blurred and non-blurred regions (outside of the masked region), with range [0, infinity), must be odd, 0 to avoid a smooth transition
72  * @param randomGenerator Optional random generator object to add tiny random nose to each blurred region, nullptr to avoid random noise
73  * @return True, if succeeded
74  * @tparam tChannels The number of channels the image has, with range [1, infinity)
75  */
76  template <unsigned int tChannels>
77  static bool blurMaskRegions8BitPerChannel(uint8_t* image, const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int imagePaddingElements, const unsigned int maskPaddingElements, const unsigned int blurBorder = 5u, RandomGenerator* randomGenerator = nullptr);
78 };
79 
80 template <unsigned int tChannels>
81 bool FrameFilterBlur::blurMaskRegions8BitPerChannel(uint8_t* image, const uint8_t* mask, const unsigned int width, const unsigned int height, const unsigned int imagePaddingElements, const unsigned int maskPaddingElements, const unsigned int blurBorder, RandomGenerator* randomGenerator)
82 {
83  static_assert(tChannels >= 1u, "Invalid channel number!");
84 
85  ocean_assert(image != nullptr);
86  ocean_assert(mask != nullptr);
87  ocean_assert(width >= 1u && height >= 1u);
88  ocean_assert(blurBorder == 0u || blurBorder % 2u == 1u);
89 
90  Frame imageFrame(FrameType(width, height, FrameType::genericPixelFormat<uint8_t, tChannels>(), FrameType::ORIGIN_UPPER_LEFT), image, Frame::CM_USE_KEEP_LAYOUT, imagePaddingElements);
91 
92  const Frame imageMask(FrameType(width, height, FrameType::FORMAT_Y8, FrameType::ORIGIN_UPPER_LEFT), mask, Frame::CM_USE_KEEP_LAYOUT, maskPaddingElements);
93 
94  // first, we determine the individual joined masks
95 
97 
99  CV::Segmentation::MaskAnalyzer::analyzeMaskSeparation8Bit(mask, width, height, maskPaddingElements, separation.data<uint32_t>(), separation.paddingElements(), maskBlocks);
100 
101  // now, we determine the average color value of each individual block and update the image content
102 
103  CV::PixelBoundingBoxes pixelBoundingBoxes;
104 
105  for (const CV::Segmentation::MaskAnalyzer::MaskBlock& maskBlock : maskBlocks)
106  {
107  ocean_assert(maskBlock.size() > 0 && maskBlock.size() <= width * height);
108 
109  CV::PixelBoundingBox pixelBoundingBox;
110 
111  uint64_t sumColors[tChannels] = {};
112 
113  size_t pixels = 0;
114 
115  for (unsigned int y = 0u; pixels != maskBlock.size() && y < separation.height(); ++y)
116  {
117  const uint32_t* const rowSepartion = separation.constrow<uint32_t>(y);
118  const uint8_t* const rowImage = imageFrame.constrow<uint8_t>(y);
119 
120  for (unsigned int x = 0u; pixels != maskBlock.size() && x < separation.width(); ++x)
121  {
122  if (rowSepartion[x] == maskBlock.id())
123  {
124  for (unsigned int n = 0u; n < tChannels; ++n)
125  {
126  sumColors[n] += rowImage[x * tChannels + n];
127  }
128 
129  ++pixels;
130 
131  pixelBoundingBox += CV::PixelPosition(x, y);
132  }
133  }
134  }
135 
136  ocean_assert(pixels != 0);
137  ocean_assert(pixelBoundingBox.isValid());
138 
139  if (pixels == 0)
140  {
141  return false;
142  }
143 
144  uint8_t averageColors[tChannels];
145 
146  for (unsigned int n = 0u; n < tChannels; ++n)
147  {
148  averageColors[n] = uint8_t((sumColors[n] + (pixels / 2)) / pixels);
149 
150  if (randomGenerator != nullptr)
151  {
152  averageColors[n] = uint8_t(minmax(0, int(averageColors[n]) + RandomI::random(*randomGenerator, -10, 10), 255));
153  }
154  }
155 
156  for (unsigned int y = pixelBoundingBox.top(); y < pixelBoundingBox.bottomEnd(); ++y)
157  {
158  const uint32_t* const rowSepartion = separation.constrow<uint32_t>(y);
159  uint8_t* const rowImage = imageFrame.row<uint8_t>(y);
160 
161  for (unsigned int x = pixelBoundingBox.left(); x < pixelBoundingBox.rightEnd(); ++x)
162  {
163  if (rowSepartion[x] == maskBlock.id())
164  {
165  for (unsigned int n = 0u; n < tChannels; ++n)
166  {
167  rowImage[x * tChannels + n] = averageColors[n];
168  }
169  }
170  }
171  }
172 
173  pixelBoundingBoxes.push_back(pixelBoundingBox);
174  }
175 
176  if (blurBorder != 0u && blurBorder % 2u != 0u)
177  {
178  // in case the user wants to apply a smooth border between the mask content and the surrounding (remaining image content)
179  // we blend the results with a Gaussian blur
180 
181  ocean_assert(maskBlocks.size() == pixelBoundingBoxes.size());
182 
183  CV::PixelPositions borderPixels;
184 
185  for (size_t n = 0; n < maskBlocks.size(); ++n)
186  {
187  const CV::PixelBoundingBox& pixelBoundingBox = pixelBoundingBoxes[n];
188 
189  const CV::PixelBoundingBox extendedBoundingBox = pixelBoundingBox.extended(blurBorder, 0u, 0u, width - 1u, height - 1u);
190 
191  Frame sourceSubFrame = imageFrame.subFrame(extendedBoundingBox.left(), extendedBoundingBox.top(), extendedBoundingBox.width(), extendedBoundingBox.height(), Frame::CM_USE_KEEP_LAYOUT);
192 
193  Frame blurredSubFrame;
194  if (!CV::FrameFilterGaussian::filter(sourceSubFrame, blurredSubFrame, blurBorder))
195  {
196  ocean_assert(false && "This should never happen!");
197  return false;
198  }
199 
200  Frame blendMask = imageMask.subFrame(extendedBoundingBox.left(), extendedBoundingBox.top(), extendedBoundingBox.width(), extendedBoundingBox.height(), Frame::CM_COPY_REMOVE_PADDING_LAYOUT);
201 
202  for (unsigned int iteration = 1u; iteration < blurBorder; ++iteration)
203  {
204  borderPixels.clear();
205 
206  for (unsigned int y = 0u; y < blendMask.height(); ++y)
207  {
208  const uint8_t* row = blendMask.constrow<uint8_t>(y);
209 
210  for (unsigned int x = 0u; x < blendMask.width(); ++x)
211  {
212  if (*row == 0xFFu)
213  {
214  if ((x > 0u && *(row - 1) != 0xFFu) || (x < blendMask.width() - 1u && *(row + 1) != 0xFFu) || (y > 0u && *(row - blendMask.width()) != 0xFFu) || (y < blendMask.height() - 1u && *(row + blendMask.width()) != 0xFFu))
215  {
216  borderPixels.emplace_back(x, y);
217  }
218  }
219 
220  ++row;
221  }
222  }
223 
224  const uint8_t targetColor = uint8_t((iteration * 255u) / blurBorder);
225 
226  uint8_t* blendMaskData = blendMask.data<uint8_t>();
227 
228  for (const CV::PixelPosition& borderPixel : borderPixels)
229  {
230  blendMaskData[borderPixel.index(blendMask.width())] = targetColor;
231  }
232  }
233 
234  CV::FrameBlender::blend8BitPerChannel<tChannels, true>(blurredSubFrame.constdata<uint8_t>(), blendMask.constdata<uint8_t>(), sourceSubFrame.data<uint8_t>(), sourceSubFrame.width(), sourceSubFrame.height(), blurredSubFrame.paddingElements(), blendMask.paddingElements(), sourceSubFrame.paddingElements());
235  }
236  }
237 
238  return true;
239 }
240 
241 }
242 
243 }
244 
245 }
246 
247 #endif // META_OCEAN_CV_SEGMENTATION_FRAME_FILTER_BLUR_H
static bool filter(const Frame &source, Frame &target, const unsigned int filterSize, Worker *worker=nullptr, ReusableMemory *reusableMemory=nullptr)
Applies a Gaussian blur filter to a given source image and copies the resulting filter results to a g...
T left() const
Returns the left (including) pixel position of this bounding box.
Definition: PixelBoundingBox.h:416
unsigned int width() const
Returns the width (the number of horizontal including pixels) of this bounding box.
Definition: PixelBoundingBox.h:482
T rightEnd() const
Returns the right (excluding) pixel position of this bounding box.
Definition: PixelBoundingBox.h:437
bool isValid() const
Returns whether this bounding box covers a valid pixel area.
Definition: PixelBoundingBox.h:577
T bottomEnd() const
Returns the bottom (excluding) pixel position of this bounding box.
Definition: PixelBoundingBox.h:451
PixelBoundingBoxT< T > extended(const unsigned int pixels, const T minLeft, const T minTop, const T maxRight, const T maxBottom) const
Returns a new bounding box by extending this bounding box with a given number of pixel in each direct...
Definition: PixelBoundingBox.h:558
T top() const
Returns the top (including) pixel position of this bounding box.
Definition: PixelBoundingBox.h:423
unsigned int height() const
Returns the height (the number of vertical including pixels) of this bounding box.
Definition: PixelBoundingBox.h:489
The following comfort class provides comfortable functions simplifying prototyping applications but a...
Definition: FrameFilterBlur.h:43
static bool blurMaskRegions(Frame &image, const Frame &mask, const unsigned int blurBorder=5u, RandomGenerator *randomGenerator=nullptr)
Blurs several masked regions in an image.
This class implements functions allowing to blur image content.
Definition: FrameFilterBlur.h:34
static bool blurMaskRegions8BitPerChannel(uint8_t *image, const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int imagePaddingElements, const unsigned int maskPaddingElements, const unsigned int blurBorder=5u, RandomGenerator *randomGenerator=nullptr)
Blurs several masked regions in an image.
Definition: FrameFilterBlur.h:81
This class implements a simple information for a block/area of mask pixels.
Definition: segmentation/MaskAnalyzer.h:46
static void analyzeMaskSeparation8Bit(const uint8_t *mask, const unsigned int width, const unsigned int height, const unsigned int maskPaddingElements, uint32_t *separation, const unsigned int separationPaddingElements, MaskBlocks &blocks)
Analyzes an 8 bit binary mask frame and separates the pixels into individual blocks of joined sub-mas...
std::vector< MaskBlock > MaskBlocks
Definition of a vector holding mask block objects.
Definition: segmentation/MaskAnalyzer.h:112
This class implements Ocean's image class.
Definition: Frame.h:1792
T * row(const unsigned int y, const unsigned int planeIndex=0u)
Returns the pointer to the pixel data of a specific row.
Definition: Frame.h:4177
Frame subFrame(const unsigned int subFrameLeft, const unsigned int subFrameTop, const unsigned int subFrameWidth, const unsigned int subFrameHeight, const CopyMode copyMode=CM_USE_KEEP_LAYOUT) const
Returns a sub-frame of this frame.
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:4168
T * data(const unsigned int planeIndex=0u)
Returns a pointer to the pixel data of a specific plane.
Definition: Frame.h:4159
@ CM_USE_KEEP_LAYOUT
The source memory is used only, no copy is created, the padding layout is preserved.
Definition: Frame.h:1801
@ CM_COPY_REMOVE_PADDING_LAYOUT
Makes a copy of the source memory, but the new plane will not contain padding elements.
Definition: Frame.h:1803
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:4193
unsigned int paddingElements(const unsigned int planeIndex=0u) const
Returns the optional number of padding elements at the end of each row for a specific plane.
Definition: Frame.h:4042
Definition of a frame type composed by the frame dimension, pixel format and pixel origin.
Definition: Frame.h:30
@ FORMAT_Y32
Pixel format with 32 bits Y frame.
Definition: Frame.h:639
@ FORMAT_Y8
Pixel format for grayscale images with byte order Y and 8 bits per pixel.
Definition: Frame.h:594
unsigned int width() const
Returns the width of the frame format in pixel.
Definition: Frame.h:3143
@ ORIGIN_UPPER_LEFT
The first pixel lies in the upper left corner, the last pixel in the lower right corner.
Definition: Frame.h:1050
unsigned int height() const
Returns the height of the frame in pixel.
Definition: Frame.h:3148
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.
T minmax(const T &lowerBoundary, const T &value, const T &upperBoundary)
This function fits a given parameter into a specified value range.
Definition: base/Utilities.h:903
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
std::vector< PixelBoundingBox > PixelBoundingBoxes
Definition of a vector holding bounding box objects with only positive coordinate values.
Definition: PixelBoundingBox.h:42
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15