img2svg 0.1.6

A rust native image to SVG converter in CLI/MCP/Library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# img2svg

A high-performance image to SVG converter written in Rust. Transform raster images (PNG, JPEG, etc.) into scalable vector graphics with cubic Bézier curves, edge-aware quantization, and advanced path optimization.

[![Tests](https://img.shields.io/badge/tests-182%20passing-brightgreen)](https://github.com/yingkitw/img2svg)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)

## Why img2svg?

### The Problem

Converting raster images to vector format is essential for:
- **Scalability**: Vector graphics scale infinitely without quality loss
- **File size optimization**: Simple shapes often result in smaller SVG files than raster counterparts
- **Editability**: Vectors can be modified in design tools (Illustrator, Inkscape, Figma)
- **Web performance**: SVGs are code-based and can be optimized, animated, and styled with CSS

### Why img2svg?

| Feature | img2svg | ImageMagick | Potrace | Vector Magic |
|---------|---------|-------------|---------|--------------|
| **Pure Rust** || ❌ (C++) | ❌ (C) | ❌ (Web only) |
| **Color Support** | ✅ Full color || ❌ B&W only ||
| **Library API** || ❌ CLI only | ❌ CLI only | ❌ Web only |
| **MCP Server** |||||
| **Local Processing** |||| ❌ (Cloud) |
| **Open Source** |||||
| **Advanced Algorithms** || ⚠️ Basic | ⚠️ Basic ||

### Key Advantages

1. **Cubic Bézier Curves**: Smooth curves via least-squares fitting with Newton-Raphson reparameterization
2. **Edge-Aware Quantization**: K-means++ with perceptual color distance and Sobel edge detection
3. **Sub-pixel Accuracy**: Marching squares contour extraction with corner-aware path splitting
4. **Smart SVG Output**: `L` for lines, `C` for curves, collinear merge, thin stripe fast path
5. **Original Color Recoloring**: Regions recolored from original image for richer photo fidelity
6. **Batch Processing**: Convert entire directories with `img2svg -i dir/ -o out/`
7. **Large Image Safety**: Auto-resize images exceeding `--max-size` to prevent OOM
8. **Fast LUT Bilateral Filter**: Precomputed range-weight LUT with fixed-point arithmetic
9. **Image filter toolkit**: Optional building blocks (Canny, morphology, CLAHE, unsharp mask, thresholding, color ops) for custom preprocessing or experiments
10. **Flexible Usage**: CLI tool, Rust library, and MCP server

## Examples & Quality

See the `examples/` directory for sample conversions demonstrating quality.

### Simple Graphics (8 colors)

| Input | Output |
|-------|--------|
| ![simple.png]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/simple.png | ![simple.svg]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/simple.svg |

**Details:**
- Command: `img2svg -i https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/simple.png -o https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/simple.svg -c 8 -s 3`
- Input: 50x50 PNG (2KB) - Basic geometric shapes
- Output: SVG with clean vector paths
- Result: Perfect edges, scalable without quality loss

### Gradients (16 colors)

| Input | Output |
|-------|--------|
| ![gradient.png]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/gradient.png | ![gradient.svg]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/gradient.svg |

**Details:**
- Command: `img2svg -i https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/gradient.png -o https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/gradient.svg -c 16 -s 5`
- Input: 100x100 PNG (1KB) - Smooth gradient
- Output: SVG with banding minimized
- Result: Smooth color transitions, vector-friendly

### Medium Complexity (16 colors)

| Input | Output |
|-------|--------|
| ![medium.png]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/medium.png | ![medium.svg]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/medium.svg |

**Details:**
- Command: `img2svg -i https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/medium.png -o https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/medium.svg -c 16 -s 5`
- Input: 100x100 PNG (2KB)
- Output: SVG with clean regions
- Result: Preserves shapes, smooth curves

### Complex Illustration (16 colors)

| Input | Output |
|-------|--------|
| ![complex.png]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/complex.png | ![complex.svg]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/complex.svg |

**Details:**
- Command: `img2svg -i https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/complex.png -o https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/complex.svg -c 16 -s 5`
- Input: 200x200 PNG (6KB) - Detailed illustration
- Output: SVG with fine details preserved
- Result: Clean paths, scalable

### Very Complex (32 colors)

| Input | Output |
|-------|--------|
| ![very_complex.png]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/very_complex.png | ![very_complex.svg]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/very_complex.svg |

**Details:**
- Command: `img2svg -i https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/very_complex.png -o https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/very_complex.svg -c 32 -s 7`
- Input: 200x200 PNG (13KB) - Highly detailed
- Output: SVG with complex paths
- Result: Details preserved, clean vector output

### Photograph with Preprocessing (Lenna)

Photographs benefit from the `--preprocess` flag which applies edge-preserving smoothing and color reduction.

| Input PNG | Output SVG (with --preprocess) |
|-----------|------------------------------|
| ![lenna.png]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/lenna.png | ![lenna.svg]https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/lenna.svg |

**Comparison:**
- **Without preprocessing**: 87 KB SVG with heavy color banding
- **With preprocessing**: 28 KB SVG (67% smaller) with cleaner regions

**Details:**
- Command: `img2svg -i https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/input/lenna.png -o https://raw.githubusercontent.com/yingkitw/img2svg/main/examples/output/lenna.svg --preprocess -c 12 -t 0.15 -s 3`
- Input: 512x512 PNG - Standard test photograph
- Output: Vector version with preprocessing applied
- Result: Preprocessing significantly reduces file size and color banding for photographs

**What preprocessing does:**
- **Bilateral filtering**: Smooths flat areas while preserving edges
- **Color reduction**: Reduces color noise before quantization
- **Result**: Cleaner color regions, smaller file size, less posterization

### Comparison with Alternatives

```bash
# ImageMagick trace (often produces jagged edges)
convert input.png svg:output-imagemagick.svg

# img2svg (smooth curves, better color accuracy)
img2svg -i input.png -o img2svg.svg -c 16 -s 5
```

**Quality Differences**:
- img2svg: Smooth curves, accurate colors, compact paths
- ImageMagick: Often produces jagged edges, limited color optimization
- Potrace: B&W only, requires pre-processing for color images

## Installation

### CLI Tool

```bash
# From crates.io
cargo install img2svg

# From source
git clone https://github.com/yingkitw/img2svg.git
cd img2svg
cargo install --path .
```

### Library

Add to your `Cargo.toml`:

```toml
[dependencies]
img2svg = "0.1"
```

### MCP Server

The MCP binary is built from the same crate as the CLI (no separate directory):

```bash
git clone https://github.com/yingkitw/img2svg.git
cd img2svg
cargo install --path .
# installs img2svg and img2svg-mcp
```

## Usage

### CLI Tool

The CLI uses the **enhanced Bézier pipeline** by default (same as the `vectorize_enhanced` API). Pass **`--original`** to use the legacy pipeline (median-cut, RDP, line segments) and to apply `--threshold`, `--smooth`, `--hierarchical`, and `--advanced` as documented below.

**Supported inputs** (via the `image` crate): PNG, JPEG, GIF, BMP, ICO, TIFF, WebP, PNM, TGA, DDS, Farbfeld.

```bash
# Basic conversion
img2svg -i input.png -o output.svg

# Photo with preprocessing (recommended for photographs)
img2svg -i photo.jpg -o photo.svg --preprocess -c 12

# High-quality graphics with more colors
img2svg -i logo.png -o logo.svg -c 32 -s 7

# Simple logo with fewer colors
img2svg -i icon.png -o icon.svg -c 8 -s 2

# Batch convert all images in a directory
img2svg -i images/ -o svgs/

# Limit max dimension for very large images (default: 4096)
img2svg -i huge_photo.jpg -o output.svg --max-size 2048
```

### Options

| Option | Short | Default | Description |
|--------|-------|---------|-------------|
| `--input` | `-i` | *required* | Input image file or directory (batch mode) |
| `--output` | `-o` | auto | Output SVG file or directory |
| `--max-size` | | 4096 | Auto-resize images exceeding this dimension (prevents OOM) |
| `--preprocess` | `-p` | false | Extra edge-preserving smoothing and color reduction **before** vectorization (recommended for photos) |
| `--colors` | `-c` | 16 | Target palette size for quantization (default pipeline and `--original`) |
| `--threshold` | `-t` | 0.1 | Edge detection threshold (0.0–1.0); **`--original` only** |
| `--smooth` | `-s` | 5 | Path smoothing level (0–10); **`--original` only** |
| `--original` | | false | Legacy pipeline: median-cut, marching squares, RDP, line-segment SVG |
| `--hierarchical` | | false | Hierarchical decomposition; **`--original` only** |
| `--advanced` | `-a` | false | Layered SVG output; **`--original` only** |

The default enhanced pipeline tunes smoothing and edge-aware quantization internally (`EnhancedOptions`); use **`--original`** if you need direct control via `--threshold` and `--smooth`.

### Rust Library

There are two conversion paths:

- **`convert` / `convert_to_svg_string`** — legacy pipeline (`vectorize` + `generate_svg`), controlled by `ConversionOptions` (same behavior as CLI **`--original`**).
- **`vectorize_enhanced` + `write_enhanced_svg` / `generate_enhanced_svg`** — default high-quality pipeline (Bézier fitting, edge-aware quantization); matches the **CLI without `--original`**.

```rust
use img2svg::{
    convert, convert_to_svg_string, load_image, ConversionOptions,
    vectorize_enhanced, write_enhanced_svg, EnhancedOptions,
};
use std::path::Path;

// Legacy pipeline (CLI equivalent: --original)
let options = ConversionOptions::default();
convert(Path::new("input.png"), Path::new("output.svg"), &options)?;

// Default / enhanced pipeline (CLI default)
let image = load_image(Path::new("input.png"))?;
let opts = EnhancedOptions {
    num_colors: 16,
    ..Default::default()
};
let data = vectorize_enhanced(&image, &opts)?;
write_enhanced_svg(&data, Path::new("output.svg"))?;

// In-memory SVG string (legacy pipeline only)
let svg = convert_to_svg_string(&image, &options)?;
```

#### Image filters (optional)

The `image_filters` module is re-exported at the crate root for preprocessing and experimentation: median filter, Prewitt/Roberts edges, Canny, morphology, Otsu/adaptive thresholding, CLAHE, gamma, unsharp mask, emboss, sepia, and related color helpers. Compose these on `ImageData` before calling `vectorize_enhanced` or `convert`.

### MCP Server

The MCP (Model Context Protocol) server is built into the same codebase and allows AI assistants (like Claude Desktop) to convert images to SVG directly.

**Note:** The MCP tool invokes the library’s **`convert` / `ConversionOptions`** path (legacy pipeline: median-cut, RDP, line-segment SVG)—the same knobs as **`img2svg --original`**. It does **not** currently call the default CLI enhanced Bézier pipeline; use the **`img2svg` binary** for that behavior.

#### Installation

The MCP server binary is built automatically with the main project:

```bash
# Build both CLI and MCP server
cargo build --release

# Or install both binaries
cargo install --path .
```

The binaries will be:
- `img2svg` - CLI tool
- `img2svg-mcp` - MCP server

#### Configuration for Claude Desktop

Add to your Claude Desktop MCP configuration file:

**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "img2svg": {
      "command": "/path/to/img2svg-mcp",
      "args": []
    }
  }
}
```

Replace `/path/to/img2svg-mcp` with the full path to the installed binary:
- If installed via `cargo install`: Run `which img2svg-mcp` to find the path
- If built locally: Usually at `target/release/img2svg-mcp`

#### Usage

Once configured, restart Claude Desktop and use the tool directly in conversations:

> "Convert the image at /path/to/logo.png to SVG with 8 colors"

The MCP server provides one tool:
- `convert_image_to_svg`: Converts raster images to SVG format
  - `input_path` (required): Path to input image
  - `output_path` (required): Path for output SVG
  - `num_colors` (optional): Number of colors (1-64, default: 16)
  - `smooth_level` (optional): Smoothing level (0-10, default: 5)
  - `threshold` (optional): Edge detection threshold (0.0-1.0, default: 0.1)

## Algorithm

img2svg uses a sophisticated multi-stage pipeline (default Bézier pipeline):

1. **LUT Bilateral Filter**: Fast edge-preserving blur with precomputed range-weight LUT (two-pass for photos)
2. **Edge Detection**: Sobel gradient for edge-aware quantization boundaries
3. **Color Quantization**: K-means++ initialization + k-means refinement with perceptual color distance
4. **Majority-Vote Smoothing**: Edge-aware smoothing merges thin artifacts (adaptive: 4 passes for graphics, 2 for photos)
5. **Original Recoloring**: Each quantized region recolored with average original pixel color for richer fidelity
6. **Contour Tracing**: Marching squares on per-color binary masks produces sub-pixel-accurate boundaries
7. **Thin Stripe Fast Path**: Contours < 2px → direct SVG rectangles (preserves line patterns)
8. **Corner-Preserving Smoothing**: Gaussian smoothing that preserves sharp corners
9. **Visvalingam-Whyatt Simplification**: Area-based point removal with corner preservation
10. **Edge Snapping + Corner Injection**: Points snapped to image edges; 90° corners injected at edge transitions
11. **Cubic Bézier Fitting**: Least-squares fit with Newton-Raphson reparameterization + G1 continuity
12. **SVG Generation**: `L` for lines, `C` for curves, collinear merge, gap-filling strokes

The original pipeline (`--original`) uses median-cut quantization, RDP simplification, and line-segment SVG paths.

## Performance

img2svg is optimized for speed and memory:

- **Speed**: Typical 1000x1000 image converts in <1 second
- **Memory**: Auto-resize for large images (configurable `--max-size`, default 4096)
- **Parallelization**: Rayon parallel path processing (smooth → simplify → Bézier fit)
- **Batch Mode**: Convert entire directories in one command

Benchmarks (1000x1000px image):

| Colors | Time | Output Size |
|--------|------|-------------|
| 8 | 0.3s | 45 KB |
| 16 | 0.5s | 78 KB |
| 32 | 0.8s | 156 KB |
| 64 | 1.4s | 312 KB |

## Tips for Best Results

### For Logos and Icons
- Use fewer colors (8-16)
- Lower smoothing (2-4)
- Higher threshold (0.15-0.2)
- Results: Clean vector shapes, small file size

### For Photos

> **Best results**: Use `--preprocess` flag which applies edge-preserving smoothing and color reduction

```bash
# Recommended for photos
img2svg -i photo.jpg -o photo.svg --preprocess -c 12 -t 0.15 -s 3
```

**What preprocessing does:**
- **Bilateral filtering**: Smooths flat areas while preserving edges
- **Color reduction**: Reduces color noise before quantization
- **Result**: Cleaner regions, smaller file size, less posterization

**Without preprocessing:**
- Use fewer colors (8-12)
- Higher threshold (0.15-0.2)
- Lower smoothing (2-4)

### For Illustrations
- Medium colors (16-32)
- Medium smoothing (4-6)
- Default threshold (0.1)

### For Clip Art
- Fewer colors (4-8)
- Higher smoothing (3-5)
- Higher threshold (0.15-0.25)

## Limitations

- **Best with**: Images with clear color boundaries (logos, icons, flat illustrations)
- **Photos**: Use `--preprocess` flag for better results, but expect some loss of detail
- **Not suitable for**: Highly detailed photorealistic images with complex gradients
- For complex photos, consider keeping the original raster format

## Contributing

Contributions are welcome! Please see [TODO.md](TODO.md) for planned improvements.

## License

Apache License 2.0 - see [LICENSE](LICENSE) for details.

## Acknowledgments

- Median-cut algorithm: Paul Heckbert (1980)
- Marching squares: William E. Lorensen (1987)
- RDP algorithm: Ramer & Douglas & Peucker (1972-1973)