File size: 16,739 Bytes
a0c40c2
 
 
 
 
f542658
 
a0c40c2
 
f542658
a0c40c2
 
 
 
 
f542658
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a0c40c2
 
 
 
 
 
f542658
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a0c40c2
 
 
 
 
 
 
 
 
 
f542658
a0c40c2
 
f542658
a0c40c2
f542658
a0c40c2
 
 
 
 
 
 
 
 
 
 
 
 
f542658
a0c40c2
 
f542658
a0c40c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f542658
a0c40c2
f542658
a0c40c2
f542658
a0c40c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f542658
a0c40c2
 
f542658
a0c40c2
 
 
 
f542658
a0c40c2
f542658
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a0c40c2
 
f542658
a0c40c2
f542658
a0c40c2
f542658
a0c40c2
 
 
 
 
 
f542658
a0c40c2
 
 
f542658
a0c40c2
f542658
 
 
 
 
 
a0c40c2
 
 
 
 
 
 
 
 
 
f542658
a0c40c2
f542658
 
 
 
 
 
 
 
 
 
a0c40c2
 
f542658
ce52f29
a0c40c2
 
f542658
 
 
 
 
 
a0c40c2
f542658
 
 
 
 
a0c40c2
 
 
 
f542658
 
 
 
 
 
 
a0c40c2
 
 
 
 
 
 
 
 
 
ce52f29
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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
import gradio as gr
import replicate
import requests
import os
import json
import asyncio
import concurrent.futures
from io import BytesIO
from PIL import Image
from typing import List, Tuple, Dict

# ν™˜κ²½ λ³€μˆ˜μ—μ„œ 토큰 κ°€μ Έμ˜€κΈ°
REPLICATE_API_TOKEN = os.getenv("RAPI_TOKEN")
FRIENDLI_TOKEN = os.getenv("FRIENDLI_TOKEN")

