Skip to main content

outline/vectorizer/
vtracer.rs

1use image::GrayImage;
2use visioncortex::PathSimplifyMode;
3use vtracer::{ColorImage, ColorMode, Config, Hierarchical, SvgFile, convert};
4
5use crate::mask::gray_to_color_image_rgba;
6use crate::{OutlineError, OutlineResult};
7
8use super::MaskVectorizer;
9
10/// Configuration for vectorizing masks into SVG paths using VTracer.
11///
12/// These options map directly to [`vtracer::Config`].
13#[derive(Debug, Clone)]
14pub struct TraceOptions {
15    pub tracer_color_mode: ColorMode,
16    pub tracer_hierarchical: Hierarchical,
17    pub tracer_mode: PathSimplifyMode,
18    pub tracer_filter_speckle: usize,
19    pub tracer_color_precision: i32,
20    pub tracer_layer_difference: i32,
21    pub tracer_corner_threshold: i32,
22    pub tracer_length_threshold: f64,
23    pub tracer_max_iterations: usize,
24    pub tracer_splice_threshold: i32,
25    pub tracer_path_precision: Option<u32>,
26    pub invert_svg: bool,
27}
28
29impl Default for TraceOptions {
30    fn default() -> Self {
31        Self {
32            tracer_color_mode: ColorMode::Binary,
33            tracer_hierarchical: Hierarchical::Stacked,
34            tracer_mode: PathSimplifyMode::Spline,
35            tracer_filter_speckle: 4,
36            tracer_color_precision: 6,
37            tracer_layer_difference: 16,
38            tracer_corner_threshold: 60,
39            tracer_length_threshold: 4.0,
40            tracer_max_iterations: 10,
41            tracer_splice_threshold: 45,
42            tracer_path_precision: Some(2),
43            invert_svg: false,
44        }
45    }
46}
47
48/// Converts grayscale masks to SVG using the VTracer library.
49///
50/// Implements [`MaskVectorizer`] to provide path tracing functionality. Pair with
51/// [`TraceOptions`] to control output style and precision.
52///
53/// # Example
54/// ```no_run
55/// use outline::{Outline, TraceOptions, VtracerSvgVectorizer};
56///
57/// let outline = Outline::new("model.onnx");
58/// let session = outline.for_image("input.jpg")?;
59/// let mask = session.matte().blur().threshold().processed()?;
60///
61/// let vectorizer = VtracerSvgVectorizer;
62/// let svg = mask.trace(&vectorizer, &TraceOptions::default())?;
63/// std::fs::write("outline.svg", svg)?;
64/// # Ok::<_, outline::OutlineError>(())
65/// ```
66#[derive(Debug, Clone, Copy, Default)]
67pub struct VtracerSvgVectorizer;
68
69impl MaskVectorizer for VtracerSvgVectorizer {
70    type Options = TraceOptions;
71    type Output = String;
72
73    fn vectorize(&self, mask: &GrayImage, options: &Self::Options) -> OutlineResult<Self::Output> {
74        trace_to_svg_string(mask, options)
75    }
76}
77
78/// The helper function that uses VTracer to trace a grayscale mask to an SVG string.
79pub fn trace_to_svg_string(
80    mask_image: &GrayImage,
81    options: &TraceOptions,
82) -> OutlineResult<String> {
83    let color_img = gray_to_color_image_rgba(mask_image, None, options.invert_svg);
84    let svg_file = trace(color_img, options)?;
85    Ok(svg_file.to_string())
86}
87
88/// Trace a ColorImage into an SVG using VTracer with the given options.
89pub fn trace(img: ColorImage, options: &TraceOptions) -> OutlineResult<SvgFile> {
90    let cfg = Config {
91        color_mode: options.tracer_color_mode.clone(),
92        hierarchical: options.tracer_hierarchical.clone(),
93        mode: options.tracer_mode,
94        filter_speckle: options.tracer_filter_speckle,
95        color_precision: options.tracer_color_precision,
96        layer_difference: options.tracer_layer_difference,
97        corner_threshold: options.tracer_corner_threshold,
98        length_threshold: options.tracer_length_threshold,
99        max_iterations: options.tracer_max_iterations,
100        splice_threshold: options.tracer_splice_threshold,
101        path_precision: options.tracer_path_precision,
102    };
103
104    let svg_file = convert(img, cfg).map_err(OutlineError::Trace)?;
105    Ok(svg_file)
106}