File size: 10,921 Bytes
4d92d66
 
 
b067401
4001c00
8cc866b
b067401
8cc866b
 
2c5175d
8cc866b
2c5175d
 
4001c00
 
 
2c5175d
8cc866b
 
 
b067401
8cc866b
b067401
473a864
4001c00
9001c78
473a864
 
9001c78
 
 
 
4001c00
 
9001c78
 
 
 
b067401
9001c78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4001c00
 
 
 
8cc866b
4001c00
b067401
8cc866b
4d92d66
4001c00
4368f06
06cc6f7
473a864
 
4368f06
 
 
 
 
 
 
 
 
 
 
 
473a864
b067401
 
9001c78
b067401
9001c78
4d92d66
473a864
 
 
b067401
4d92d66
9001c78
 
 
4d92d66
9001c78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4d92d66
9001c78
 
 
 
 
 
 
 
 
8cc866b
9001c78
8cc866b
9001c78
 
 
 
b067401
 
 
 
 
 
4d92d66
9001c78
 
b067401
4d92d66
9001c78
 
 
 
 
 
 
5b1b3d5
4d92d66
 
 
 
 
 
 
 
 
 
 
 
 
 
b067401
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4d92d66
 
473a864
4d92d66
 
 
b067401
 
 
 
4d92d66
 
 
 
 
473a864
 
 
4d92d66
 
 
 
1758655
 
4d92d66
 
 
1758655
473a864
1758655
4d92d66
 
7429c3f
5531c50
 
473a864
4d92d66
 
b067401
 
 
 
 
8cc866b
b067401
473a864
 
8bb0ce0
 
 
473a864
5b1b3d5
 
473a864
 
 
 
 
 
 
b067401
24467aa
473a864
5b1b3d5
4d92d66
473a864
5531c50
473a864
5531c50
473a864
b067401
 
 
 
 
 
5531c50
473a864
 
 
 
b067401
4d92d66
473a864
 
 
b067401
4d92d66
 
473a864
 
 
 
5531c50
473a864
 
5531c50
 
 
 
 
 
 
 
 
 
 
473a864
b067401
5531c50
 
473a864
 
 
 
4d92d66
 
 
 
 
 
 
 
 
 
 
 
473a864
4d92d66
 
473a864
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
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")
    


    # AI 파일 헤더 생성 및 파일 변환 로직 (이전과 동일)
    
    # 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:
            with open(svg_path, "r", encoding='utf-8') as f:
                svg_content = f.read()
            preview = gr.HTML(f'<svg viewBox="0 0 {image.width} {image.height}">{svg_content}</svg>')
            outputs.append(svg_path)
        
        # AI 파일 처리
        if save_ai:
            ai_path, preview_html = convert_svg_to_ai(svg_path)
            outputs.append(ai_path)
            ai_preview = 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)