# μŠ€νƒ€μΌ μ •μ˜
STYLE_TEMPLATES = {
    "3D Style (Pixar-like)": {
        "name": "3D Style",
        "description": "Pixar-esque 3D render with volumetric lighting",
        "example": "A fluffy ginger cat wearing a tiny spacesuit, floating amidst a vibrant nebula in a 3D render. The cat is gazing curiously at a swirling planet with rings made of candy. Background is filled with sparkling stars and colorful gas clouds, lit with soft, volumetric lighting. Style: Pixar-esque, highly detailed, playful. Colors: Deep blues, purples, oranges, and pinks. Rendered in Octane, 8k resolution."
    },
    "Elegant SWOT Quadrant": {
        "name": "SWOT Analysis",
        "description": "Flat-design 4-grid layout with minimal shadows",
        "example": "Elegant SWOT quadrant: flat-design 4-grid on matte-white backdrop, thin pastel separators, top-left 'Strengths' panel shows glowing shield icon and subtle motif, top-right 'Weaknesses' panel with cracked chain icon in soft crimson, bottom-left 'Opportunities' panel with sunrise-over-horizon icon in optimistic teal, bottom-right 'Threats' panel with storm-cloud & lightning icon in deep indigo, minimal shadows, no text, no watermark, 16:9, 4K"
    },
    "Colorful Mind Map": {
        "name": "Mind Map",
        "description": "Hand-drawn educational style with vibrant colors",
        "example": "A handrawn colorful mind map diagram: educational style, vibrant colors, clear hierarchy, golden ratio layout. Central concept with branching sub-topics, each branch with unique color coding, organic flowing connections, doodle-style icons for each node"
    },
    "Business Workflow": {
        "name": "Business Process",
        "description": "End-to-end business workflow with clear phases",
        "example": "A detailed hand-drawn diagram illustrating an end-to-end business workflow with Market Analysis, Strategy Development, Product Design, Implementation, and Post-Launch Review phases. Clear directional arrows, iconography for each component, vibrant educational yet professional style"
    },
    "Industrial Design": {
        "name": "Product Design",
        "description": "Sleek industrial design concept sketch",
        "example": "A sleek industrial design concept: Curved metallic body with minimal bezel, Touchscreen panel for settings, Modern matte black finish, Hand-drawn concept sketch style with annotations and dimension lines"
    },
    "3D Bubble Chart": {
        "name": "Bubble Chart",
        "description": "Clean 3D bubble visualization",
        "example": "3-D bubble chart on clean white 2Γ—2 grid, quadrant titles hidden, four translucent spheres in lime, azure, amber, magenta, gentle depth-of-field, modern consulting aesthetic, no text, 4K"
    },
    "Timeline Ribbon": {
        "name": "Timeline",
        "description": "Horizontal ribbon timeline with cyber-futuristic vibe",
        "example": "Horizontal ribbon timeline, milestone pins glowing hot pink on charcoal, year markers as circles, faint motion streaks, cyber-futuristic vibe, no text, 1920Γ—1080"
    },
    "Risk Heat Map": {
        "name": "Heat Map",
        "description": "Risk assessment heat map with gradient colors",
        "example": "Risk Heat Map: square grid, smooth gradient from mint to fire-red, cells beveled, simple legend strip hidden, long subtle shadow, sterile white frame, no text"
    },
    "Pyramid/Funnel": {
        "name": "Funnel Chart",
        "description": "Multi-layer gradient funnel visualization",
        "example": "Pyramid / Funnel: 5-layer gradient funnel narrowing downwards, top vivid sky-blue, mid mint-green, bottom sunset-orange, glass reflection, minimal background, no text"
    },
    "KPI Dashboard": {
        "name": "Dashboard",
        "description": "Dark-mode analytics dashboard with sci-fi interface",
        "example": "KPI Dashboard: Dark-mode analytic dashboard, three glass speedometers glowing neon lime, two sparkline charts under, black glass background, sci-fi interface, no text, 4K"
    },
    "Value Chain": {
        "name": "Value Chain",
        "description": "Horizontal value chain with industrial look",
        "example": "Value Chain Diagram: Horizontal value chain blocks, steel-blue gradient bars with subtle bevel, small gear icons above each segment, sleek industrial look, shadow cast, no text"
    },
    "Gantt Chart": {
        "name": "Gantt Chart",
        "description": "Hand-drawn style Gantt chart with playful colors",
        "example": "Gantt Chart: Hand-drawn style Gantt bars sketched with vibrant markers on dotted grid notebook page, sticky-note color palette, playful yet organized, perspective tilt, no text"
    },
    "Mobile App Mockup": {
        "name": "App Mockup",
        "description": "Clean wireframe for mobile app design",
        "example": "MOCKUP DESIGN: A clean hand-drawn style wireframe for a mobile app with Title screen, Login screen, Dashboard with sections, Bottom navigation bar, minimalist design with annotations"
    },
    "Flowchart": {
        "name": "Flowchart",
        "description": "Vibrant flowchart with minimalistic icons",
        "example": "FLOWCHART DESIGN: A hand-drawn style flowchart, vibrant colors, minimalistic icons showing process flow from START to END with decision points, branches, and clear directional arrows"
    }
}

