Img2Num C++ (Internal Developer Docs) dev
API Documentation
Loading...
Searching...
No Matches
Graph Class Reference
+ Collaboration diagram for Graph:

Public Member Functions

 Graph (std::unique_ptr< std::vector< Node_ptr > > &nodes, int width, int height)
 
bool add_edge (int32_t node_id1, int32_t node_id2)
 
bool merge_nodes (const Node_ptr &node_to_keep, const Node_ptr &node_to_remove)
 
void clear_unconnected_nodes ()
 
const std::vector< Node_ptr > & get_nodes () const
 
bool all_areas_bigger_than (int32_t min_area)
 
const size_t size ()
 
void discover_edges (const std::vector< int32_t > &region_labels, const int32_t width, const int32_t height)
 
void merge_small_area_nodes (const int32_t min_area)
 
void compute_contours ()
 

Protected Member Functions

void hash_node_ids (void)
 
void process_overlapping_edges ()
 

Protected Attributes

int m_width
 
int m_height
 
std::unique_ptr< std::vector< Node_ptr > > m_nodes
 
std::unordered_map< int32_t, int32_t > m_node_ids
 

Detailed Description

Definition at line 33 of file graph.h.

Constructor & Destructor Documentation

◆ Graph()

Graph::Graph ( std::unique_ptr< std::vector< Node_ptr > > &  nodes,
int  width,
int  height 
)
inline

Definition at line 43 of file graph.h.

44 : m_nodes(std::move(nodes)), m_width(width), m_height(height) {
45 hash_node_ids();
46 }

◆ ~Graph()

Graph::~Graph ( )
inline

Definition at line 48 of file graph.h.

48 {
49 // Break the circular references so the shared_ptrs can reach 0
50 for (auto &node : *m_nodes) {
51 node->clear_all();
52 }
53 }

Member Function Documentation

◆ add_edge()

bool Graph::add_edge ( int32_t  node_id1,
int32_t  node_id2 
)

Definition at line 48 of file graph.cpp.

48 {
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)};
52
53 if (node1_it == end_node_ids || node2_it == end_node_ids) {
54 return false;
55 }
56
57 const int32_t idx1{node1_it->second};
58 const int32_t idx2{node2_it->second};
59
60 m_nodes->at(idx1)->add_edge(m_nodes->at(idx2));
61 m_nodes->at(idx2)->add_edge(m_nodes->at(idx1));
62 return true;
63}

◆ all_areas_bigger_than()

bool Graph::all_areas_bigger_than ( int32_t  min_area)

Definition at line 38 of file graph.cpp.

38 {
39 for (auto &n : *m_nodes) {
40 if (n->area() < min_area) {
41 return false;
42 }
43 }
44
45 return true;
46}

◆ clear_unconnected_nodes()

void Graph::clear_unconnected_nodes ( )

Definition at line 94 of file graph.cpp.

94 {
95 std::vector<Node_ptr> &nodes{*m_nodes};
96
97 nodes.erase(std::remove_if(nodes.begin(), nodes.end(),
98 [](const Node_ptr &n) { return n->area() == 0; }),
99 nodes.end());
100
101 hash_node_ids();
102}

◆ compute_contours()

void Graph::compute_contours ( )

Definition at line 208 of file graph.cpp.

208 {
209 // overlap edge pixels
210 // then compute contours
211 process_overlapping_edges();
212 // ask each Node to compute contours
213 for (const Node_ptr &n : get_nodes()) {
214 if (n->area() == 0) continue;
215 n->compute_contour();
216 }
217
218 // smoothing
219 std::vector<std::vector<Point>> all_contours;
220 for (const Node_ptr &n : get_nodes()) {
221 if (n->area() == 0) continue;
222
223 ColoredContours *c0 = &n->m_contours;
224 for (size_t i = 0; i < c0->contours.size(); ++i) {
225 all_contours.push_back(c0->contours[i]);
226 }
227 }
228
229 contours::coupled_smooth(
230 all_contours, Rect{0.0f, 0.0f, static_cast<float>(m_width), static_cast<float>(m_height)});
231
232 std::vector<std::vector<QuadBezier>> all_curves;
233 fit_curve_reduction(all_contours, all_curves, 0.5f);
234
235 int j = 0;
236 for (const Node_ptr &n : get_nodes()) {
237 if (n->area() == 0) continue;
238
239 ColoredContours *c0 = &n->m_contours;
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());
242
243 c0->curves[i].resize(all_curves[j].size());
244 std::copy(all_curves[j].begin(), all_curves[j].end(), c0->curves[i].begin());
245 j++;
246 }
247 }
248}

◆ discover_edges()

void Graph::discover_edges ( const std::vector< int32_t > &  region_labels,
const int32_t  width,
const int32_t  height 
)

Definition at line 104 of file graph.cpp.

105 {
106 // Moore 8-connected neighbourhood
107 constexpr int8_t dirs[8][2]{{1, 0}, {-1, 0}, {0, 1}, {0, -1},
108 {1, 1}, {-1, -1}, {-1, 1}, {1, -1}};
109
110 int32_t rneigh[8];
111
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]};
116
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]};
120
121 if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
122 rneigh[k] = region_labels[ny * width + nx];
123 } else {
124 rneigh[k] = rid; // ignore out-of-bounds
125 }
126 }
127
128 for (int32_t r : rneigh) {
129 if (r != rid) {
130 add_edge(rid, r);
131 }
132 }
133 }
134 }
135}

◆ get_nodes()

const std::vector< Node_ptr > & Graph::get_nodes ( ) const
inline

Definition at line 60 of file graph.h.

60 {
61 return *m_nodes;
62 }

◆ hash_node_ids()

