Skip to content

Releases

vectors (latest)

Choose a tag to compare

@github-actions github-actions released this 14 Nov 09:01
1f3daef

vectors

CI License: MIT

Topics: svg, vectorization, rust, cli, wasm, mcp

vectors — turn PNG and JPEG images into clean SVG paths

Live demo: https://hieudoanm.github.io/vectors/

vectors demo

Turn PNG and JPEG images into clean SVG paths — and rasterize SVG back to PNG at any size — entirely offline.

vectors is a classical raster-to-vector tracer written from scratch in Rust. It quantizes an image into a palette, segments pixels into regions, walks their boundaries, simplifies the result, fits Bézier curves, and emits SVG. No machine-learning models, no network calls, no service dependency — and the same input always produces byte-identical output.

The tracer ships as a CLI, an HTTP API, an MCP server, a Rust library (compiled to WebAssembly for the web app), and a Next.js landing page with an in-browser converter.

Table of Contents

The Problem

Turning a raster logo back into usable vector paths is usually either an online upload form, a heavyweight design tool, or a black box that changes its output between runs. A practical tracer needs to:

  • Accept ordinary PNG and JPEG input without a pre-processing step.
  • Handle colour palettes, transparency, holes, and anti-aliasing.
  • Produce editable SVG — path elements with fill, not an embedded bitmap.
  • Be deterministic, so a committed fixture or baseline does not churn.
  • Work offline as a single binary, a library, and a server, from the same code.
  • Go the other way too: render an SVG to a sized PNG for icons and previews.

vectors is built around those constraints.

The Solution

vectors runs one linear pipeline — raster → preprocess → segment → contour → simplify → Bézier → vector model → SVG — where each stage consumes the previous stage's type and knows nothing about how the next one is rendered. The tracer is deterministic by construction (no RNG, no hash-order dependence), and is measured by rasterizing its own SVG output with an independent renderer rather than by eyeballing the paths.

The inverse direction, SVG → PNG, parses the document with resvg, draws it straight into a straight-alpha buffer, and encodes it with image. Width and height are explicit flags, so the same binary can produce a 512 px icon or a 4096 px comparison raster.

Features

Tracing

  • Palette and binary modes — median-cut colour quantization (default 8 colours) or a single luminance threshold for black-and-white output.
  • 8-connected segmentation — flood fill per region, with an area filter for anti-aliasing specks.
  • Proper holes — outer contour plus holes per path, emitted with fill-rule="evenodd".
  • Douglas–Peucker simplification — fewer points without moving the shape.
  • Least-squares Bézier fitting — fit–measure–split, tunable per pixel.
  • Debug artefacts — --debug-dir writes the quantized image, regions, curves, and curve SVG so you can see which stage went wrong.

Surfaces

  • CLI — one command for a single file; omit the output path to stream SVG to stdout.
  • HTTP API — vectors serve exposes /health, /info, and POST /vectors (multipart or URL-safe base64), with per-request overrides.
  • MCP server — vectors mcp serve speaks newline-delimited JSON-RPC on stdio and advertises a vectors_trace tool for agent clients.
  • Library — trace, to_svg, Raster, TraceConfig, and SvgOptions; VectorImage is SVG-independent so another serializer is additive.
  • Web app — a Next.js landing page with an in-browser converter that runs the same tracer compiled to WebAssembly; conversion happens entirely client-side.

SVG → PNG

  • Exact or proportional sizing — --width alone preserves the aspect ratio; --width and --height together set the exact grid.
  • Transparent by default — add --background '#ffffff' to composite an opaque backdrop.
  • No extra tooling — resvg comes from the module's raster feature, not from a system package.

Quality harness (optional)

  • Measured accuracy — --features eval adds eval, baseline, golden, and sweep subcommands that rasterize the emitted SVG, compare it to the source, and score MAE, PSNR, SSIM, edge agreement, primitives, and bytes.
  • Committed baselines — one row per image, so a regression names the image that broke instead of hiding in an average.
  • Parameter sweeps — explore colors, threshold, tolerances, and area limits across the golden suite.

Key Benefits

  • Offline and private — a static binary with no network calls; images never leave the machine.
  • Deterministic — identical input yields byte-identical SVG, which makes fixtures, baselines, and diffs meaningful.
  • One pipeline, many surfaces — CLI, HTTP, MCP, library, and desktop all call the same trace; the surfaces cannot drift.
  • Editable output — real <path> geometry with fills and holes, not a bitmap wrapped in SVG.
  • Bidirectional — trace raster to SVG and render SVG back to PNG, including sized exports.
  • Honest quality claims — accuracy is measured against a committed baseline by rasterizing the actual emitted SVG.

Get Started

Prerequisites

  • Rust 1.88 or newer (for the CLI and library).
  • Node.js 26.12.1 and pnpm 12.15.0 (only for the landing page and workspace scripts).

Build the CLI

git clone https://github.com/hieudoanm/vectors.git
cd vectors/crates/vectors-cli
cargo build --release
../target/release/vectors --help

The release binary lands at crates/target/release/vectors (the workspace target). A prebuilt vectors
binary is also attached to the rolling vectors-latest
release; crates/vectors-cli/scripts/install.sh downloads it.

Convert your first image

vectors logo.png logo.svg
vectors logo.png | pango-view -          # SVG to stdout
vectors logo.svg logo.png --width 512    # back to a sized PNG

Build everything

pnpm install
pnpm build        # turbo: Rust crates and web
pnpm lint
pnpm test
pnpm format

CLI

vectors [OPTIONS] [INPUT] [OUTPUT]
       vectors [OPTIONS] [INPUT] [OUTPUT] <COMMAND>

The output extension decides the format: PNG/JPEG input traces to .svg, while a
.png output rasterizes the result (and is the only valid output for an SVG
input). Omit the output path to print SVG to stdout.

Flag What it does
-c, --colors <N> Palette size; 1 or 2 selects black-and-white tracing (default 8).
-t, --threshold <L> Luminance cut for binary mode (default 128).
-d, --detail <full|balanced|bold> How aggressively small shapes are dropped.
--min-area <PX> Ignore regions smaller than this; floored by --detail.
--simplify-tolerance <PX> Douglas–Peucker tolerance (default 1).
--bezier-tolerance <PX> Maximum Bézier deviation (default 0.5).
--backdrop <COLOR> Backdrop under transparent pixels during tracing (default #ffffff).
--background-index <N> Palette index treated as background and never traced.
--precision <N> Decimal places in emitted coordinates (default 2).
--width <PX>, --height <PX> Target PNG size; width alone keeps the aspect ratio.
--background <COLOR> Opaque backdrop when writing a PNG (default: keep alpha).
--debug-dir <DIR> Write per-stage debug PNGs.
-q, --quiet Suppress the summary written to stderr.

Subcommands: info (palette without tracing), serve (HTTP), mcp serve
(stdio JSON-RPC), plus the feature-gated eval, baseline, golden, and
sweep.

Examples

vectors icon.png icon.svg --colors 4
vectors photo.jpg out.svg --colors 1 --threshold 140
vectors logo.png logo.svg --detail bold --debug-dir ./trace
vectors logo.png logo.png --width 1024
vectors logo.svg logo.png --width 512 --height 512 --background '#ffffff'
vectors info logo.png
vectors serve --bind 0.0.0.0:8080
vectors mcp se...
Read more