1#include "internal/graph.h"
8#include "internal/Pixel.h"
9#include "internal/bezier.h"
17 static_cast<float>(a.blue)};
19 static_cast<float>(b.blue)};
20 return std::sqrt((af.red - bf.red) * (af.red - bf.red) +
21 (af.green - bf.green) * (af.green - bf.green) +
22 (af.blue - bf.blue) * (af.blue - bf.blue));
31void Graph::hash_node_ids() {
32 for (int32_t i{0}; i < m_nodes->size(); i++) {
33 const int32_t key{m_nodes->at(i)->id()};
38bool Graph::all_areas_bigger_than(int32_t min_area) {
39 for (
auto &n : *m_nodes) {
40 if (n->area() < min_area) {
48bool Graph::add_edge(int32_t node_id1, int32_t node_id2) {
49 auto end_node_ids{m_node_ids.end()};
50 auto node1_it{m_node_ids.find(node_id1)};
51 auto node2_it{m_node_ids.find(node_id2)};
53 if (node1_it == end_node_ids || node2_it == end_node_ids) {
57 const int32_t idx1{node1_it->second};
58 const int32_t idx2{node2_it->second};
60 m_nodes->at(idx1)->add_edge(m_nodes->at(idx2));
61 m_nodes->at(idx2)->add_edge(m_nodes->at(idx1));
65bool Graph::merge_nodes(
const Node_ptr &node_to_keep,
const Node_ptr &node_to_remove) {
66 auto end_node_ids{m_node_ids.end()};
67 auto node1_it{m_node_ids.find(node_to_keep->id())};
68 auto node2_it{m_node_ids.find(node_to_remove->id())};
70 if (node1_it == end_node_ids || node2_it == end_node_ids) {
74 const int32_t idx_k{node1_it->second};
75 const int32_t idx_r{node2_it->second};
78 for (Node_ptr n : m_nodes->at(idx_r)->edges()) {
79 if (n->id() != node_to_keep->id()) {
81 n->remove_edge(node_to_remove);
82 n->add_edge(node_to_keep);
83 node_to_keep->add_edge(n);
87 node_to_keep->add_pixels(node_to_remove->get_pixels());
89 node_to_remove->clear_all();
94void Graph::clear_unconnected_nodes() {
95 std::vector<Node_ptr> &nodes{*m_nodes};
97 nodes.erase(std::remove_if(nodes.begin(), nodes.end(),
98 [](
const Node_ptr &n) { return n->area() == 0; }),
104void Graph::discover_edges(
const std::vector<int32_t> ®ion_labels,
const int32_t width,
105 const int32_t height) {
107 constexpr int8_t dirs[8][2]{{1, 0}, {-1, 0}, {0, 1}, {0, -1},
108 {1, 1}, {-1, -1}, {-1, 1}, {1, -1}};
112 for (int32_t y{0}; y < height; ++y) {
113 for (int32_t x{0}; x < width; ++x) {
114 const int32_t idx{y * width + x};
115 const int32_t rid{region_labels[idx]};
117 for (int32_t k{0}; k < 8; ++k) {
118 const int32_t nx{x + dirs[k][0]};
119 const int32_t ny{y + dirs[k][1]};
121 if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
122 rneigh[k] = region_labels[ny * width + nx];
128 for (int32_t r : rneigh) {
137void Graph::process_overlapping_edges() {
139 std::vector<int32_t> label_map(m_width * m_height, 0);
141 for (
const Node_ptr &n : get_nodes()) {
142 if (n->area() == 0)
continue;
144 for (
auto &[_, p] : n->get_pixels()) {
145 label_map[p.y * m_width + p.x] = n->id();
149 constexpr int8_t dirs[8][2]{{1, 0}, {-1, 0}, {0, 1}, {0, -1},
150 {1, 1}, {-1, -1}, {-1, 1}, {1, -1}};
153 for (
const Node_ptr &n : get_nodes()) {
154 if (n->area() == 0)
continue;
155 int32_t val = n->id();
157 for (
auto &[_, p] : n->get_pixels()) {
162 for (
int k = 0; k < 8; ++k) {
163 int nx = x + dirs[k][0];
164 int ny = y + dirs[k][1];
167 if (nx < 0 || nx >= m_width || ny < 0 || ny >= m_height)
continue;
169 int32_t n_val = label_map[ny * m_width + nx];
172 if (n_val != 0 && n_val != val && val < n_val) {
173 bool is_too_thin =
false;
176 for (
int mk = 0; mk < 8; ++mk) {
177 int mx = nx + dirs[mk][0];
178 int my = ny + dirs[mk][1];
180 if (mx < 0 || mx >= m_width || my < 0 || my >= m_height)
continue;
182 int32_t m_val = label_map[my * m_width + mx];
184 if (m_val != 0 && m_val != val && m_val != n_val) {
192 Node_ptr neighbor_node =
193 m_nodes->at(m_node_ids[n_val]);
196 neighbor_node->add_edge_pixel(
XY{x, y});
200 n->add_edge_pixel(
XY{nx, ny});
208void Graph::compute_contours() {
211 process_overlapping_edges();
213 for (
const Node_ptr &n : get_nodes()) {
214 if (n->area() == 0)
continue;
215 n->compute_contour();
219 std::vector<std::vector<Point>> all_contours;
220 for (
const Node_ptr &n : get_nodes()) {
221 if (n->area() == 0)
continue;
224 for (
size_t i = 0; i < c0->contours.size(); ++i) {
225 all_contours.push_back(c0->contours[i]);
229 contours::coupled_smooth(
230 all_contours,
Rect{0.0f, 0.0f,
static_cast<float>(m_width),
static_cast<float>(m_height)});
232 std::vector<std::vector<QuadBezier>> all_curves;
233 fit_curve_reduction(all_contours, all_curves, 0.5f);
236 for (
const Node_ptr &n : get_nodes()) {
237 if (n->area() == 0)
continue;
240 for (
size_t i = 0; i < c0->contours.size(); ++i) {
241 std::copy(all_contours[j].begin(), all_contours[j].end(), c0->contours[i].begin());
243 c0->curves[i].resize(all_curves[j].size());
244 std::copy(all_curves[j].begin(), all_curves[j].end(), c0->curves[i].begin());
250void Graph::merge_small_area_nodes(
const int32_t min_area) {
252 while (!all_areas_bigger_than(min_area)) {
253 for (
const Node_ptr &n : get_nodes()) {
254 if (n->area() < min_area) {
255 std::vector<Node_ptr> neighbors;
256 neighbors.reserve(n->num_edges());
257 std::copy(n->edges().begin(), n->edges().end(), std::back_inserter(neighbors));
261 Node_ptr best_neighbor =
nullptr;
262 float best_score = std::numeric_limits<float>::max();
263 for (
const Node_ptr &ne : n->edges()) {
264 if (ne->area() > 0) {
266 float score =
static_cast<float>(ne->area()) + 10.f * cdist;
267 if (score < best_score) {
275 if (!best_neighbor) {
279 if (best_neighbor->area() >= n->area()) {
280 merge_nodes(best_neighbor, n);
282 merge_nodes(n, best_neighbor);
287 clear_unconnected_nodes();