Spaces:
Running
Running
import gradio as gr | |
import vtracer | |
import os | |
def create_preview_html(svg_path, image_width, image_height): | |
"""SVG를 HTML 미리보기로 변환""" | |
try: | |
with open(svg_path, 'r', encoding='utf-8') as f: | |
svg_content = f.read() | |
# SVG를 HTML로 감싸서 반환하되, 크기를 제어 | |
preview_html = f''' | |
<div style="width:100%; height:100%; background-color: white; padding: 20px; max-width: 800px; margin: 0 auto;"> | |
<div style="width:100%; max-height:600px; overflow:auto;"> | |
<svg style="width:100%; height:auto;" viewBox="0 0 {image_width} {image_height}"> | |
{svg_content[svg_content.find('>')+1:]} | |
</svg> | |
</div> | |
</div> | |
''' | |
return preview_html | |
except Exception as e: | |
print(f"미리보기 생성 실패: {str(e)}") | |
return None | |
def convert_svg_to_ai(svg_path, image_width, image_height): | |
"""SVG를 AI 파일로 변환""" | |
ai_path = svg_path.replace('.svg', '.ai') | |
# SVG 파일 읽기 | |
with open(svg_path, 'r') as svg_file: | |
svg_content = svg_file.read() | |
# AI 파일 헤더 | |
ai_header = """%!PS-Adobe-3.0 | |
%%Creator: Adobe Illustrator(TM) SVG Converter | |
%%AI8_CreatorVersion: 24.0.0 | |
%%For: VectorFlow | |
%%Title: Generated AI File | |
%%CreationDate: %(date)s | |
%%BoundingBox: 0 0 800 600 | |
%%HiResBoundingBox: 0 0 800 600 | |
%%DocumentData: Clean7Bit | |
%%LanguageLevel: 2 | |
%%DocumentNeededResources: procset Adobe_packedarray 2.0 0 | |
%%+ procset Adobe_cmykcolor 1.1 0 | |
%%+ procset Adobe_cshow 1.1 0 | |
%%+ procset Adobe_customcolor 1.0 0 | |
%%+ procset Adobe_typography_AI5 1.0 1 | |
%%+ procset Adobe_pattern_AI3 1.0 1 | |
%%+ procset Adobe_Illustrator_AI3 1.0 1 | |
%%EndComments | |
%%BeginProlog | |
""" | |
# AI 파일 생성 | |
with open(ai_path, 'w', encoding='utf-8') as ai_file: | |
ai_file.write(ai_header) | |
ai_file.write("\n%%BeginDocument\n") | |
ai_file.write("/SVGContent\n<<\n") | |
ai_file.write("/Type /SVG\n") | |
ai_file.write("/Version 1.1\n") | |
ai_file.write("/Content [\n") | |
ai_file.write(svg_content) | |
ai_file.write("\n]\n") | |
ai_file.write(">>\ndef\n") | |
ai_file.write("\n%%EndDocument\n") | |
ai_file.write("\n%%Trailer\n%%EOF\n") | |
# HTML 미리보기 생성 | |
preview_html = create_preview_html(svg_path, image_width, image_height) | |
return ai_path, preview_html | |
def convert_to_vector( | |
image, | |
save_svg, | |
save_ai, | |
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 | |
): | |
if not (save_svg or save_ai): | |
return None, None, None, None # Preview, SVG output, AI output, AI preview | |
if image is None: | |
return None, None, None, None | |
input_path = "temp_input.jpg" | |
svg_path = "svg_output.svg" | |
outputs = [] | |
preview = None | |
ai_preview = None | |
try: | |
# 입력 이미지를 임시 파일로 저장 | |
image.save(input_path) | |
# VTracer를 사용하여 이미지를 SVG로 변환 | |
vtracer.convert_image_to_svg_py( | |
input_path, | |
svg_path, | |
colormode=colormode, | |
hierarchical=hierarchical, | |
mode=mode, | |
filter_speckle=int(filter_speckle), | |
color_precision=int(color_precision), | |
layer_difference=int(layer_difference), | |
corner_threshold=int(corner_threshold), | |
length_threshold=float(length_threshold), | |
max_iterations=int(max_iterations), | |
splice_threshold=int(splice_threshold), | |
path_precision=int(path_precision) | |
) | |
# SVG 파일 처리 | |
if save_svg: | |
preview = gr.HTML(create_preview_html(svg_path, image.width, image.height)) | |
outputs.append(svg_path) | |
# AI 파일 처리 | |
if save_ai: | |
ai_path, ai_preview_html = convert_svg_to_ai(svg_path, image.width, image.height) | |
outputs.append(ai_path) | |
ai_preview = gr.HTML(ai_preview_html) | |
if not save_svg: # SVG가 선택되지 않았다면 임시 파일 삭제 | |
os.remove(svg_path) | |
return ( | |
preview, | |
outputs[0] if outputs else None, | |
outputs[1] if len(outputs) > 1 else None, | |
ai_preview | |
) | |
except Exception as e: | |
print(f"Error during conversion: {str(e)}") | |
return None, None, None, None | |
finally: | |
# 임시 파일 정리 | |
if os.path.exists(input_path): | |
try: | |
os.remove(input_path) | |
except: | |
pass | |
css = """ | |
#col-container { | |
margin: 0 auto; | |
max-width: 960px; | |
} | |
.generate-btn { | |
background: linear-gradient(90deg, #4B79A1 0%, #283E51 100%) !important; | |
border: none !important; | |
color: white !important; | |
} | |
.generate-btn:hover { | |
transform: translateY(-2px); | |
box-shadow: 0 5px 15px rgba(0,0,0,0.2); | |
} | |
.container { | |
max-width: 1200px; | |
margin: 0 auto; | |
padding: 20px; | |
} | |
.header { | |
text-align: center; | |
margin-bottom: 40px; | |
} | |
.header h1 { | |
font-size: 2.5em; | |
color: #2c3e50; | |
margin-bottom: 10px; | |
} | |
.header p { | |
color: #7f8c8d; | |
font-size: 1.2em; | |
} | |
""" | |
# Gradio 인터페이스 정의 | |
with gr.Blocks(css=css) as app: | |
with gr.Column(elem_id="col-container"): | |
gr.HTML(""" | |
<div class="header"> | |
<h1>VectorFlow ⚡</h1> | |
<p>Transform your images into professional vector graphics</p> | |
<p style="font-size: 0.9em; color: #95a5a6;">Supports JPG, PNG, WEBP → SVG, AI</p> | |
</div> | |
""") | |
with gr.Row(): | |
with gr.Column(): | |
image_input = gr.Image(type="pil", label="Upload Image") | |
with gr.Row(): | |
save_svg = gr.Checkbox(value=True, label="Save as SVG") | |
save_ai = gr.Checkbox(value=False, label="Save as AI") | |
with gr.Accordion("Advanced Settings", open=False): | |
with gr.Accordion("Clustering", open=False): | |
colormode = gr.Radio([("COLOR","color"),("B/W", "binary")], value="color", label="Color Mode", show_label=False) | |
filter_speckle = gr.Slider(0, 128, value=4, step=1, label="Filter Speckle", info="Cleaner") | |
color_precision = gr.Slider(1, 8, value=6, step=1, label="Color Precision", info="More accurate") | |
layer_difference = gr.Slider(0, 128, value=16, step=1, label="Gradient Step", info="Less layers") | |
hierarchical = gr.Radio([("STACKED","stacked"), ("CUTOUT","cutout")], value="stacked", label="Hierarchical Mode",show_label=False) | |
with gr.Accordion("Curve Fitting", open=False): | |
mode = gr.Radio([("SPLINE","spline"),("POLYGON", "polygon"), ("PIXEL","none")], value="spline", label="Mode", show_label=False) | |
corner_threshold = gr.Slider(0, 180, value=60, step=1, label="Corner Threshold", info="Smoother") | |
length_threshold = gr.Slider(3.5, 10, value=4.0, step=0.1, label="Segment Length", info="More coarse") | |
splice_threshold = gr.Slider(0, 180, value=45, step=1, label="Splice Threshold", info="Less accurate") | |
max_iterations = gr.Slider(1, 20, value=10, step=1, label="Max Iterations", visible=False) | |
path_precision = gr.Slider(1, 10, value=3, step=1, label="Path Precision", visible=False) | |
output_text = gr.Textbox(label="Selected Mode", visible=False) | |
with gr.Row(): | |
clear_button = gr.Button("Clear") | |
convert_button = gr.Button("✨ Convert", variant='primary', elem_classes=["generate-btn"]) | |
with gr.Column(): | |
with gr.Tabs(): | |
with gr.Tab("SVG Preview"): | |
preview = gr.HTML(label="SVG Preview") | |
svg_output = gr.File(label="Download SVG", visible=True) | |
with gr.Tab("AI Preview"): | |
ai_preview = gr.HTML(label="AI Preview") | |
ai_output = gr.File(label="Download AI", visible=True) | |
examples = [ | |
["examples/11.jpg", True, False], | |
["examples/02.jpg", True, False], | |
["examples/03.jpg", True, False], | |
] | |
gr.Examples( | |
examples=examples, | |
fn=convert_to_vector, | |
inputs=[ | |
image_input, | |
save_svg, | |
save_ai | |
], | |
outputs=[preview, svg_output, ai_output, ai_preview], | |
cache_examples=False, | |
run_on_click=True | |
) | |
# 이벤트 핸들러 | |
def clear_inputs(): | |
return [None] * 12 | |
def update_output_visibility(save_svg, save_ai): | |
return ( | |
gr.update(visible=save_svg), | |
gr.update(visible=save_ai), | |
gr.update(visible=save_svg), | |
gr.update(visible=save_ai) | |
) | |
# 체크박스 상태에 따른 출력 파일 컴포넌트 표시/숨김 | |
save_svg.change( | |
update_output_visibility, | |
inputs=[save_svg, save_ai], | |
outputs=[preview, ai_preview, svg_output, ai_output] | |
) | |
save_ai.change( | |
update_output_visibility, | |
inputs=[save_svg, save_ai], | |
outputs=[preview, ai_preview, svg_output, ai_output] | |
) | |
# 변환 버튼 클릭 이벤트 | |
convert_button.click( | |
convert_to_vector, | |
inputs=[ | |
image_input, | |
save_svg, | |
save_ai, | |
colormode, | |
hierarchical, | |
mode, | |
filter_speckle, | |
color_precision, | |
layer_difference, | |
corner_threshold, | |
length_threshold, | |
max_iterations, | |
splice_threshold, | |
path_precision | |
], | |
outputs=[preview, svg_output, ai_output, ai_preview] | |
) | |
# Clear 버튼 이벤트 | |
clear_button.click( | |
clear_inputs, | |
outputs=[ | |
image_input, | |
colormode, | |
hierarchical, | |
mode, | |
filter_speckle, | |
color_precision, | |
layer_difference, | |
corner_threshold, | |
length_threshold, | |
max_iterations, | |
splice_threshold, | |
path_precision | |
] | |
) | |
app.launch(debug=True) |