Files
vtracer/docs/design
Chris Tsang 46a1b90ccd Add watershed clustering: hierarchical watershed frontend
An alternative region-forming frontend selected by --clustering watershed
(the color_mode field is replaced by clustering: color-cluster | bw |
watershed across CLI, Rust, Python, and Node — it selects the algorithm,
not a color space).

The algorithm is the watershed hierarchy by volume on the 4-adjacency
pixel graph, implemented from the papers:

  Cousty, Bertrand, Najman, Couprie, "Watershed Cuts: Minimum Spanning
  Forests and the Drop of Water Principle", IEEE TPAMI 31(8), 2009.
  Najman, Cousty, Perret, "Playing with Kruskal", ISMM 2013.

Edge weights are the max per-channel color difference between adjacent
pixels (no gradient image); a counting-sorted Kruskal pass builds the
binary partition tree as a flat parents array; a leaves-to-root pass
computes subtree area and volume; each merge's persistence (the volume of
the smaller side, plateau-corrected) becomes its MST edge's saliency; and
cutting the hierarchy is single-linkage over MST edges below the cut
level. Watershed cuts label every pixel — no watershed-line pixel class —
so the output is a strict, gapless partition that drops straight into
both stacked and cutout modes. Integer arithmetic and flat u32 arrays
throughout; deterministic across platforms; ~66 ms on a 1400x775 photo.

The one dial, --watershed-detail (0..=255), maps exponentially to a
target region count (each +25.5 doubles it) since the persistence
distribution is far too skewed for a linear threshold. filter_speckle
absorbs undersized basins into their most color-similar neighbour rather
than dropping them, preserving the partition. The largest region is
emitted first as a solid full-canvas background layer so stacked mode
keeps its seam-free overdraw; the mosaic flatten is unaffected.

Tests: partition invariant (disjoint masks tiling the canvas), detail
monotonicity, min-area absorption, determinism, watershed cases in the
pipeline/golden suites, stacked-vs-cutout interior equivalence, a
watershed seam test, and SegmentKey coverage for the new params.
2026-07-27 14:45:25 +01:00
..
2026-07-23 18:11:19 +01:00
2026-07-23 18:11:19 +01:00
2026-07-23 18:11:19 +01:00
2026-07-23 18:11:19 +01:00

VTracer 1.0 Design Documents

VTracer is being rearchitected from a single hardcoded pipeline into a vectorization framework. These documents describe the target design.

Document Contents
architecture.md Workspace layout, core IR, stage traits, pipeline driver, optimizer & SVG writer, CLI
mosaic.md The seam-free cutout/mosaic mode: boundary-graph tracing and shared-edge curve fitting
bindings.md Python (PyPI), wasm, and the new Node.js (npm) package
roadmap.md Milestones and verification strategy

Motivation

VTracer today (0.6.x) is a thin driver around the visioncortex crate: one pipeline (color clustering → per-cluster tracing → SVG string), a CLI, a pyo3 binding, and a web demo that duplicates the pipeline. The rewrite turns it into a framework with pluggable stages:

  1. Frontend — any algorithm that produces clusters/segmentation from a raster image
  2. Curve fitting backend — pluggable polyline→curve fitters (pixel, polygon, spline, future potrace-style)
  3. Color fitting — mapping cluster colors to final paints, including custom fixed palettes
  4. Optimizer — a pass pipeline that shrinks output (relative path syntax, shorthand commands, precision reduction)
  5. True mosaic cutout — a perfect, gapless tessellation with shared boundary geometry, replacing today's fake cutout (which re-clusters a re-rendered image and shows seams)

The project stays backend/CLI focused, and everything except image file I/O compiles to wasm32-unknown-unknown.

Decisions

  • visioncortex remains a dependency, wrapped behind traits. Development uses a path/[patch] dependency on the local checkout; API additions are committed to visioncortex directly and published as 0.8.x releases. Verified that everything the new design needs is already public: the fitting primitives (fit_points_with_bezier, find_corners, subdivide_keep_corners, reduce, PathSimplify::*) and cluster pixel access via ClustersView.
  • In-repo rewrite, clean break. New workspace layout, new API, version bump. Old CLI flags are kept only where they map naturally.
  • Python binding stays (ported to the new API). The webapp GUI is dropped; a wasm library crate replaces it.
  • New Node.js library published to npm, using the wasm build internally plus a native image reader (sharp).

Pipeline at a glance

             ┌───────────┐   ┌──────────────┐   ┌─────────────────────────────┐
 raster ───▶ │ Frontend  │ ─▶│ ColorFitter* │ ─▶│ Compositing                 │
  image      │ (segment) │   │ (palette,    │   │  Stacked: closed outlines   │
             └───────────┘   │  quantize,   │   │  Mosaic:  boundary graph +  │
                             │  merge)      │   │           shared-edge fit   │
                             └──────────────┘   └──────────────┬──────────────┘
                                                               │  CurveFitter
                                                               ▼  (pixel/polygon/spline)
                                                ┌──────────────────────────────┐
                                     SVG  ◀──── │ VectorDoc ─ OptimizerPass* ─ │
                                                │            SvgWriter         │
                                                └──────────────────────────────┘