| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "egorova_l_binary_convex_hull/seq/include/ops_seq.hpp" | ||
| 2 | |||
| 3 | #include <algorithm> | ||
| 4 | #include <cstddef> | ||
| 5 | #include <cstdint> | ||
| 6 | #include <stack> | ||
| 7 | #include <tuple> | ||
| 8 | #include <utility> // для std::move | ||
| 9 | #include <vector> | ||
| 10 | |||
| 11 | #include "egorova_l_binary_convex_hull/common/include/common.hpp" | ||
| 12 | |||
| 13 | namespace { | ||
| 14 | |||
| 15 | using Point = egorova_l_binary_convex_hull::Point; | ||
| 16 | |||
| 17 | int64_t CrossProduct(const Point &a, const Point &b, const Point &c) { | ||
| 18 | 1344 | return (static_cast<int64_t>(b.x - a.x) * (c.y - a.y)) - (static_cast<int64_t>(b.y - a.y) * (c.x - a.x)); | |
| 19 | } | ||
| 20 | |||
| 21 | // Helper function for building lower hull | ||
| 22 | 72 | void BuildLowerHull(const std::vector<Point> &points, size_t n, std::vector<Point> &hull) { | |
| 23 |
2/2✓ Branch 0 taken 616 times.
✓ Branch 1 taken 72 times.
|
688 | for (size_t i = 0; i < n; ++i) { |
| 24 |
2/2✓ Branch 0 taken 672 times.
✓ Branch 1 taken 360 times.
|
1032 | while (hull.size() >= 2) { |
| 25 | const Point &a = hull[hull.size() - 2]; | ||
| 26 | const Point &b = hull.back(); | ||
| 27 |
2/2✓ Branch 0 taken 416 times.
✓ Branch 1 taken 256 times.
|
672 | if (CrossProduct(a, b, points[i]) <= 0) { |
| 28 | hull.pop_back(); | ||
| 29 | } else { | ||
| 30 | break; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | hull.push_back(points[i]); | ||
| 34 | } | ||
| 35 | 72 | } | |
| 36 | |||
| 37 | // Helper function for building upper hull | ||
| 38 | 72 | void BuildUpperHull(const std::vector<Point> &points, size_t n, size_t lower_size, std::vector<Point> &hull) { | |
| 39 |
2/2✓ Branch 0 taken 544 times.
✓ Branch 1 taken 72 times.
|
616 | for (size_t i = n - 1; i > 0; --i) { |
| 40 | 544 | const size_t idx = i - 1; | |
| 41 |
2/2✓ Branch 0 taken 672 times.
✓ Branch 1 taken 288 times.
|
960 | while (hull.size() > lower_size) { |
| 42 | const Point &a = hull[hull.size() - 2]; | ||
| 43 | const Point &b = hull.back(); | ||
| 44 |
2/2✓ Branch 0 taken 416 times.
✓ Branch 1 taken 256 times.
|
672 | if (CrossProduct(a, b, points[idx]) <= 0) { |
| 45 | hull.pop_back(); | ||
| 46 | } else { | ||
| 47 | break; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | hull.push_back(points[idx]); | ||
| 51 | } | ||
| 52 | 72 | } | |
| 53 | |||
| 54 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
|
72 | void BuildConvexHull(std::vector<Point> &points, std::vector<Point> &hull) { |
| 55 | const size_t n = points.size(); | ||
| 56 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
|
72 | if (n <= 2) { |
| 57 | ✗ | hull.assign(points.begin(), points.end()); | |
| 58 | ✗ | return; | |
| 59 | } | ||
| 60 | |||
| 61 | // Sort points in-place | ||
| 62 | std::ranges::sort(points, | ||
| 63 | [](const Point &lhs, const Point &rhs) { return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y); }); | ||
| 64 | |||
| 65 | hull.clear(); | ||
| 66 | 72 | hull.reserve(n + 1); // Reserve space to avoid reallocations | |
| 67 | |||
| 68 | // Build lower hull | ||
| 69 | 72 | BuildLowerHull(points, n, hull); | |
| 70 | |||
| 71 | // Build upper hull | ||
| 72 | const size_t lower_size = hull.size(); | ||
| 73 | 72 | BuildUpperHull(points, n, lower_size, hull); | |
| 74 | |||
| 75 | // Remove the last point if it's the same as the first (closed polygon) | ||
| 76 |
3/6✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 72 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 72 times.
✗ Branch 5 not taken.
|
72 | if (hull.size() > 1 && hull.front().x == hull.back().x && hull.front().y == hull.back().y) { |
| 77 | hull.pop_back(); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | // Helper function to process a single direction | ||
| 82 | 2464 | inline void TryAddNeighbor(const std::vector<uint8_t> &image, int width, int height, int next_x, int next_y, int label, | |
| 83 | std::vector<int> &labels, std::stack<Point> &stack) { | ||
| 84 |
2/4✓ Branch 0 taken 2464 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2464 times.
✗ Branch 3 not taken.
|
2464 | if (next_x >= 0 && next_x < width && next_y >= 0 && next_y < height) { |
| 85 |
2/2✓ Branch 0 taken 1600 times.
✓ Branch 1 taken 864 times.
|
2464 | const size_t next_index = (static_cast<size_t>(next_y) * static_cast<size_t>(width)) + static_cast<size_t>(next_x); |
| 86 |
4/4✓ Branch 0 taken 1600 times.
✓ Branch 1 taken 864 times.
✓ Branch 2 taken 544 times.
✓ Branch 3 taken 1056 times.
|
2464 | if (image[next_index] != 0 && labels[next_index] == 0) { |
| 87 | 544 | labels[next_index] = label; | |
| 88 | 544 | stack.push({next_x, next_y}); | |
| 89 | } | ||
| 90 | } | ||
| 91 | 2464 | } | |
| 92 | |||
| 93 | 72 | void ProcessComponent(const std::vector<uint8_t> &image, int width, int height, int start_x, int start_y, int label, | |
| 94 | std::vector<int> &labels, std::vector<Point> &component_points) { | ||
| 95 | std::stack<Point> stack; | ||
| 96 |
1/2✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
|
72 | stack.push({start_x, start_y}); |
| 97 | |||
| 98 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
|
72 | const size_t start_index = (static_cast<size_t>(start_y) * static_cast<size_t>(width)) + static_cast<size_t>(start_x); |
| 99 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
|
72 | labels[start_index] = label; |
| 100 | |||
| 101 | component_points.clear(); | ||
| 102 |
1/2✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
|
72 | component_points.reserve(100); // Reserve some space to avoid reallocations |
| 103 | |||
| 104 |
2/2✓ Branch 0 taken 616 times.
✓ Branch 1 taken 72 times.
|
688 | while (!stack.empty()) { |
| 105 |
1/2✓ Branch 0 taken 616 times.
✗ Branch 1 not taken.
|
616 | const Point current = stack.top(); |
| 106 | stack.pop(); | ||
| 107 | component_points.push_back(current); | ||
| 108 | |||
| 109 | // Process all 4 directions using helper function | ||
| 110 |
1/2✓ Branch 1 taken 616 times.
✗ Branch 2 not taken.
|
616 | TryAddNeighbor(image, width, height, current.x + 1, current.y, label, labels, stack); |
| 111 |
1/2✓ Branch 1 taken 616 times.
✗ Branch 2 not taken.
|
616 | TryAddNeighbor(image, width, height, current.x - 1, current.y, label, labels, stack); |
| 112 |
1/2✓ Branch 1 taken 616 times.
✗ Branch 2 not taken.
|
616 | TryAddNeighbor(image, width, height, current.x, current.y + 1, label, labels, stack); |
| 113 |
1/2✓ Branch 1 taken 616 times.
✗ Branch 2 not taken.
|
616 | TryAddNeighbor(image, width, height, current.x, current.y - 1, label, labels, stack); |
| 114 | } | ||
| 115 | 72 | } | |
| 116 | |||
| 117 | } // namespace | ||
| 118 | |||
| 119 | namespace egorova_l_binary_convex_hull { | ||
| 120 | |||
| 121 |
1/2✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
|
56 | BinaryConvexHullSEQ::BinaryConvexHullSEQ(const InType &in) { |
| 122 | SetTypeOfTask(GetStaticTypeOfTask()); | ||
| 123 | GetInput() = in; | ||
| 124 | 56 | } | |
| 125 | |||
| 126 | 56 | bool BinaryConvexHullSEQ::ValidationImpl() { | |
| 127 | const auto &input = GetInput(); | ||
| 128 |
3/6✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 56 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 56 times.
|
56 | return input.width > 0 && input.height > 0 && !input.data.empty(); |
| 129 | } | ||
| 130 | |||
| 131 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
|
56 | bool BinaryConvexHullSEQ::PreProcessingImpl() { |
| 132 | GetOutput().clear(); | ||
| 133 | 56 | return true; | |
| 134 | } | ||
| 135 | |||
| 136 | 56 | bool BinaryConvexHullSEQ::RunImpl() { | |
| 137 | 56 | const int width = GetInput().width; | |
| 138 | 56 | const int height = GetInput().height; | |
| 139 | 56 | const auto &image = GetInput().data; | |
| 140 | 56 | const size_t image_size = static_cast<size_t>(width) * static_cast<size_t>(height); | |
| 141 | |||
| 142 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 56 times.
|
56 | std::vector<int> labels(image_size, 0); |
| 143 | int label_counter = 0; | ||
| 144 | |||
| 145 | // Clear output and reserve some space | ||
| 146 | auto &output = GetOutput(); | ||
| 147 | output.clear(); | ||
| 148 |
1/2✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
|
56 | output.reserve(10); // Reserve space for expected number of components |
| 149 | |||
| 150 |
2/2✓ Branch 0 taken 840 times.
✓ Branch 1 taken 56 times.
|
896 | for (int row = 0; row < height; ++row) { |
| 151 |
2/2✓ Branch 0 taken 15400 times.
✓ Branch 1 taken 840 times.
|
16240 | for (int col = 0; col < width; ++col) { |
| 152 |
2/2✓ Branch 0 taken 616 times.
✓ Branch 1 taken 14784 times.
|
15400 | const size_t index = (static_cast<size_t>(row) * static_cast<size_t>(width)) + static_cast<size_t>(col); |
| 153 |
4/4✓ Branch 0 taken 616 times.
✓ Branch 1 taken 14784 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 544 times.
|
15400 | if (image[index] != 0 && labels[index] == 0) { |
| 154 | 72 | ++label_counter; | |
| 155 | 72 | std::vector<Point> component_points; | |
| 156 | |||
| 157 |
1/2✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
|
72 | ProcessComponent(image, width, height, col, row, label_counter, labels, component_points); |
| 158 | |||
| 159 |
1/2✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
|
72 | if (!component_points.empty()) { |
| 160 | 72 | std::vector<Point> hull; | |
| 161 |
1/2✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
|
72 | BuildConvexHull(component_points, hull); |
| 162 | output.push_back(std::move(hull)); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | 56 | return true; | |
| 168 | } | ||
| 169 | |||
| 170 | 56 | bool BinaryConvexHullSEQ::PostProcessingImpl() { | |
| 171 | 56 | return true; | |
| 172 | } | ||
| 173 | |||
| 174 | } // namespace egorova_l_binary_convex_hull | ||
| 175 |