nbiish commited on
Commit
bb16d68
·
unverified ·
1 Parent(s): 555dd5b

Add Any2SVG - Image to SVG vectorization with MCP server support

Browse files
.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
- title: Any2svg
3
- emoji:
4
- colorFrom: yellow
5
- colorTo: gray
6
- sdk: gradio
7
- sdk_version: 6.0.1
8
- app_file: app.py
9
- pinned: false
10
- short_description: I need svg stuff for svg things
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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