| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-FileCopyrightText: 2024 - 2025 Arm Limited and/or its affiliates <open-source-office@arm.com> | ||
| 2 | // | ||
| 3 | // SPDX-License-Identifier: Apache-2.0 | ||
| 4 | |||
| 5 | #ifndef KLEIDICV_SIGMA_H | ||
| 6 | #define KLEIDICV_SIGMA_H | ||
| 7 | |||
| 8 | #include <cmath> | ||
| 9 | #include <cstdint> | ||
| 10 | #include <cstdlib> | ||
| 11 | |||
| 12 | #include "kleidicv/config.h" | ||
| 13 | |||
| 14 | namespace KLEIDICV_TARGET_NAMESPACE { | ||
| 15 | |||
| 16 | 84 | static constexpr size_t get_half_kernel_size(size_t kernel_size) | |
| 17 | KLEIDICV_STREAMING { | ||
| 18 | // since kernel sizes are odd, "half" here means that | ||
| 19 | // the extra element is included | ||
| 20 | 84 | return (kernel_size >> 1) + 1; | |
| 21 | } | ||
| 22 | |||
| 23 | // This function is not marked as streaming compatible, as std::round is also | ||
| 24 | // not streaming compatible. Returns false if all the kernel's energy is | ||
| 25 | // concentrated in the mid element, in that case the filtering is equivalent to | ||
| 26 | // copying the input image. | ||
| 27 | 956 | static bool generate_gaussian_half_kernel(uint8_t* half_kernel, | |
| 28 | size_t half_size, float sigma) { | ||
| 29 | // Define the mid point of the full kernel range. | ||
| 30 | 956 | const size_t kMid = half_size - 1; | |
| 31 | |||
| 32 | // Define the full kernel size. | ||
| 33 | 956 | const size_t kKernelSize = kMid * 2 + 1; | |
| 34 | |||
| 35 | // Calculate the sigma manually in case it is not defined. | ||
| 36 |
2/2✓ Branch 0 taken 756 times.
✓ Branch 1 taken 200 times.
|
956 | if (sigma == 0.0) { |
| 37 | 200 | sigma = static_cast<float>(kKernelSize) * 0.15F + 0.35F; | |
| 38 | 200 | } | |
| 39 | |||
| 40 | // Temporary float half-kernel. | ||
| 41 | 956 | float half_kernel_float[255]; | |
| 42 | |||
| 43 | // Prepare the sigma value for later multiplication inside a loop. | ||
| 44 | 956 | float coefficient = 1 / -(2 * sigma * sigma); | |
| 45 | |||
| 46 | 956 | float sum = 0.0; | |
| 47 | 956 | size_t j = kMid; | |
| 48 |
2/2✓ Branch 0 taken 4892 times.
✓ Branch 1 taken 956 times.
|
5848 | for (size_t i = 0; i < kMid; i++, j--) { |
| 49 | 4892 | half_kernel_float[i] = | |
| 50 | 4892 | std::exp(static_cast<float>(j) * static_cast<float>(j) * coefficient); | |
| 51 | 4892 | sum += half_kernel_float[i]; | |
| 52 | 4892 | } | |
| 53 | |||
| 54 | // This multiplier serves two purposes: | ||
| 55 | // * Normalizes the kernel values, so the sum of the final values is 1. | ||
| 56 | // (The 'sum' variable only accounts for the half of the kernel values | ||
| 57 | // without the mid point. This is the reason for the division by | ||
| 58 | // '(sum * 2 + 1)'.) | ||
| 59 | // * Converts the values to fixed-point (uint8_t), where all the bits are used | ||
| 60 | // for the fractional part. (Result is less than 1.0.) This is the | ||
| 61 | // reason for the multiplication by 256. | ||
| 62 | 956 | float multiplier = 256 / (sum * 2 + 1); | |
| 63 | |||
| 64 | // Normalize the kernel and convert it to fixed-point format. Rounding errors | ||
| 65 | // are diffused in the kernel. | ||
| 66 | 956 | float error = 0.0; | |
| 67 |
2/2✓ Branch 0 taken 4892 times.
✓ Branch 1 taken 956 times.
|
5848 | for (size_t i = 0; i < kMid; i++) { |
| 68 | 4892 | float value = half_kernel_float[i] * multiplier - error; | |
| 69 | 4892 | float value_rounded = std::round(value); | |
| 70 | 4892 | half_kernel[i] = static_cast<uint8_t>(value_rounded); | |
| 71 | 4892 | error = value_rounded - value; | |
| 72 | 4892 | } | |
| 73 | 956 | uint16_t mid_value = static_cast<uint16_t>(std::round(multiplier - error)); | |
| 74 |
2/2✓ Branch 0 taken 364 times.
✓ Branch 1 taken 592 times.
|
956 | if (mid_value == 256) { |
| 75 | 364 | return false; | |
| 76 | } | ||
| 77 | 592 | half_kernel[kMid] = static_cast<uint8_t>(mid_value); | |
| 78 | 592 | return true; | |
| 79 | 956 | } | |
| 80 | |||
| 81 | } // namespace KLEIDICV_TARGET_NAMESPACE | ||
| 82 | |||
| 83 | #endif // KLEIDICV_SIGMA_H | ||
| 84 |