def generate_prompt_with_llm(topic: str, style_example: str = None) -> str:
    """μ£Όμ œμ™€ μŠ€νƒ€μΌ 예제λ₯Ό λ°›μ•„μ„œ LLM을 μ‚¬μš©ν•΄ 이미지 ν”„λ‘¬ν”„νŠΈλ₯Ό 생성"""
    url = "https://api.friendli.ai/dedicated/v1/chat/completions"
    headers = {
        "Authorization": f"Bearer {FRIENDLI_TOKEN}",
        "Content-Type": "application/json"
    }
    
    # κ°•ν™”λœ μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈ
    system_prompt = """You are an expert image prompt engineer specializing in creating detailed, visually rich prompts for AI image generation. 

Your task is to create prompts that:
1. Are highly specific and visual, describing exact details, colors, lighting, and composition
2. Include style references (e.g., "3D render", "hand-drawn", "flat design", "industrial sketch")
3. Specify technical details like resolution, aspect ratio, or rendering style when appropriate
4. Use descriptive adjectives for materials, textures, and atmospheres
5. Avoid abstract concepts - focus on concrete visual elements

Important guidelines:
- If given a style example, adapt the topic to match that specific visual style
- Maintain the technical vocabulary and visual descriptors from the style example
- Always output ONLY the prompt without any explanation or additional text
- Make prompts between 50-150 words for optimal results"""
    
    user_message = f"Topic: {topic}"
    if style_example:
        user_message += f"\n\nStyle reference to follow:\n{style_example}\n\nAdapt the topic to match this exact visual style."
    
    payload = {
        "model": "dep89a2fld32mcm",
        "messages": [
            {
                "role": "system",
                "content": system_prompt
            },
            {
                "role": "user",
                "content": user_message
            }
        ],
        "max_tokens": 300,
        "top_p": 0.8,
        "temperature": 0.7,
        "stream": False
    }
    
    try:
        response = requests.post(url, json=payload, headers=headers)
        if response.status_code == 200:
            result = response.json()
            return result['choices'][0]['message']['content'].strip()
        else:
            return f"ν”„λ‘¬ν”„νŠΈ 생성 μ‹€νŒ¨: {response.status_code}"
    except Exception as e:
        return f"ν”„λ‘¬ν”„νŠΈ 생성 쀑 였λ₯˜ λ°œμƒ: {str(e)}"

def translate_to_english(text: str) -> str:
    """ν•œκΈ€ ν…μŠ€νŠΈλ₯Ό μ˜μ–΄λ‘œ λ²ˆμ—­ (LLM μ‚¬μš©)"""
    if not any(ord('κ°€') <= ord(char) <= ord('힣') for char in text):
        return text
    
    url = "https://api.friendli.ai/dedicated/v1/chat/completions"
    headers = {
        "Authorization": f"Bearer {FRIENDLI_TOKEN}",
        "Content-Type": "application/json"
    }
    
    payload = {
        "model": "dep89a2fld32mcm",
        "messages": [
            {
                "role": "system",
                "content": "You are a translator. Translate the given Korean text to English. Only return the translation without any explanation."
            },
            {
                "role": "user",
                "content": text
            }
        ],
        "max_tokens": 500,
        "top_p": 0.8,
        "stream": False
    }
    
    try:
        response = requests.post(url, json=payload, headers=headers)
        if response.status_code == 200:
            result = response.json()
            return result['choices'][0]['message']['content'].strip()
        else:
            return text
    except Exception as e:
        return text

def generate_image(prompt: str, seed: int = 10) -> Tuple[Image.Image, str]:
    """Replicate APIλ₯Ό μ‚¬μš©ν•΄ 이미지 생성"""
    try:
        english_prompt = translate_to_english(prompt)
        
        if not REPLICATE_API_TOKEN:
            return None, "RAPI_TOKEN ν™˜κ²½λ³€μˆ˜κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."
        
        client = replicate.Client(api_token=REPLICATE_API_TOKEN)
        
        input_params = {
            "seed": seed,
            "prompt": english_prompt,
            "speed_mode": "Extra Juiced πŸš€ (even more speed)",
            "output_quality": 80
        }
        
        output = client.run(
            "prunaai/hidream-l1-fast:17c237d753218fed0ed477cb553902b6b75735f48c128537ab829096ef3d3645",
            input=input_params
        )
        
        if output:
            if isinstance(output, str) and output.startswith('http'):
                response = requests.get(output)
                img = Image.open(BytesIO(response.content))
                return img, english_prompt
            else:
                img = Image.open(BytesIO(output.read()))
                return img, english_prompt
        else:
            return None, "이미지 생성 μ‹€νŒ¨"
            
    except Exception as e:
        return None, f"였λ₯˜: {str(e)}"

