Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 7 additions & 10 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# This file is autogenerated by maturin v1.11.5
# To update, run
#
# maturin generate-ci github
#
# Python wheels for crates/vtracer-py (maturin). Regenerate the skeleton with:
# maturin generate-ci github -m crates/vtracer-py/Cargo.toml
name: Python

on:
Expand Down Expand Up @@ -42,7 +39,7 @@ jobs:
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter --manifest-path cmdapp/Cargo.toml
args: --release --out dist --find-interpreter --manifest-path crates/vtracer-py/Cargo.toml
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
manylinux: auto
- name: Upload wheels
Expand Down Expand Up @@ -73,7 +70,7 @@ jobs:
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter --manifest-path cmdapp/Cargo.toml
args: --release --out dist --find-interpreter --manifest-path crates/vtracer-py/Cargo.toml
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
manylinux: musllinux_1_2
- name: Upload wheels
Expand Down Expand Up @@ -106,7 +103,7 @@ jobs:
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter --manifest-path cmdapp/Cargo.toml
args: --release --out dist --find-interpreter --manifest-path crates/vtracer-py/Cargo.toml
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
- name: Upload wheels
uses: actions/upload-artifact@v5
Expand All @@ -132,7 +129,7 @@ jobs:
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --find-interpreter --manifest-path cmdapp/Cargo.toml
args: --release --out dist --find-interpreter --manifest-path crates/vtracer-py/Cargo.toml
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
- name: Upload wheels
uses: actions/upload-artifact@v5
Expand All @@ -148,7 +145,7 @@ jobs:
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist --manifest-path cmdapp/Cargo.toml
args: --out dist --manifest-path crates/vtracer-py/Cargo.toml
- name: Upload sdist
uses: actions/upload-artifact@v5
with:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
name: Release

# Builds the `vtracer` CLI binary (crates/vtracer-cli) for each target.

on:
release:
types: [published]
Expand Down
36 changes: 29 additions & 7 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ on:
branches:
- master
- 0.*.x
- pr/**/ci
- ci-*

concurrency:
Expand All @@ -23,13 +22,36 @@ env:
CARGO_TERM_COLOR: always

jobs:
build:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: cargo build --workspace --verbose
- name: Test
run: cargo test --workspace --verbose

wasm:
name: wasm-safety (core)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: rustup target add wasm32-unknown-unknown
- name: Build core for wasm32
run: cargo build --target wasm32-unknown-unknown -p vtracer

nodejs:
name: Node package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- name: Build & test
working-directory: nodejs
run: |
wasm-pack build --target nodejs --out-dir pkg
node test.js
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
target
Cargo.lock
*.sublime*
.vscode
.vscode
.DS_Store
29 changes: 27 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
[workspace]

members = [
"cmdapp",
"crates/vtracer",
"crates/vtracer-cli",
]

# The pre-1.0 webapp is kept in the tree for now but is no longer part of the
# build. It is superseded by the crates/ workspace above.
exclude = [
"webapp",
# pyo3 extension-module cdylib; built with maturin, not the core workspace.
"crates/vtracer-py",
# wasm-bindgen cdylib; built with wasm-pack as the Node package's core.
"nodejs",
]
resolver = "2"

resolver = "2"

[workspace.package]
version = "1.0.0-alpha.1"
authors = ["Chris Tsang <chris.2y3@outlook.com>"]
edition = "2021"
license = "MIT OR Apache-2.0"
homepage = "http://www.visioncortex.org/vtracer"
repository = "https://github.com/visioncortex/vtracer/"

[workspace.dependencies]
visioncortex = "0.9"
# For local development against an unreleased visioncortex, add a patch:
# [patch.crates-io]
# visioncortex = { path = "../visioncortex" }
162 changes: 89 additions & 73 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
<a href="https://github.com/visioncortex/vtracer/releases">Download</a>
</h3>

<sub>Built with 🦀 by <a href="https://www.visioncortex.org/">The Vision Cortex Research Group</a></sub>
</div>

## Introduction
Expand All @@ -30,63 +29,89 @@ VTracer is originally designed for processing high resolution scans of historic

