Spaces:
Running
Running
from flask import Flask, request, jsonify, send_file | |
from flask_cors import CORS | |
from PIL import Image | |
import vtracer | |
import io | |
import os | |
import logging | |
app = Flask(__name__) | |
CORS(app, resources={r"/convert-to-vector": {"origins": "*"}}) | |
# Configure logging | |
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') | |
def convert_to_vector(image): | |
input_path = "temp_input.png" # Use .png to support PNG inputs | |
output_svg_path = "temp_output.svg" | |
try: | |
# Save the input image | |
app.logger.debug(f"Saving input image to {input_path}") | |
image.save(input_path) | |
# Convert to SVG using VTracer | |
app.logger.debug("Converting image to SVG") | |
vtracer.convert_image_to_svg_py( | |
input_path, | |
output_svg_path, | |
colormode="color", | |
hierarchical="stacked", | |
mode="spline", | |
filter_speckle=4, | |
color_precision=6, | |
layer_difference=16, | |
corner_threshold=60, | |
length_threshold=4.0, | |
max_iterations=10, | |
splice_threshold=45, | |
path_precision=3 | |
) | |
# Read the SVG | |
app.logger.debug(f"Reading SVG from {output_svg_path}") | |
with open(output_svg_path, "r") as f: | |
svg_content = f.read() | |
# Validate SVG | |
if not svg_content.startswith('<?xml') or '<svg' not in svg_content: | |
app.logger.error(f"Invalid SVG content: {svg_content[:100]}") | |
raise ValueError("Generated SVG is invalid") | |
app.logger.debug(f"SVG content length: {len(svg_content)}, first 100 chars: {svg_content[:100]}") | |
return svg_content | |
finally: | |
# Clean up | |
app.logger.debug("Cleaning up temporary files") | |
for path in [input_path, output_svg_path]: | |
if os.path.exists(path): | |
os.remove(path) | |
def convert_to_vector_endpoint(): | |
try: | |
# Handle image data | |
if request.data: | |
app.logger.debug("Received raw image data") | |
# Try to open the image without assuming format | |
try: | |
image = Image.open(io.BytesIO(request.data)) | |
except Exception as e: | |
app.logger.error(f"Failed to open image: {str(e)}") | |
return jsonify({'error': f"Invalid image data: {str(e)}"}), 400 | |
elif 'image' in request.files: | |
app.logger.debug("Received multipart image data") | |
file = request.files['image'] | |
try: | |
image = Image.open(file) | |
except Exception as e: | |
app.logger.error(f"Failed to open image: {str(e)}") | |
return jsonify({'error': f"Invalid image data: {str(e)}"}), 400 | |
else: | |
app.logger.error("No image data provided") | |
return jsonify({'error': 'No image data provided'}), 400 | |
# Log image format and mode | |
app.logger.debug(f"Image format: {image.format}, mode: {image.mode}") | |
# Convert to RGB if needed (for vtracer compatibility) | |
if image.mode not in ['RGB', 'RGBA']: | |
image = image.convert('RGB') | |
elif image.mode == 'RGBA': | |
# Optionally convert RGBA to RGB to avoid alpha channel issues | |
image = image.convert('RGB') | |
# Process image | |
app.logger.debug("Processing image") | |
svg_content = convert_to_vector(image) | |
# Return SVG | |
app.logger.debug("Sending SVG response") | |
return send_file( | |
io.BytesIO(svg_content.encode('utf-8')), | |
mimetype='image/svg+xml', | |
as_attachment=True, | |
download_name='vector_output.svg' | |
) | |
except Exception as e: | |
app.logger.error(f"Error processing request: {str(e)}") | |
return jsonify({'error': str(e)}), 500 | |
def index(): | |
return jsonify({'status': 'Image to Vector Converter API is running'}) | |
if __name__ == '__main__': | |
app.run(host='0.0.0.0', port=7860) |