| 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 |