GCC Code Coverage Report


Directory: ./
File: tasks/marin_l_mark_components/all/src/ops_all.cpp
Date: 2026-06-04 20:25:32
Exec Total Coverage
Lines: 229 248 92.3%
Functions: 22 23 95.7%
Branches: 188 288 65.3%

Line Branch Exec Source
1 #include "marin_l_mark_components/all/include/ops_all.hpp"
2
3 #include <mpi.h>
4
5 #include <algorithm>
6 #include <cstddef>
7 #include <cstdint>
8 #include <vector>
9
10 #include "marin_l_mark_components/common/include/common.hpp"
11 #include "oneapi/tbb/blocked_range.h"
12 #include "oneapi/tbb/global_control.h"
13 #include "oneapi/tbb/parallel_for.h"
14 #include "util/include/util.hpp"
15
16 namespace marin_l_mark_components {
17
18 namespace {
19
20 constexpr std::uint64_t kMaxPixels = 100000000ULL;
21 constexpr int kMinRowsPerStripe = 64;
22
23 struct StripeSetup {
24 int num_stripes = 1;
25 int total_max_labels = 1;
26 std::vector<int> stripe_bounds;
27 std::vector<int> stripe_base_label;
28 };
29
30 12 std::vector<int> BuildCounts(int total_items, int proc_count) {
31 12 std::vector<int> counts(static_cast<std::size_t>(proc_count), 0);
32
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
36 for (int rank = 0; rank < proc_count; ++rank) {
33 24 counts[static_cast<std::size_t>(rank)] =
34 24 (((rank + 1) * total_items) / proc_count) - ((rank * total_items) / proc_count);
35 }
36 12 return counts;
37 }
38
39 24 std::vector<int> BuildDisplacements(const std::vector<int> &counts) {
40 24 std::vector<int> displs(counts.size(), 0);
41
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 24 times.
48 for (std::size_t idx = 1; idx < counts.size(); ++idx) {
42 24 displs[idx] = displs[idx - 1] + counts[idx - 1];
43 }
44 24 return displs;
45 }
46
47 12 std::vector<int> BuildPixelCounts(const std::vector<int> &row_counts, int width) {
48 12 std::vector<int> pixel_counts(row_counts.size(), 0);
49
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
36 for (std::size_t rank = 0; rank < row_counts.size(); ++rank) {
50 24 pixel_counts[rank] = row_counts[rank] * width;
51 }
52 12 return pixel_counts;
53 }
54
55
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 std::vector<std::uint8_t> FlattenImage(const Image &img) {
56
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 if (img.empty() || img.front().empty()) {
57 return {};
58 }
59
60 6 const int height = static_cast<int>(img.size());
61 6 const int width = static_cast<int>(img.front().size());
62 6 std::vector<std::uint8_t> flat(static_cast<std::size_t>(height) * static_cast<std::size_t>(width), 0);
63
64
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 6 times.
56 for (int row = 0; row < height; ++row) {
65 50 const std::size_t row_offset = static_cast<std::size_t>(row) * static_cast<std::size_t>(width);
66
2/2
✓ Branch 0 taken 472 times.
✓ Branch 1 taken 50 times.
522 for (int col = 0; col < width; ++col) {
67 472 flat[row_offset + static_cast<std::size_t>(col)] =
68 472 static_cast<std::uint8_t>(img[static_cast<std::size_t>(row)][static_cast<std::size_t>(col)]);
69 }
70 }
71
72 return flat;
73 }
74
75 int FindRoot(std::vector<int> &parent, int x) {
76 int root = x;
77
6/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 246 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 5 times.
355 while (parent[static_cast<std::size_t>(root)] != root) {
78 root = parent[static_cast<std::size_t>(root)];
79 }
80
81 int current = x;
82
6/8
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 94 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 246 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 5 times.
355 while (current != root) {
83 5 const int next = parent[static_cast<std::size_t>(current)];
84 5 parent[static_cast<std::size_t>(current)] = root;
85 current = next;
86 }
87
88 return root;
89 }
90
91 5 void UnionLabels(std::vector<int> &parent, int a, int b) {
92 const int root_a = FindRoot(parent, a);
93 const int root_b = FindRoot(parent, b);
94
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 if (root_a == root_b) {
95 return;
96 }
97
98
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (root_a < root_b) {
99 1 parent[static_cast<std::size_t>(root_b)] = root_a;
100 } else {
101 parent[static_cast<std::size_t>(root_a)] = root_b;
102 }
103 }
104
105 12 StripeSetup BuildStripeSetup(int height, int width, int concurrency) {
106 12 StripeSetup setup;
107
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 if (height <= 0 || width <= 0) {
108 return setup;
109 }
110
111
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 setup.num_stripes = std::min(height, concurrency * 2);
112
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 if (setup.num_stripes > 0 && height / setup.num_stripes < kMinRowsPerStripe) {
113
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
24 setup.num_stripes = std::max(1, height / kMinRowsPerStripe);
114 }
115 12 setup.num_stripes = std::max(1, setup.num_stripes);
116
117
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 setup.stripe_bounds.assign(static_cast<std::size_t>(setup.num_stripes) + 1ULL, 0);
118
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
36 for (int stripe = 0; stripe <= setup.num_stripes; ++stripe) {
119 24 setup.stripe_bounds[static_cast<std::size_t>(stripe)] = (stripe * height) / setup.num_stripes;
120 }
121
122
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 setup.stripe_base_label.assign(static_cast<std::size_t>(setup.num_stripes), 0);
123
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 for (int stripe = 0; stripe < setup.num_stripes; ++stripe) {
124 12 setup.stripe_base_label[static_cast<std::size_t>(stripe)] = setup.total_max_labels;
125 12 const int stripe_height = setup.stripe_bounds[static_cast<std::size_t>(stripe) + 1ULL] -
126 12 setup.stripe_bounds[static_cast<std::size_t>(stripe)];
127 12 setup.total_max_labels += ((stripe_height * width) / 2) + 1;
128 }
129
130 return setup;
131 }
132
133 void InitializeParents(std::vector<int> &parent, int total_max_labels) {
134 12 oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<int>(0, total_max_labels),
135 24 [&](const oneapi::tbb::blocked_range<int> &range) {
136
4/4
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 124 times.
✓ Branch 2 taken 134 times.
✓ Branch 3 taken 134 times.
516 for (int label = range.begin(); label != range.end(); ++label) {
137 258 parent[static_cast<std::size_t>(label)] = label;
138 }
139 });
140 }
141
142 145 void AssignPixelLabel(std::vector<int> &labels_flat, std::vector<int> &parent, std::size_t idx, int left_label,
143 int top_label, int &next_label) {
144
4/4
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 37 times.
✓ Branch 2 taken 94 times.
✓ Branch 3 taken 14 times.
145 if (left_label == 0 && top_label == 0) {
145 94 labels_flat[idx] = next_label++;
146 94 return;
147 }
148
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 37 times.
51 if (left_label == 0) {
149 14 labels_flat[idx] = top_label;
150 14 return;
151 }
152
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 20 times.
37 if (top_label == 0) {
153 17 labels_flat[idx] = left_label;
154 17 return;
155 }
156
157 const int min_label = std::min(left_label, top_label);
158 20 labels_flat[idx] = min_label;
159
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (left_label != top_label) {
160 UnionLabels(parent, left_label, top_label);
161 }
162 }
163
164 12 void LabelStripe(const std::vector<std::uint8_t> &binary_flat, std::vector<int> &labels_flat, std::vector<int> &parent,
165 const std::vector<int> &stripe_bounds, const std::vector<int> &stripe_base_label,
166 std::vector<int> &stripe_label_end, int width, int stripe) {
167 12 const int start_row = stripe_bounds[static_cast<std::size_t>(stripe)];
168 12 const int end_row = stripe_bounds[static_cast<std::size_t>(stripe) + 1ULL];
169 12 int next_label = stripe_base_label[static_cast<std::size_t>(stripe)];
170
171
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 12 times.
62 for (int row = start_row; row < end_row; ++row) {
172 50 const std::size_t row_offset = static_cast<std::size_t>(row) * static_cast<std::size_t>(width);
173 const bool has_top = row > start_row;
174
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 12 times.
50 const std::size_t top_row_offset = has_top ? row_offset - static_cast<std::size_t>(width) : 0;
175
176
2/2
✓ Branch 0 taken 472 times.
✓ Branch 1 taken 50 times.
522 for (int col = 0; col < width; ++col) {
177
2/2
✓ Branch 0 taken 327 times.
✓ Branch 1 taken 145 times.
472 const std::size_t idx = row_offset + static_cast<std::size_t>(col);
178
2/2
✓ Branch 0 taken 327 times.
✓ Branch 1 taken 145 times.
472 if (binary_flat[idx] == 0U) {
179 327 continue;
180 }
181
182
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 13 times.
145 const int left_label = (col > 0) ? labels_flat[idx - 1ULL] : 0;
183
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 28 times.
145 const int top_label = has_top ? labels_flat[top_row_offset + static_cast<std::size_t>(col)] : 0;
184 145 AssignPixelLabel(labels_flat, parent, idx, left_label, top_label, next_label);
185 }
186 }
187
188 12 stripe_label_end[static_cast<std::size_t>(stripe)] = next_label;
189 12 }
190
191 12 void MergeLocalStripeBorders(const std::vector<std::uint8_t> &binary_flat, const std::vector<int> &stripe_bounds,
192 std::vector<int> &labels_flat, std::vector<int> &parent, int width, int num_stripes) {
193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 for (int stripe = 0; stripe < num_stripes - 1; ++stripe) {
194 const int boundary_row = stripe_bounds[static_cast<std::size_t>(stripe) + 1ULL];
195 const std::size_t top_row_offset = static_cast<std::size_t>(boundary_row - 1) * static_cast<std::size_t>(width);
196 const std::size_t bottom_row_offset = static_cast<std::size_t>(boundary_row) * static_cast<std::size_t>(width);
197
198 for (int col = 0; col < width; ++col) {
199 const std::size_t top_idx = top_row_offset + static_cast<std::size_t>(col);
200 const std::size_t bottom_idx = bottom_row_offset + static_cast<std::size_t>(col);
201 if (binary_flat[top_idx] == 1U && binary_flat[bottom_idx] == 1U) {
202 const int top_label = labels_flat[top_idx];
203 const int bottom_label = labels_flat[bottom_idx];
204 if (top_label > 0 && bottom_label > 0 && top_label != bottom_label) {
205 UnionLabels(parent, top_label, bottom_label);
206 }
207 }
208 }
209 }
210 12 }
211
212 12 int CompactLocalLabels(std::vector<int> &labels_flat, std::vector<int> &parent,
213 const std::vector<int> &stripe_base_label, const std::vector<int> &stripe_label_end,
214 int total_max_labels) {
215
2/2
✓ Branch 0 taken 246 times.
✓ Branch 1 taken 12 times.
258 for (int label = 1; label < total_max_labels; ++label) {
216 246 parent[static_cast<std::size_t>(label)] = FindRoot(parent, label);
217 }
218
219 12 std::vector<int> compacted(static_cast<std::size_t>(total_max_labels), 0);
220 int next_compact_id = 1;
221
222
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 for (std::size_t stripe = 0; stripe < stripe_base_label.size(); ++stripe) {
223
2/2
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 12 times.
106 for (int label = stripe_base_label[stripe]; label < stripe_label_end[stripe]; ++label) {
224
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
94 const int root = parent[static_cast<std::size_t>(label)];
225
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
94 if (compacted[static_cast<std::size_t>(root)] == 0) {
226 94 compacted[static_cast<std::size_t>(root)] = next_compact_id++;
227 }
228 94 compacted[static_cast<std::size_t>(label)] = compacted[static_cast<std::size_t>(root)];
229 }
230 }
231
232 12 oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<std::size_t>(0, labels_flat.size()),
233
1/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12 [&](const oneapi::tbb::blocked_range<std::size_t> &range) {
234
4/4
✓ Branch 0 taken 135 times.
✓ Branch 1 taken 135 times.
✓ Branch 2 taken 337 times.
✓ Branch 3 taken 337 times.
944 for (std::size_t idx = range.begin(); idx != range.end(); ++idx) {
235
4/4
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 88 times.
✓ Branch 2 taken 98 times.
✓ Branch 3 taken 239 times.
472 const int label = labels_flat[idx];
236
4/4
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 88 times.
✓ Branch 2 taken 98 times.
✓ Branch 3 taken 239 times.
472 if (label != 0) {
237 145 labels_flat[idx] = compacted[static_cast<std::size_t>(label)];
238 }
239 }
240 });
241
242
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
24 return next_compact_id - 1;
243 }
244
245 12 void ApplyRankOffset(std::vector<int> &labels_flat, int label_offset) {
246
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (label_offset == 0) {
247 return;
248 }
249
250 6 oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<std::size_t>(0, labels_flat.size()),
251 6 [&](const oneapi::tbb::blocked_range<std::size_t> &range) {
252
4/4
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 65 times.
✓ Branch 2 taken 177 times.
✓ Branch 3 taken 177 times.
484 for (std::size_t idx = range.begin(); idx != range.end(); ++idx) {
253
4/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 54 times.
✓ Branch 3 taken 123 times.
242 if (labels_flat[idx] != 0) {
254 74 labels_flat[idx] += label_offset;
255 }
256 }
257 });
258 }
259
260 std::size_t FindNextNonEmptyRank(const std::vector<int> &row_counts, std::size_t rank) {
261 12 std::size_t next_rank = rank + 1;
262
3/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
12 while (next_rank < row_counts.size() && row_counts[next_rank] == 0) {
263 ++next_rank;
264 }
265 return next_rank;
266 }
267
268 bool HasAdjacentBoundary(const std::vector<int> &row_counts, const std::vector<int> &row_displs, std::size_t rank,
269 std::size_t next_rank) {
270
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (next_rank >= row_counts.size()) {
271 return false;
272 }
273
274 6 const int boundary_row = row_displs[rank] + row_counts[rank];
275 6 return row_displs[next_rank] == boundary_row;
276 }
277
278 6 void MergeBoundaryRow(const std::vector<std::uint8_t> &global_binary_flat, std::vector<int> &global_labels_flat,
279 std::vector<int> &parent, int boundary_row, int width) {
280 6 const std::size_t top_row_offset = static_cast<std::size_t>(boundary_row - 1) * static_cast<std::size_t>(width);
281 6 const std::size_t bottom_row_offset = static_cast<std::size_t>(boundary_row) * static_cast<std::size_t>(width);
282
283
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 6 times.
57 for (int col = 0; col < width; ++col) {
284 51 const std::size_t top_idx = top_row_offset + static_cast<std::size_t>(col);
285
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 34 times.
51 const std::size_t bottom_idx = bottom_row_offset + static_cast<std::size_t>(col);
286
4/4
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 5 times.
51 if (global_binary_flat[top_idx] != 1U || global_binary_flat[bottom_idx] != 1U) {
287 46 continue;
288 }
289
290 5 const int top_label = global_labels_flat[top_idx];
291 5 const int bottom_label = global_labels_flat[bottom_idx];
292
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 if (top_label > 0 && bottom_label > 0 && top_label != bottom_label) {
293 5 UnionLabels(parent, top_label, bottom_label);
294 }
295 }
296 6 }
297
298 6 void MergeRankBorders(const std::vector<std::uint8_t> &global_binary_flat, std::vector<int> &global_labels_flat,
299 std::vector<int> &parent, const std::vector<int> &row_counts, const std::vector<int> &row_displs,
300 int width) {
301
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 for (std::size_t rank = 0; rank < row_counts.size(); ++rank) {
302
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (row_counts[rank] == 0) {
303 continue;
304 }
305
306 const std::size_t next_rank = FindNextNonEmptyRank(row_counts, rank);
307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!HasAdjacentBoundary(row_counts, row_displs, rank, next_rank)) {
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (next_rank >= row_counts.size()) {
309 break;
310 }
311 continue;
312 }
313
314 const int boundary_row = row_displs[rank] + row_counts[rank];
315
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (boundary_row <= 0) {
316 break;
317 }
318 6 MergeBoundaryRow(global_binary_flat, global_labels_flat, parent, boundary_row, width);
319 }
320 6 }
321
322 6 void CompactGlobalLabels(std::vector<int> &global_labels_flat, std::vector<int> &parent, int total_labels) {
323
2/2
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 6 times.
100 for (int label = 1; label <= total_labels; ++label) {
324 94 parent[static_cast<std::size_t>(label)] = FindRoot(parent, label);
325 }
326
327 6 std::vector<int> compacted(static_cast<std::size_t>(total_labels) + 1ULL, 0);
328 int next_compact_id = 1;
329
330
2/2
✓ Branch 0 taken 472 times.
✓ Branch 1 taken 6 times.
478 for (int &label : global_labels_flat) {
331
2/2
✓ Branch 0 taken 327 times.
✓ Branch 1 taken 145 times.
472 if (label == 0) {
332 327 continue;
333 }
334
335
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 52 times.
145 const int root = parent[static_cast<std::size_t>(label)];
336
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 52 times.
145 if (compacted[static_cast<std::size_t>(root)] == 0) {
337 93 compacted[static_cast<std::size_t>(root)] = next_compact_id++;
338 }
339 145 label = compacted[static_cast<std::size_t>(root)];
340 }
341 6 }
342
343 } // namespace
344
345
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MarinLMarkComponentsALL::MarinLMarkComponentsALL(const InType &in) {
346 SetTypeOfTask(GetStaticTypeOfTask());
347 GetInput() = in;
348 12 }
349
350 bool MarinLMarkComponentsALL::IsBinary(const Image &img) {
351
2/4
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
112 for (const auto &row : img) {
352
2/4
✓ Branch 0 taken 944 times.
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1044 for (int pixel : row) {
353
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 944 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
944 if (pixel != 0 && pixel != 1) {
354 return false;
355 }
356 }
357 }
358 return true;
359 }
360
361 12 bool MarinLMarkComponentsALL::ValidationImpl() {
362 12 MPI_Comm_rank(MPI_COMM_WORLD, &rank_);
363 12 MPI_Comm_size(MPI_COMM_WORLD, &world_size_);
364
365 const auto &img = GetInput().binary;
366
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if (img.empty() || img.front().empty()) {
367 return false;
368 }
369
370 const std::size_t width = img.front().size();
371
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 12 times.
112 for (const auto &row : img) {
372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
100 if (row.size() != width) {
373 return false;
374 }
375 }
376
377 return IsBinary(img);
378 }
379
380
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 bool MarinLMarkComponentsALL::PreProcessingImpl() {
381 const auto &img = GetInput().binary;
382 12 height_ = static_cast<int>(img.size());
383 12 width_ = static_cast<int>(img.front().size());
384
385
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 if (height_ <= 0 || width_ <= 0) {
386 return false;
387 }
388
389 12 const std::uint64_t total_pixels = static_cast<std::uint64_t>(height_) * static_cast<std::uint64_t>(width_);
390
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (total_pixels > kMaxPixels) {
391 return false;
392 }
393
394 local_binary_flat_.clear();
395
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 global_labels_flat_.assign(static_cast<std::size_t>(total_pixels), 0);
396 labels_out_.clear();
397 return true;
398 }
399
400 12 bool MarinLMarkComponentsALL::RunImpl() {
401 12 const std::vector<int> row_counts = BuildCounts(height_, world_size_);
402
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 const std::vector<int> row_displs = BuildDisplacements(row_counts);
403
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 const std::vector<int> pixel_counts = BuildPixelCounts(row_counts, width_);
404
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 const std::vector<int> pixel_displs = BuildDisplacements(pixel_counts);
405
406 12 std::vector<std::uint8_t> global_binary_flat;
407
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank_ == 0) {
408
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 global_binary_flat = FlattenImage(GetInput().binary);
409 }
410
411
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 const int local_height = row_counts[static_cast<std::size_t>(rank_)];
412 12 const int local_pixels = pixel_counts[static_cast<std::size_t>(rank_)];
413
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 local_binary_flat_.assign(static_cast<std::size_t>(local_pixels), 0);
414
2/6
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
12 std::vector<int> local_labels_flat(static_cast<std::size_t>(local_pixels), 0);
415
416
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Scatterv(global_binary_flat.data(), pixel_counts.data(), pixel_displs.data(), MPI_UINT8_T,
417 local_binary_flat_.data(), local_pixels, MPI_UINT8_T, 0, MPI_COMM_WORLD);
418
419
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 const int thread_count = std::max(1, ppc::util::GetNumThreads());
420 const oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism,
421
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 static_cast<std::size_t>(thread_count));
422
423 12 int local_component_count = 0;
424
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (local_height > 0) {
425
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 const StripeSetup setup = BuildStripeSetup(local_height, width_, thread_count);
426
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 std::vector<int> parent(static_cast<std::size_t>(setup.total_max_labels), 0);
427
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 InitializeParents(parent, setup.total_max_labels);
428
429
1/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12 std::vector<int> stripe_label_end(static_cast<std::size_t>(setup.num_stripes), 0);
430 24 oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<int>(0, setup.num_stripes),
431
1/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
24 [&](const oneapi::tbb::blocked_range<int> &range) {
432
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
24 for (int stripe = range.begin(); stripe != range.end(); ++stripe) {
433 12 LabelStripe(local_binary_flat_, local_labels_flat, parent, setup.stripe_bounds, setup.stripe_base_label,
434 stripe_label_end, width_, stripe);
435 }
436 12 });
437
438 12 MergeLocalStripeBorders(local_binary_flat_, setup.stripe_bounds, local_labels_flat, parent, width_,
439 12 setup.num_stripes);
440
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 local_component_count = CompactLocalLabels(local_labels_flat, parent, setup.stripe_base_label, stripe_label_end,
441
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 setup.total_max_labels);
442 12 }
443
444
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 std::vector<int> local_component_counts(static_cast<std::size_t>(world_size_), 0);
445
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Allgather(&local_component_count, 1, MPI_INT, local_component_counts.data(), 1, MPI_INT, MPI_COMM_WORLD);
446
447
1/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12 std::vector<int> component_offsets(static_cast<std::size_t>(world_size_), 0);
448 int total_component_count = 0;
449
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
36 for (int rank = 0; rank < world_size_; ++rank) {
450 24 component_offsets[static_cast<std::size_t>(rank)] = total_component_count;
451 24 total_component_count += local_component_counts[static_cast<std::size_t>(rank)];
452 }
453
454
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 ApplyRankOffset(local_labels_flat, component_offsets[static_cast<std::size_t>(rank_)]);
455
456
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Gatherv(local_labels_flat.data(), local_pixels, MPI_INT, global_labels_flat_.data(), pixel_counts.data(),
457 pixel_displs.data(), MPI_INT, 0, MPI_COMM_WORLD);
458
459
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank_ == 0) {
460
1/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
6 std::vector<int> global_parent(static_cast<std::size_t>(total_component_count) + 1ULL, 0);
461 100 std::ranges::generate(global_parent, [value = 0]() mutable { return value++; });
462 6 MergeRankBorders(global_binary_flat, global_labels_flat_, global_parent, row_counts, row_displs, width_);
463
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 CompactGlobalLabels(global_labels_flat_, global_parent, total_component_count);
464 }
465
466
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Bcast(global_labels_flat_.data(), static_cast<int>(global_labels_flat_.size()), MPI_INT, 0, MPI_COMM_WORLD);
467
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Barrier(MPI_COMM_WORLD);
468 12 return true;
469 }
470
471 12 bool MarinLMarkComponentsALL::PostProcessingImpl() {
472 12 ConvertLabelsToOutput();
473 12 OutType out;
474
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 out.labels = labels_out_;
475 GetOutput() = out;
476 12 return true;
477 }
478
479 12 void MarinLMarkComponentsALL::ConvertLabelsToOutput() {
480 12 labels_out_.resize(static_cast<std::size_t>(height_));
481
482 24 oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<int>(0, height_),
483 112 [&](const oneapi::tbb::blocked_range<int> &range) {
484
2/2
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 100 times.
200 for (int row = range.begin(); row != range.end(); ++row) {
485 100 labels_out_[static_cast<std::size_t>(row)].resize(static_cast<std::size_t>(width_));
486 100 const std::size_t row_offset = static_cast<std::size_t>(row) * static_cast<std::size_t>(width_);
487
2/2
✓ Branch 0 taken 944 times.
✓ Branch 1 taken 100 times.
1044 for (int col = 0; col < width_; ++col) {
488 944 labels_out_[static_cast<std::size_t>(row)][static_cast<std::size_t>(col)] =
489 944 global_labels_flat_[row_offset + static_cast<std::size_t>(col)];
490 }
491 }
492 100 });
493 12 }
494
495 } // namespace marin_l_mark_components
496