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
43 region_array[size_t(index(x, y))] = label_value;
45 out_pixels->push_back(pix);
48 while (!queue.empty()) {
51 for (
auto& d : dirs) {
56 if ((x1 >= 0) && (x1 <
int(width)) && (y1 >= 0) && (y1 <
int(height)) &&
57 (label_array[
size_t(index(x1, y1))] == target_value) &&
58 (region_array[
size_t(index(x1, y1))] == -1)) {
60 color_array[4 * size_t(index(x1, y1))],
61 color_array[4 * size_t(index(x1, y1)) + 1],
62 color_array[4 * size_t(index(x1, y1)) + 2], x1, y1
64 region_array[size_t(index(x1, y1))] = label_value;
65 out_pixels->push_back(pix1);
77 const uint8_t* data, std::vector<int32_t>& labels, std::vector<int32_t>& regions,
int width,
78 int height, std::vector<Node_ptr>& nodes
80 auto index = [width](
int x,
int y) {
84 regions.resize(
static_cast<size_t>(height) *
static_cast<size_t>(width), -1);
87 for (
int i = 0; i < width; i++) {
88 for (
int j = 0; j < height; j++) {
89 int label {labels[size_t(index(i, j))]};
90 int rlab {regions[size_t(index(i, j))]};
96 std::unique_ptr<std::vector<RGBXY>> p_ptr = std::make_unique<std::vector<RGBXY>>();
98 flood_fill(labels, regions, data, i, j, label, r_lbl, width, height, p_ptr);
99 int num_pixels = p_ptr->size();
102 Node_ptr n_ptr = std::make_shared<Node>(r_lbl, p_ptr);
103 nodes.push_back(n_ptr);
109void visualize_contours(
110 const std::vector<std::vector<Point>>& contours,
115 static std::mt19937 rng(std::random_device {}());
116 static std::uniform_int_distribution<int32_t> dist(0, 255);
117 for (
const auto& c : contours) {
119 static_cast<uint8_t
>(dist(rng)),
static_cast<uint8_t
>(dist(rng)),
120 static_cast<uint8_t
>(dist(rng)), 255
123 for (
const auto& p : c) {
124 int32_t _x {
static_cast<int32_t
>(p.x) + xmin};
125 int32_t _y {
static_cast<int32_t
>(p.y) + ymin};
128 if (_x < 0 || _x >= width || _y < 0 || _y >= height)
131 results(_x, _y) = rand_color;
136std::string contourToSVGPath(
const std::vector<Point>& contour) {
140 std::ostringstream path;
141 path << std::fixed << std::setprecision(2);
144 path <<
"M " << contour[0].x <<
" " << contour[0].y <<
" ";
147 for (
size_t i = 1; i < contour.size(); ++i) {
148 path <<
"L " << contour[i].x <<
" " << contour[i].y <<
" ";
156std::string contourToSVGCurve(
const std::vector<QuadBezier>& curves) {
160 std::ostringstream path;
161 path << std::fixed << std::setprecision(2);
163 for (
size_t i = 0; i < curves.size(); ++i) {
164 const auto& c = curves[i];
166 path <<
"M " << c.p0.x <<
" " << c.p0.y <<
" ";
167 path <<
"Q " << c.p1.x <<
" " << c.p1.y <<
" " << c.p2.x <<
" " << c.p2.y <<
" ";
175std::string contoursResultToSVG(
const ColoredContours& result,
const int width,
const int height) {
176 std::ostringstream svg;
177 svg <<
"<svg xmlns=\"http://www.w3.org/2000/svg\" fill-rule=\"evenodd\" "
179 << width <<
"\" height=\"" << height <<
"\">\n";
181 for (
size_t i = 0; i < result.curves.size(); ++i) {
182 std::string pathData = contourToSVGCurve(result.curves[i]);
184 const auto& px = result.colors[i];
185 std::ostringstream oss;
186 oss <<
"#" << std::hex << std::uppercase << std::setw(2) << std::setfill(
'0')
187 <<
static_cast<int>(px.red) << std::setw(2) << std::setfill(
'0')
188 <<
static_cast<int>(px.green) << std::setw(2) << std::setfill(
'0')
189 <<
static_cast<int>(px.blue);
192 svg <<
" <path d=\"" << pathData <<
"\" fill=\"" << oss.str() <<
"\" />\n";
207 const uint8_t* data,
const int32_t* labels,
const int width,
const int height,
208 const int min_area,
const int min_thickness = 0
210 const int32_t num_pixels {width * height};
211 std::vector<int32_t> labels_vector {labels, labels + num_pixels};
212 std::vector<int32_t> region_labels;
215 std::vector<Node_ptr> nodes;
216 region_labeling(data, labels_vector, region_labels, width, height, nodes);
219 std::unique_ptr<std::vector<Node_ptr>> node_ptr =
220 std::make_unique<std::vector<Node_ptr>>(std::move(nodes));
221 Graph G(node_ptr, width, height);
224 G.discover_edges(region_labels, width, height);
227 G.merge_small_area_nodes(min_area, min_thickness);
231 for (
auto& n : G.get_nodes()) {
235 auto [r, g, b] = n->color();
236 for (
auto& [_, p] : n->get_pixels()) {
237 results(p.x, p.y) = {r, g, b};
243 G.compute_contours();
247 for (
auto& n : G.get_nodes()) {
251 for (
auto& c : node_contours.contours) {
252 all_contours.contours.push_back(c);
254 for (
auto& c : node_contours.hierarchy) {
255 all_contours.hierarchy.push_back(c);
257 for (
bool b : node_contours.is_hole) {
258 all_contours.is_hole.push_back(b);
260 for (
auto& c : node_contours.colors) {
261 all_contours.colors.push_back(c);
263 for (
auto& c : node_contours.curves) {
264 all_contours.curves.push_back(c);
269 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.