2#include "internal/bezier.h"
3#include "internal/contours.h"
4#include "internal/graph.h"
24 std::vector<int32_t>& label_array, std::vector<int32_t>& region_array,
25 const uint8_t* color_array,
int x,
int y,
int target_value,
int label_value,
size_t width,
26 size_t height, std::unique_ptr<std::vector<RGBXY>>& out_pixels
29 auto index = [width](
int x,
int y) {
34 int dirs[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
37 color_array[4 * size_t(index(x, y))], color_array[4 * size_t(index(x, y)) + 1],
38 color_array[4 * size_t(index(x, y)) + 2], x, y};
42 region_array[size_t(index(x, y))] = label_value;
44 out_pixels->push_back(pix);
47 while (!queue.empty()) {
50 for (
auto& d : dirs) {
55 if ((x1 >= 0) && (x1 <
int(width)) && (y1 >= 0) && (y1 <
int(height)) &&
56 (label_array[
size_t(index(x1, y1))] == target_value) &&
57 (region_array[
size_t(index(x1, y1))] == -1)) {
59 color_array[4 * size_t(index(x1, y1))],
60 color_array[4 * size_t(index(x1, y1)) + 1],
61 color_array[4 * size_t(index(x1, y1)) + 2], x1, y1};
62 region_array[size_t(index(x1, y1))] = label_value;
63 out_pixels->push_back(pix1);
75 const uint8_t* data, std::vector<int32_t>& labels, std::vector<int32_t>& regions,
int width,
76 int height, std::vector<Node_ptr>& nodes
78 auto index = [width](
int x,
int y) {
82 regions.resize(
static_cast<size_t>(height) *
static_cast<size_t>(width), -1);
85 for (
int i = 0; i < width; i++) {
86 for (
int j = 0; j < height; j++) {
87 int label {labels[size_t(index(i, j))]};
88 int rlab {regions[size_t(index(i, j))]};
94 std::unique_ptr<std::vector<RGBXY>> p_ptr = std::make_unique<std::vector<RGBXY>>();
96 flood_fill(labels, regions, data, i, j, label, r_lbl, width, height, p_ptr);
97 int num_pixels = p_ptr->size();
100 Node_ptr n_ptr = std::make_shared<Node>(r_lbl, p_ptr);
101 nodes.push_back(n_ptr);
107void visualize_contours(
108 const std::vector<std::vector<Point>>& contours,
113 static std::mt19937 rng(std::random_device {}());
114 static std::uniform_int_distribution<int32_t> dist(0, 255);
115 for (
const auto& c : contours) {
117 static_cast<uint8_t
>(dist(rng)),
static_cast<uint8_t
>(dist(rng)),
118 static_cast<uint8_t
>(dist(rng)), 255};
120 for (
const auto& p : c) {
121 int32_t _x {
static_cast<int32_t
>(p.x) + xmin};
122 int32_t _y {
static_cast<int32_t
>(p.y) + ymin};
125 if (_x < 0 || _x >= width || _y < 0 || _y >= height)
128 results(_x, _y) = rand_color;
133std::string contourToSVGPath(
const std::vector<Point>& contour) {
137 std::ostringstream path;
138 path << std::fixed << std::setprecision(2);
141 path <<
"M " << contour[0].x <<
" " << contour[0].y <<
" ";
144 for (
size_t i = 1; i < contour.size(); ++i) {
145 path <<
"L " << contour[i].x <<
" " << contour[i].y <<
" ";
153std::string contourToSVGCurve(
const std::vector<QuadBezier>& curves) {
157 std::ostringstream path;
158 path << std::fixed << std::setprecision(2);
160 for (
size_t i = 0; i < curves.size(); ++i) {
161 const auto& c = curves[i];
163 path <<
"M " << c.p0.x <<
" " << c.p0.y <<
" ";
164 path <<
"Q " << c.p1.x <<
" " << c.p1.y <<
" " << c.p2.x <<
" " << c.p2.y <<
" ";
172std::string contoursResultToSVG(
const ColoredContours& result,
const int width,
const int height) {
173 std::ostringstream svg;
174 svg <<
"<svg xmlns=\"http://www.w3.org/2000/svg\" fill-rule=\"evenodd\" "
176 << width <<
"\" height=\"" << height <<
"\">\n";
178 for (
size_t i = 0; i < result.curves.size(); ++i) {
179 std::string pathData = contourToSVGCurve(result.curves[i]);
181 const auto& px = result.colors[i];
182 std::ostringstream oss;
183 oss <<
"#" << std::hex << std::uppercase << std::setw(2) << std::setfill(
'0')
184 <<
static_cast<int>(px.red) << std::setw(2) << std::setfill(
'0')
185 <<
static_cast<int>(px.green) << std::setw(2) << std::setfill(
'0')
186 <<
static_cast<int>(px.blue);
189 svg <<
" <path d=\"" << pathData <<
"\" fill=\"" << oss.str() <<
"\" />\n";
204 const uint8_t* data,
const int32_t* labels,
const int width,
const int height,
205 const int min_area,
const int min_thickness = 0
207 const int32_t num_pixels {width * height};
208 std::vector<int32_t> labels_vector {labels, labels + num_pixels};
209 std::vector<int32_t> region_labels;
212 std::vector<Node_ptr> nodes;
213 region_labeling(data, labels_vector, region_labels, width, height, nodes);
216 std::unique_ptr<std::vector<Node_ptr>> node_ptr =
217 std::make_unique<std::vector<Node_ptr>>(std::move(nodes));
218 Graph G(node_ptr, width, height);
221 G.discover_edges(region_labels, width, height);
224 G.merge_small_area_nodes(min_area, min_thickness);
228 for (
auto& n : G.get_nodes()) {
232 auto [r, g, b] = n->color();
233 for (
auto& [_, p] : n->get_pixels()) {
234 results(p.x, p.y) = {r, g, b};
240 G.compute_contours();
244 for (
auto& n : G.get_nodes()) {
248 for (
auto& c : node_contours.contours) {
249 all_contours.contours.push_back(c);
251 for (
auto& c : node_contours.hierarchy) {
252 all_contours.hierarchy.push_back(c);
254 for (
bool b : node_contours.is_hole) {
255 all_contours.is_hole.push_back(b);
257 for (
auto& c : node_contours.colors) {
258 all_contours.colors.push_back(c);
260 for (
auto& c : node_contours.curves) {
261 all_contours.curves.push_back(c);
266 return contoursResultToSVG(all_contours, width, height);
Core image processing functions for img2num project.
std::string labels_to_svg(const uint8_t *data, const int32_t *labels, const int width, const int height, const int min_area, const int min_thickness)
Convert labeled regions of an image into an SVG string.