nbiish commited on
Add Any2SVG - Image to SVG vectorization with MCP server support
Browse files- .gitignore +19 -0
- README.md +59 -13
- app.py +275 -0
- dna/__init__.py +8 -0
- dna/atoms/__init__.py +21 -0
- dna/atoms/vectorizer.py +182 -0
- dna/molecules/__init__.py +3 -0
- dna/molecules/mcp_handler.py +84 -0
- dna/proteins/__init__.py +3 -0
- knowledge-base/architecture.md +55 -0
- knowledge-base/mcp-usage.md +72 -0
- requirements.txt +4 -0
.gitignore
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Output
|
| 2 |
+
output/
|
| 3 |
+
*.svg
|
| 4 |
+
|
| 5 |
+
# Python
|
| 6 |
+
__pycache__/
|
| 7 |
+
*.py[cod]
|
| 8 |
+
*$py.class
|
| 9 |
+
.venv/
|
| 10 |
+
venv/
|
| 11 |
+
.env
|
| 12 |
+
|
| 13 |
+
# IDE
|
| 14 |
+
.vscode/
|
| 15 |
+
.idea/
|
| 16 |
+
|
| 17 |
+
# OS
|
| 18 |
+
.DS_Store
|
| 19 |
+
Thumbs.db
|
README.md
CHANGED
|
@@ -1,13 +1,59 @@
|
|
| 1 |
-
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Any2SVG - Image to SVG Vectorization Tool
|
| 2 |
+
|
| 3 |
+
A Gradio 6 application that converts raster images (PNG, JPG, WebP, etc.) to SVG vector graphics. Deployable as a Hugging Face Space and usable as an MCP server.
|
| 4 |
+
|
| 5 |
+
## Features
|
| 6 |
+
|
| 7 |
+
- **Multi-format Support**: Accepts PNG, JPG, JPEG, WebP, BMP, GIF, TIFF
|
| 8 |
+
- **High-Quality Vectorization**: Uses vtracer for professional-grade conversion
|
| 9 |
+
- **Configurable Parameters**: Control color mode, path precision, and more
|
| 10 |
+
- **MCP Server Ready**: Built-in MCP server support for AI agent integration
|
| 11 |
+
- **Output Directory Support**: Save SVGs to a configurable output directory
|
| 12 |
+
|
| 13 |
+
## Installation
|
| 14 |
+
|
| 15 |
+
```bash
|
| 16 |
+
cd any-to-svg
|
| 17 |
+
pip install -r requirements.txt
|
| 18 |
+
```
|
| 19 |
+
|
| 20 |
+
## Usage
|
| 21 |
+
|
| 22 |
+
### As a Web Application
|
| 23 |
+
|
| 24 |
+
```bash
|
| 25 |
+
python app.py
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
### As an MCP Server
|
| 29 |
+
|
| 30 |
+
The app automatically exposes an MCP endpoint at:
|
| 31 |
+
```
|
| 32 |
+
http://localhost:7860/gradio_api/mcp/sse
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
Add to your MCP client config:
|
| 36 |
+
```json
|
| 37 |
+
{
|
| 38 |
+
"mcpServers": {
|
| 39 |
+
"any2svg": {
|
| 40 |
+
"url": "http://localhost:7860/gradio_api/mcp/sse"
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
### Environment Variables
|
| 47 |
+
|
| 48 |
+
- `SVG_OUTPUT_DIR`: Directory to save generated SVG files (default: `./output`)
|
| 49 |
+
- `GRADIO_MCP_SERVER`: Set to `true` to enable MCP server mode
|
| 50 |
+
|
| 51 |
+
## Deployment to Hugging Face Spaces
|
| 52 |
+
|
| 53 |
+
1. Create a new Space on Hugging Face
|
| 54 |
+
2. Upload all files from this directory
|
| 55 |
+
3. The Space will automatically start as both a web app and MCP server
|
| 56 |
+
|
| 57 |
+
## License
|
| 58 |
+
|
| 59 |
+
MIT
|
app.py
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Any2SVG - Image to SVG Vectorization Tool
|
| 3 |
+
|
| 4 |
+
A Gradio 6 application that converts raster images to SVG vector graphics.
|
| 5 |
+
Supports MCP server integration for AI agent tool calling.
|
| 6 |
+
|
| 7 |
+
Purpose: Convert any raster image (PNG, JPG, WebP, etc.) to SVG format
|
| 8 |
+
Inputs: Image file, vectorization parameters
|
| 9 |
+
Outputs: SVG file path or SVG content
|
| 10 |
+
Examples: Logo vectorization, artwork conversion, icon generation
|
| 11 |
+
Edge cases: Very large images, transparent backgrounds, gradients
|
| 12 |
+
Errors: Invalid image format, file I/O errors
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
import os
|
| 16 |
+
import sys
|
| 17 |
+
from pathlib import Path
|
| 18 |
+
|
| 19 |
+
import gradio as gr
|
| 20 |
+
from PIL import Image
|
| 21 |
+
|
| 22 |
+
# Add dna to path for imports
|
| 23 |
+
sys.path.insert(0, str(Path(__file__).parent))
|
| 24 |
+
|
| 25 |
+
from dna.atoms.vectorizer import (
|
| 26 |
+
VectorizerConfig,
|
| 27 |
+
image_to_svg_file,
|
| 28 |
+
image_to_svg_string,
|
| 29 |
+
validate_image,
|
| 30 |
+
VectorizationError,
|
| 31 |
+
InvalidImageError,
|
| 32 |
+
)
|
| 33 |
+
from dna.molecules.mcp_handler import get_output_directory, process_mcp_request
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def convert_image_to_svg(
|
| 37 |
+
image: Image.Image,
|
| 38 |
+
color_mode: str = "color",
|
| 39 |
+
filter_speckle: int = 4,
|
| 40 |
+
color_precision: int = 6,
|
| 41 |
+
corner_threshold: int = 60,
|
| 42 |
+
path_precision: int = 3,
|
| 43 |
+
) -> tuple[str, str]:
|
| 44 |
+
"""
|
| 45 |
+
Convert a raster image to SVG vector graphics.
|
| 46 |
+
|
| 47 |
+
Args:
|
| 48 |
+
image: The input image to convert (PIL Image).
|
| 49 |
+
color_mode: Vectorization mode - 'color' for full color, 'binary' for black/white.
|
| 50 |
+
filter_speckle: Remove small artifacts (pixels). Higher = more filtering.
|
| 51 |
+
color_precision: Color quantization bits (1-8). Lower = fewer colors.
|
| 52 |
+
corner_threshold: Angle threshold for corner detection (degrees).
|
| 53 |
+
path_precision: Decimal precision for path coordinates.
|
| 54 |
+
|
| 55 |
+
Returns:
|
| 56 |
+
Tuple of (svg_file_path, svg_content) - path to saved SVG and its content.
|
| 57 |
+
|
| 58 |
+
Raises:
|
| 59 |
+
ValueError: If image is None or invalid.
|
| 60 |
+
IOError: If file operations fail.
|
| 61 |
+
"""
|
| 62 |
+
config = VectorizerConfig(
|
| 63 |
+
color_mode=color_mode,
|
| 64 |
+
filter_speckle=filter_speckle,
|
| 65 |
+
color_precision=color_precision,
|
| 66 |
+
corner_threshold=corner_threshold,
|
| 67 |
+
path_precision=path_precision,
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
output_dir = get_output_directory()
|
| 71 |
+
svg_path = image_to_svg_file(image, output_dir, config=config)
|
| 72 |
+
|
| 73 |
+
with open(svg_path, "r", encoding="utf-8") as f:
|
| 74 |
+
svg_content = f.read()
|
| 75 |
+
|
| 76 |
+
return str(svg_path), svg_content
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def process_image(
|
| 80 |
+
image: Image.Image,
|
| 81 |
+
color_mode: str,
|
| 82 |
+
filter_speckle: int,
|
| 83 |
+
color_precision: int,
|
| 84 |
+
corner_threshold: int,
|
| 85 |
+
path_precision: int,
|
| 86 |
+
) -> tuple[str, str, str]:
|
| 87 |
+
"""
|
| 88 |
+
Process an image and convert it to SVG format.
|
| 89 |
+
|
| 90 |
+
Args:
|
| 91 |
+
image: The input raster image to vectorize.
|
| 92 |
+
color_mode: 'color' for full color output, 'binary' for black and white.
|
| 93 |
+
filter_speckle: Speckle filter size to remove small artifacts (1-100).
|
| 94 |
+
color_precision: Color precision bits for quantization (1-8).
|
| 95 |
+
corner_threshold: Corner detection angle threshold in degrees (0-180).
|
| 96 |
+
path_precision: Decimal precision for SVG path coordinates (1-8).
|
| 97 |
+
|
| 98 |
+
Returns:
|
| 99 |
+
Tuple of (status_message, svg_file_path, svg_preview_content).
|
| 100 |
+
"""
|
| 101 |
+
try:
|
| 102 |
+
svg_path, svg_content = convert_image_to_svg(
|
| 103 |
+
image=image,
|
| 104 |
+
color_mode=color_mode,
|
| 105 |
+
filter_speckle=filter_speckle,
|
| 106 |
+
color_precision=color_precision,
|
| 107 |
+
corner_threshold=corner_threshold,
|
| 108 |
+
path_precision=path_precision,
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
status = f"✅ SVG saved to: {svg_path}"
|
| 112 |
+
return status, svg_path, svg_content
|
| 113 |
+
|
| 114 |
+
except Exception as e:
|
| 115 |
+
error_msg = f"❌ Error: {str(e)}"
|
| 116 |
+
return error_msg, "", ""
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
# Build Gradio Interface
|
| 120 |
+
with gr.Blocks(
|
| 121 |
+
title="Any2SVG - Image to SVG Converter",
|
| 122 |
+
theme=gr.themes.Soft(),
|
| 123 |
+
) as demo:
|
| 124 |
+
gr.Markdown(
|
| 125 |
+
"""
|
| 126 |
+
# 🎨 Any2SVG - Image to SVG Converter
|
| 127 |
+
|
| 128 |
+
Convert raster images (PNG, JPG, WebP, etc.) to scalable vector graphics (SVG).
|
| 129 |
+
|
| 130 |
+
**Features:**
|
| 131 |
+
- High-quality vectorization using vtracer
|
| 132 |
+
- Configurable color modes and precision
|
| 133 |
+
- MCP server support for AI agent integration
|
| 134 |
+
"""
|
| 135 |
+
)
|
| 136 |
+
|
| 137 |
+
with gr.Row():
|
| 138 |
+
with gr.Column(scale=1):
|
| 139 |
+
input_image = gr.Image(
|
| 140 |
+
label="Input Image",
|
| 141 |
+
type="pil",
|
| 142 |
+
sources=["upload", "clipboard"],
|
| 143 |
+
)
|
| 144 |
+
|
| 145 |
+
with gr.Accordion("Vectorization Settings", open=False):
|
| 146 |
+
color_mode = gr.Radio(
|
| 147 |
+
choices=["color", "binary"],
|
| 148 |
+
value="color",
|
| 149 |
+
label="Color Mode",
|
| 150 |
+
info="'color' for full color, 'binary' for black/white",
|
| 151 |
+
)
|
| 152 |
+
filter_speckle = gr.Slider(
|
| 153 |
+
minimum=1,
|
| 154 |
+
maximum=100,
|
| 155 |
+
value=4,
|
| 156 |
+
step=1,
|
| 157 |
+
label="Filter Speckle",
|
| 158 |
+
info="Remove small artifacts (higher = more filtering)",
|
| 159 |
+
)
|
| 160 |
+
color_precision = gr.Slider(
|
| 161 |
+
minimum=1,
|
| 162 |
+
maximum=8,
|
| 163 |
+
value=6,
|
| 164 |
+
step=1,
|
| 165 |
+
label="Color Precision",
|
| 166 |
+
info="Color quantization bits (lower = fewer colors)",
|
| 167 |
+
)
|
| 168 |
+
corner_threshold = gr.Slider(
|
| 169 |
+
minimum=0,
|
| 170 |
+
maximum=180,
|
| 171 |
+
value=60,
|
| 172 |
+
step=1,
|
| 173 |
+
label="Corner Threshold",
|
| 174 |
+
info="Angle threshold for corner detection (degrees)",
|
| 175 |
+
)
|
| 176 |
+
path_precision = gr.Slider(
|
| 177 |
+
minimum=1,
|
| 178 |
+
maximum=8,
|
| 179 |
+
value=3,
|
| 180 |
+
step=1,
|
| 181 |
+
label="Path Precision",
|
| 182 |
+
info="Decimal precision for path coordinates",
|
| 183 |
+
)
|
| 184 |
+
|
| 185 |
+
convert_btn = gr.Button("🔄 Convert to SVG", variant="primary", size="lg")
|
| 186 |
+
|
| 187 |
+
with gr.Column(scale=1):
|
| 188 |
+
status_output = gr.Textbox(
|
| 189 |
+
label="Status",
|
| 190 |
+
interactive=False,
|
| 191 |
+
)
|
| 192 |
+
svg_file_output = gr.File(
|
| 193 |
+
label="Download SVG",
|
| 194 |
+
)
|
| 195 |
+
svg_preview = gr.Code(
|
| 196 |
+
label="SVG Preview",
|
| 197 |
+
language="xml",
|
| 198 |
+
lines=15,
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
# Event handler
|
| 202 |
+
convert_btn.click(
|
| 203 |
+
fn=process_image,
|
| 204 |
+
inputs=[
|
| 205 |
+
input_image,
|
| 206 |
+
color_mode,
|
| 207 |
+
filter_speckle,
|
| 208 |
+
color_precision,
|
| 209 |
+
corner_threshold,
|
| 210 |
+
path_precision,
|
| 211 |
+
],
|
| 212 |
+
outputs=[status_output, svg_file_output, svg_preview],
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
# Examples
|
| 216 |
+
gr.Examples(
|
| 217 |
+
examples=[
|
| 218 |
+
["https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/280px-PNG_transparency_demonstration_1.png"],
|
| 219 |
+
],
|
| 220 |
+
inputs=[input_image],
|
| 221 |
+
label="Example Images",
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
# MCP-only tool for direct conversion
|
| 226 |
+
@gr.api(
|
| 227 |
+
inputs=[
|
| 228 |
+
gr.Image(type="pil"),
|
| 229 |
+
gr.Textbox(value="color"),
|
| 230 |
+
gr.Number(value=4),
|
| 231 |
+
gr.Number(value=6),
|
| 232 |
+
],
|
| 233 |
+
outputs=[gr.Textbox(), gr.Textbox()],
|
| 234 |
+
)
|
| 235 |
+
def image_to_svg(
|
| 236 |
+
image: Image.Image,
|
| 237 |
+
color_mode: str = "color",
|
| 238 |
+
filter_speckle: int = 4,
|
| 239 |
+
color_precision: int = 6,
|
| 240 |
+
) -> tuple[str, str]:
|
| 241 |
+
"""
|
| 242 |
+
Convert any raster image to SVG vector graphics.
|
| 243 |
+
|
| 244 |
+
This tool accepts an image and returns the path to the generated SVG file
|
| 245 |
+
along with the SVG content. The SVG is saved to the configured output directory.
|
| 246 |
+
|
| 247 |
+
Args:
|
| 248 |
+
image: The input raster image (PNG, JPG, WebP, etc.) to vectorize.
|
| 249 |
+
color_mode: Vectorization mode - 'color' for full color, 'binary' for B&W.
|
| 250 |
+
filter_speckle: Speckle filter size (1-100). Higher removes more artifacts.
|
| 251 |
+
color_precision: Color precision bits (1-8). Lower means fewer colors.
|
| 252 |
+
|
| 253 |
+
Returns:
|
| 254 |
+
Tuple of (svg_file_path, svg_content).
|
| 255 |
+
"""
|
| 256 |
+
svg_path, svg_content = convert_image_to_svg(
|
| 257 |
+
image=image,
|
| 258 |
+
color_mode=color_mode,
|
| 259 |
+
filter_speckle=int(filter_speckle),
|
| 260 |
+
color_precision=int(color_precision),
|
| 261 |
+
)
|
| 262 |
+
return svg_path, svg_content
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
if __name__ == "__main__":
|
| 266 |
+
# Ensure output directory exists
|
| 267 |
+
get_output_directory()
|
| 268 |
+
|
| 269 |
+
# Launch with MCP server enabled
|
| 270 |
+
demo.launch(
|
| 271 |
+
mcp_server=True,
|
| 272 |
+
server_name="0.0.0.0",
|
| 273 |
+
server_port=7860,
|
| 274 |
+
share=False,
|
| 275 |
+
)
|
dna/__init__.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
DNA Architecture - Any2SVG core components.
|
| 3 |
+
|
| 4 |
+
Layers:
|
| 5 |
+
- atoms: Core utilities, data types, state management
|
| 6 |
+
- molecules: Composite components, deployment scripts
|
| 7 |
+
- proteins: High-level flows and orchestration
|
| 8 |
+
"""
|
dna/atoms/__init__.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Atoms - Core utilities and data types for Any2SVG.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from .vectorizer import (
|
| 6 |
+
VectorizerConfig,
|
| 7 |
+
VectorizationError,
|
| 8 |
+
InvalidImageError,
|
| 9 |
+
validate_image,
|
| 10 |
+
image_to_svg_string,
|
| 11 |
+
image_to_svg_file,
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
__all__ = [
|
| 15 |
+
"VectorizerConfig",
|
| 16 |
+
"VectorizationError",
|
| 17 |
+
"InvalidImageError",
|
| 18 |
+
"validate_image",
|
| 19 |
+
"image_to_svg_string",
|
| 20 |
+
"image_to_svg_file",
|
| 21 |
+
]
|
dna/atoms/vectorizer.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Vectorizer Atom - Core image to SVG conversion utilities.
|
| 3 |
+
|
| 4 |
+
Purpose: Provide low-level vectorization functions using vtracer
|
| 5 |
+
Inputs: PIL Image, vectorization parameters
|
| 6 |
+
Outputs: SVG content string or file path
|
| 7 |
+
Examples: convert_to_svg(image, colormode="color")
|
| 8 |
+
Edge cases: Transparent images, very large files, corrupted images
|
| 9 |
+
Errors: InvalidImageError, VectorizationError, IOError
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import os
|
| 13 |
+
import tempfile
|
| 14 |
+
import uuid
|
| 15 |
+
from dataclasses import dataclass
|
| 16 |
+
from pathlib import Path
|
| 17 |
+
from typing import Optional
|
| 18 |
+
|
| 19 |
+
import vtracer
|
| 20 |
+
from PIL import Image
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class VectorizationError(Exception):
|
| 24 |
+
"""Raised when vectorization fails."""
|
| 25 |
+
pass
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class InvalidImageError(Exception):
|
| 29 |
+
"""Raised when input image is invalid."""
|
| 30 |
+
pass
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
@dataclass
|
| 34 |
+
class VectorizerConfig:
|
| 35 |
+
"""Configuration for vectorization parameters."""
|
| 36 |
+
color_mode: str = "color" # "color" or "binary"
|
| 37 |
+
hierarchical: str = "stacked" # "stacked" or "cutout"
|
| 38 |
+
filter_speckle: int = 4 # 1-100
|
| 39 |
+
color_precision: int = 6 # 1-8
|
| 40 |
+
layer_difference: int = 16
|
| 41 |
+
corner_threshold: int = 60 # 0-180 degrees
|
| 42 |
+
length_threshold: float = 4.0
|
| 43 |
+
max_iterations: int = 10
|
| 44 |
+
splice_threshold: int = 45 # degrees
|
| 45 |
+
path_precision: int = 3 # 1-8
|
| 46 |
+
|
| 47 |
+
def validate(self) -> None:
|
| 48 |
+
"""Validate configuration parameters."""
|
| 49 |
+
if self.color_mode not in ("color", "binary"):
|
| 50 |
+
raise ValueError(f"Invalid color_mode: {self.color_mode}")
|
| 51 |
+
if self.hierarchical not in ("stacked", "cutout"):
|
| 52 |
+
raise ValueError(f"Invalid hierarchical: {self.hierarchical}")
|
| 53 |
+
if not 1 <= self.filter_speckle <= 100:
|
| 54 |
+
raise ValueError(f"filter_speckle must be 1-100: {self.filter_speckle}")
|
| 55 |
+
if not 1 <= self.color_precision <= 8:
|
| 56 |
+
raise ValueError(f"color_precision must be 1-8: {self.color_precision}")
|
| 57 |
+
if not 0 <= self.corner_threshold <= 180:
|
| 58 |
+
raise ValueError(f"corner_threshold must be 0-180: {self.corner_threshold}")
|
| 59 |
+
if not 1 <= self.path_precision <= 8:
|
| 60 |
+
raise ValueError(f"path_precision must be 1-8: {self.path_precision}")
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def validate_image(image: Optional[Image.Image]) -> Image.Image:
|
| 64 |
+
"""
|
| 65 |
+
Validate and prepare image for vectorization.
|
| 66 |
+
|
| 67 |
+
Args:
|
| 68 |
+
image: PIL Image to validate
|
| 69 |
+
|
| 70 |
+
Returns:
|
| 71 |
+
Validated PIL Image
|
| 72 |
+
|
| 73 |
+
Raises:
|
| 74 |
+
InvalidImageError: If image is None or invalid
|
| 75 |
+
"""
|
| 76 |
+
if image is None:
|
| 77 |
+
raise InvalidImageError("No image provided")
|
| 78 |
+
|
| 79 |
+
if not isinstance(image, Image.Image):
|
| 80 |
+
raise InvalidImageError(f"Expected PIL Image, got {type(image)}")
|
| 81 |
+
|
| 82 |
+
# Convert RGBA to RGB with white background if needed for binary mode
|
| 83 |
+
if image.mode == "RGBA":
|
| 84 |
+
background = Image.new("RGBA", image.size, (255, 255, 255, 255))
|
| 85 |
+
background.paste(image, mask=image.split()[3])
|
| 86 |
+
image = background.convert("RGB")
|
| 87 |
+
elif image.mode not in ("RGB", "L", "P"):
|
| 88 |
+
image = image.convert("RGB")
|
| 89 |
+
|
| 90 |
+
return image
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def image_to_svg_string(
|
| 94 |
+
image: Image.Image,
|
| 95 |
+
config: Optional[VectorizerConfig] = None,
|
| 96 |
+
) -> str:
|
| 97 |
+
"""
|
| 98 |
+
Convert PIL Image to SVG string.
|
| 99 |
+
|
| 100 |
+
Args:
|
| 101 |
+
image: PIL Image to convert
|
| 102 |
+
config: Vectorization configuration
|
| 103 |
+
|
| 104 |
+
Returns:
|
| 105 |
+
SVG content as string
|
| 106 |
+
|
| 107 |
+
Raises:
|
| 108 |
+
InvalidImageError: If image is invalid
|
| 109 |
+
VectorizationError: If conversion fails
|
| 110 |
+
"""
|
| 111 |
+
config = config or VectorizerConfig()
|
| 112 |
+
config.validate()
|
| 113 |
+
image = validate_image(image)
|
| 114 |
+
|
| 115 |
+
# Create temp files for vtracer
|
| 116 |
+
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_in:
|
| 117 |
+
input_path = tmp_in.name
|
| 118 |
+
image.save(input_path, "PNG")
|
| 119 |
+
|
| 120 |
+
with tempfile.NamedTemporaryFile(suffix=".svg", delete=False) as tmp_out:
|
| 121 |
+
output_path = tmp_out.name
|
| 122 |
+
|
| 123 |
+
try:
|
| 124 |
+
vtracer.convert_image_to_svg_py(
|
| 125 |
+
input_path,
|
| 126 |
+
output_path,
|
| 127 |
+
colormode=config.color_mode,
|
| 128 |
+
hierarchical=config.hierarchical,
|
| 129 |
+
filter_speckle=config.filter_speckle,
|
| 130 |
+
color_precision=config.color_precision,
|
| 131 |
+
layer_difference=config.layer_difference,
|
| 132 |
+
corner_threshold=config.corner_threshold,
|
| 133 |
+
length_threshold=config.length_threshold,
|
| 134 |
+
max_iterations=config.max_iterations,
|
| 135 |
+
splice_threshold=config.splice_threshold,
|
| 136 |
+
path_precision=config.path_precision,
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
with open(output_path, "r", encoding="utf-8") as f:
|
| 140 |
+
return f.read()
|
| 141 |
+
|
| 142 |
+
except Exception as e:
|
| 143 |
+
raise VectorizationError(f"Vectorization failed: {e}") from e
|
| 144 |
+
|
| 145 |
+
finally:
|
| 146 |
+
for path in (input_path, output_path):
|
| 147 |
+
if os.path.exists(path):
|
| 148 |
+
os.unlink(path)
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def image_to_svg_file(
|
| 152 |
+
image: Image.Image,
|
| 153 |
+
output_dir: Path,
|
| 154 |
+
filename: Optional[str] = None,
|
| 155 |
+
config: Optional[VectorizerConfig] = None,
|
| 156 |
+
) -> Path:
|
| 157 |
+
"""
|
| 158 |
+
Convert PIL Image to SVG file.
|
| 159 |
+
|
| 160 |
+
Args:
|
| 161 |
+
image: PIL Image to convert
|
| 162 |
+
output_dir: Directory to save SVG
|
| 163 |
+
filename: Optional filename (auto-generated if None)
|
| 164 |
+
config: Vectorization configuration
|
| 165 |
+
|
| 166 |
+
Returns:
|
| 167 |
+
Path to saved SVG file
|
| 168 |
+
|
| 169 |
+
Raises:
|
| 170 |
+
InvalidImageError: If image is invalid
|
| 171 |
+
VectorizationError: If conversion fails
|
| 172 |
+
"""
|
| 173 |
+
svg_content = image_to_svg_string(image, config)
|
| 174 |
+
|
| 175 |
+
output_dir.mkdir(parents=True, exist_ok=True)
|
| 176 |
+
filename = filename or f"vectorized_{uuid.uuid4().hex[:8]}.svg"
|
| 177 |
+
output_path = output_dir / filename
|
| 178 |
+
|
| 179 |
+
with open(output_path, "w", encoding="utf-8") as f:
|
| 180 |
+
f.write(svg_content)
|
| 181 |
+
|
| 182 |
+
return output_path
|
dna/molecules/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Molecules - Composite components for Any2SVG.
|
| 3 |
+
"""
|
dna/molecules/mcp_handler.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
MCP Handler Molecule - MCP server integration for Any2SVG.
|
| 3 |
+
|
| 4 |
+
Purpose: Handle MCP tool requests and file management
|
| 5 |
+
Inputs: MCP tool calls with image data
|
| 6 |
+
Outputs: SVG file paths and content
|
| 7 |
+
Examples: MCP client calls image_to_svg tool
|
| 8 |
+
Edge cases: Large files, network timeouts, invalid URLs
|
| 9 |
+
Errors: MCPError, FileNotFoundError, NetworkError
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
import os
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
from typing import Optional
|
| 15 |
+
from urllib.parse import urlparse
|
| 16 |
+
|
| 17 |
+
from PIL import Image
|
| 18 |
+
|
| 19 |
+
from ..atoms.vectorizer import (
|
| 20 |
+
VectorizerConfig,
|
| 21 |
+
image_to_svg_file,
|
| 22 |
+
image_to_svg_string,
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def get_output_directory() -> Path:
|
| 27 |
+
"""
|
| 28 |
+
Get the configured SVG output directory.
|
| 29 |
+
|
| 30 |
+
Returns:
|
| 31 |
+
Path to output directory (created if needed)
|
| 32 |
+
"""
|
| 33 |
+
output_dir = Path(os.environ.get("SVG_OUTPUT_DIR", "./output"))
|
| 34 |
+
output_dir.mkdir(parents=True, exist_ok=True)
|
| 35 |
+
return output_dir
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def process_mcp_request(
|
| 39 |
+
image: Image.Image,
|
| 40 |
+
color_mode: str = "color",
|
| 41 |
+
filter_speckle: int = 4,
|
| 42 |
+
color_precision: int = 6,
|
| 43 |
+
save_to_file: bool = True,
|
| 44 |
+
) -> dict:
|
| 45 |
+
"""
|
| 46 |
+
Process an MCP tool request for image vectorization.
|
| 47 |
+
|
| 48 |
+
Args:
|
| 49 |
+
image: PIL Image to convert
|
| 50 |
+
color_mode: "color" or "binary"
|
| 51 |
+
filter_speckle: Speckle filter size (1-100)
|
| 52 |
+
color_precision: Color precision bits (1-8)
|
| 53 |
+
save_to_file: Whether to save SVG to file
|
| 54 |
+
|
| 55 |
+
Returns:
|
| 56 |
+
Dict with 'path' (if saved), 'content', and 'success' keys
|
| 57 |
+
"""
|
| 58 |
+
config = VectorizerConfig(
|
| 59 |
+
color_mode=color_mode,
|
| 60 |
+
filter_speckle=filter_speckle,
|
| 61 |
+
color_precision=color_precision,
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
result = {"success": False, "path": None, "content": None, "error": None}
|
| 65 |
+
|
| 66 |
+
try:
|
| 67 |
+
if save_to_file:
|
| 68 |
+
output_dir = get_output_directory()
|
| 69 |
+
svg_path = image_to_svg_file(image, output_dir, config=config)
|
| 70 |
+
|
| 71 |
+
with open(svg_path, "r", encoding="utf-8") as f:
|
| 72 |
+
svg_content = f.read()
|
| 73 |
+
|
| 74 |
+
result["path"] = str(svg_path)
|
| 75 |
+
result["content"] = svg_content
|
| 76 |
+
else:
|
| 77 |
+
result["content"] = image_to_svg_string(image, config)
|
| 78 |
+
|
| 79 |
+
result["success"] = True
|
| 80 |
+
|
| 81 |
+
except Exception as e:
|
| 82 |
+
result["error"] = str(e)
|
| 83 |
+
|
| 84 |
+
return result
|
dna/proteins/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Proteins - High-level flows and orchestration for Any2SVG.
|
| 3 |
+
"""
|
knowledge-base/architecture.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Any2SVG Architecture
|
| 2 |
+
|
| 3 |
+
## Overview
|
| 4 |
+
|
| 5 |
+
Any2SVG is a Gradio 6 application that converts raster images to SVG vector graphics. It's designed to work both as a standalone web application and as an MCP (Model Context Protocol) server for AI agent integration.
|
| 6 |
+
|
| 7 |
+
## DNA Architecture
|
| 8 |
+
|
| 9 |
+
The project follows the DNA architecture pattern:
|
| 10 |
+
|
| 11 |
+
```
|
| 12 |
+
any-to-svg/
|
| 13 |
+
├── app.py # Main Gradio application
|
| 14 |
+
├── dna/
|
| 15 |
+
│ ├── atoms/ # Core utilities
|
| 16 |
+
│ │ └── vectorizer.py # Low-level vtracer wrapper
|
| 17 |
+
│ ├── molecules/ # Composite components
|
| 18 |
+
│ │ └── mcp_handler.py # MCP request processing
|
| 19 |
+
│ └── proteins/ # High-level orchestration
|
| 20 |
+
├── knowledge-base/ # Documentation
|
| 21 |
+
└── output/ # Generated SVG files
|
| 22 |
+
```
|
| 23 |
+
|
| 24 |
+
## Components
|
| 25 |
+
|
| 26 |
+
### Atoms Layer
|
| 27 |
+
- `vectorizer.py`: Core vectorization using vtracer library
|
| 28 |
+
- `VectorizerConfig`: Configuration dataclass
|
| 29 |
+
- `image_to_svg_string()`: Convert to SVG string
|
| 30 |
+
- `image_to_svg_file()`: Convert and save to file
|
| 31 |
+
|
| 32 |
+
### Molecules Layer
|
| 33 |
+
- `mcp_handler.py`: MCP integration
|
| 34 |
+
- `process_mcp_request()`: Handle MCP tool calls
|
| 35 |
+
- `get_output_directory()`: Manage output paths
|
| 36 |
+
|
| 37 |
+
### Application Layer
|
| 38 |
+
- `app.py`: Gradio interface and MCP server
|
| 39 |
+
- Web UI with configurable parameters
|
| 40 |
+
- MCP tool endpoint at `/gradio_api/mcp/sse`
|
| 41 |
+
|
| 42 |
+
## MCP Integration
|
| 43 |
+
|
| 44 |
+
The application exposes an MCP tool `image_to_svg` that:
|
| 45 |
+
1. Accepts image input (URL or base64)
|
| 46 |
+
2. Converts to SVG using vtracer
|
| 47 |
+
3. Saves to configured output directory
|
| 48 |
+
4. Returns file path and SVG content
|
| 49 |
+
|
| 50 |
+
## Security Considerations
|
| 51 |
+
|
| 52 |
+
- Input validation on all image uploads
|
| 53 |
+
- Sanitized file paths for output
|
| 54 |
+
- Environment variable protection for sensitive config
|
| 55 |
+
- No arbitrary code execution
|
knowledge-base/mcp-usage.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MCP Server Usage Guide
|
| 2 |
+
|
| 3 |
+
## Connecting to Any2SVG MCP Server
|
| 4 |
+
|
| 5 |
+
### Local Development
|
| 6 |
+
|
| 7 |
+
When running locally, the MCP server is available at:
|
| 8 |
+
```
|
| 9 |
+
http://localhost:7860/gradio_api/mcp/sse
|
| 10 |
+
```
|
| 11 |
+
|
| 12 |
+
### Hugging Face Spaces
|
| 13 |
+
|
| 14 |
+
When deployed to HF Spaces:
|
| 15 |
+
```
|
| 16 |
+
https://YOUR-USERNAME-any2svg.hf.space/gradio_api/mcp/sse
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
## Client Configuration
|
| 20 |
+
|
| 21 |
+
### Claude Desktop / Cursor / Cline
|
| 22 |
+
|
| 23 |
+
Add to your MCP client config:
|
| 24 |
+
|
| 25 |
+
```json
|
| 26 |
+
{
|
| 27 |
+
"mcpServers": {
|
| 28 |
+
"any2svg": {
|
| 29 |
+
"url": "http://localhost:7860/gradio_api/mcp/sse"
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
}
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
### For SSE-incompatible clients (e.g., Claude Desktop)
|
| 36 |
+
|
| 37 |
+
Use mcp-remote:
|
| 38 |
+
|
| 39 |
+
```json
|
| 40 |
+
{
|
| 41 |
+
"mcpServers": {
|
| 42 |
+
"any2svg": {
|
| 43 |
+
"command": "npx",
|
| 44 |
+
"args": [
|
| 45 |
+
"mcp-remote",
|
| 46 |
+
"http://localhost:7860/gradio_api/mcp/sse"
|
| 47 |
+
]
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
```
|
| 52 |
+
|
| 53 |
+
## Available Tools
|
| 54 |
+
|
| 55 |
+
### image_to_svg
|
| 56 |
+
|
| 57 |
+
Convert any raster image to SVG vector graphics.
|
| 58 |
+
|
| 59 |
+
**Parameters:**
|
| 60 |
+
- `image` (required): Image URL or base64 data
|
| 61 |
+
- `color_mode` (optional): "color" or "binary" (default: "color")
|
| 62 |
+
- `filter_speckle` (optional): 1-100 (default: 4)
|
| 63 |
+
- `color_precision` (optional): 1-8 (default: 6)
|
| 64 |
+
|
| 65 |
+
**Returns:**
|
| 66 |
+
- `svg_file_path`: Path to saved SVG file
|
| 67 |
+
- `svg_content`: SVG markup string
|
| 68 |
+
|
| 69 |
+
## Environment Variables
|
| 70 |
+
|
| 71 |
+
- `SVG_OUTPUT_DIR`: Directory for saved SVG files (default: `./output`)
|
| 72 |
+
- `GRADIO_MCP_SERVER`: Set to `true` to enable MCP mode
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradio[mcp]>=5.0.0
|
| 2 |
+
vtracer>=0.6.0
|
| 3 |
+
Pillow>=10.0.0
|
| 4 |
+
numpy>=1.24.0
|