| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "ivanova_p_marking_components_on_binary_image/tbb/include/ops_tbb.hpp" | ||
| 2 | |||
| 3 | #include <tbb/tbb.h> | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <string> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "ivanova_p_marking_components_on_binary_image/common/include/common.hpp" | ||
| 10 | #include "ivanova_p_marking_components_on_binary_image/data/image_generator.hpp" | ||
| 11 | |||
| 12 | namespace ivanova_p_marking_components_on_binary_image { | ||
| 13 | |||
| 14 |
2/2✓ Branch 0 taken 52 times.
✓ Branch 1 taken 6 times.
|
58 | IvanovaPMarkingComponentsOnBinaryImageTBB::IvanovaPMarkingComponentsOnBinaryImageTBB(const InType &in) { |
| 15 | SetTypeOfTask(GetStaticTypeOfTask()); | ||
| 16 | 58 | GetInput() = in; | |
| 17 | GetOutput().clear(); | ||
| 18 | |||
| 19 | 58 | test_image.width = 0; | |
| 20 |
2/2✓ Branch 0 taken 52 times.
✓ Branch 1 taken 6 times.
|
58 | test_image.height = 0; |
| 21 | test_image.data.clear(); | ||
| 22 | 58 | } | |
| 23 | |||
| 24 | 58 | bool IvanovaPMarkingComponentsOnBinaryImageTBB::ValidationImpl() { | |
| 25 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
58 | if (test_image.width <= 0 || test_image.height <= 0) { |
| 26 | 58 | int test_case = GetInput(); | |
| 27 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 42 times.
|
58 | if (test_case >= 11 && test_case <= 14) { |
| 28 | std::string filename; | ||
| 29 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
|
16 | switch (test_case) { |
| 30 | case 11: | ||
| 31 | filename = "tasks/ivanova_p_marking_components_on_binary_image/data/image.txt"; | ||
| 32 | break; | ||
| 33 | case 12: | ||
| 34 | filename = "tasks/ivanova_p_marking_components_on_binary_image/data/image2.txt"; | ||
| 35 | break; | ||
| 36 | case 13: | ||
| 37 | filename = "tasks/ivanova_p_marking_components_on_binary_image/data/image3.txt"; | ||
| 38 | break; | ||
| 39 | case 14: | ||
| 40 | filename = "tasks/ivanova_p_marking_components_on_binary_image/data/image4.txt"; | ||
| 41 | break; | ||
| 42 | default: | ||
| 43 | filename = ""; | ||
| 44 | break; | ||
| 45 | } | ||
| 46 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
32 | test_image = LoadImageFromTxt(filename); |
| 47 | } else { | ||
| 48 | int size = ExtractImageSize(test_case); | ||
| 49 | 84 | test_image = CreateTestImage(size, size, test_case); | |
| 50 | } | ||
| 51 | } | ||
| 52 |
2/4✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
|
58 | return test_image.width > 0 && !test_image.data.empty(); |
| 53 | } | ||
| 54 | |||
| 55 | 58 | bool IvanovaPMarkingComponentsOnBinaryImageTBB::PreProcessingImpl() { | |
| 56 | input_image_ = test_image; | ||
| 57 | 58 | width_ = input_image_.width; | |
| 58 | 58 | height_ = input_image_.height; | |
| 59 | 58 | int total_pixels = width_ * height_; | |
| 60 | |||
| 61 | 58 | labels_.assign(total_pixels, 0); | |
| 62 | |||
| 63 | // Инициализируем DSU: размер +1, так как метки теперь начинаются с 1 (idx + 1) | ||
| 64 | 58 | parent_.resize(total_pixels + 1); | |
| 65 |
2/2✓ Branch 0 taken 420622 times.
✓ Branch 1 taken 58 times.
|
420680 | for (int i = 0; i <= total_pixels; ++i) { |
| 66 | 420622 | parent_[i] = i; | |
| 67 | } | ||
| 68 | |||
| 69 | 58 | current_label_ = 0; | |
| 70 | 58 | return true; | |
| 71 | } | ||
| 72 | |||
| 73 | ✗ | int IvanovaPMarkingComponentsOnBinaryImageTBB::FindRoot(int label) { | |
| 74 | int root = label; | ||
| 75 |
4/6✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1020 times.
✓ Branch 2 taken 940 times.
✓ Branch 3 taken 1020 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
3004 | while (parent_[root] != root) { |
| 76 | root = parent_[root]; | ||
| 77 | } | ||
| 78 | |||
| 79 | // Сжатие путей (Path compression) для ускорения последующих поисков | ||
| 80 | int current = label; | ||
| 81 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 1020 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1020 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
2040 | while (parent_[current] != root) { |
| 82 | int next = parent_[current]; | ||
| 83 | ✗ | parent_[current] = root; | |
| 84 | current = next; | ||
| 85 | } | ||
| 86 | ✗ | return root; | |
| 87 | } | ||
| 88 | |||
| 89 | 1020 | void IvanovaPMarkingComponentsOnBinaryImageTBB::UnionLabels(int label1, int label2) { | |
| 90 | int root1 = FindRoot(label1); | ||
| 91 | int root2 = FindRoot(label2); | ||
| 92 | |||
| 93 |
2/2✓ Branch 0 taken 80 times.
✓ Branch 1 taken 940 times.
|
1020 | if (root1 != root2) { |
| 94 |
1/2✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
|
80 | if (root1 < root2) { |
| 95 | 80 | parent_[root2] = root1; | |
| 96 | } else { | ||
| 97 | ✗ | parent_[root1] = root2; | |
| 98 | } | ||
| 99 | } | ||
| 100 | 1020 | } | |
| 101 | |||
| 102 | ✗ | void IvanovaPMarkingComponentsOnBinaryImageTBB::ProcessPixel(int xx, int yy, int idx) { | |
| 103 | ✗ | int left_label = (xx > 0) ? labels_[idx - 1] : 0; | |
| 104 | ✗ | int top_label = (yy > 0) ? labels_[idx - width_] : 0; | |
| 105 | |||
| 106 | ✗ | bool left_exists = (left_label != 0); | |
| 107 | ✗ | bool top_exists = (top_label != 0); | |
| 108 | |||
| 109 | ✗ | if (!left_exists && !top_exists) { | |
| 110 | ✗ | current_label_++; | |
| 111 | ✗ | labels_[idx] = current_label_; | |
| 112 | ✗ | parent_[current_label_] = current_label_; | |
| 113 | } else { | ||
| 114 | ✗ | int label = left_exists ? left_label : top_label; | |
| 115 | ✗ | labels_[idx] = label; | |
| 116 | |||
| 117 | ✗ | if (left_exists && top_exists && left_label != top_label) { | |
| 118 | ✗ | UnionLabels(std::min(left_label, top_label), std::max(left_label, top_label)); | |
| 119 | } | ||
| 120 | } | ||
| 121 | ✗ | } | |
| 122 | |||
| 123 | ✗ | int IvanovaPMarkingComponentsOnBinaryImageTBB::FindLocalRoot(int label) { | |
| 124 | int root = label; | ||
| 125 |
3/6✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
28 | while (parent_[root] != root) { |
| 126 | root = parent_[root]; | ||
| 127 | } | ||
| 128 | ✗ | return root; | |
| 129 | } | ||
| 130 | |||
| 131 | ✗ | void IvanovaPMarkingComponentsOnBinaryImageTBB::UnionLocalRoots(int root1, int root2) { | |
| 132 |
2/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
16 | if (root1 != root2) { |
| 133 |
1/4✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
4 | if (root1 < root2) { |
| 134 | 4 | parent_[root2] = root1; | |
| 135 | } else { | ||
| 136 | ✗ | parent_[root1] = root2; | |
| 137 | } | ||
| 138 | } | ||
| 139 | ✗ | } | |
| 140 | |||
| 141 | 420564 | void IvanovaPMarkingComponentsOnBinaryImageTBB::ProcessStripePixel(int xx, int yy, int idx, int start_row) { | |
| 142 |
2/2✓ Branch 0 taken 29552 times.
✓ Branch 1 taken 391012 times.
|
420564 | if (input_image_.data[idx] == 0) { |
| 143 | return; | ||
| 144 | } | ||
| 145 | |||
| 146 |
2/2✓ Branch 0 taken 29508 times.
✓ Branch 1 taken 44 times.
|
29552 | int left_label = (xx > 0) ? labels_[idx - 1] : 0; |
| 147 |
2/2✓ Branch 0 taken 27872 times.
✓ Branch 1 taken 1680 times.
|
29552 | int top_label = (yy > start_row) ? labels_[idx - width_] : 0; |
| 148 | |||
| 149 |
2/2✓ Branch 0 taken 232 times.
✓ Branch 1 taken 29320 times.
|
29552 | if (left_label == 0 && top_label == 0) { |
| 150 | 232 | labels_[idx] = idx + 1; | |
| 151 | } else { | ||
| 152 |
2/2✓ Branch 0 taken 1820 times.
✓ Branch 1 taken 27500 times.
|
29320 | int label = (left_label != 0) ? left_label : top_label; |
| 153 | 29320 | labels_[idx] = label; | |
| 154 | |||
| 155 |
4/4✓ Branch 0 taken 24900 times.
✓ Branch 1 taken 4420 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 24884 times.
|
29320 | if (left_label != 0 && top_label != 0 && left_label != top_label) { |
| 156 | int root1 = FindLocalRoot(left_label); | ||
| 157 | int root2 = FindLocalRoot(top_label); | ||
| 158 | UnionLocalRoots(root1, root2); | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | 58 | void IvanovaPMarkingComponentsOnBinaryImageTBB::MergeBoundariesTbb(int num_threads, int rows_per_thread) { | |
| 164 |
2/2✓ Branch 0 taken 170 times.
✓ Branch 1 taken 58 times.
|
228 | for (int thread_id = 0; thread_id < num_threads - 1; ++thread_id) { |
| 165 | 170 | int boundary_row = (thread_id + 1) * rows_per_thread; | |
| 166 |
2/2✓ Branch 0 taken 162 times.
✓ Branch 1 taken 8 times.
|
170 | if (boundary_row >= height_) { |
| 167 | 8 | continue; | |
| 168 | } | ||
| 169 | |||
| 170 |
2/2✓ Branch 0 taken 12432 times.
✓ Branch 1 taken 162 times.
|
12594 | for (int xx = 0; xx < width_; ++xx) { |
| 171 | 12432 | int top_idx = ((boundary_row - 1) * width_) + xx; | |
| 172 | 12432 | int bottom_idx = (boundary_row * width_) + xx; | |
| 173 | |||
| 174 |
2/2✓ Branch 0 taken 1020 times.
✓ Branch 1 taken 11412 times.
|
12432 | int top_label = labels_[top_idx]; |
| 175 | 12432 | int bottom_label = labels_[bottom_idx]; | |
| 176 | |||
| 177 |
3/4✓ Branch 0 taken 1020 times.
✓ Branch 1 taken 11412 times.
✓ Branch 2 taken 1020 times.
✗ Branch 3 not taken.
|
12432 | if (top_label != 0 && bottom_label != 0 && top_label != bottom_label) { |
| 178 | 1020 | UnionLabels(top_label, bottom_label); | |
| 179 | } | ||
| 180 | } | ||
| 181 | } | ||
| 182 | 58 | } | |
| 183 | |||
| 184 | 58 | void IvanovaPMarkingComponentsOnBinaryImageTBB::FirstPass() { | |
| 185 | int num_threads = std::max(1, tbb::this_task_arena::max_concurrency()); | ||
| 186 | 58 | int rows_per_thread = (height_ + num_threads - 1) / num_threads; | |
| 187 | |||
| 188 | // Фаза 1: Истинная параллельная обработка независимых полос | ||
| 189 | 58 | tbb::parallel_for(0, num_threads, [&](int thread_id) { | |
| 190 | 228 | int start_row = thread_id * rows_per_thread; | |
| 191 |
2/2✓ Branch 0 taken 220 times.
✓ Branch 1 taken 8 times.
|
228 | int end_row = std::min(start_row + rows_per_thread, height_); |
| 192 |
2/2✓ Branch 0 taken 220 times.
✓ Branch 1 taken 8 times.
|
228 | if (start_row >= height_) { |
| 193 | return; | ||
| 194 | } | ||
| 195 | |||
| 196 |
2/2✓ Branch 0 taken 4292 times.
✓ Branch 1 taken 220 times.
|
4512 | for (int yy = start_row; yy < end_row; ++yy) { |
| 197 |
2/2✓ Branch 0 taken 420564 times.
✓ Branch 1 taken 4292 times.
|
424856 | for (int xx = 0; xx < width_; ++xx) { |
| 198 | 420564 | int idx = (yy * width_) + xx; | |
| 199 | 420564 | ProcessStripePixel(xx, yy, idx, start_row); | |
| 200 | } | ||
| 201 | } | ||
| 202 | }); | ||
| 203 | |||
| 204 | // Фаза 2: Быстрое последовательное объединение на стыках полос | ||
| 205 | 58 | MergeBoundariesTbb(num_threads, rows_per_thread); | |
| 206 | |||
| 207 | 58 | current_label_ = 1; | |
| 208 | 58 | } | |
| 209 | |||
| 210 | 58 | void IvanovaPMarkingComponentsOnBinaryImageTBB::SecondPass() { | |
| 211 | 58 | int total_pixels = width_ * height_; | |
| 212 | |||
| 213 | // 1. Параллельно сжимаем пути для всех пикселей | ||
| 214 | 58 | tbb::parallel_for(0, total_pixels, [&](int i) { | |
| 215 |
2/2✓ Branch 0 taken 29552 times.
✓ Branch 1 taken 391012 times.
|
420564 | if (labels_[i] != 0) { |
| 216 | int root = labels_[i]; | ||
| 217 |
2/2✓ Branch 0 taken 12520 times.
✓ Branch 1 taken 29552 times.
|
42072 | while (parent_[root] != root) { |
| 218 | root = parent_[root]; | ||
| 219 | } | ||
| 220 | 29552 | labels_[i] = root; | |
| 221 | } | ||
| 222 | }); | ||
| 223 | |||
| 224 | // 2. Быстрый проход для создания непрерывных ID | ||
| 225 | // ИСПРАВЛЕНИЕ: Вектор должен вмещать метки вплоть до total_pixels | ||
| 226 | 58 | std::vector<int> root_mapping(total_pixels + 1, 0); | |
| 227 | int next_label = 1; | ||
| 228 |
2/2✓ Branch 0 taken 420564 times.
✓ Branch 1 taken 58 times.
|
420622 | for (int i = 0; i < total_pixels; ++i) { |
| 229 |
2/2✓ Branch 0 taken 29552 times.
✓ Branch 1 taken 391012 times.
|
420564 | if (labels_[i] != 0) { |
| 230 | int r = labels_[i]; | ||
| 231 |
2/2✓ Branch 0 taken 148 times.
✓ Branch 1 taken 29404 times.
|
29552 | if (root_mapping[r] == 0) { |
| 232 | 148 | root_mapping[r] = next_label++; | |
| 233 | } | ||
| 234 | } | ||
| 235 | } | ||
| 236 | 58 | current_label_ = next_label - 1; | |
| 237 | |||
| 238 | // 3. Параллельно присваиваем новые нормализованные метки | ||
| 239 |
2/6✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 58 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
58 | tbb::parallel_for(0, total_pixels, [&](int i) { |
| 240 |
2/2✓ Branch 0 taken 29552 times.
✓ Branch 1 taken 391012 times.
|
420564 | if (labels_[i] != 0) { |
| 241 | 29552 | labels_[i] = root_mapping[labels_[i]]; | |
| 242 | } | ||
| 243 | }); | ||
| 244 | 58 | } | |
| 245 | |||
| 246 | ✗ | void IvanovaPMarkingComponentsOnBinaryImageTBB::InitLabelsTbb(int /*total_pixels*/) { | |
| 247 | // Не используется | ||
| 248 | ✗ | } | |
| 249 | |||
| 250 | ✗ | void IvanovaPMarkingComponentsOnBinaryImageTBB::MergeHorizontalPairsTbb() { | |
| 251 | // Не используется | ||
| 252 | ✗ | } | |
| 253 | |||
| 254 | ✗ | void IvanovaPMarkingComponentsOnBinaryImageTBB::MergeVerticalPairsTbb() { | |
| 255 | // Не используется | ||
| 256 | ✗ | } | |
| 257 | |||
| 258 | ✗ | void IvanovaPMarkingComponentsOnBinaryImageTBB::FinalizeRootsTbb(int /*total_pixels*/) { | |
| 259 | // Не используется | ||
| 260 | ✗ | } | |
| 261 | |||
| 262 | ✗ | void IvanovaPMarkingComponentsOnBinaryImageTBB::NormalizeLabelsTbb(int /*total_pixels*/) { | |
| 263 | // Не используется | ||
| 264 | ✗ | } | |
| 265 | |||
| 266 | 58 | bool IvanovaPMarkingComponentsOnBinaryImageTBB::RunImpl() { | |
| 267 |
2/4✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
✗ Branch 3 not taken.
|
58 | if (width_ <= 0 || height_ <= 0) { |
| 268 | return false; | ||
| 269 | } | ||
| 270 | |||
| 271 | 58 | FirstPass(); | |
| 272 | |||
| 273 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | if (current_label_ > 0) { |
| 274 | 58 | SecondPass(); | |
| 275 | } | ||
| 276 | |||
| 277 | return true; | ||
| 278 | } | ||
| 279 | |||
| 280 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
|
58 | bool IvanovaPMarkingComponentsOnBinaryImageTBB::PostProcessingImpl() { |
| 281 | OutType &output = GetOutput(); | ||
| 282 | output.clear(); | ||
| 283 | 58 | output.reserve(3 + labels_.size()); | |
| 284 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | output.push_back(width_); |
| 285 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | output.push_back(height_); |
| 286 |
1/2✓ Branch 0 taken 58 times.
✗ Branch 1 not taken.
|
58 | output.push_back(current_label_); |
| 287 |
3/4✓ Branch 0 taken 420564 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 420564 times.
✓ Branch 3 taken 58 times.
|
420622 | for (int l : labels_) { |
| 288 | output.push_back(l); | ||
| 289 | } | ||
| 290 | 58 | return true; | |
| 291 | } | ||
| 292 | |||
| 293 | } // namespace ivanova_p_marking_components_on_binary_image | ||
| 294 |