Ocean
Loading...
Searching...
No Matches
FrameFilterGaussian.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_GAUSSIAN_H
9#define META_OCEAN_CV_FRAME_FILTER_GAUSSIAN_H
10
11#include "ocean/cv/CV.h"
13
14#include "ocean/base/Frame.h"
15#include "ocean/base/Memory.h"
17
18namespace Ocean
19{
20
21namespace CV
22{
23
24/**
25 * This class implements Gaussian image blur filters.
26 * @ingroup cv
27 */
28class OCEAN_CV_EXPORT FrameFilterGaussian
29{
30 public:
31
32 /**
33 * This class holds re-usable memory for the filtering process.
34 */
36 {
37 friend class FrameFilterGaussian;
38
39 public:
40
41 /**
42 * Default constructor.
43 */
44 ReusableMemory() = default;
45
46 protected:
47
48 /// The reusable memory object for the separable filter.
50
51 /// The reusable memory for horizontal filter factors.
53
54 /// The reusable memory for vertical filter factors.
56
57 /// The reusable memory for several response rows.
59 };
60
61 public:
62
63 /**
64 * Calculates the ideal size of a box filter for a specified sigma defining the shape of the Gauss distribution.
65 * @param sigma The sigma defining the shape of the Gauss distribution in pixel, with range (0, infinity)
66 * @return The ideal size of the box filter in pixel, with range [1, infinity], will be odd
67 * @tparam T The data type of sigma, should be 'float' or 'double'
68 */
69 template <typename T>
70 static inline unsigned int sigma2filterSize(const T sigma);
71
72 /**
73 * Calculates the sigma corresponding to a specified box filter so that the Gauss distribution using the sigma represents the box filter.
74 * @param filterSize The size of the filter in pixel, with range [1, infinity), must be odd
75 * @return The resulting sigma in pixel, with range (0, infinity)
76 * @tparam T The data type of sigma, should be 'float' or 'double'
77 */
78 template <typename T>
79 static inline T filterSize2sigma(const unsigned int filterSize);
80
81 /**
82 * Determines 1D Gaussian blur filter factors for a given filter size.
83 * The resulting filter will be normalized for filter values with floating point precision and will not be normalized for filter values with integer precision.<br>
84 * This function will determine the sigma based on the specified size of the filter by using 'filterSize2sigma'.
85 * @param filterSize The size of the filter in pixel, with range [1, infinity), must be odd
86 * @param filter The buffer receiving the resulting filter values, must be valid
87 * @param denominator Optional resulting denominator if the resulting filter values are not normalized
88 * @tparam T The data type of the filter elements, e.g., 'unsigned int', or 'float'
89 * @see filterSize2sigma(), determineFilterFactorsWithExplicitSigma().
90 */
91 template <typename T>
92 static void determineFilterFactors(const unsigned int filterSize, T* filter, T* denominator = nullptr);
93
94 /**
95 * Determines 1D Gaussian blur filter factors for a given filter size.
96 * The resulting filter will be normalized for filter values with floating point precision and will not be normalized for filter values with integer precision.<br>
97 * Information: This function is the equivalent to OpenCV's cv::getGaussianKerne().
98 * @param filterSize The size of the filter in pixel, with range [1, infinity), must be odd
99 * @param sigma The explicit sigma which will be used to determine the filter values, with range (0, infinity)
100 * @param filter The buffer receiving the resulting filter values, must be valid
101 * @param denominator Optional resulting denominator if the resulting filter values are not normalized
102 * @tparam T The data type of the filter elements, must be 'unsigned int', or 'float', or 'double'
103 * @see determineFilterFactors(), filterSize2sigma().
104 */
105 template <typename T>
106 static void determineFilterFactorsWithExplicitSigma(const unsigned int filterSize, const float sigma, T* filter, T* denominator = nullptr);
107
108 /**
109 * Applies a Gaussian blur filter to a given source image and copies the resulting filter results to a given output frame.
110 * If the target frame type does not match the source frame type the target frame type will be adjusted.
111 * Information: This function is the equivalent to OpenCV's cv::GaussianBlur().
112 * @param source The source frame to which the blur filter will be applied, must be valid
113 * @param target The target frame receiving the blurred image content, will be set to the correct frame type if invalid or not matching
114 * @param filterSize The size of the filter to be applied, with range [1, min(source.width(), source.height())], must be odd
115 * @param worker Optional worker object to distribute the computational load
116 * @param reusableMemory An optional object holding reusable memory which can be used during filtering, nullptr otherwise
117 * @return True, if succeeded
118 */
119 static bool filter(const Frame& source, Frame& target, const unsigned int filterSize, Worker* worker = nullptr, ReusableMemory* reusableMemory = nullptr);
120
121 /**
122 * Applies a Gaussian blur filter to a given frame.
123 * In case the given frame is a read-only frame, the frame will be replaced with a new frame owning the memory.<br>
124 * In case the given frame is a writable frame, the filter will be applied in place.
125 * @param frame The frame to which the blur filter will be applied, must be valid
126 * @param filterSize The size of the filter to be applied, with range [1, min(source.width(), source.height())], must be odd
127 * @param worker Optional worker object to distribute the computational load
128 * @param reusableMemory An optional object holding reusable memory which can be used during filtering, nullptr otherwise
129 * @return True, if succeeded
130 */
131 static bool filter(Frame& frame, const unsigned int filterSize, Worker* worker = nullptr, ReusableMemory* reusableMemory = nullptr);
132
133 /**
134 * Applies a Gaussian blur filter to a given frame.
135 * @param source The source frame to be filtered, must be valid
136 * @param target The target frame receiving the filtered results, can be the same memory pointer as 'source', must be valid
137 * @param width The width of the source (and target) frame in pixel, with range [tFilterSize, infinity)
138 * @param height The height of the source (and target) frame in pixel, with range [tFilterSize, infinity)
139 * @param channels The number of channels the source frame (and target frame) has, with range [1, infinity)
140 * @param sourcePaddingElements The number of padding elements at the end of each source row, in elements, with range [0, infinity)
141 * @param targetPaddingElements The number of padding elements at the end of each target row, in elements, with range [0, infinity)
142 * @param horizontalFilterSize The number of elements the horizontal filter has, with range [1, width], must be odd
143 * @param verticalFilterSize The number of elements the vertical filter has, with range [1, height], must be odd
144 * @param sigma The Optional sigma that is applied explicitly, with range (0, infinity), -1 to calculate the sigma automatically based on the filter sizes
145 * @param worker Optional worker object to distribute the computation
146 * @param reusableMemory An optional object holding reusable memory which can be used during filtering, nullptr otherwise
147 * @param processorInstructions The set of available instructions, may be any combination of instructions
148 * @return True, if succeeded
149 * @tparam T The data type of each pixel channel of the source frame (and target frame) e.g., 'uint8_t', or 'float'
150 * @tparam TFilter The data type of each filter elements e.g., 'unsigned int', or 'float'
151 */
152 template <typename T, typename TFilter>
153 static bool filter(const T* source, T* target, const unsigned int width, const unsigned int height, const unsigned int channels, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int horizontalFilterSize, const unsigned int verticalFilterSize, const float sigma = -1.0f, Worker* worker = nullptr, ReusableMemory* reusableMemory = nullptr, const ProcessorInstructions processorInstructions = Processor::get().instructions());
154
155 /**
156 * Applies a Gaussian blur filter to a given frame.
157 * @param frame The frame to be filtered, must be valid
158 * @param width The width of the frame in pixel, with range [tFilterSize, infinity)
159 * @param height The height of the frame in pixel, with range [tFilterSize, infinity)
160 * @param channels The number of channels the source frame (and target frame) has, with range [1, infinity)
161 * @param framePaddingElements The number of padding elements at the end of each frame row, in elements, with range [0, infinity)
162 * @param horizontalFilterSize The number of elements the horizontal filter has, with range [1, width], must be odd
163 * @param verticalFilterSize The number of elements the vertical filter has, with range [1, height], must be odd
164 * @param sigma The Optional sigma that is applied explicitly, with range (0, infinity), -1 to calculate the sigma automatically based on the filter sizes
165 * @param worker Optional worker object to distribute the computation
166 * @param reusableMemory An optional object holding reusable memory which can be used during filtering, nullptr otherwise
167 * @param processorInstructions The set of available instructions, may be any combination of instructions
168 * @return True, if succeeded
169 * @tparam T The data type of each pixel channel of the source frame (and target frame) e.g., 'uint8_t', or 'float'
170 * @tparam TFilter The data type of each filter elements e.g., 'unsigned int', or 'float'
171 */
172 template <typename T, typename TFilter>
173 static inline bool filter(T* frame, const unsigned int width, const unsigned int height, const unsigned int channels, const unsigned int framePaddingElements, const unsigned int horizontalFilterSize, const unsigned int verticalFilterSize, const float sigma = -1.0f, Worker* worker = nullptr, ReusableMemory* reusableMemory = nullptr, const ProcessorInstructions processorInstructions = Processor::get().instructions());
174
175 protected:
176
177#if defined(OCEAN_HARDWARE_NEON_VERSION) && OCEAN_HARDWARE_NEON_VERSION >= 10
178
179 /**
180 * Applies a horizontal and vertical filtering with a Gaussian kernel with size 3, applying a horizontal and vertical 121 filter kernel.
181 * The frame must be a 1 channel 8 bit per pixel image.<br>
182 * Instead of applying a separated horizontal and vertical filter, the function applies the 2D filter directly to speed up the process significantly.<br>
183 * This function applies NEON instructions and can handle frames with width >= 18 pixels only.
184 * @param source The source frame to be filtered, must be valid
185 * @param target The target frame receiving the filtered results, must be valid
186 * @param width The width of the source (and target) frame in pixel, with range [18, infinity)
187 * @param height The height of the source (and target) frame in pixel, with range [1, infinity)
188 * @param sourcePaddingElements Optional padding elements at the end of each source row, in elements, with range [0, infinity)
189 * @param targetPaddingElements Optional padding elements at the end of each target row, in elements, with range [0, infinity)
190 * @param reusableMemory An optional object holding reusable memory which can be used during filtering, nullptr otherwise
191 */
192 static inline void filter1Channel8Bit121NEON(const uint8_t* source, uint8_t* target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, ReusableMemory* reusableMemory);
193
194#endif // OCEAN_HARDWARE_NEON_VERSION >= 10
195
196#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 41
197
198 /**
199 * Applies a horizontal and vertical filtering with a Gaussian kernel with size 3, applying a horizontal and vertical 121 filter kernel.
200 * The frame must be a 1 channel 8 bit per pixel image.<br>
201 * Instead of applying a separated horizontal and vertical filter, the function applies the 2D filter directly to speed up the process significantly.<br>
202 * This function applies SSE4.1 instructions and can handle frames with width >= 18 pixels only.
203 * @param source The source frame to be filtered, must be valid
204 * @param target The target frame receiving the filtered results, must be valid
205 * @param width The width of the source (and target) frame in pixel, with range [18, infinity)
206 * @param height The height of the source (and target) frame in pixel, with range [1, infinity)
207 * @param sourcePaddingElements Optional padding elements at the end of each source row, in elements, with range [0, infinity)
208 * @param targetPaddingElements Optional padding elements at the end of each target row, in elements, with range [0, infinity)
209 * @param reusableMemory An optional object holding reusable memory which can be used during filtering, nullptr otherwise
210 */
211 static inline void filter1Channel8Bit121SSE(const uint8_t* source, uint8_t* target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, ReusableMemory* reusableMemory);
212
213#endif // OCEAN_HARDWARE_SSE_VERSION >= 41
214};
215
216template <typename T>
217inline unsigned int FrameFilterGaussian::sigma2filterSize(const T sigma)
218{
219 ocean_assert(sigma > NumericT<T>::eps());
220
221 const unsigned int size = (unsigned int)NumericT<T>::ceil((sigma - T(0.8)) * T(6.666666666) + T(2.999)) | 0x01u; // bitwise or to create an odd size
222
223 ocean_assert(size >= 1u);
224 ocean_assert(size % 2u == 1u);
225
226 return size;
227}
228
229template <typename T>
230inline T FrameFilterGaussian::filterSize2sigma(const unsigned int filterSize)
231{
232 ocean_assert(filterSize >= 1u && (filterSize % 2u) == 1u);
233
234 return T(0.3) * (T(filterSize / 2u) - T(1)) + T(0.8);
235}
236
237template <>
238inline void FrameFilterGaussian::determineFilterFactorsWithExplicitSigma<unsigned int>(const unsigned int filterSize, const float sigma, unsigned int* filter, unsigned int* denominator)
239{
240 ocean_assert(filterSize % 2u == 1u);
241 ocean_assert(filter != nullptr);
242
243 std::vector<float> floatFilter(filterSize);
244 determineFilterFactorsWithExplicitSigma<float>(filterSize, sigma, floatFilter.data());
245
246 const float factor = 1.0f / floatFilter[0];
247
248 unsigned int filterSum = 0u;
249
250 for (unsigned int n = 0u; n < filterSize; ++n)
251 {
252 filter[n] = (unsigned int)(floatFilter[n] * factor + 0.5f);
253 filterSum += filter[n];
254 }
255
256 if (denominator)
257 {
258 *denominator = filterSum;
259 }
260}
261
262template <typename T>
263void FrameFilterGaussian::determineFilterFactorsWithExplicitSigma(const unsigned int filterSize, const float sigma, T* filter, T* denominator)
264{
265 static_assert(std::is_same<float, T>::value || std::is_same<double, T>::value, "Invalid data type for a filter!");
266
267 ocean_assert(filterSize % 2u == 1u);
268 ocean_assert(sigma > NumericF::eps());
269
270 ocean_assert(filter != nullptr);
271
272 // we calculate e ^ -(x^2 / 2 * sigma^2)
273 // while x = i - (filterSize / 2)
274
275 const unsigned int filterSize_2 = filterSize / 2u;
276
277 const T scaleFactor = T(-0.5f / (sigma * sigma));
278
279 T filterSum = T(0);
280
281 for (unsigned int n = 0u; n < filterSize; ++n)
282 {
283 const int i = int(n - filterSize_2);
284
285 filter[n] = NumericT<T>::exp(scaleFactor * T(i) * T(i));
286
287 filterSum += filter[n];
288 }
289
290 const T invFilterSum = T(1) / filterSum;
291
292 for (unsigned int n = 0u; n < filterSize; ++n)
293 {
294 filter[n] *= invFilterSum;
295 }
296
297#ifdef OCEAN_DEBUG
298 {
299 T debugFilterSum = T(0);
300 for (unsigned int n = 0u; n < filterSize; ++n)
301 {
302 debugFilterSum += filter[n];
303 }
304 ocean_assert(NumericT<T>::isEqual(debugFilterSum, T(1)));
305 }
306#endif
307
308 if (denominator)
309 {
310 *denominator = T(1);
311 }
312}
313
314template <>
315inline void FrameFilterGaussian::determineFilterFactors<unsigned int>(const unsigned int filterSize, unsigned int* filter, unsigned int* denominator)
316{
317 ocean_assert(filterSize % 2u == 1u);
318 ocean_assert(filter != nullptr);
319
320 if (filterSize <= 7u)
321 {
322 static constexpr std::array<unsigned int, 16> predefinedFilters =
323 {
324 1u,
325 1u, 2u, 1u,
326 1u, 4u, 6u, 4u, 1u,
327 1u, 4u, 7u, 9u, 7u, 4u, 1u
328 };
329
330 static constexpr std::array<unsigned int, 4> predefinedDenominators =
331 {
332 1u,
333 4u,
334 16u,
335 33u
336 };
337
338 static constexpr std::array<unsigned int, 4> offsets =
339 {
340 0u,
341 1u,
342 4u,
343 9u
344 };
345
346 ocean_assert(filterSize / 2u < offsets.size());
347 const unsigned int filterOffset = offsets[filterSize / 2u];
348
349 for (unsigned int n = 0u; n < filterSize; ++n)
350 {
351 ocean_assert(filterOffset + n < predefinedFilters.size());
352 filter[n] = predefinedFilters[filterOffset + n];
353 }
354
355
356 if (denominator != nullptr)
357 {
358 ocean_assert(filterSize / 2u < predefinedDenominators.size());
359 *denominator = predefinedDenominators[filterSize / 2u];
360 }
361
362 return;
363 }
364
365 const float sigma = filterSize2sigma<float>(filterSize);
366
367 determineFilterFactorsWithExplicitSigma<unsigned int>(filterSize, sigma, filter, denominator);
368}
369
370template <typename T>
371void FrameFilterGaussian::determineFilterFactors(const unsigned int filterSize, T* filter, T* denominator)
372{
373 ocean_assert(filterSize % 2u == 1u);
374 ocean_assert(filter != nullptr);
375
376 if (filterSize <= 7u)
377 {
378 static constexpr std::array<float, 16> predefinedFilters =
379 {
380 1.0f,
381 0.25f, 0.5f, 0.25f,
382 0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f,
383 0.03125f, 0.109375f, 0.21875f, 0.28125f, 0.21875f, 0.109375f, 0.03125f,
384
385 };
386
387 static constexpr std::array<unsigned int, 4> offsets =
388 {
389 0u,
390 1u,
391 4u,
392 9u
393 };
394
395 ocean_assert(filterSize / 2u < offsets.size());
396 const unsigned int filterOffset = offsets[filterSize / 2u];
397
398 for (unsigned int n = 0u; n < filterSize; ++n)
399 {
400 ocean_assert(filterOffset + n < predefinedFilters.size());
401 filter[n] = T(predefinedFilters[filterOffset + n]);
402 }
403
404 if (denominator != nullptr)
405 {
406 *denominator = T(1);
407 }
408
409 return;
410 }
411
412 const float sigma = filterSize2sigma<float>(filterSize);
413
414 determineFilterFactorsWithExplicitSigma<T>(filterSize, sigma, filter, denominator);
415}
416
417template <typename T, typename TFilter>
418bool FrameFilterGaussian::filter(const T* source, T* target, const unsigned int width, const unsigned int height, const unsigned int channels, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, const unsigned int horizontalFilterSize, const unsigned int verticalFilterSize, const float sigma, Worker* worker, ReusableMemory* reusableMemory, const ProcessorInstructions processorInstructions)
419{
420 ocean_assert(source != nullptr && target != nullptr);
421 ocean_assert(width >= horizontalFilterSize && height >= verticalFilterSize);
422
423 ocean_assert(horizontalFilterSize >= 1u && horizontalFilterSize % 2u == 1u);
424 ocean_assert(verticalFilterSize >= 1u && verticalFilterSize % 2u == 1u);
425 if (horizontalFilterSize == 0u || horizontalFilterSize % 2u != 1u || verticalFilterSize == 0u || verticalFilterSize % 2u != 1u)
426 {
427 return false;
428 }
429
430#if defined(OCEAN_HARDWARE_NEON_VERSION) && OCEAN_HARDWARE_NEON_VERSION >= 10
431
432 // we have a special implementation for small filter kernels
433
434 if (std::is_same<T, uint8_t>::value && std::is_same<TFilter, unsigned int>::value)
435 {
436 if (width >= 18u && channels == 1u && horizontalFilterSize == 3u && verticalFilterSize == 3u && sigma <= 0.0f)
437 {
438 filter1Channel8Bit121NEON((const uint8_t*)(source), (uint8_t*)(target), width, height, sourcePaddingElements, targetPaddingElements, reusableMemory);
439 return true;
440 }
441 }
442
443#endif // OCEAN_HARDWARE_NEON_VERSION >= 10
444
445#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 41
446
447 // we have a special implementation for small filter kernels
448
449 if (std::is_same<T, uint8_t>::value && std::is_same<TFilter, unsigned int>::value)
450 {
451 if (width >= 18u && channels == 1u && horizontalFilterSize == 3u && verticalFilterSize == 3u && sigma <= 0.0f)
452 {
453 filter1Channel8Bit121SSE((const uint8_t*)(source), (uint8_t*)(target), width, height, sourcePaddingElements, targetPaddingElements, reusableMemory);
454 return true;
455 }
456 }
457
458#endif // OCEAN_HARDWARE_SSE_VERSION >= 41
459
460 FrameFilterSeparable::ReusableMemory* separableReusableMemory = reusableMemory != nullptr ? &reusableMemory->separableReusableMemory_ : nullptr;
461
462 std::vector<TFilter> localHorizontalFilter;
463 TFilter* horizontalFilter = nullptr;
464
465 if (reusableMemory != nullptr)
466 {
467 if (reusableMemory->horizontalFilterMemory_.size() != horizontalFilterSize * sizeof(TFilter))
468 {
469 reusableMemory->horizontalFilterMemory_ = Memory::create<TFilter>(horizontalFilterSize);
470 }
471
472 horizontalFilter = reusableMemory->horizontalFilterMemory_.data<TFilter>();
473 }
474 else
475 {
476 localHorizontalFilter.resize(horizontalFilterSize);
477 horizontalFilter = localHorizontalFilter.data();
478 }
479
480 if (sigma <= 0.0f)
481 {
482 determineFilterFactors(horizontalFilterSize, horizontalFilter);
483 }
484 else
485 {
486 determineFilterFactorsWithExplicitSigma(horizontalFilterSize, sigma, horizontalFilter);
487 }
488
489 if (horizontalFilterSize == verticalFilterSize)
490 {
491 return FrameFilterSeparable::filter<T, TFilter>(source, target, width, height, channels, sourcePaddingElements, targetPaddingElements, horizontalFilter, horizontalFilterSize, horizontalFilter, horizontalFilterSize, worker, separableReusableMemory, processorInstructions);
492 }
493 else
494 {
495 std::vector<TFilter> localVerticalFilter;
496 TFilter* verticalFilter = nullptr;
497
498 if (reusableMemory != nullptr)
499 {
500 if (reusableMemory->verticalFilterMemory_.size() != verticalFilterSize * sizeof(TFilter))
501 {
502 reusableMemory->verticalFilterMemory_ = Memory::create<TFilter>(verticalFilterSize);
503 }
504
505 verticalFilter = reusableMemory->verticalFilterMemory_.data<TFilter>();
506 }
507 else
508 {
509 localVerticalFilter.resize(verticalFilterSize);
510 verticalFilter = localVerticalFilter.data();
511 }
512
513 if (sigma <= 0.0f)
514 {
515 determineFilterFactors(verticalFilterSize, verticalFilter);
516 }
517 else
518 {
519 determineFilterFactorsWithExplicitSigma(verticalFilterSize, sigma, verticalFilter);
520 }
521
522 return FrameFilterSeparable::filter<T, TFilter>(source, target, width, height, channels, sourcePaddingElements, targetPaddingElements, horizontalFilter, horizontalFilterSize, verticalFilter, verticalFilterSize, worker, separableReusableMemory, processorInstructions);
523 }
524}
525
526template <typename T, typename TFilter>
527inline bool FrameFilterGaussian::filter(T* frame, const unsigned int width, const unsigned int height, const unsigned int channels, const unsigned int framePaddingElements, const unsigned int horizontalFilterSize, const unsigned int verticalFilterSize, const float sigma, Worker* worker, ReusableMemory* reusableMemory, const ProcessorInstructions processorInstructions)
528{
529 return filter<T, TFilter>(frame, frame, width, height, channels, framePaddingElements, framePaddingElements, horizontalFilterSize, verticalFilterSize, sigma, worker, reusableMemory, processorInstructions);
530}
531
532#if defined(OCEAN_HARDWARE_SSE_VERSION) && OCEAN_HARDWARE_SSE_VERSION >= 41
533
534inline void FrameFilterGaussian::filter1Channel8Bit121SSE(const uint8_t* source, uint8_t* target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, ReusableMemory* reusableMemory)
535{
536 ocean_assert(source != nullptr);
537 ocean_assert(target != nullptr);
538 ocean_assert(width >= 18u);
539 ocean_assert(height >= 1u);
540
541 const __m128i constant_2_u_16x8 = _mm_set1_epi16(2);
542 const __m128i constant_8_u_16x8 = _mm_set1_epi16(8);
543 const __m128i zero_128 = _mm_setzero_si128();
544
545 const unsigned int sourceStrideElements = width * 1u + sourcePaddingElements;
546 const unsigned int targetStrideElements = width * 1u + targetPaddingElements;
547
548 const unsigned int innerPixels = width - 2u;
549
550 Memory memoryResponseRows; // memory for three response rows, each row contains 'innerPixels' uint16_t elements
551 uint16_t* responseRows = nullptr;
552
553 const unsigned int reusableMemoryNecessaryElements = width * 4u;
554
555 if (reusableMemory != nullptr)
556 {
557 if (reusableMemory->responseRowsMemory_.size() != reusableMemoryNecessaryElements * sizeof(uint16_t))
558 {
559 reusableMemory->responseRowsMemory_ = Memory::create<uint16_t>(reusableMemoryNecessaryElements);
560 }
561
562 responseRows = reusableMemory->responseRowsMemory_.data<uint16_t>();
563 }
564 else
565 {
566 memoryResponseRows = Memory::create<uint16_t>(reusableMemoryNecessaryElements);
567 responseRows = memoryResponseRows.data<uint16_t>();
568 }
569
570 ocean_assert(responseRows != nullptr);
571
572 uint16_t* responseTopRow = responseRows + width * 0u;
573
574 // first, we determine the horizontal filter response for the 1D filter [1 2 1]
575
576 responseTopRow[0] = source[0] * 3u + source[1]; // special handling for first pixel response
577
578 for (unsigned int n = 0u; n < innerPixels; n += 16u)
579 {
580 if (n + 16u > innerPixels)
581 {
582 ocean_assert(n >= 16u && innerPixels > 16u);
583 const unsigned int newN = innerPixels - 16u;
584
585 const unsigned int offset = n - newN;
586 ocean_assert_and_suppress_unused(offset < innerPixels, offset);
587
588 ocean_assert(n > newN);
589
590 n = newN;
591
592 // the for loop will stop after this iteration
593 ocean_assert(n + 16u == innerPixels);
594 ocean_assert(!(n + 16u < innerPixels));
595 }
596
597 const __m128i source_0_u_8x16 = _mm_loadu_si128((const __m128i*)(source + n + 0u));
598 const __m128i source_1_u_8x16 = _mm_loadu_si128((const __m128i*)(source + n + 1u));
599 const __m128i source_2_u_8x16 = _mm_loadu_si128((const __m128i*)(source + n + 2u));
600
601 // result = source0 + source2 (widening to 16-bit)
602 const __m128i source_0_low_u_16x8 = _mm_unpacklo_epi8(source_0_u_8x16, zero_128);
603 const __m128i source_0_high_u_16x8 = _mm_unpackhi_epi8(source_0_u_8x16, zero_128);
604 const __m128i source_2_low_u_16x8 = _mm_unpacklo_epi8(source_2_u_8x16, zero_128);
605 const __m128i source_2_high_u_16x8 = _mm_unpackhi_epi8(source_2_u_8x16, zero_128);
606
607 __m128i low_u_16x8 = _mm_add_epi16(source_0_low_u_16x8, source_2_low_u_16x8);
608 __m128i high_u_16x8 = _mm_add_epi16(source_0_high_u_16x8, source_2_high_u_16x8);
609
610 // result += 2 * source1
611 const __m128i source_1_low_u_16x8 = _mm_unpacklo_epi8(source_1_u_8x16, zero_128);
612 const __m128i source_1_high_u_16x8 = _mm_unpackhi_epi8(source_1_u_8x16, zero_128);
613
614 low_u_16x8 = _mm_add_epi16(low_u_16x8, _mm_mullo_epi16(source_1_low_u_16x8, constant_2_u_16x8));
615 high_u_16x8 = _mm_add_epi16(high_u_16x8, _mm_mullo_epi16(source_1_high_u_16x8, constant_2_u_16x8));
616
617 _mm_storeu_si128((__m128i*)(responseTopRow + 1u + n + 0u), low_u_16x8);
618 _mm_storeu_si128((__m128i*)(responseTopRow + 1u + n + 8u), high_u_16x8);
619 }
620
621 responseTopRow[width - 1u] = source[width - 2u] + source[width - 1u] * 3u; // special handling for last pixel response
622
623 // due to border mirroring, our top and center row is identical for the first iteration
624 uint16_t* responseCenterRow = responseTopRow;
625 uint16_t* responseBottomRow = responseRows + width * 2u;
626 uint8_t* const sourceExtraCopy = (uint8_t*)(responseRows + width * 3u);
627
628 source += sourceStrideElements;
629
630 for (unsigned int y = 0u; y < height; ++y)
631 {
632 if (y == height - 2u)
633 {
634 // we need to make a copy of the last source row for in-place filtering
635 memcpy(sourceExtraCopy, source, width * sizeof(uint8_t));
636 }
637
638 // for each iteration, we have a pre-calculated (horizontal) response for the top and center row already
639
640 responseBottomRow[0u] = source[0] * 3u + source[1];
641
642 // handle left pixel: (outside) (inside)
643 // | 3 1 1 | 2 1
644 // | [6] 2 2 | [4] 2
645 // | 3 1 the filter factors are based on: 1 | 2 1
646
647 // using scoped value for intermediate storage as source and target can be identical e.g., for in-place filtering
648 const ScopedValueT<uint8_t> firstPixelValue(*target, uint8_t((responseTopRow[0] + responseCenterRow[0] * 2u + responseBottomRow[0] + 8u) / 16u));
649
650 for (unsigned int n = 0u; n < innerPixels; n += 16u)
651 {
652 if (n + 16u > innerPixels)
653 {
654 ocean_assert(n >= 16u && innerPixels > 16u);
655 const unsigned int newN = innerPixels - 16u;
656
657 const unsigned int offset = n - newN;
658 ocean_assert_and_suppress_unused(offset < innerPixels, offset);
659
660 ocean_assert(n > newN);
661
662 n = newN;
663
664 // the for loop will stop after this iteration
665 ocean_assert(n + 16u == innerPixels);
666 ocean_assert(!(n + 16u < innerPixels));
667 }
668
669 const __m128i sourceBottom_0_u_8x16 = _mm_loadu_si128((const __m128i*)(source + n + 0u));
670 const __m128i sourceBottom_1_u_8x16 = _mm_loadu_si128((const __m128i*)(source + n + 1u));
671 const __m128i sourceBottom_2_u_8x16 = _mm_loadu_si128((const __m128i*)(source + n + 2u));
672
673 // bottomResult = bottomSource0 + bottomSource2 (widening to 16-bit)
674 const __m128i bottomSource_0_low_u_16x8 = _mm_unpacklo_epi8(sourceBottom_0_u_8x16, zero_128);
675 const __m128i bottomSource_0_high_u_16x8 = _mm_unpackhi_epi8(sourceBottom_0_u_8x16, zero_128);
676 const __m128i bottomSource_2_low_u_16x8 = _mm_unpacklo_epi8(sourceBottom_2_u_8x16, zero_128);
677 const __m128i bottomSource_2_high_u_16x8 = _mm_unpackhi_epi8(sourceBottom_2_u_8x16, zero_128);
678
679 __m128i bottomLow_u_16x8 = _mm_add_epi16(bottomSource_0_low_u_16x8, bottomSource_2_low_u_16x8);
680 __m128i bottomHigh_u_16x8 = _mm_add_epi16(bottomSource_0_high_u_16x8, bottomSource_2_high_u_16x8);
681
682 // bottomResult += 2 * bottomSource1
683 const __m128i bottomSource_1_low_u_16x8 = _mm_unpacklo_epi8(sourceBottom_1_u_8x16, zero_128);
684 const __m128i bottomSource_1_high_u_16x8 = _mm_unpackhi_epi8(sourceBottom_1_u_8x16, zero_128);
685
686 bottomLow_u_16x8 = _mm_add_epi16(bottomLow_u_16x8, _mm_mullo_epi16(bottomSource_1_low_u_16x8, constant_2_u_16x8));
687 bottomHigh_u_16x8 = _mm_add_epi16(bottomHigh_u_16x8, _mm_mullo_epi16(bottomSource_1_high_u_16x8, constant_2_u_16x8));
688
689
690 // load the pre-calculated values for top
691 const __m128i topLow_u_16x8 = _mm_loadu_si128((const __m128i*)(responseTopRow + 1u + n + 0u));
692 const __m128i topHigh_u_16x8 = _mm_loadu_si128((const __m128i*)(responseTopRow + 1u + n + 8u));
693
694 // load the pre-calculated values for center
695 const __m128i centerLow_u_16x8 = _mm_loadu_si128((const __m128i*)(responseCenterRow + 1u + n + 0u));
696 const __m128i centerHigh_u_16x8 = _mm_loadu_si128((const __m128i*)(responseCenterRow + 1u + n + 8u));
697
698 // result = top + bottom
699 __m128i resultLow_u_16x8 = _mm_add_epi16(topLow_u_16x8, bottomLow_u_16x8);
700 __m128i resultHigh_u_16x8 = _mm_add_epi16(topHigh_u_16x8, bottomHigh_u_16x8);
701
702 // result += 2 * center
703 resultLow_u_16x8 = _mm_add_epi16(resultLow_u_16x8, _mm_mullo_epi16(centerLow_u_16x8, constant_2_u_16x8));
704 resultHigh_u_16x8 = _mm_add_epi16(resultHigh_u_16x8, _mm_mullo_epi16(centerHigh_u_16x8, constant_2_u_16x8));
705
706 // write the results for the bottom row so that we can use them as new pre-calculated values in the next iteration
707 // as we may re-calculate the last 16 pixels once again in the very last iteration, we cannot simply write the results to the center row
708 _mm_storeu_si128((__m128i*)(responseBottomRow + 1u + n + 0u), bottomLow_u_16x8);
709 _mm_storeu_si128((__m128i*)(responseBottomRow + 1u + n + 8u), bottomHigh_u_16x8);
710
711 // result = (result + 8) / 16, with rounding, then narrow to 8-bit
712 resultLow_u_16x8 = _mm_srli_epi16(_mm_add_epi16(resultLow_u_16x8, constant_8_u_16x8), 4);
713 resultHigh_u_16x8 = _mm_srli_epi16(_mm_add_epi16(resultHigh_u_16x8, constant_8_u_16x8), 4);
714
715 const __m128i result_u_8x16 = _mm_packus_epi16(resultLow_u_16x8, resultHigh_u_16x8);
716
717 _mm_storeu_si128((__m128i*)(target + 1u + n), result_u_8x16);
718 }
719
720 responseBottomRow[width - 1u] = source[width - 2u] + source[width - 1u] * 3u;
721
722 // handle right pixel: (inside) (outside)
723 // 1 3 | 1 2 | 1
724 // 2 [6] | 2 [4] | 2
725 // 1 3 | 1 2 | 1
726
727 target[width - 1u] = uint8_t((responseTopRow[width - 1u] + responseCenterRow[width - 1u] * 2u + responseBottomRow[width - 1u] + 8u) / 16u);
728
729 source += sourceStrideElements;
730 target += targetStrideElements;
731
732 std::swap(responseTopRow, responseCenterRow);
733
734 if (y == 0u)
735 {
736 // the next row will not have any border mirroring anymore
737
738 responseCenterRow = responseRows + width * 1u;
739 }
740 else if (y == height - 2u)
741 {
742 // the next iteration will handle the last row in the frame
743 // the bottom row will be mirrored which is actually the last row again
744
745 source = sourceExtraCopy;
746 }
747
748 std::swap(responseCenterRow, responseBottomRow);
749 }
750}
751
752#endif // OCEAN_HARDWARE_SSE_VERSION >= 41
753
754#if defined(OCEAN_HARDWARE_NEON_VERSION) && OCEAN_HARDWARE_NEON_VERSION >= 10
755
756inline void FrameFilterGaussian::filter1Channel8Bit121NEON(const uint8_t* source, uint8_t* target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, ReusableMemory* reusableMemory)
757{
758 ocean_assert(source != nullptr);
759 ocean_assert(target != nullptr);
760 ocean_assert(width >= 18u);
761 ocean_assert(height >= 1u);
762
763 // [2, 2, 2, 2, 2, 2, 2, 2]
764 const uint8x8_t constant_2_u_8x8 = vdup_n_u8(2u);
765 const uint16x8_t constant_2_u_16x8 = vdupq_n_u16(2u);
766
767 const unsigned int sourceStrideElements = width * 1u + sourcePaddingElements;
768 const unsigned int targetStrideElements = width * 1u + targetPaddingElements;
769
770 const unsigned int innerPixels = width - 2u;
771
772 Memory memoryResponseRows; // memory for three response rows, each row contains 'innerPixels' uint16_t elements
773 uint16_t* responseRows = nullptr;
774
775 const unsigned int reusableMemoryNecessaryElements = width * 4u;
776
777 if (reusableMemory != nullptr)
778 {
779 if (reusableMemory->responseRowsMemory_.size() != reusableMemoryNecessaryElements * sizeof(uint16_t))
780 {
781 reusableMemory->responseRowsMemory_ = Memory::create<uint16_t>(reusableMemoryNecessaryElements);
782 }
783
784 responseRows = reusableMemory->responseRowsMemory_.data<uint16_t>();
785 }
786 else
787 {
788 memoryResponseRows = Memory::create<uint16_t>(reusableMemoryNecessaryElements);
789 responseRows = memoryResponseRows.data<uint16_t>();
790 }
791
792 ocean_assert(responseRows != nullptr);
793
794 uint16_t* responseTopRow = responseRows + width * 0u;
795
796 // first, we determine the horizontal filter response for the 1D filter [1 2 1]
797
798 responseTopRow[0] = source[0] * 3u + source[1]; // special handling for first pixel response
799
800 for (unsigned int n = 0u; n < innerPixels; n += 16u)
801 {
802 if (n + 16u > innerPixels)
803 {
804 ocean_assert(n >= 16u && innerPixels > 16u);
805 const unsigned int newN = innerPixels - 16u;
806
807 const unsigned int offset = n - newN;
808 ocean_assert_and_suppress_unused(offset < innerPixels, offset);
809
810 ocean_assert(n > newN);
811
812 n = newN;
813
814 // the for loop will stop after this iteration
815 ocean_assert(n + 16u == innerPixels);
816 ocean_assert(!(n + 16u < innerPixels));
817 }
818
819 const uint8x16_t source_0_u_8x16 = vld1q_u8(source + n + 0u);
820 const uint8x16_t source_1_u_8x16 = vld1q_u8(source + n + 1u);
821 const uint8x16_t source_2_u_8x16 = vld1q_u8(source + n + 2u);
822
823 // result = source0 + source2
824 uint16x8_t low_u_16x8 = vaddl_u8(vget_low_u8(source_0_u_8x16), vget_low_u8(source_2_u_8x16));
825 uint16x8_t high_u_16x8 = vaddl_u8(vget_high_u8(source_0_u_8x16), vget_high_u8(source_2_u_8x16));
826
827 // result += 2 * source1
828 low_u_16x8 = vmlal_u8(low_u_16x8, vget_low_u8(source_1_u_8x16), constant_2_u_8x8);
829 high_u_16x8 = vmlal_u8(high_u_16x8, vget_high_u8(source_1_u_8x16), constant_2_u_8x8);
830
831 vst1q_u16(responseTopRow + 1u + n + 0u, low_u_16x8);
832 vst1q_u16(responseTopRow + 1u + n + 8u, high_u_16x8);
833 }
834
835 responseTopRow[width - 1u] = source[width - 2u] + source[width - 1u] * 3u; // special handling for last pixel response
836
837 // due to border mirroring, our top and center row is identical for the first iteration
838 uint16_t* responseCenterRow = responseTopRow;
839 uint16_t* responseBottomRow = responseRows + width * 2u;
840 uint8_t* const sourceExtraCopy = (uint8_t*)(responseRows + width * 3u);
841
842 source += sourceStrideElements;
843
844 for (unsigned int y = 0u; y < height; ++y)
845 {
846 if (y == height - 2u)
847 {
848 // we need to make a copy of the last source row for in-place filtering
849 memcpy(sourceExtraCopy, source, width * sizeof(uint8_t));
850 }
851
852 // for each iteration, we have a pre-calculated (horizontal) response for the top and center row already
853
854 responseBottomRow[0u] = source[0] * 3u + source[1];
855
856 // handle left pixel: (outside) (inside)
857 // | 3 1 1 | 2 1
858 // | [6] 2 2 | [4] 2
859 // | 3 1 the filter factors are based on: 1 | 2 1
860
861 // using scoped value for intermediate storage as source and target can be identical e.g., for in-place filtering
862 const ScopedValueT<uint8_t> firstPixelValue(*target, uint8_t((responseTopRow[0] + responseCenterRow[0] * 2u + responseBottomRow[0] + 8u) / 16u));
863
864 for (unsigned int n = 0u; n < innerPixels; n += 16u)
865 {
866 if (n + 16u > innerPixels)
867 {
868 ocean_assert(n >= 16u && innerPixels > 16u);
869 const unsigned int newN = innerPixels - 16u;
870
871 const unsigned int offset = n - newN;
872 ocean_assert_and_suppress_unused(offset < innerPixels, offset);
873
874 ocean_assert(n > newN);
875
876 n = newN;
877
878 // the for loop will stop after this iteration
879 ocean_assert(n + 16u == innerPixels);
880 ocean_assert(!(n + 16u < innerPixels));
881 }
882
883 const uint8x16_t sourceBottom_0_u_8x16 = vld1q_u8(source + n + 0u);
884 const uint8x16_t sourceBottom_1_u_8x16 = vld1q_u8(source + n + 1u);
885 const uint8x16_t sourceBottom_2_u_8x16 = vld1q_u8(source + n + 2u);
886
887 // bottomResult = bottomSource0 + bottomSource2
888 uint16x8_t bottomLow_u_16x8 = vaddl_u8(vget_low_u8(sourceBottom_0_u_8x16), vget_low_u8(sourceBottom_2_u_8x16));
889 uint16x8_t bottomHigh_u_16x8 = vaddl_u8(vget_high_u8(sourceBottom_0_u_8x16), vget_high_u8(sourceBottom_2_u_8x16));
890
891 // bottomResult += 2 * bottomSource1
892 bottomLow_u_16x8 = vmlal_u8(bottomLow_u_16x8, vget_low_u8(sourceBottom_1_u_8x16), constant_2_u_8x8);
893 bottomHigh_u_16x8 = vmlal_u8(bottomHigh_u_16x8, vget_high_u8(sourceBottom_1_u_8x16), constant_2_u_8x8);
894
895
896 // load the pre-calculated values for top
897 const uint16x8_t topLow_u_16x8 = vld1q_u16(responseTopRow + 1u + n + 0u);
898 const uint16x8_t topHigh_u_16x8 = vld1q_u16(responseTopRow + 1u + n + 8u);
899
900 // load the pre-calculated values for bottom
901 const uint16x8_t centerLow_u_16x8 = vld1q_u16(responseCenterRow + 1u + n + 0u);
902 const uint16x8_t centerHigh_u_16x8 = vld1q_u16(responseCenterRow + 1u + n + 8u);
903
904 // result = top + bottom
905 uint16x8_t resultLow_u_16x8 = vaddq_u16(topLow_u_16x8, bottomLow_u_16x8);
906 uint16x8_t resultHigh_u_16x8 = vaddq_u16(topHigh_u_16x8, bottomHigh_u_16x8);
907
908 // result += 2 * center
909 resultLow_u_16x8 = vmlaq_u16(resultLow_u_16x8, centerLow_u_16x8, constant_2_u_16x8);
910 resultHigh_u_16x8 = vmlaq_u16(resultHigh_u_16x8, centerHigh_u_16x8, constant_2_u_16x8);
911
912 // write the results for the bottom row so that we can use them as new pre-calculated values in the next iteration
913 // as we may re-calculate the last 16 pixels once again in the very last iteration, we cannot simply write the results to the center row
914 vst1q_u16(responseBottomRow + 1u + n + 0u, bottomLow_u_16x8);
915 vst1q_u16(responseBottomRow + 1u + n + 8u, bottomHigh_u_16x8);
916
917 // result = (result + 8) / 16
918 const uint8x16_t result_u_8x16 = vcombine_u8(vrshrn_n_u16(resultLow_u_16x8, 4), vrshrn_n_u16(resultHigh_u_16x8, 4));
919
920 vst1q_u8(target + 1u + n, result_u_8x16);
921 }
922
923 responseBottomRow[width - 1u] = source[width - 2u] + source[width - 1u] * 3u;
924
925 // handle right pixel: (inside) (outside)
926 // 1 3 | 1 2 | 1
927 // 2 [6] | 2 [4] | 2
928 // 1 3 | 1 2 | 1
929
930 target[width - 1u] = uint8_t((responseTopRow[width - 1u] + responseCenterRow[width - 1u] * 2u + responseBottomRow[width - 1u] + 8u) / 16u);
931
932 source += sourceStrideElements;
933 target += targetStrideElements;
934
935 std::swap(responseTopRow, responseCenterRow);
936
937 if (y == 0u)
938 {
939 // the next row will not have any border mirroring anymore
940
941 responseCenterRow = responseRows + width * 1u;
942 }
943 else if (y == height - 2u)
944 {
945 // the next iteration will handle the last row in the frame
946 // the bottom row will be mirrored which is actually the last row again
947
948 source = sourceExtraCopy;
949 }
950
951 std::swap(responseCenterRow, responseBottomRow);
952 }
953}
954
955#endif // OCEAN_HARDWARE_NEON_VERSION >= 10
956
957}
958
959}
960
961#endif // META_OCEAN_CV_FRAME_FILTER_GAUSSIAN_H
This class holds re-usable memory for the filtering process.
Definition FrameFilterGaussian.h:36
ReusableMemory()=default
Default constructor.
Memory horizontalFilterMemory_
The reusable memory for horizontal filter factors.
Definition FrameFilterGaussian.h:52
Memory verticalFilterMemory_
The reusable memory for vertical filter factors.
Definition FrameFilterGaussian.h:55
FrameFilterSeparable::ReusableMemory separableReusableMemory_
The reusable memory object for the separable filter.
Definition FrameFilterGaussian.h:49
Memory responseRowsMemory_
The reusable memory for several response rows.
Definition FrameFilterGaussian.h:58
This class implements Gaussian image blur filters.
Definition FrameFilterGaussian.h:29
static T filterSize2sigma(const unsigned int filterSize)
Calculates the sigma corresponding to a specified box filter so that the Gauss distribution using the...
Definition FrameFilterGaussian.h:230
static void filter1Channel8Bit121SSE(const uint8_t *source, uint8_t *target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, ReusableMemory *reusableMemory)
Applies a horizontal and vertical filtering with a Gaussian kernel with size 3, applying a horizontal...
Definition FrameFilterGaussian.h:534
static void determineFilterFactors(const unsigned int filterSize, T *filter, T *denominator=nullptr)
Determines 1D Gaussian blur filter factors for a given filter size.
Definition FrameFilterGaussian.h:371
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...
static bool filter(Frame &frame, const unsigned int filterSize, Worker *worker=nullptr, ReusableMemory *reusableMemory=nullptr)
Applies a Gaussian blur filter to a given frame.
static unsigned int sigma2filterSize(const T sigma)
Calculates the ideal size of a box filter for a specified sigma defining the shape of the Gauss distr...
Definition FrameFilterGaussian.h:217
static void determineFilterFactorsWithExplicitSigma(const unsigned int filterSize, const float sigma, T *filter, T *denominator=nullptr)
Determines 1D Gaussian blur filter factors for a given filter size.
Definition FrameFilterGaussian.h:263
static void filter1Channel8Bit121NEON(const uint8_t *source, uint8_t *target, const unsigned int width, const unsigned int height, const unsigned int sourcePaddingElements, const unsigned int targetPaddingElements, ReusableMemory *reusableMemory)
Applies a horizontal and vertical filtering with a Gaussian kernel with size 3, applying a horizontal...
Definition FrameFilterGaussian.h:756
This class holds re-usable memory for the filtering process.
Definition FrameFilterSeparable.h:40
This class implements Ocean's image class.
Definition Frame.h:1879
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 T exp(const T value)
Returns the base-e exponential function of a given value.
Definition Numeric.h:1647
static constexpr T eps()
Returns a small epsilon.
This class implements a scoped value that allows to change a specified value at the end of a scope.
Definition ScopedValue.h:24
This class implements a worker able to distribute function calls over different threads.
Definition Worker.h:33
ProcessorInstructions
Definition of individual processor instruction types.
Definition base/Processor.h:22
The namespace covering the entire Ocean framework.
Definition Accessor.h:15