void Graph::hash_node_ids ( void  )
protected

Definition at line 31 of file graph.cpp.

31 {
32 for (int32_t i{0}; i < m_nodes->size(); i++) {
33 const int32_t key{m_nodes->at(i)->id()};
34 m_node_ids[key] = i;
35 }
36}

◆ merge_nodes()

bool Graph::merge_nodes ( const Node_ptr &  node_to_keep,
const Node_ptr &  node_to_remove 
)

Definition at line 65 of file graph.cpp.

65 {
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())};
69
70 if (node1_it == end_node_ids || node2_it == end_node_ids) {
71 return false;
72 }
73
74 const int32_t idx_k{node1_it->second};
75 const int32_t idx_r{node2_it->second};
76
77 // transfer edges from node_to_remove to node_to_keep
78 for (Node_ptr n : m_nodes->at(idx_r)->edges()) {
79 if (n->id() != node_to_keep->id()) {
80 // prevents self referencing
81 n->remove_edge(node_to_remove);
82 n->add_edge(node_to_keep);
83 node_to_keep->add_edge(n);
84 }
85 }
86
87 node_to_keep->add_pixels(node_to_remove->get_pixels());
88
89 node_to_remove->clear_all();
90
91 return true;
92}

◆ merge_small_area_nodes()

void Graph::merge_small_area_nodes ( const int32_t  min_area)

Definition at line 250 of file graph.cpp.

250 {
251 int32_t counter{0};
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));
258
259 ImageLib::RGBPixel<uint8_t> col = n->color();
260
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) {
265 float cdist = ImageLib::RGBPixel<uint8_t>::colorDistance(ne->color(), col);
266 float score = static_cast<float>(ne->area()) + 10.f * cdist;
267 if (score < best_score) {
268 best_score = score;
269 best_neighbor = ne;
270 }
271 }
272 }
273
274 // no valid neighbor found, skip this node
275 if (!best_neighbor) {
276 continue;
277 }
278
279 if (best_neighbor->area() >= n->area()) {
280 merge_nodes(best_neighbor, n);
281 } else {
282 merge_nodes(n, best_neighbor);
283 }
284 }
285 }
286
287 clear_unconnected_nodes();
288 ++counter;
289 }
290}

◆ process_overlapping_edges()

void Graph::process_overlapping_edges ( )
protected

Definition at line 137 of file graph.cpp.

137 {
138 // 1. Build the Global Label Map ONCE (0 = background, else = node->id())
139 std::vector<int32_t> label_map(m_width * m_height, 0);
140
141 for (const Node_ptr &n : get_nodes()) {
142 if (n->area() == 0) continue;
143
144 for (auto &[_, p] : n->get_pixels()) {
145 label_map[p.y * m_width + p.x] = n->id();
146 }
147 }
148
149 constexpr int8_t dirs[8][2]{{1, 0}, {-1, 0}, {0, 1}, {0, -1},
150 {1, 1}, {-1, -1}, {-1, 1}, {1, -1}};
151
152 // 2. Iterate directly over the pixels of each node
153 for (const Node_ptr &n : get_nodes()) {
154 if (n->area() == 0) continue;
155 int32_t val = n->id();
156
157 for (auto &[_, p] : n->get_pixels()) {
158 int x = p.x;
159 int y = p.y;
160
161 // Check 8 neighbors in the global map
162 for (int k = 0; k < 8; ++k) {
163 int nx = x + dirs[k][0];
164 int ny = y + dirs[k][1];
165
166 // Fast boundary check (replaces std::clamp)
167 if (nx < 0 || nx >= m_width || ny < 0 || ny >= m_height) continue;
168
169 int32_t n_val = label_map[ny * m_width + nx];
170
171 // Is it a neighbor? AND have we not processed this pairing yet?
172 if (n_val != 0 && n_val != val && val < n_val) {
173 bool is_too_thin = false;
174
175 // Check around the neighbor pixel for a 3rd region (pinching)
176 for (int mk = 0; mk < 8; ++mk) {
177 int mx = nx + dirs[mk][0];
178 int my = ny + dirs[mk][1];
179
180 if (mx < 0 || mx >= m_width || my < 0 || my >= m_height) continue;
181
182 int32_t m_val = label_map[my * m_width + mx];
183
184 if (m_val != 0 && m_val != val && m_val != n_val) {
185 is_too_thin = true;
186 break; // CRITICAL: Stop checking immediately once proven thin!
187 }
188 }
189
190 if (is_too_thin) {
191 // Give our pixel to the neighbor
192 Node_ptr neighbor_node =
193 m_nodes->at(m_node_ids[n_val]); // get_node_by_id(n_val); // Assuming
194 // you have this lookup
195 if (neighbor_node) {
196 neighbor_node->add_edge_pixel(XY{x, y});
197 }
198 } else {
199 // Take the neighbor's pixel
200 n->add_edge_pixel(XY{nx, ny});
201 }
202 }
203 }
204 }
205 }
206}
Definition node.h:38

◆ size()

const size_t Graph::size ( )
inline

Definition at line 65 of file graph.h.

65 {
66 return m_nodes->size();
67 }

Member Data Documentation

◆ m_height

int Graph::m_height
protected

Definition at line 35 of file graph.h.

◆ m_node_ids

std::unordered_map<int32_t, int32_t> Graph::m_node_ids
protected

Definition at line 37 of file graph.h.

◆ m_nodes

std::unique_ptr<std::vector<Node_ptr> > Graph::m_nodes
protected

Definition at line 36 of file graph.h.

◆ m_width

int Graph::m_width
protected

Definition at line 35 of file graph.h.


The documentation for this class was generated from the following files: