Img2Num C++ (Internal Developer Docs)  dev
API Documentation
node.cpp
1 #include "internal/node.h"
2 
3 #include <climits>
4 
5 /*
6  Node class
7  */
8 
9 XY Node::centroid() const {
10  XY centroid{0, 0};
11  const int32_t m_pixels_size{static_cast<int32_t>(m_pixels->size())};
12 
13  // Guard against division by zero after loop
14  if (m_pixels_size == 0) {
15  return centroid;
16  }
17 
18  for (auto &[_, pos] : *m_pixels) {
19  centroid.x += pos.x;
20  centroid.y += pos.y;
21  }
22 
23  centroid.x /= m_pixels_size;
24  centroid.y /= m_pixels_size;
25 
26  return centroid;
27 }
28 
29 ImageLib::RGBPixel<uint8_t> Node::color() const {
30  const int32_t m_pixels_size{static_cast<int32_t>(m_pixels->size())};
31 
32  // Guard against division by zero after loop
33  if (m_pixels_size == 0) {
34  return {0, 0, 0};
35  }
36 
37  float r{0};
38  float g{0};
39  float b{0};
40  for (auto &[color, _] : *m_pixels) {
41  r += color.red;
42  g += color.green;
43  b += color.blue;
44  }
45 
46  r /= m_pixels_size;
47  g /= m_pixels_size;
48  b /= m_pixels_size;
49 
50  // Accept lossy conversion - the difference is very minimal
51  return {static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b)};
52 }
53 
54 std::array<int32_t, 4> Node::bounding_box_xywh() const {
55  if (m_pixels->empty()) {
56  return {0, 0, 0, 0};
57  }
58 
59  int32_t x_min{INT_MAX};
60  int32_t y_min{INT_MAX};
61  int32_t x_max{0};
62  int32_t y_max{0};
63  for (auto &[_, p] : *m_pixels) {
64  if (p.x < x_min) {
65  x_min = p.x;
66  }
67  if (p.x > x_max) {
68  x_max = p.x;
69  }
70  if (p.y < y_min) {
71  y_min = p.y;
72  }
73  if (p.y > y_max) {
74  y_max = p.y;
75  }
76  }
77 
78  for (auto &p : m_edge_pixels) {
79  if (p.x < x_min) {
80  x_min = p.x;
81  }
82  if (p.x > x_max) {
83  x_max = p.x;
84  }
85  if (p.y < y_min) {
86  y_min = p.y;
87  }
88  if (p.y > y_max) {
89  y_max = p.y;
90  }
91  }
92 
93  const int32_t w{x_max - x_min + 1};
94  const int32_t h{y_max - y_min + 1};
95 
96  return std::array<int32_t, 4>{x_min, y_min, w, h};
97 }
98 
99 std::array<int, 4> Node::create_binary_image(std::vector<uint8_t> &binary) const {
100  std::array<int, 4> xywh{bounding_box_xywh()};
101 
102  binary.resize(static_cast<size_t>(xywh[2]) * static_cast<size_t>(xywh[3]), 0);
103 
104  for (auto &[_, p] : *m_pixels) {
105  int32_t _x = p.x - xywh[0];
106  int32_t _y = p.y - xywh[1];
107  binary[_y * xywh[2] + _x] = 1;
108  }
109 
110  // include the edge pixels to ensure contour overlap with neighbor
111  for (auto &p : m_edge_pixels) {
112  int32_t _x = p.x - xywh[0];
113  int32_t _y = p.y - xywh[1];
114  binary[_y * xywh[2] + _x] = 1;
115  }
116 
117  return xywh;
118 }
119 
120 void Node::clear_contour(void) {
121  m_contours.contours.clear();
122  m_contours.hierarchy.clear();
123  m_contours.is_hole.clear();
124  m_contours.colors.clear();
125  m_contours.curves.clear();
126 }
127 
128 void Node::compute_contour(void) {
129  // return list of all contours present in Node.
130  // usually 1 sometimes more if holes are present
131 
132  clear_contour();
133 
134  std::vector<uint8_t> binary;
135  std::array<int, 4> xywh{create_binary_image(binary)};
136 
137  int xmin = xywh[0];
138  int ymin = xywh[1];
139  int bw = xywh[2];
140  int bh = xywh[3];
141 
142  ContoursResult contour_res = contours::find_contours(binary, bw, bh);
143 
144  for (size_t cidx = 0; cidx < contour_res.contours.size(); ++cidx) {
145  auto &contour = contour_res.contours[cidx];
146  for (auto &p : contour) {
147  p.x += xmin;
148  p.y += ymin;
149  }
150 
151  // if (contour_res.is_hole[cidx]) { continue; }
152 
153  ImageLib::RGBPixel<uint8_t> _col = color();
154  ImageLib::RGBAPixel<uint8_t> col{_col.red, _col.green, _col.blue, 255};
155  m_contours.contours.push_back(contour);
156  m_contours.hierarchy.push_back(contour_res.hierarchy[cidx]);
157  m_contours.is_hole.push_back(contour_res.is_hole[cidx]);
158  m_contours.colors.push_back(col);
159  }
160 
161  m_contours.curves.resize(m_contours.contours.size());
162 }
163 
164 void Node::add_pixels(const std::vector<RGBXY> &new_pixels) {
165  for (auto &c : new_pixels) {
166  m_pixels->push_back(c);
167  }
168 }
169 
170 void Node::add_edge_pixel(const XY edge_pixel) {
171  m_edge_pixels.insert(edge_pixel);
172 }
173 
174 void Node::clear_edge_pixels() {
175  m_edge_pixels.clear();
176 }
177 
178 void Node::clear_all() {
179  m_edges.clear();
180  m_pixels->clear();
181  m_edge_pixels.clear();
182 }
Definition: node.h:38