def generate_images_parallel(topic: str, selected_styles: List[str], seed: int) -> List[Dict]:
    """μ„ νƒλœ μŠ€νƒ€μΌλ“€μ— λŒ€ν•΄ λ³‘λ ¬λ‘œ 이미지 생성"""
    results = []
    
    # 각 μŠ€νƒ€μΌμ— λŒ€ν•œ ν”„λ‘¬ν”„νŠΈ 생성
    prompts = []
    for style_name in selected_styles:
        if style_name in STYLE_TEMPLATES:
            style_info = STYLE_TEMPLATES[style_name]
            prompt = generate_prompt_with_llm(topic, style_info["example"])
            prompts.append({
                "style": style_name,
                "prompt": prompt,
                "style_info": style_info
            })
    
    # λ³‘λ ¬λ‘œ 이미지 생성
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        future_to_style = {}
        for prompt_data in prompts:
            future = executor.submit(generate_image, prompt_data["prompt"], seed)
            future_to_style[future] = prompt_data
        
        for future in concurrent.futures.as_completed(future_to_style):
            prompt_data = future_to_style[future]
            try:
                img, used_prompt = future.result()
                results.append({
                    "style": prompt_data["style"],
                    "image": img,
                    "prompt": prompt_data["prompt"],
                    "used_prompt": used_prompt,
                    "success": img is not None
                })
            except Exception as e:
                results.append({
                    "style": prompt_data["style"],
                    "image": None,
                    "prompt": prompt_data["prompt"],
                    "used_prompt": str(e),
                    "success": False
                })
    
    return results

def process_multiple_styles(topic: str, selected_styles: List[str], seed: int):
    """μ—¬λŸ¬ μŠ€νƒ€μΌλ‘œ 이미지 생성 처리"""
    if not topic.strip():
        return [], "주제λ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”."
    
    if not selected_styles:
        return [], "μ΅œμ†Œ ν•˜λ‚˜μ˜ μŠ€νƒ€μΌμ„ μ„ νƒν•΄μ£Όμ„Έμš”."
    
    status = f"μ„ νƒλœ {len(selected_styles)}개 μŠ€νƒ€μΌλ‘œ 이미지 생성 쀑..."
    
    # λ³‘λ ¬λ‘œ 이미지 생성
    results = generate_images_parallel(topic, selected_styles, seed)
    
    # κ²°κ³Ό 정리
    images = []
    prompts_info = []
    
    for result in results:
        if result["success"]:
            images.append((result["image"], result["style"]))
            prompts_info.append(f"**{result['style']}**\nν”„λ‘¬ν”„νŠΈ: {result['prompt']}\n")
        else:
            prompts_info.append(f"**{result['style']}** - 생성 μ‹€νŒ¨: {result['used_prompt']}\n")
    
    final_status = f"총 {len(images)}개 이미지 생성 μ™„λ£Œ\n\n" + "\n".join(prompts_info)
    
    return images, final_status