Technical descriptions of the [tracing algorithm](https://www.visioncortex.org/vtracer-docs) and [clustering algorithm](https://www.visioncortex.org/impression-docs).

## Web App

VTracer and its [core library](//github.com/visioncortex/visioncortex) is implemented in [Rust](//www.rust-lang.org/). It provides us a solid foundation to develop robust and efficient algorithms and easily bring it to interactive applications. The webapp is a perfect showcase of the capability of the Rust + wasm platform.
## Desktop App (coming soon)

![screenshot](docs/images/screenshot-01.png)

![screenshot](docs/images/screenshot-02.png)

## Cmd App

Input and output can be given as positional arguments or as named flags:

```sh
visioncortex VTracer 0.6.0
A cmd app to convert images into vector graphics.

USAGE:
vtracer [OPTIONS] --input <input> --output <output>

FLAGS:
-h, --help Prints help information
-V, --version Prints version information

OPTIONS:
--colormode <color_mode> True color image `color` (default) or Binary image `bw`
-p, --color_precision <color_precision> Number of significant bits to use in an RGB channel
-c, --corner_threshold <corner_threshold> Minimum momentary angle (degree) to be considered a corner
-f, --filter_speckle <filter_speckle> Discard patches smaller than X px in size
-g, --gradient_step <gradient_step> Color difference between gradient layers
--hierarchical <hierarchical>
Hierarchical clustering `stacked` (default) or non-stacked `cutout`. Only applies to color mode.

-i, --input <input> Path to input raster image
-m, --mode <mode> Curver fitting mode `pixel`, `polygon`, `spline`
-o, --output <output> Path to output vector graphics
--path_precision <path_precision> Number of decimal places to use in path string
--preset <preset> Use one of the preset configs `bw`, `poster`, `photo`
-l, --segment_length <segment_length>
Perform iterative subdivide smooth until all segments are shorter than this length

-s, --splice_threshold <splice_threshold> Minimum angle displacement (degree) to splice a spline
vtracer input.jpg output.svg
# equivalent to:
vtracer --input input.jpg --output output.svg
```

Full options (flag names are kebab-case, e.g. `--filter-speckle`):

```sh
Usage: vtracer [OPTIONS] [INPUT] [OUTPUT]

Arguments:
[INPUT] Input raster image (positional; or use --input)
[OUTPUT] Output SVG (positional; or use --output)

Options:
-i, --input <INPUT> Path to the input raster image
-o, --output <OUTPUT> Path to the output SVG
--preset <PRESET> Start from a preset: bw, poster, photo
--colormode <COLORMODE> Color image `color` (default) or binary image `bw`
--hierarchical <HIERARCHICAL> Clustering: `stacked` (default) or `cutout` (seam-free mosaic)
-m, --mode <MODE> Curve-fitting mode: `pixel`, `polygon`, `spline`
-f, --filter-speckle <FILTER_SPECKLE> Discard patches smaller than X px in size (0..=128)
-p, --color-precision <COLOR_PRECISION> Significant bits per RGB channel (1..=8)
-g, --gradient-step <GRADIENT_STEP> Color difference between gradient layers (0..=255)
-c, --corner-threshold <CORNER_THRESHOLD> Minimum momentary angle (degrees) to be a corner (0..=180)
-l, --segment-length <SEGMENT_LENGTH> Subdivide until all segments are shorter than this (3.5..=10)
-s, --splice-threshold <SPLICE_THRESHOLD> Minimum angle displacement (degrees) to splice a spline (0..=180)
--path-precision <PATH_PRECISION> Decimal places to use in path coordinates
--palette <PALETTE> Fixed palette: comma-separated hex colors, e.g. '#112233,#445566'
--palette-file <PALETTE_FILE> Fixed palette from a file (hex colors, comma/newline separated)
--max-colors <MAX_COLORS> Auto-quantize to at most N colors
--optimize <OPTIMIZE> Output optimization: 0 = off, 1 = quantize+simplify, 2 = + shorthands
-h, --help Print help
-V, --version Print version
```

### New in 1.0

- **Positional arguments** — `vtracer in.png out.svg`.
- **`--hierarchical cutout`** is now a true seam-free mosaic (a gapless
tessellation with shared boundaries), replacing the old re-clustered cutout.
- **`--palette` / `--palette-file`** — snap colors to a fixed palette
(nearest in OKLab); **`--max-colors`** auto-quantizes the palette.
- **`--optimize`** — output size passes (coordinate quantization, redundant-
point removal, relative/shorthand path encoding).

## Downloads

You can download pre-built binaries from [Releases](https://github.com/visioncortex/vtracer/releases).

You can also install the program from source from [crates.io/vtracer](https://crates.io/crates/vtracer):

```sh
cargo install vtracer
cargo install vtracer-cli
```

> You are strongly advised to not download from any other third-party sources

### Usage

```sh
./vtracer --input input.jpg --output output.svg
# simplest form
./vtracer input.jpg output.svg

# black & white line art
./vtracer input.jpg output.svg --preset bw

# seam-free mosaic (gapless tessellation)
./vtracer input.jpg output.svg --hierarchical cutout

# constrain to a fixed palette
./vtracer input.jpg output.svg --palette '#1b1b1b,#e0c088,#5a7d3c,#8fb0d0'
```

### Rust Library
Expand All @@ -99,26 +124,43 @@ cargo add vtracer

### Python Library

Since `0.6`, [`vtracer`](https://pypi.org/project/vtracer/) is also packaged as Python native extensions, thanks to the awesome [pyo3](https://github.com/PyO3/pyo3) project.
[`vtracer`](https://pypi.org/project/vtracer/) is also packaged as a Python native extension (built with [pyo3](https://github.com/PyO3/pyo3) + [maturin](https://www.maturin.rs), from the `crates/vtracer-py` crate).

```sh
pip install vtracer
```

## In the wild
```python
import vtracer

# one-liners
vtracer.convert_file("in.png", "out.svg")
svg = vtracer.convert_bytes(open("in.png", "rb").read())

# rich, reusable config + presets
cfg = vtracer.Config(mode="polygon", hierarchical="cutout")
cfg.palette = ["#1b1b1b", "#e0c088", "#5a7d3c"]
svg = cfg.convert_bytes(data)
vtracer.Config.poster().convert_file("photo.jpg", "poster.svg")
```

See [`crates/vtracer-py`](crates/vtracer-py/README.md) for the full API.

### Node.js Library

[`@visioncortex/vtracer`](https://www.npmjs.com/package/@visioncortex/vtracer) is available for Node as a WebAssembly build (from the [`nodejs`](nodejs/README.md) package) — image decoding and vectorization both run in wasm, so there is **no native dependency**.

VTracer is used by the following products (open a PR to add yours):
```sh
npm install @visioncortex/vtracer
```

<table>
<tbody>
<tr>
<td><a href="https://logo.aliyun.com/logo#/name"><img src="docs/images/aliyun-logo.png" width="250"/></a>
<br>Smart logo design
</td>
<td></td>
</tr>
</tbody>
</table>
```js
const vtracer = require('@visioncortex/vtracer');

await vtracer.convertFile('in.png', 'out.svg', { mode: 'polygon' });
const svg = vtracer.convertBuffer(buffer, { preset: 'poster' });
const svg2 = vtracer.convertPixels(rgba, width, height, { colorMode: 'bw' });
```

## Citations

Expand All @@ -129,29 +171,3 @@ VTracer has since been cited by a few academic papers in computer graphics / vis
+ arXiv 2023 [StarVector: Generating Scalable Vector Graphics Code from Images](https://arxiv.org/abs/2312.11556)
+ arXiv 2024 [Text-Based Reasoning About Vector Graphics](https://arxiv.org/abs/2404.06479)
+ arXiv 2024 [Delving into LLMs' visual understanding ability using SVG to bridge image and text](https://openreview.net/pdf?id=pwlm6Po61I)

## How did VTracer come about?

> The following content is an excerpt from my [unpublished memoir](https://github.com/visioncortex/memoir).

At my teenage, two open source projects in the vector graphics space inspired me the most: Potrace and Anti-Grain Geometry (AGG).

Many years later, in 2020, I was developing a video processing engine. And it became evident that it requires way more investment to be commercially viable. So before abandoning the project, I wanted to publish *something* as open-source for posterity. At that time, I already developed a prototype vector graphics tracer. It can convert high-resolution scans of hand-drawn blueprints into vectors. But it can only process black and white images, and can only output polygons, not splines.

The plan was to fully develop the vectorizer: to handle color images and output splines. I recruited a very talented intern, [@shpun817](https://github.com/shpun817), to work on VTracer. I grafted the frontend of the video processing engine - the ["The Clustering Algorithm"](https://www.visioncortex.org/impression-docs#the-clustering-algorithm) as the pre-processor.

Three months later, we published the first version on Reddit. Out of my surprise, the response of such an underwhelming project was overwhelming.

## What's next?

There are several things in my mind:

1. Path simplification. Implement a post-process filter to the output paths to further reduce the number of splines.

2. Perfect cut-out mode. Right now in cut-out mode, the shapes do not share boundaries perfectly, but have seams.

3. Pencil tracing. Instead of tracing shapes as closed paths, may be we can attempt to skeletonize the shapes as open paths. The output would be clean, fixed width strokes.

4. Image cleaning. Right now the tracer works best on losslessly compressed pngs. If an image suffered from jpeg noises, it could impact the tracing quality. We might be able to develop a pre-filtering pass that denoises the input.

If you are interested in working on them or willing to sponsor its development, feel free to get in touch.
3 changes: 0 additions & 3 deletions cmdapp/.gitignore

This file was deleted.

Loading
Loading