KleidiCV Coverage Report


Directory: ./
File: kleidicv/include/kleidicv/filters/sigma.h
Date: 2026-01-20 20:58:59
Exec Total Coverage
Lines: 31 31 100.0%
Functions: 5 5 100.0%
Branches: 8 8 100.0%

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