| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "egorova_l_binary_convex_hull/all/include/ops_all.hpp" | ||
| 2 | |||
| 3 | #include <mpi.h> | ||
| 4 | #include <omp.h> | ||
| 5 | |||
| 6 | #include <algorithm> | ||
| 7 | #include <cstddef> | ||
| 8 | #include <cstdint> | ||
| 9 | #include <functional> | ||
| 10 | #include <limits> | ||
| 11 | #include <queue> | ||
| 12 | #include <utility> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | #include "egorova_l_binary_convex_hull/common/include/common.hpp" | ||
| 16 | |||
| 17 | namespace egorova_l_binary_convex_hull { | ||
| 18 | |||
| 19 | namespace { | ||
| 20 | |||
| 21 | struct Pix { | ||
| 22 | int px, py; | ||
| 23 | }; | ||
| 24 | |||
| 25 | // Сериализация оболочек для MPI | ||
| 26 | 16 | std::vector<int> SerializeHulls(const std::vector<std::vector<Point>> &hulls) { | |
| 27 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | std::vector<int> buf; |
| 28 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | buf.push_back(static_cast<int>(hulls.size())); |
| 29 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 16 times.
|
28 | for (const auto &hull : hulls) { |
| 30 |
1/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
12 | buf.push_back(static_cast<int>(hull.size())); |
| 31 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 12 times.
|
49 | for (const auto &pp : hull) { |
| 32 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 23 times.
|
37 | buf.push_back(pp.x); |
| 33 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 1 times.
|
37 | buf.push_back(pp.y); |
| 34 | } | ||
| 35 | } | ||
| 36 | 16 | return buf; | |
| 37 | } | ||
| 38 | |||
| 39 | 32 | std::vector<std::vector<Point>> DeserializeHulls(const std::vector<int> &buf) { | |
| 40 |
1/2✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
|
32 | std::vector<std::vector<Point>> hulls; |
| 41 |
1/2✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
|
32 | if (buf.empty()) { |
| 42 | return hulls; | ||
| 43 | } | ||
| 44 | size_t pos = 0; | ||
| 45 | 32 | const int nn = buf[pos++]; | |
| 46 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | hulls.resize(nn); |
| 47 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 32 times.
|
56 | for (int ii = 0; ii < nn; ++ii) { |
| 48 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | const int sz = buf[pos++]; |
| 49 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | hulls[ii].reserve(sz); |
| 50 |
2/2✓ Branch 0 taken 74 times.
✓ Branch 1 taken 24 times.
|
98 | for (int jj = 0; jj < sz; ++jj) { |
| 51 |
1/2✓ Branch 1 taken 74 times.
✗ Branch 2 not taken.
|
74 | hulls[ii].push_back({buf[pos], buf[pos + 1]}); |
| 52 | 74 | pos += 2; | |
| 53 | } | ||
| 54 | } | ||
| 55 | return hulls; | ||
| 56 | ✗ | } | |
| 57 | |||
| 58 | // Проверка соседа в BFS (локальная полоса) | ||
| 59 | 336 | inline void TryPushLocal(const std::vector<uint8_t> &image, int width, int local_rows, int row_start, int nx, int ny, | |
| 60 | std::vector<uint8_t> &visited, std::queue<Pix> &bfs) { | ||
| 61 |
3/4✓ Branch 0 taken 336 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 325 times.
✓ Branch 3 taken 11 times.
|
336 | if (nx < 0 || nx >= width || ny < 0 || ny >= local_rows) { |
| 62 | return; | ||
| 63 | } | ||
| 64 | 325 | const size_t local_ni = ((static_cast<size_t>(ny) * static_cast<size_t>(width)) + static_cast<size_t>(nx)); | |
| 65 | 325 | const size_t global_ni = | |
| 66 |
2/2✓ Branch 0 taken 208 times.
✓ Branch 1 taken 117 times.
|
325 | ((static_cast<size_t>(ny + row_start) * static_cast<size_t>(width)) + static_cast<size_t>(nx)); |
| 67 |
4/4✓ Branch 0 taken 208 times.
✓ Branch 1 taken 117 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 136 times.
|
325 | if (image[global_ni] != 0 && visited[local_ni] == 0) { |
| 68 | 72 | visited[local_ni] = 1; | |
| 69 | 72 | bfs.push({nx, ny}); | |
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | // Один BFS, возвращает компоненту (в глобальных координатах) | ||
| 74 | 12 | std::vector<Point> BfsOneComponent(const std::vector<uint8_t> &image, int width, int local_rows, int row_start, | |
| 75 | int start_x, int start_y, std::vector<uint8_t> &visited) { | ||
| 76 | 12 | std::vector<Point> comp; | |
| 77 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | comp.reserve(256); |
| 78 | std::queue<Pix> bfs; | ||
| 79 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | bfs.push({start_x, start_y}); |
| 80 |
2/2✓ Branch 0 taken 84 times.
✓ Branch 1 taken 12 times.
|
96 | while (!bfs.empty()) { |
| 81 | 84 | const auto cur = bfs.front(); | |
| 82 | bfs.pop(); | ||
| 83 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | comp.push_back({cur.px, cur.py + row_start}); |
| 84 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | TryPushLocal(image, width, local_rows, row_start, cur.px + 1, cur.py, visited, bfs); |
| 85 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | TryPushLocal(image, width, local_rows, row_start, cur.px - 1, cur.py, visited, bfs); |
| 86 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | TryPushLocal(image, width, local_rows, row_start, cur.px, cur.py + 1, visited, bfs); |
| 87 |
1/2✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
|
84 | TryPushLocal(image, width, local_rows, row_start, cur.px, cur.py - 1, visited, bfs); |
| 88 | } | ||
| 89 | 12 | return comp; | |
| 90 | } | ||
| 91 | |||
| 92 | 16 | std::vector<std::vector<Point>> FindLocalComponents(const std::vector<uint8_t> &image, int width, int local_rows, | |
| 93 | int row_start) { | ||
| 94 | 16 | std::vector<std::vector<Point>> comps; | |
| 95 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | std::vector<uint8_t> visited(((static_cast<size_t>(local_rows) * static_cast<size_t>(width))), 0); |
| 96 | |||
| 97 |
2/2✓ Branch 0 taken 115 times.
✓ Branch 1 taken 16 times.
|
131 | for (int ly = 0; ly < local_rows; ++ly) { |
| 98 | 115 | const int gy = row_start + ly; | |
| 99 |
2/2✓ Branch 0 taken 2025 times.
✓ Branch 1 taken 115 times.
|
2140 | for (int col = 0; col < width; ++col) { |
| 100 | 2025 | const size_t local_idx = ((static_cast<size_t>(ly) * static_cast<size_t>(width)) + static_cast<size_t>(col)); | |
| 101 |
2/2✓ Branch 0 taken 84 times.
✓ Branch 1 taken 1941 times.
|
2025 | const size_t global_idx = ((static_cast<size_t>(gy) * static_cast<size_t>(width)) + static_cast<size_t>(col)); |
| 102 |
4/4✓ Branch 0 taken 84 times.
✓ Branch 1 taken 1941 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 12 times.
|
2025 | if (image[global_idx] == 0 || visited[local_idx] != 0) { |
| 103 | 2013 | continue; | |
| 104 | } | ||
| 105 | 12 | visited[local_idx] = 1; | |
| 106 |
1/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
24 | comps.push_back(BfsOneComponent(image, width, local_rows, row_start, col, ly, visited)); |
| 107 | } | ||
| 108 | } | ||
| 109 | 16 | return comps; | |
| 110 | ✗ | } | |
| 111 | |||
| 112 | // Вычисление bounding box'ов всех оболочек | ||
| 113 | struct BBox { | ||
| 114 | std::vector<int> min_x, max_x, min_y, max_y; | ||
| 115 | }; | ||
| 116 | |||
| 117 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | BBox ComputeBBoxes(const std::vector<std::vector<Point>> &hulls) { |
| 118 | 14 | const int nn = static_cast<int>(hulls.size()); | |
| 119 | 14 | BBox bb; | |
| 120 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | bb.min_x.assign(nn, std::numeric_limits<int>::max()); |
| 121 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | bb.max_x.assign(nn, std::numeric_limits<int>::min()); |
| 122 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | bb.min_y.assign(nn, std::numeric_limits<int>::max()); |
| 123 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | bb.max_y.assign(nn, std::numeric_limits<int>::min()); |
| 124 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 14 times.
|
38 | for (int ii = 0; ii < nn; ++ii) { |
| 125 |
2/2✓ Branch 0 taken 74 times.
✓ Branch 1 taken 24 times.
|
98 | for (const auto &pp : hulls[ii]) { |
| 126 |
4/4✓ Branch 0 taken 24 times.
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 32 times.
|
98 | bb.min_x[ii] = std::min(bb.min_x[ii], pp.x); |
| 127 | 74 | bb.max_x[ii] = std::max(bb.max_x[ii], pp.x); | |
| 128 |
4/4✓ Branch 0 taken 24 times.
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 32 times.
|
98 | bb.min_y[ii] = std::min(bb.min_y[ii], pp.y); |
| 129 | 74 | bb.max_y[ii] = std::max(bb.max_y[ii], pp.y); | |
| 130 | } | ||
| 131 | } | ||
| 132 | 14 | return bb; | |
| 133 | ✗ | } | |
| 134 | |||
| 135 | // Union-Find для объединения смежных по Y и пересекающихся по X оболочек | ||
| 136 | 14 | void UnionAdjacent(int nn, const BBox &bb, std::vector<int> &parent, const std::function<int(int)> &find) { | |
| 137 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 14 times.
|
38 | for (int ii = 0; ii < nn; ++ii) { |
| 138 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 24 times.
|
36 | for (int jj = ii + 1; jj < nn; ++jj) { |
| 139 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
|
24 | if (find(ii) == find(jj)) { |
| 140 | ✗ | continue; | |
| 141 | } | ||
| 142 |
3/4✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
|
12 | const bool y_adj = (bb.max_y[ii] + 1 == bb.min_y[jj]) || (bb.max_y[jj] + 1 == bb.min_y[ii]); |
| 143 |
4/4✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 6 times.
|
12 | const bool x_ovl = (bb.min_x[ii] <= bb.max_x[jj]) && (bb.min_x[jj] <= bb.max_x[ii]); |
| 144 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (y_adj && x_ovl) { |
| 145 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
8 | parent[find(ii)] = find(jj); |
| 146 | } | ||
| 147 | } | ||
| 148 | } | ||
| 149 | 14 | } | |
| 150 | |||
| 151 | 14 | std::vector<std::vector<Point>> MergeHulls(const std::vector<std::vector<Point>> &all_hulls) { | |
| 152 | 14 | const int nn = static_cast<int>(all_hulls.size()); | |
| 153 | 14 | std::vector<int> parent(nn); | |
| 154 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 14 times.
|
38 | for (int ii = 0; ii < nn; ++ii) { |
| 155 | 24 | parent[ii] = ii; | |
| 156 | } | ||
| 157 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
|
64 | std::function<int(int)> find = [&](int ii) -> int { return parent[ii] == ii ? ii : (parent[ii] = find(parent[ii])); }; |
| 158 | |||
| 159 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | const BBox bb = ComputeBBoxes(all_hulls); |
| 160 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | UnionAdjacent(nn, bb, parent, find); |
| 161 | |||
| 162 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | std::vector<std::vector<Point>> groups(nn); |
| 163 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 14 times.
|
38 | for (int ii = 0; ii < nn; ++ii) { |
| 164 | 24 | const int root = find(ii); | |
| 165 |
2/2✓ Branch 0 taken 74 times.
✓ Branch 1 taken 24 times.
|
98 | for (const auto &pp : all_hulls[ii]) { |
| 166 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 58 times.
|
74 | groups[root].push_back(pp); |
| 167 | } | ||
| 168 | } | ||
| 169 | 14 | return groups; | |
| 170 | 14 | } | |
| 171 | |||
| 172 | // Сборка всех оболочек со всех процессов через Allgatherv | ||
| 173 | 16 | std::vector<std::vector<Point>> GatherAllHulls(const std::vector<std::vector<Point>> &local_hulls, int p_count) { | |
| 174 | 16 | std::vector<int> s_buf = SerializeHulls(local_hulls); | |
| 175 | 16 | const int s_size = static_cast<int>(s_buf.size()); | |
| 176 | |||
| 177 |
2/6✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
16 | std::vector<int> r_sizes(p_count, 0); |
| 178 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | MPI_Allgather(&s_size, 1, MPI_INT, r_sizes.data(), 1, MPI_INT, MPI_COMM_WORLD); |
| 179 | |||
| 180 |
1/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
16 | std::vector<int> r_displs(p_count, 0); |
| 181 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
|
32 | for (int ii = 1; ii < p_count; ++ii) { |
| 182 | 16 | r_displs[ii] = r_displs[ii - 1] + r_sizes[ii - 1]; | |
| 183 | } | ||
| 184 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | const int total_size = r_displs[p_count - 1] + r_sizes[p_count - 1]; |
| 185 | |||
| 186 |
2/6✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
16 | std::vector<int> r_buf(total_size); |
| 187 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | MPI_Allgatherv(s_buf.data(), s_size, MPI_INT, r_buf.data(), r_sizes.data(), r_displs.data(), MPI_INT, MPI_COMM_WORLD); |
| 188 | |||
| 189 | 16 | std::vector<std::vector<Point>> all_hulls; | |
| 190 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | all_hulls.reserve(64); |
| 191 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 16 times.
|
48 | for (int ii = 0; ii < p_count; ++ii) { |
| 192 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (r_sizes[ii] == 0) { |
| 193 | ✗ | continue; | |
| 194 | } | ||
| 195 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | std::vector<int> ph(r_buf.begin() + r_displs[ii], r_buf.begin() + r_displs[ii] + r_sizes[ii]); |
| 196 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | auto dec = DeserializeHulls(ph); |
| 197 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 32 times.
|
56 | for (auto &hh : dec) { |
| 198 | all_hulls.push_back(std::move(hh)); | ||
| 199 | } | ||
| 200 | 32 | } | |
| 201 | 16 | return all_hulls; | |
| 202 | ✗ | } | |
| 203 | |||
| 204 | // BFS для FindComponents (без полос) | ||
| 205 | ✗ | inline void TryPushGlobal(const std::vector<uint8_t> &image, int width, int height, int nx, int ny, | |
| 206 | std::vector<uint8_t> &visited, std::queue<Pix> &bfs) { | ||
| 207 | ✗ | if (nx < 0 || nx >= width || ny < 0 || ny >= height) { | |
| 208 | return; | ||
| 209 | } | ||
| 210 | ✗ | const size_t ni = ((static_cast<size_t>(ny) * static_cast<size_t>(width)) + static_cast<size_t>(nx)); | |
| 211 | ✗ | if (image[ni] != 0 && visited[ni] == 0) { | |
| 212 | ✗ | visited[ni] = 1; | |
| 213 | ✗ | bfs.push({nx, ny}); | |
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | ✗ | std::vector<Point> BfsComponentGlobal(const std::vector<uint8_t> &image, int width, int height, int start_x, | |
| 218 | int start_y, std::vector<uint8_t> &visited) { | ||
| 219 | ✗ | std::vector<Point> comp; | |
| 220 | std::queue<Pix> bfs; | ||
| 221 | ✗ | bfs.push({start_x, start_y}); | |
| 222 | ✗ | while (!bfs.empty()) { | |
| 223 | ✗ | const auto cur = bfs.front(); | |
| 224 | bfs.pop(); | ||
| 225 | ✗ | comp.emplace_back(cur.px, cur.py); | |
| 226 | ✗ | TryPushGlobal(image, width, height, cur.px + 1, cur.py, visited, bfs); | |
| 227 | ✗ | TryPushGlobal(image, width, height, cur.px - 1, cur.py, visited, bfs); | |
| 228 | ✗ | TryPushGlobal(image, width, height, cur.px, cur.py + 1, visited, bfs); | |
| 229 | ✗ | TryPushGlobal(image, width, height, cur.px, cur.py - 1, visited, bfs); | |
| 230 | } | ||
| 231 | ✗ | return comp; | |
| 232 | } | ||
| 233 | |||
| 234 | // BFS с labels (для ProcessComponent) | ||
| 235 | ✗ | inline void TryPushLabels(const std::vector<uint8_t> &image, int width, int height, int nx, int ny, int label, | |
| 236 | std::vector<int> &labels, std::queue<Pix> &bfs) { | ||
| 237 | ✗ | if (nx < 0 || nx >= width || ny < 0 || ny >= height) { | |
| 238 | return; | ||
| 239 | } | ||
| 240 | ✗ | const size_t ni = ((static_cast<size_t>(ny) * static_cast<size_t>(width)) + static_cast<size_t>(nx)); | |
| 241 | ✗ | if (image[ni] != 0 && labels[ni] == 0) { | |
| 242 | ✗ | labels[ni] = label; | |
| 243 | ✗ | bfs.push({nx, ny}); | |
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | } // namespace | ||
| 248 | |||
| 249 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | BinaryConvexHullALL::BinaryConvexHullALL(const InType &in) { |
| 250 | SetTypeOfTask(GetStaticTypeOfTask()); | ||
| 251 | GetInput() = in; | ||
| 252 | 16 | } | |
| 253 | |||
| 254 | 16 | bool BinaryConvexHullALL::ValidationImpl() { | |
| 255 | 16 | int rank = 0; | |
| 256 | 16 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |
| 257 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
|
16 | if (rank == 0) { |
| 258 | const auto &in = GetInput(); | ||
| 259 |
3/6✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
8 | return in.width > 0 && in.height > 0 && !in.data.empty(); |
| 260 | } | ||
| 261 | return true; | ||
| 262 | } | ||
| 263 | |||
| 264 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | bool BinaryConvexHullALL::PreProcessingImpl() { |
| 265 | GetOutput().clear(); | ||
| 266 | 16 | return true; | |
| 267 | } | ||
| 268 | |||
| 269 | 16 | bool BinaryConvexHullALL::RunImpl() { | |
| 270 | 16 | int rank = 0; | |
| 271 | 16 | int p_count = 1; | |
| 272 | 16 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); | |
| 273 | 16 | MPI_Comm_size(MPI_COMM_WORLD, &p_count); | |
| 274 | |||
| 275 | // Bcast размеров и изображения | ||
| 276 | 16 | int width = 0; | |
| 277 | 16 | int height = 0; | |
| 278 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
|
16 | if (rank == 0) { |
| 279 | 8 | width = GetInput().width; | |
| 280 | 8 | height = GetInput().height; | |
| 281 | } | ||
| 282 | 16 | MPI_Bcast(&width, 1, MPI_INT, 0, MPI_COMM_WORLD); | |
| 283 | 16 | MPI_Bcast(&height, 1, MPI_INT, 0, MPI_COMM_WORLD); | |
| 284 | |||
| 285 |
2/4✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
|
16 | if (width <= 0 || height <= 0) { |
| 286 | GetOutput().clear(); | ||
| 287 | ✗ | return true; | |
| 288 | } | ||
| 289 | |||
| 290 | 16 | std::vector<uint8_t> image; | |
| 291 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
|
16 | if (rank == 0) { |
| 292 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | image = GetInput().data; |
| 293 | } else { | ||
| 294 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | image.resize((static_cast<size_t>(width) * static_cast<size_t>(height))); |
| 295 | } | ||
| 296 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | MPI_Bcast(image.data(), static_cast<int>(image.size()), MPI_BYTE, 0, MPI_COMM_WORLD); |
| 297 | |||
| 298 | // Распределение строк | ||
| 299 | 16 | const int chunk = height / p_count; | |
| 300 | 16 | const int rem = height % p_count; | |
| 301 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
|
16 | const int row_start = ((rank * chunk) + std::min(rank, rem)); |
| 302 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
|
16 | const int local_rows = chunk + ((rank < rem) ? 1 : 0); |
| 303 | |||
| 304 | // BFS локально + OpenMP для оболочек | ||
| 305 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | std::vector<std::vector<Point>> local_comps = FindLocalComponents(image, width, local_rows, row_start); |
| 306 |
2/4✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
|
16 | std::vector<std::vector<Point>> local_hulls(local_comps.size()); |
| 307 | 16 | const int n_local = static_cast<int>(local_comps.size()); | |
| 308 | 16 | #pragma omp parallel for schedule(dynamic, 1) default(none) shared(local_comps, local_hulls, n_local) | |
| 309 | for (int ii = 0; ii < n_local; ++ii) { | ||
| 310 | BuildConvexHull(local_comps[ii], local_hulls[ii]); | ||
| 311 | } | ||
| 312 | |||
| 313 | // Allgatherv → каждый процесс получает все оболочки | ||
| 314 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | std::vector<std::vector<Point>> all_hulls = GatherAllHulls(local_hulls, p_count); |
| 315 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 14 times.
|
16 | if (all_hulls.empty()) { |
| 316 | GetOutput().clear(); | ||
| 317 | 2 | return true; | |
| 318 | } | ||
| 319 | |||
| 320 | // Union-find + финальные оболочки | ||
| 321 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | std::vector<std::vector<Point>> groups = MergeHulls(all_hulls); |
| 322 | GetOutput().clear(); | ||
| 323 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 14 times.
|
38 | for (auto &group : groups) { |
| 324 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
|
24 | if (!group.empty()) { |
| 325 | 20 | std::vector<Point> hh; | |
| 326 |
1/2✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
|
20 | BuildConvexHull(group, hh); |
| 327 | GetOutput().push_back(std::move(hh)); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | return true; | ||
| 331 | 16 | } | |
| 332 | |||
| 333 | 16 | bool BinaryConvexHullALL::PostProcessingImpl() { | |
| 334 | 16 | return true; | |
| 335 | } | ||
| 336 | |||
| 337 | ✗ | int64_t BinaryConvexHullALL::CrossProduct(const Point &a, const Point &b, const Point &c) { | |
| 338 | 245 | return (((static_cast<int64_t>(b.x - a.x) * static_cast<int64_t>(c.y - a.y)) - | |
| 339 |
2/2✓ Branch 0 taken 52 times.
✓ Branch 1 taken 72 times.
|
124 | (static_cast<int64_t>(b.y - a.y) * static_cast<int64_t>(c.x - a.x)))); |
| 340 | } | ||
| 341 | |||
| 342 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | void BinaryConvexHullALL::BuildConvexHull(std::vector<Point> &points, std::vector<Point> &hull) { |
| 343 | hull.clear(); | ||
| 344 |
1/2✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
|
32 | if (points.empty()) { |
| 345 | 4 | return; | |
| 346 | } | ||
| 347 |
9/78✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 66 not taken.
✓ Branch 67 taken 126 times.
✓ Branch 68 taken 40 times.
✓ Branch 69 taken 86 times.
✗ Branch 70 not taken.
✓ Branch 71 taken 40 times.
✓ Branch 72 taken 92 times.
✓ Branch 73 taken 126 times.
✓ Branch 74 taken 86 times.
✓ Branch 75 taken 40 times.
✗ Branch 76 not taken.
✓ Branch 77 taken 86 times.
|
344 | std::ranges::sort(points, [](const Point &a, const Point &b) { return a.x < b.x || (a.x == b.x && a.y < b.y); }); |
| 348 | const auto last_unique = std::ranges::unique(points).begin(); | ||
| 349 | points.erase(last_unique, points.end()); | ||
| 350 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 28 times.
|
32 | if (points.size() < 3) { |
| 351 | hull.clear(); | ||
| 352 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
|
10 | for (const auto &pp : points) { |
| 353 | hull.push_back(pp); | ||
| 354 | } | ||
| 355 | return; | ||
| 356 | } | ||
| 357 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | std::vector<Point> res; |
| 358 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | res.reserve(points.size() + 1); |
| 359 |
2/2✓ Branch 0 taken 152 times.
✓ Branch 1 taken 28 times.
|
180 | for (const auto &pp : points) { |
| 360 |
4/4✓ Branch 0 taken 121 times.
✓ Branch 1 taken 106 times.
✓ Branch 2 taken 75 times.
✓ Branch 3 taken 46 times.
|
227 | while (res.size() >= 2 && CrossProduct(res[res.size() - 2], res.back(), pp) <= 0) { |
| 361 | res.pop_back(); | ||
| 362 | } | ||
| 363 | res.push_back(pp); | ||
| 364 | } | ||
| 365 | const size_t lower_size = res.size(); | ||
| 366 |
2/2✓ Branch 0 taken 124 times.
✓ Branch 1 taken 28 times.
|
152 | for (int ii = static_cast<int>(points.size()) - 2; ii >= 0; --ii) { |
| 367 |
4/4✓ Branch 0 taken 124 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 72 times.
|
196 | while (res.size() > lower_size && CrossProduct(res[res.size() - 2], res.back(), points[ii]) <= 0) { |
| 368 | res.pop_back(); | ||
| 369 | } | ||
| 370 |
1/2✓ Branch 0 taken 124 times.
✗ Branch 1 not taken.
|
124 | res.push_back(points[ii]); |
| 371 | } | ||
| 372 |
1/2✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
|
28 | if (res.size() > 1) { |
| 373 | res.pop_back(); | ||
| 374 | } | ||
| 375 | hull.swap(res); | ||
| 376 | } | ||
| 377 | |||
| 378 | ✗ | std::vector<std::vector<Point>> BinaryConvexHullALL::FindComponents(const std::vector<uint8_t> &image, int width, | |
| 379 | int height) { | ||
| 380 | ✗ | std::vector<std::vector<Point>> components; | |
| 381 | ✗ | std::vector<uint8_t> visited(((static_cast<size_t>(width) * static_cast<size_t>(height))), 0); | |
| 382 | ✗ | for (int row = 0; row < height; ++row) { | |
| 383 | ✗ | for (int col = 0; col < width; ++col) { | |
| 384 | ✗ | const size_t idx = ((static_cast<size_t>(row) * static_cast<size_t>(width)) + static_cast<size_t>(col)); | |
| 385 | ✗ | if (image[idx] == 0 || visited[idx] != 0) { | |
| 386 | ✗ | continue; | |
| 387 | } | ||
| 388 | ✗ | visited[idx] = 1; | |
| 389 | ✗ | components.push_back(BfsComponentGlobal(image, width, height, col, row, visited)); | |
| 390 | } | ||
| 391 | } | ||
| 392 | ✗ | return components; | |
| 393 | ✗ | } | |
| 394 | |||
| 395 | ✗ | void BinaryConvexHullALL::ProcessComponent(const std::vector<uint8_t> &image, int width, int height, int start_x, | |
| 396 | int start_y, int label, std::vector<int> &labels, | ||
| 397 | std::vector<Point> &component_points) { | ||
| 398 | ✗ | if (start_x < 0 || start_x >= width || start_y < 0 || start_y >= height) { | |
| 399 | component_points.clear(); | ||
| 400 | ✗ | return; | |
| 401 | } | ||
| 402 | ✗ | const size_t si = ((static_cast<size_t>(start_y) * static_cast<size_t>(width)) + static_cast<size_t>(start_x)); | |
| 403 | ✗ | labels[si] = label; | |
| 404 | std::queue<Pix> bfs; | ||
| 405 | ✗ | bfs.push({start_x, start_y}); | |
| 406 | component_points.clear(); | ||
| 407 | ✗ | component_points.reserve(1000); | |
| 408 | ✗ | while (!bfs.empty()) { | |
| 409 | ✗ | const auto cur = bfs.front(); | |
| 410 | bfs.pop(); | ||
| 411 | ✗ | component_points.emplace_back(cur.px, cur.py); | |
| 412 | ✗ | TryPushLabels(image, width, height, cur.px + 1, cur.py, label, labels, bfs); | |
| 413 | ✗ | TryPushLabels(image, width, height, cur.px - 1, cur.py, label, labels, bfs); | |
| 414 | ✗ | TryPushLabels(image, width, height, cur.px, cur.py + 1, label, labels, bfs); | |
| 415 | ✗ | TryPushLabels(image, width, height, cur.px, cur.py - 1, label, labels, bfs); | |
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | } // namespace egorova_l_binary_convex_hull | ||
| 420 |