Per-image vectorizer selection works

This commit is contained in:
jaseg 2021-01-28 23:15:36 +01:00
parent f65cd52304
commit 6d1a7750c5
5 changed files with 81 additions and 6 deletions

View file

@ -93,15 +93,27 @@ namespace gerbolyze {
class ImageVectorizer {
public:
virtual ~ImageVectorizer() {};
virtual void vectorize_image(cairo_t *cr, const pugi::xml_node &node, ClipperLib::Paths &clip_path, cairo_matrix_t &viewport_matrix, PolygonSink &sink, double min_feature_size_px) = 0;
};
ImageVectorizer *makeVectorizer(const std::string &name);
class VectorizerSelectorizer {
public:
VectorizerSelectorizer(const std::string default_vectorizer="dev-null", const std::string defs="");
ImageVectorizer *select(const pugi::xml_node &img);
private:
std::string m_default;
std::map<std::string, std::string> m_map;
};
class RenderSettings {
public:
double m_minimum_feature_size_mm = 0.1;
ImageVectorizer *m_vec = nullptr;
VectorizerSelectorizer &m_vec_sel;
};
class SVGDocument {

View file

@ -43,7 +43,10 @@ int main(int argc, char **argv) {
"Comma-separated list of group IDs to export.",
1},
{"vectorizer", {"-b", "--vectorizer"},
"Vectorizer to use for bitmap images. One of poisson-disc (default), hex-grid, square-grid, binary-contours.",
"Vectorizer to use for bitmap images. One of poisson-disc (default), hex-grid, square-grid, binary-contours, dev-null.",
1},
{"vectorizer_map", {"--vectorizer-map"},
"Map from image element id to vectorizer. Overrides --vectorizer. Format: id1=vectorizer,id2=vectorizer,...",
1},
{"exclude_groups", {"-e", "--exclude-groups"},
"Comma-separated list of group IDs to exclude from export. Takes precedence over --only-groups.",
@ -169,6 +172,7 @@ int main(int argc, char **argv) {
id_match(args["exclude_groups"], sel.exclude);
string vectorizer = args["vectorizer"] ? args["vectorizer"] : "poisson-disc";
/* Check argument */
ImageVectorizer *vec = makeVectorizer(vectorizer);
if (!vec) {
cerr << "Unknown vectorizer \"" << vectorizer << "\"." << endl;
@ -176,10 +180,12 @@ int main(int argc, char **argv) {
fmt << usage.str() << argparser;
return EXIT_FAILURE;
}
delete vec;
VectorizerSelectorizer vec_sel(vectorizer, args["vectorizer_map"] ? args["vectorizer_map"] : "");
RenderSettings rset {
0.1,
vec
vec_sel,
};
doc.render(rset, flattener ? *flattener : *sink, &sel);

View file

@ -173,11 +173,16 @@ void gerbolyze::SVGDocument::export_svg_group(const RenderSettings &rset, const
export_svg_path(rset, node, clip_path);
} else if (name == "image") {
if (!rset.m_vec)
ImageVectorizer *vec = rset.m_vec_sel.select(node);
if (!vec) {
cerr << "Cannot resolve vectorizer for node \"" << node.attribute("id").value() << "\"" << endl;
continue;
}
double min_feature_size_px = mm_to_doc_units(rset.m_minimum_feature_size_mm);
rset.m_vec->vectorize_image(cr, node, clip_path, viewport_matrix, *polygon_sink, min_feature_size_px);
vec->vectorize_image(cr, node, clip_path, viewport_matrix, *polygon_sink, min_feature_size_px);
delete vec;
} else if (name == "defs") {
/* ignore */
} else {

View file

@ -39,6 +39,8 @@ ImageVectorizer *gerbolyze::makeVectorizer(const std::string &name) {
return new VoronoiVectorizer(SQUAREGRID, /* relax */ false);
else if (name == "binary-contours")
return new OpenCVContoursVectorizer();
else if (name == "dev-null")
return new DevNullVectorizer();
return nullptr;
}
@ -215,11 +217,15 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_
/* Scale intermediate image (step 1.2) to have <scale_featuresize_factor> pixels per min_feature_size. */
cv::Mat scaled(cv::Size{(int)round(px_w), (int)round(px_h)}, img.type());
cv::resize(img, scaled, scaled.size(), 0, 0);
cerr << "scaled " << img.cols << ", " << img.rows << " -> " << scaled.cols << ", " << scaled.rows << endl;
img.release();
/* Blur image with a kernel larger than our minimum feature size to avoid aliasing. */
cv::Mat blurred(scaled.size(), scaled.type());
int blur_size = (int)ceil(fmax(scaled.cols / width, scaled.rows / height) * center_distance);
if (blur_size%2 == 0)
blur_size += 1;
cerr << "blur size " << blur_size << endl;
cv::GaussianBlur(scaled, blurred, {blur_size, blur_size}, 0, 0);
scaled.release();
@ -251,7 +257,11 @@ void gerbolyze::VoronoiVectorizer::vectorize_image(cairo_t *cr, const pugi::xml_
double pxd = (double)blurred.at<unsigned char>(
(int)round(center.y / height * blurred.rows),
(int)round(center.x / width * blurred.cols)) / 255.0;
fill_factors[sites[i].index] = sqrt(pxd);
/* FIXME: This is a workaround for a memory corruption bug that happens with the square-grid setting. When using
* square-grid on a fairly small test image, sometimes sites[i].index will be out of bounds here.
*/
if (sites[i].index < fill_factors.size())
fill_factors[sites[i].index] = sqrt(pxd);
}
/* Minimum gap between adjacent scaled site polygons. */
@ -446,3 +456,38 @@ void gerbolyze::OpenCVContoursVectorizer::vectorize_image(cairo_t *cr, const pug
cairo_restore(cr);
}
gerbolyze::VectorizerSelectorizer::VectorizerSelectorizer(const string default_vectorizer, const string defs)
: m_default(default_vectorizer) {
istringstream foo(defs);
string elem;
while (std::getline(foo, elem, ',')) {
size_t pos = elem.find_first_of("=");
if (pos == string::npos) {
cerr << "Error parsing vectorizer selection string at element \"" << elem << "\"" << endl;
continue;
}
const string parsed_id = elem.substr(0, pos);
const string mapping = elem.substr(pos+1);
m_map[parsed_id] = mapping;
}
cerr << "parsed " << m_map.size() << " vectorizers" << endl;
for (auto &elem : m_map) {
cerr << " " << elem.first << " -> " << elem.second << endl;
}
}
ImageVectorizer *gerbolyze::VectorizerSelectorizer::select(const pugi::xml_node &img) {
const string id = img.attribute("id").value();
cerr << "selecting vectorizer for image \"" << id << "\"" << endl;
if (m_map.contains(id)) {
cerr << " -> found" << endl;
return makeVectorizer(m_map[id]);
}
cerr << " -> default" << endl;
return makeVectorizer(m_default);
}

View file

@ -43,6 +43,13 @@ namespace gerbolyze {
virtual void vectorize_image(cairo_t *cr, const pugi::xml_node &node, ClipperLib::Paths &clip_path, cairo_matrix_t &viewport_matrix, PolygonSink &sink, double min_feature_size_px);
};
class DevNullVectorizer : public ImageVectorizer {
public:
DevNullVectorizer() {}
virtual void vectorize_image(cairo_t *, const pugi::xml_node &, ClipperLib::Paths &, cairo_matrix_t &, PolygonSink &, double) {}
};
void parse_img_meta(const pugi::xml_node &node, double &x, double &y, double &width, double &height);
std::string read_img_data(const pugi::xml_node &node);
void draw_bg_rect(cairo_t *cr, double width, double height, ClipperLib::Paths &clip_path, PolygonSink &sink, cairo_matrix_t &viewport_matrix);