# Gradio μΈν„°νŽ˜μ΄μŠ€ 생성
with gr.Blocks(title="AI λ©€ν‹°μŠ€νƒ€μΌ 이미지 생성기", theme=gr.themes.Soft()) as demo:
    gr.Markdown("""
    # 🎨 AI λ©€ν‹°μŠ€νƒ€μΌ 이미지 생성기
    
    주제λ₯Ό μž…λ ₯ν•˜κ³  μ›ν•˜λŠ” μŠ€νƒ€μΌμ„ μ„ νƒν•˜λ©΄, 각 μŠ€νƒ€μΌμ— λ§žλŠ” 이미지λ₯Ό λ™μ‹œμ— μƒμ„±ν•©λ‹ˆλ‹€.
    """)
    
    with gr.Row():
        with gr.Column(scale=1):
            topic_input = gr.Textbox(
                label="주제 μž…λ ₯",
                placeholder="예: 우주λ₯Ό μ—¬ν–‰ν•˜λŠ” 고양이, 미래의 λ„μ‹œ, ν˜μ‹ μ μΈ μ œν’ˆ λ””μžμΈ",
                lines=2
            )
            
            gr.Markdown("### μŠ€νƒ€μΌ 선택 (볡수 선택 κ°€λŠ₯)")
            
            # μŠ€νƒ€μΌ μ²΄ν¬λ°•μŠ€ κ·Έλ£Ή
            style_checkboxes = gr.CheckboxGroup(
                choices=list(STYLE_TEMPLATES.keys()),
                label="생성할 μŠ€νƒ€μΌ",
                value=["3D Style (Pixar-like)"],
                info="각 μŠ€νƒ€μΌμ€ κ³ μœ ν•œ μ‹œκ°μ  νŠΉμ„±μ„ κ°€μ§€κ³  μžˆμŠ΅λ‹ˆλ‹€"
            )
            
            seed_input = gr.Slider(
                minimum=1,
                maximum=100,
                value=10,
                step=1,
                label="μ‹œλ“œ κ°’ (λ™μΌν•œ μ‹œλ“œλŠ” λ™μΌν•œ 이미지 생성)"
            )
            
            generate_btn = gr.Button("πŸš€ μ„ νƒν•œ μŠ€νƒ€μΌλ‘œ 이미지 생성", variant="primary", size="lg")
            
        with gr.Column(scale=2):
            # 가러리둜 μ—¬λŸ¬ 이미지 ν‘œμ‹œ
            output_gallery = gr.Gallery(
                label="μƒμ„±λœ 이미지듀",
                show_label=True,
                elem_id="gallery",
                columns=2,
                rows=3,
                object_fit="contain",
                height="auto"
            )
            
            status_text = gr.Markdown(
                value="생성 μƒνƒœ 및 ν”„λ‘¬ν”„νŠΈ 정보가 여기에 ν‘œμ‹œλ©λ‹ˆλ‹€."
            )
    
    # μŠ€νƒ€μΌ μ„€λͺ… μ„Ήμ…˜
    with gr.Accordion("πŸ“š μŠ€νƒ€μΌ κ°€μ΄λ“œ", open=False):
        style_guide_text = "### μ‚¬μš© κ°€λŠ₯ν•œ μŠ€νƒ€μΌ:\n\n"
        for style_name, style_info in STYLE_TEMPLATES.items():
            style_guide_text += f"**{style_name}**: {style_info['description']}\n\n"
        gr.Markdown(style_guide_text)
    
    # 이미지 생성 이벀트
    generate_btn.click(
        fn=process_multiple_styles,
        inputs=[topic_input, style_checkboxes, seed_input],
        outputs=[output_gallery, status_text]
    )
    
    gr.Markdown("""
    ---
    ### πŸ’‘ μ‚¬μš© 팁:
    - **주제**: ꡬ체적일수둝 쒋은 κ²°κ³Όλ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€
    - **μŠ€νƒ€μΌ μ‘°ν•©**: μ—¬λŸ¬ μŠ€νƒ€μΌμ„ μ„ νƒν•˜λ©΄ λ‹€μ–‘ν•œ μ‹œκ°μ  ν‘œν˜„μ„ 비ꡐ할 수 μžˆμŠ΅λ‹ˆλ‹€
    - **병렬 처리**: μ„ νƒν•œ λͺ¨λ“  μŠ€νƒ€μΌμ˜ 이미지가 λ™μ‹œμ— μƒμ„±λ©λ‹ˆλ‹€
    - **μ‹œλ“œ κ°’**: λ™μΌν•œ μ‹œλ“œλ‘œ μž¬ν˜„ κ°€λŠ₯ν•œ κ²°κ³Όλ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€
    
    각 μŠ€νƒ€μΌμ€ μ „λ¬Έμ μœΌλ‘œ νλ ˆμ΄μ…˜λœ 예제λ₯Ό 기반으둜 ν”„λ‘¬ν”„νŠΈλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
    """)

# μ•± μ‹€ν–‰
if __name__ == "__main__":
    # ν™˜κ²½ λ³€μˆ˜ 확인
    if not REPLICATE_API_TOKEN:
        print("κ²½κ³ : RAPI_TOKEN ν™˜κ²½ λ³€μˆ˜κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
    if not FRIENDLI_TOKEN:
        print("κ²½κ³ : FRIENDLI_TOKEN ν™˜κ²½ λ³€μˆ˜κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
    
    demo.launch()