GCC Code Coverage Report


Directory: ./
File: tasks/batkov_f_contrast_enh_lin_hist_stretch/stl/src/ops_stl.cpp
Date: 2026-06-04 20:25:32
Exec Total Coverage
Lines: 86 95 90.5%
Functions: 14 15 93.3%
Branches: 48 72 66.7%

Line Branch Exec Source
1 #include "batkov_f_contrast_enh_lin_hist_stretch/stl/include/ops_stl.hpp"
2
3 #include <algorithm>
4 #include <array>
5 #include <barrier>
6 #include <cstddef>
7 #include <cstdint>
8 #include <iterator>
9 #include <limits>
10 #include <thread>
11 #include <vector>
12
13 #include "batkov_f_contrast_enh_lin_hist_stretch/common/include/common.hpp"
14 #include "util/include/util.hpp"
15
16 namespace batkov_f_contrast_enh_lin_hist_stretch {
17
18 namespace {
19
20 constexpr size_t kParallelThreshold = 100000;
21 constexpr size_t kMinGrainSize = 1U << 16U;
22
23 struct Range {
24 size_t begin;
25 size_t end;
26
27 30 Range(size_t begin_value, size_t end_value) noexcept : begin(begin_value), end(end_value) {}
28 };
29
30 struct MinMax {
31 uint8_t min_val;
32 uint8_t max_val;
33
34 104 MinMax() noexcept : min_val(std::numeric_limits<uint8_t>::max()), max_val(std::numeric_limits<uint8_t>::min()) {}
35
36 MinMax(uint8_t min_value, uint8_t max_value) noexcept : min_val(min_value), max_val(max_value) {}
37 };
38
39 48 size_t ChooseThreadCount(size_t size, size_t requested_threads) noexcept {
40
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 30 times.
48 if (size < kParallelThreshold || requested_threads <= 1) {
41 return 1;
42 }
43
44
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 const size_t by_grain = std::max<size_t>(1, size / kMinGrainSize);
45 36 return std::max<size_t>(1, std::min({requested_threads, size, by_grain}));
46 }
47
48 Range MakeRange(size_t size, size_t thread_index, size_t thread_count) noexcept {
49 52 return {(size * thread_index) / thread_count, (size * (thread_index + 1)) / thread_count};
50 }
51
52 std::array<uint8_t, 256> BuildStretchLut(uint8_t min_el, uint8_t max_el) {
53 48 std::array<uint8_t, 256> lut{};
54
55 48 const int min_value = static_cast<int>(min_el);
56 48 const int max_value = static_cast<int>(max_el);
57 48 const int range = max_value - min_value;
58
59
4/4
✓ Branch 0 taken 4608 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 7680 times.
✓ Branch 3 taken 30 times.
12336 for (size_t pixel = 0; pixel < lut.size(); ++pixel) {
60 12288 const int value = static_cast<int>(pixel);
61
62
4/4
✓ Branch 0 taken 1818 times.
✓ Branch 1 taken 2790 times.
✓ Branch 2 taken 4230 times.
✓ Branch 3 taken 3450 times.
12288 if (value <= min_value) {
63 6048 lut.at(pixel) = 0;
64
3/4
✓ Branch 0 taken 2088 times.
✓ Branch 1 taken 702 times.
✓ Branch 2 taken 3450 times.
✗ Branch 3 not taken.
6240 } else if (value >= max_value) {
65 5538 lut.at(pixel) = std::numeric_limits<uint8_t>::max();
66 } else {
67 702 lut.at(pixel) = static_cast<uint8_t>(((value - min_value) * 255) / range);
68 }
69 }
70
71 return lut;
72 }
73
74 uint8_t GetLutValue(const std::array<uint8_t, 256> &lut, uint8_t pixel) noexcept {
75 25442880 return *std::next(lut.cbegin(), static_cast<std::ptrdiff_t>(pixel));
76 }
77
78 52 MinMax FindLocalMinMax(const InType &input, const Range &range) {
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 auto input_it = std::next(input.cbegin(), static_cast<std::ptrdiff_t>(range.begin));
80
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 const auto input_last = std::next(input.cbegin(), static_cast<std::ptrdiff_t>(range.end));
81
82 MinMax result;
83
2/2
✓ Branch 0 taken 9072864 times.
✓ Branch 1 taken 52 times.
9072916 for (; input_it != input_last; ++input_it) {
84
2/2
✓ Branch 0 taken 204 times.
✓ Branch 1 taken 9072660 times.
9072864 result.min_val = std::min(result.min_val, *input_it);
85 9072864 result.max_val = std::max(result.max_val, *input_it);
86 }
87
88 52 return result;
89 }
90
91 MinMax CombineLocalMinMax(const std::vector<MinMax> &locals) noexcept {
92 MinMax result;
93
94
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 18 times.
70 for (const auto &local : locals) {
95 result.min_val = std::min(result.min_val, local.min_val);
96 result.max_val = std::max(result.max_val, local.max_val);
97 }
98
99 return result;
100 }
101
102 void CopyRange(const InType &input, OutType &output, const Range &range) {
103 const auto input_first = std::next(input.cbegin(), static_cast<std::ptrdiff_t>(range.begin));
104 const auto input_last = std::next(input.cbegin(), static_cast<std::ptrdiff_t>(range.end));
105 const auto output_first = std::next(output.begin(), static_cast<std::ptrdiff_t>(range.begin));
106
107 std::copy(input_first, input_last, output_first);
108 }
109
110 82 void ApplyLutRange(const InType &input, OutType &output, const Range &range, const std::array<uint8_t, 256> &lut) {
111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 auto input_it = std::next(input.cbegin(), static_cast<std::ptrdiff_t>(range.begin));
112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 const auto input_last = std::next(input.cbegin(), static_cast<std::ptrdiff_t>(range.end));
113 auto output_it = std::next(output.begin(), static_cast<std::ptrdiff_t>(range.begin));
114
115
2/2
✓ Branch 0 taken 12721440 times.
✓ Branch 1 taken 82 times.
12721522 for (; input_it != input_last; ++input_it, ++output_it) {
116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12721440 times.
25442880 *output_it = GetLutValue(lut, *input_it);
117 }
118 82 }
119
120 52 void ApplyResultRange(const InType &input, OutType &output, const Range &range, bool copy_only,
121 const std::array<uint8_t, 256> &lut) {
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if (copy_only) {
123 CopyRange(input, output, range);
124 return;
125 }
126
127 52 ApplyLutRange(input, output, range, lut);
128 }
129
130
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 void StretchSequential(const InType &input, OutType &output) {
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 const auto minmax = std::ranges::minmax_element(input.cbegin(), input.cend());
132 30 const uint8_t min_el = *minmax.max;
133
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 const uint8_t max_el = *minmax.min;
134 const Range full_range(0, input.size());
135
136
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if (min_el == max_el) {
137 CopyRange(input, output, full_range);
138 return;
139 }
140
141 const auto lut = BuildStretchLut(min_el, max_el);
142 30 ApplyLutRange(input, output, full_range, lut);
143 }
144
145 18 void StretchParallel(const InType &input, OutType &output, size_t thread_count) {
146 18 const size_t size = input.size();
147 18 std::vector<MinMax> locals(thread_count);
148 18 std::array<uint8_t, 256> lut{};
149 18 bool copy_only = false;
150
151 18 auto completion = [&locals, &lut, &copy_only]() noexcept {
152 18 const MinMax global = CombineLocalMinMax(locals);
153 18 copy_only = global.min_val == global.max_val;
154
155
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (!copy_only) {
156 36 lut = BuildStretchLut(global.min_val, global.max_val);
157 }
158 18 };
159
160
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 std::barrier<decltype(completion)> barrier(static_cast<std::ptrdiff_t>(thread_count), completion);
161
162 18 std::vector<std::thread> threads;
163
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 threads.reserve(thread_count);
164
165
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 18 times.
70 for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) {
166
1/2
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
52 threads.emplace_back([&, thread_index]() {
167 52 const Range range = MakeRange(size, thread_index, thread_count);
168
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 locals.at(thread_index) = FindLocalMinMax(input, range);
169 52 barrier.arrive_and_wait();
170 52 ApplyResultRange(input, output, range, copy_only, lut);
171 52 });
172 }
173
174
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 18 times.
70 for (auto &thread : threads) {
175
1/2
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
52 thread.join();
176 }
177 36 }
178
179
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 18 times.
48 void StretchContrast(const InType &input, OutType &output, size_t requested_threads) {
180 48 const size_t thread_count = ChooseThreadCount(input.size(), requested_threads);
181
182
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 18 times.
48 if (thread_count == 1) {
183 30 StretchSequential(input, output);
184 30 return;
185 }
186
187 18 StretchParallel(input, output, thread_count);
188 }
189
190 } // namespace
191
192
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 BatkovFContrastEnhLinHistStretchSTL::BatkovFContrastEnhLinHistStretchSTL(const InType &in) {
193 SetTypeOfTask(GetStaticTypeOfTask());
194
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 GetInput() = in;
195 48 }
196
197 48 bool BatkovFContrastEnhLinHistStretchSTL::ValidationImpl() {
198 48 return !GetInput().empty();
199 }
200
201 48 bool BatkovFContrastEnhLinHistStretchSTL::PreProcessingImpl() {
202 48 GetOutput().resize(GetInput().size());
203 48 return true;
204 }
205
206 48 bool BatkovFContrastEnhLinHistStretchSTL::RunImpl() {
207 const auto &input = GetInput();
208 auto &output = GetOutput();
209
210 48 const size_t requested_threads = static_cast<size_t>(std::max(1, ppc::util::GetNumThreads()));
211 48 StretchContrast(input, output, requested_threads);
212
213 48 return true;
214 }
215
216 48 bool BatkovFContrastEnhLinHistStretchSTL::PostProcessingImpl() {
217 48 return !GetOutput().empty();
218 }
219
220 } // namespace batkov_f_contrast_enh_lin_hist_stretch
221