Ocean
FrameFilterMax.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_CV_FRAME_FILTER_MAX_H
9 #define META_OCEAN_CV_FRAME_FILTER_MAX_H
10 
11 #include "ocean/cv/CV.h"
13 
14 #include "ocean/base/Frame.h"
15 #include "ocean/base/Worker.h"
16 
17 #include "ocean/math/Numeric.h"
18 
19 namespace Ocean
20 {
21 
22 namespace CV
23 {
24 
25 /**
26  * This class implements filters based on the max function.
27  * @ingroup cv
28  */
29 class OCEAN_CV_EXPORT FrameFilterMax : protected FrameFilterSorted
30 {
31  public:
32 
33  /**
34  * The following comfort class provides comfortable functions simplifying prototyping applications but also increasing binary size of the resulting applications.
35  * Best practice is to avoid using these functions if binary size matters,<br>
36  * as for every comfort function a corresponding function exists with specialized functionality not increasing binary size significantly.<br>
37  */
38  class OCEAN_CV_EXPORT Comfort
39  {
40  public:
41 
42  /**
43  * Filters a frame with a max filter with arbitrary size (a square patch).
44  * @param source The source image to be filtered, must be valid
45  * @param target The target frame with same size and pixel format receiving the filtered result, must be valid
46  * @param filterSize Size of the filter edge in pixel, must be odd with range [1, infinity)
47  * @param worker Optional worker object to distribute the computation
48  * @return True, if succeeded
49  */
50  static bool filter(const Frame& source, Frame& target, const unsigned int filterSize, Worker* worker);
51 
52  /**
53  * Filters a frame with a max filter with arbitrary size (a square patch).
54  * @param frame The image to be filtered, must be valid
55  * @param filterSize Size of the filter edge in pixel, must be odd with range [1, infinity)
56  * @param worker Optional worker object to distribute the computation
57  * @return True, if succeeded
58  */
59  static bool filter(Frame& frame, const unsigned int filterSize, Worker* worker);
60  };
61 
62  public:
63 
64  /**
65  * Filters a frame with a max filter with arbitrary size (a square patch).
66  * @param source The source image to be filtered, must be valid
67  * @param target The target frame with same size and pixel format receiving the filtered result, must be valid
68  * @param width The width of the input frame in pixel, with range [filterSize / 2, infinity)
69  * @param height The height of the input frame in pixel, with range [filterSize / 2, infinity)
70  * @param sourcePaddingElements The number of padding elements at the end of each source row, in elements, with range [0, infinity)
71  * @param targetPaddingElements The number of padding elements at the end of each target row, in elements, with range [0, infinity)
72  * @param filterSize Size of the filter edge in pixel, must be odd with range [1, infinity)
73  * @param worker Optional worker object to distribute the computation
74  * @tparam T Data type of the data elements
75  * @tparam tChannels Number of data channels, with range [1, infinity)
76  */
77  template <typename T, unsigned int tChannels>
78  static void filter(const T* source, T* target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int filterSize, Worker* worker = nullptr);
79 
80  /**
81  * Filters a frame with a max filter with arbitrary size (a square patch).
82  * @param frame The image to be filtered, must be valid
83  * @param width The width of the input frame in pixel, with range [filterSize / 2, infinity)
84  * @param height The height of the input frame in pixel, with range [filterSize / 2, infinity)
85  * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
86  * @param filterSize Size of the filter edge in pixel, must be odd with range [1, infinity)
87  * @param worker Optional worker object to distribute the computation
88  * @tparam T Data type of the data elements
89  * @tparam tChannels Number of data channels, with range [1, infinity)
90  */
91  template <typename T, unsigned int tChannels>
92  static void filter(T* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int filterSize, Worker* worker = nullptr);
93 
94  protected:
95 
96  /**
97  * Filters a subset of an integer frame with a max filter with arbitrary size.
98  * @param source The source image to be filtered, must be valid
99  * @param target The target frame with same size and pixel format receiving the filtered result, must be valid
100  * @param width The width of the input frame in pixel, with range [filterSize / 2, infinity)
101  * @param height The height of the input frame in pixel, with range [filterSize / 2, infinity)
102  * @param sourcePaddingElements The number of padding elements at the end of each source row, in elements, with range [0, infinity)
103  * @param targetPaddingElements The number of padding elements at the end of each target row, in elements, with range [0, infinity)
104  * @param filterSize Size of the filter edge in pixel, must be odd with range [1, infinity)
105  * @param firstRow First row to be handled, with range [0, height - 1]
106  * @param numberRows Number of rows to be handled, with range [1, height - firstRow]
107  * @tparam T Data type of the data elements, must be an integer data type
108  * @tparam tChannels Number of data channels, with range [1, infinity)
109  */
110  template <typename T, unsigned int tChannels, typename THistogram>
111  static void filterHistogramSubset(const T* source, T* target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int filterSize, const unsigned int firstRow, const unsigned int numberRows);
112 
113  /**
114  * Filters a subset of a floating point frame with a max filter with arbitrary size.
115  * @param source The source image to be filtered, must be valid
116  * @param target The target frame with same size and pixel format receiving the filtered result, must be valid
117  * @param width The width of the input frame in pixel, with range [filterSize / 2, infinity)
118  * @param height The height of the input frame in pixel, with range [filterSize / 2, infinity)
119  * @param sourcePaddingElements The number of padding elements at the end of each source row, in elements, with range [0, infinity)
120  * @param targetPaddingElements The number of padding elements at the end of each target row, in elements, with range [0, infinity)
121  * @param filterSize Size of the filter edge in pixel, must be odd with range [1, infinity)
122  * @param firstRow First row to be handled, with range [0, height - 1]
123  * @param numberRows Number of rows to be handled, with range [1, height - firstRow]
124  * @tparam T Data type of the data elements, must be a valid data type
125  * @tparam tChannels Number of data channels, with range [1, infinity)
126  */
127  template <typename T, unsigned int tChannels>
128  static void filterSequentialSubset(const T* source, T* target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int filterSize, const unsigned int firstRow, const unsigned int numberRows);
129 };
130 
131 template <typename T, unsigned int tChannels>
132 void FrameFilterMax::filter(const T* source, T* target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int filterSize, Worker* worker)
133 {
134  static_assert(tChannels != 0u, "Invalid channel number!");
135 
136  ocean_assert(source != nullptr && target != nullptr && source != target);
137  ocean_assert(filterSize / 2u <= width && filterSize / 2u <= height);
138 
139  if constexpr (std::is_floating_point<T>::value || sizeof(T) > sizeof(uint16_t))
140  {
141  if (worker)
142  {
143  worker->executeFunction(Worker::Function::createStatic(&filterSequentialSubset<T, tChannels>, source, target, width, height, sourcePaddingElements, targetPaddingElements, filterSize, 0u, 0u), 0u, height, 7u, 8u, 20u);
144  }
145  else
146  {
147  filterSequentialSubset<T, tChannels>(source, target, width, height, sourcePaddingElements, targetPaddingElements, filterSize, 0u, height);
148  }
149  }
150  else if (filterSize < 5u)
151  {
152  if (worker)
153  {
154  worker->executeFunction(Worker::Function::createStatic(&filterSequentialSubset<T, tChannels>, source, target, width, height, sourcePaddingElements, targetPaddingElements, filterSize, 0u, 0u), 0u, height, 7u, 8u, 20u);
155  }
156  else
157  {
158  filterSequentialSubset<T, tChannels>(source, target, width, height, sourcePaddingElements, targetPaddingElements, filterSize, 0u, height);
159  }
160  }
161  else
162  {
163  ocean_assert(!std::is_floating_point<T>::value);
164 
165  ocean_assert(uint64_t(filterSize * filterSize) < uint64_t(NumericT<uint16_t>::maxValue()));
166 
167  constexpr size_t histogramElements = 1 << sizeof(T) * 8;
168 
169  ocean_assert(sizeof(T) != sizeof(uint8_t) || histogramElements == 256);
170  ocean_assert(sizeof(T) != sizeof(uint16_t) || histogramElements == 65536);
171 
173 
174  if (worker)
175  {
176  worker->executeFunction(Worker::Function::createStatic(&filterHistogramSubset<T, tChannels, Histogram>, source, target, width, height, sourcePaddingElements, targetPaddingElements, filterSize, 0u, 0u), 0u, height, 7u, 8u, 20u);
177  }
178  else
179  {
180  filterHistogramSubset<T, tChannels, Histogram>(source, target, width, height, sourcePaddingElements, targetPaddingElements, filterSize, 0u, height);
181  }
182  }
183 }
184 
185 template <typename T, unsigned int tChannels>
186 void FrameFilterMax::filter(T* frame, const unsigned int width, const unsigned int height, const unsigned int framePaddingElements, const unsigned int filterSize, Worker* worker)
187 {
188  static_assert(tChannels != 0u, "Invalid channel number!");
189 
190  ocean_assert(frame != nullptr);
191  ocean_assert(filterSize / 2u <= width && filterSize / 2u <= height);
192 
193  Memory memory(width * height * sizeof(T) * tChannels);
194 
195  constexpr unsigned int memoryPaddingElements = 0u;
196  filter<T, tChannels>(frame, memory.data<T>(), width, height, framePaddingElements, memoryPaddingElements, filterSize, worker);
197 
198  if (framePaddingElements == 0u)
199  {
200  memcpy(frame, memory.data(), memory.size());
201  }
202  else
203  {
204  const T* memoryData = memory.data<T>();
205 
206  for (unsigned int y = 0u; y < height; ++y)
207  {
208  memcpy(frame, memoryData, width * tChannels * sizeof(T));
209 
210  frame += width * tChannels + framePaddingElements;
211  memoryData += width * tChannels + memoryPaddingElements;
212  }
213  }
214 }
215 
216 template <typename T, unsigned int tChannels, typename THistogram>
217 void FrameFilterMax::filterHistogramSubset(const T* source, T* target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int filterSize, const unsigned int firstRow, const unsigned int numberRows)
218 {
219  static_assert(!std::is_floating_point<T>::value, "Invalid data type!");
220  static_assert(tChannels != 0u, "Invalid channel number");
221 
222  ocean_assert(source != nullptr && target != nullptr);
223 
224  ocean_assert(filterSize >= 3u && filterSize % 2u == 1u);
225 
226  const unsigned int filterSize_2 = filterSize / 2u;
227  ocean_assert(filterSize_2 <= width && filterSize_2 <= height);
228 
229  const unsigned int endRow = firstRow + numberRows;
230 
231  const unsigned int sourceStrideElements = width * tChannels + sourcePaddingElements;
232  const unsigned int targetStrideElements = width * tChannels + targetPaddingElements;
233 
234  THistogram histogram[tChannels];
235 
236  // fill initial histogram
237 
238  for (unsigned int y = clampLower(firstRow, filterSize_2); y <= clampUpper(firstRow, filterSize_2, height); ++y)
239  {
240  const T* sourceRow = source + y * sourceStrideElements;
241 
242  for (unsigned int x = 0u; x <= filterSize_2; ++x)
243  {
244  for (unsigned int n = 0u; n < tChannels; ++n)
245  {
246  histogram[n].pushValue(*sourceRow++);
247  }
248  }
249  }
250 
251  ocean_assert(histogram[0].values() >= (filterSize_2 + 1u) * (filterSize_2 + 1u));
252  ocean_assert(histogram[0].values() <= (filterSize_2 + 1u) * filterSize);
253 
254  THistogram previousHistograms[tChannels];
255 
256  for (unsigned int y = firstRow; y < endRow; ++y)
257  {
258  T* targetRow = target + y * targetStrideElements;
259 
260  // making a copy which we can use when we start with the next row
261  for (unsigned int n = 0u; n < tChannels; ++n)
262  {
263  previousHistograms[n] = histogram[n];
264  }
265 
266  for (unsigned int x = 0u; x < width; ++x)
267  {
268  for (unsigned int n = 0u; n < tChannels; ++n)
269  {
270  ocean_assert(histogram[n]);
271  *targetRow++ = histogram[n].maxValue();
272  }
273 
274  if (x != width - 1u)
275  {
276  ocean_assert(x + 1u < width);
277 
278  // horizontal histogram update
279 
280  for (unsigned int yy = clampLower(y, filterSize_2); yy <= clampUpper(y, filterSize_2, height); ++yy)
281  {
282  const T* sourceRow = source + yy * sourceStrideElements;
283 
284  const unsigned int xxLeft = x - filterSize_2;
285  const unsigned int xxRight = x + filterSize_2 + 1u;
286 
287  if (xxLeft < width) // handling negative cases: int(xxLeft) < 0
288  {
289  for (unsigned int n = 0u; n < tChannels; ++n)
290  {
291  const T popValue = sourceRow[xxLeft * tChannels + n];
292 
293  ocean_assert(histogram[n].hasValue(popValue));
294  histogram[n].popValue(popValue);
295  }
296  }
297 
298  if (xxRight < width)
299  {
300  for (unsigned int n = 0u; n < tChannels; ++n)
301  {
302  const T pushValue = sourceRow[xxRight * tChannels + n];
303 
304  histogram[n].pushValue(pushValue);
305  }
306  }
307  }
308  }
309 
310  ocean_assert(histogram[0].values() <= filterSize * filterSize);
311  }
312 
313  if (y != endRow - 1u)
314  {
315  ocean_assert(y + 1u < endRow);
316 
317  // vertical histogram update at the beginning of the row
318 
319  for (unsigned int n = 0u; n < tChannels; ++n)
320  {
321  histogram[n] = previousHistograms[n];
322  }
323 
324  const unsigned int yyTop = y - filterSize_2;
325  const unsigned int yyBottom = y + filterSize_2 + 1u;
326 
327  if (yyTop < height) // handling negative cases: int(yyTop) < 0
328  {
329  const T* sourceRow = source + yyTop * sourceStrideElements;
330 
331  for (unsigned int x = 0u; x <= filterSize_2; ++x)
332  {
333  for (unsigned int n = 0u; n < tChannels; ++n)
334  {
335  const T popValue = sourceRow[x * tChannels + n];
336 
337  ocean_assert(histogram[n].hasValue(popValue));
338  histogram[n].popValue(popValue);
339  }
340  }
341  }
342 
343  if (yyBottom < height)
344  {
345  const T* sourceRow = source + yyBottom * sourceStrideElements;
346 
347  for (unsigned int x = 0u; x <= filterSize_2; ++x)
348  {
349  for (unsigned int n = 0u; n < tChannels; ++n)
350  {
351  const T pushValue = sourceRow[x * tChannels + n];
352 
353  histogram[n].pushValue(pushValue);
354  }
355  }
356  }
357  }
358  }
359 }
360 
361 template <typename T, unsigned int tChannels>
362 void FrameFilterMax::filterSequentialSubset(const T* source, T* target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int filterSize, const unsigned int firstRow, const unsigned int numberRows)
363 {
364  static_assert(tChannels != 0u, "Invalid channel number");
365 
366  ocean_assert(source != nullptr && target != nullptr);
367  ocean_assert(firstRow + numberRows <= height);
368 
369  const unsigned int sourceStrideElements = width * tChannels + sourcePaddingElements;
370  const unsigned int targetStrideElements = width * tChannels + targetPaddingElements;
371 
372  const unsigned int filterSize_2 = filterSize / 2u;
373 
374  T maxValues[tChannels];
375 
376  for (unsigned int y = firstRow; y < firstRow + numberRows; ++y)
377  {
378  T* targetRow = target + y * targetStrideElements;
379 
380  for (unsigned int x = 0u; x < width; ++x)
381  {
382  for (unsigned int n = 0u; n < tChannels; ++n)
383  {
384  maxValues[n] = NumericT<T>::minValue();
385  }
386 
387  for (unsigned int xx = max(0, int(x) - int(filterSize_2)); xx < min(x + filterSize_2 + 1u, width); ++xx)
388  {
389  for (unsigned int yy = max(0, int(y) - int(filterSize_2)); yy < min(y + filterSize_2 + 1u, height); ++yy)
390  {
391  ocean_assert(xx < width && yy < height);
392 
393  const T* const sourcePixel = source + yy * sourceStrideElements + xx * tChannels;
394 
395  for (unsigned int n = 0u; n < tChannels; ++n)
396  {
397  if (sourcePixel[n] > maxValues[n])
398  {
399  maxValues[n] = sourcePixel[n];
400  }
401  }
402  }
403  }
404 
405  for (unsigned int n = 0u; n < tChannels; ++n)
406  {
407  targetRow[n] = maxValues[n];
408  }
409 
410  targetRow += tChannels;
411  }
412  }
413 }
414 
415 }
416 
417 }
418 
419 #endif // META_OCEAN_CV_FRAME_FILTER_MAX_H
The following comfort class provides comfortable functions simplifying prototyping applications but a...
Definition: FrameFilterMax.h:39
static bool filter(Frame &frame, const unsigned int filterSize, Worker *worker)
Filters a frame with a max filter with arbitrary size (a square patch).
static bool filter(const Frame &source, Frame &target, const unsigned int filterSize, Worker *worker)
Filters a frame with a max filter with arbitrary size (a square patch).
This class implements filters based on the max function.
Definition: FrameFilterMax.h:30
static void filterSequentialSubset(const T *source, T *target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int filterSize, const unsigned int firstRow, const unsigned int numberRows)
Filters a subset of a floating point frame with a max filter with arbitrary size.
Definition: FrameFilterMax.h:362
static void filterHistogramSubset(const T *source, T *target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int filterSize, const unsigned int firstRow, const unsigned int numberRows)
Filters a subset of an integer frame with a max filter with arbitrary size.
Definition: FrameFilterMax.h:217
static void filter(const T *source, T *target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int filterSize, Worker *worker=nullptr)
Filters a frame with a max filter with arbitrary size (a square patch).
Definition: FrameFilterMax.h:132
This class implements a histogram for integer values.
Definition: FrameFilterSorted.h:42
This class implements the base class for all filters relying on sorted filter values.
Definition: FrameFilterSorted.h:31
static unsigned int clampLower(const unsigned int index, const unsigned int lowerOffset)
Returns the lower clamped offset to an index.
Definition: FrameFilterSorted.h:428
static unsigned int clampUpper(const unsigned int index, const unsigned int upperOffset, const unsigned int size)
Returns the upper clamped offset to an index.
Definition: FrameFilterSorted.h:433
This class implements an image histogram.
Definition: Histogram.h:32
static Caller< void > createStatic(typename StaticFunctionPointerMaker< void, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass, NullClass >::Type function)
Creates a new caller container for a static function with no function parameter.
Definition: Caller.h:2876
This class implements Ocean's image class.
Definition: Frame.h:1792
This class implements an object able to allocate memory.
Definition: base/Memory.h:22
size_t size() const
Returns the size of the memory in bytes.
Definition: base/Memory.h:386
void * data()
Returns the pointer to the writable memory which is allocated by this object.
Definition: base/Memory.h:303
This class provides basic numeric functionalities.
Definition: Numeric.h:57
static constexpr T minValue()
Returns the min scalar value.
Definition: Numeric.h:3250
This class implements a worker able to distribute function calls over different threads.
Definition: Worker.h:33
bool executeFunction(const Function &function, const unsigned int first, const unsigned int size, const unsigned int firstIndex=(unsigned int)(-1), const unsigned int sizeIndex=(unsigned int)(-1), const unsigned int minimalIterations=1u, const unsigned int threadIndex=(unsigned int)(-1))
Executes a callback function separable by two function parameters.
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15