Update app.py
Browse files
app.py
CHANGED
@@ -3,26 +3,117 @@ import replicate
|
|
3 |
import requests
|
4 |
import os
|
5 |
import json
|
|
|
|
|
6 |
from io import BytesIO
|
7 |
from PIL import Image
|
|
|
8 |
|
9 |
# 환경 변수에서 토큰 가져오기
|
10 |
REPLICATE_API_TOKEN = os.getenv("RAPI_TOKEN")
|
11 |
FRIENDLI_TOKEN = os.getenv("FRIENDLI_TOKEN")
|
12 |
|
13 |
-
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
url = "https://api.friendli.ai/dedicated/v1/chat/completions"
|
16 |
headers = {
|
17 |
"Authorization": f"Bearer {FRIENDLI_TOKEN}",
|
18 |
"Content-Type": "application/json"
|
19 |
}
|
20 |
|
21 |
-
#
|
22 |
-
system_prompt = """
|
23 |
-
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
payload = {
|
28 |
"model": "dep89a2fld32mcm",
|
@@ -33,11 +124,12 @@ def generate_prompt_with_llm(topic):
|
|
33 |
},
|
34 |
{
|
35 |
"role": "user",
|
36 |
-
"content":
|
37 |
}
|
38 |
],
|
39 |
-
"max_tokens":
|
40 |
"top_p": 0.8,
|
|
|
41 |
"stream": False
|
42 |
}
|
43 |
|
@@ -51,11 +143,10 @@ def generate_prompt_with_llm(topic):
|
|
51 |
except Exception as e:
|
52 |
return f"프롬프트 생성 중 오류 발생: {str(e)}"
|
53 |
|
54 |
-
def translate_to_english(text):
|
55 |
"""한글 텍스트를 영어로 번역 (LLM 사용)"""
|
56 |
-
# 간단한 한글 체크
|
57 |
if not any(ord('가') <= ord(char) <= ord('힣') for char in text):
|
58 |
-
return text
|
59 |
|
60 |
url = "https://api.friendli.ai/dedicated/v1/chat/completions"
|
61 |
headers = {
|
@@ -86,23 +177,20 @@ def translate_to_english(text):
|
|
86 |
result = response.json()
|
87 |
return result['choices'][0]['message']['content'].strip()
|
88 |
else:
|
89 |
-
return text
|
90 |
except Exception as e:
|
91 |
-
return text
|
92 |
|
93 |
-
def generate_image(prompt, seed=10):
|
94 |
"""Replicate API를 사용해 이미지 생성"""
|
95 |
try:
|
96 |
-
# 한글이 포함된 경우 영어로 번역
|
97 |
english_prompt = translate_to_english(prompt)
|
98 |
|
99 |
-
# Replicate 클라이언트 초기화
|
100 |
if not REPLICATE_API_TOKEN:
|
101 |
return None, "RAPI_TOKEN 환경변수가 설정되지 않았습니다."
|
102 |
|
103 |
client = replicate.Client(api_token=REPLICATE_API_TOKEN)
|
104 |
|
105 |
-
# 이미지 생성
|
106 |
input_params = {
|
107 |
"seed": seed,
|
108 |
"prompt": english_prompt,
|
@@ -115,65 +203,117 @@ def generate_image(prompt, seed=10):
|
|
115 |
input=input_params
|
116 |
)
|
117 |
|
118 |
-
# 이미지 데이터 읽기
|
119 |
if output:
|
120 |
-
# output이 URL인 경우
|
121 |
if isinstance(output, str) and output.startswith('http'):
|
122 |
response = requests.get(output)
|
123 |
img = Image.open(BytesIO(response.content))
|
124 |
-
return img,
|
125 |
-
# output이 바이너리 데이터인 경우
|
126 |
else:
|
127 |
img = Image.open(BytesIO(output.read()))
|
128 |
-
return img,
|
129 |
else:
|
130 |
return None, "이미지 생성 실패"
|
131 |
|
132 |
except Exception as e:
|
133 |
-
return None, f"
|
134 |
|
135 |
-
def
|
136 |
-
"""
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
|
151 |
# Gradio 인터페이스 생성
|
152 |
-
with gr.Blocks(title="AI 이미지 생성기") as demo:
|
153 |
gr.Markdown("""
|
154 |
-
# 🎨 AI 이미지 생성기
|
155 |
|
156 |
-
주제를
|
157 |
-
프롬프트는 자동 생성되거나 직접 편집할 수 있으며, 한글 프롬프트는 자동으로 영어로 번역됩니다.
|
158 |
""")
|
159 |
|
160 |
with gr.Row():
|
161 |
with gr.Column(scale=1):
|
162 |
topic_input = gr.Textbox(
|
163 |
label="주제 입력",
|
164 |
-
placeholder="예: 우주를 여행하는
|
165 |
lines=2
|
166 |
)
|
167 |
|
168 |
-
|
169 |
-
label="생성된 프롬프트 (편집 가능)",
|
170 |
-
placeholder="자동으로 생성된 프롬프트가 여기에 표시됩니다.",
|
171 |
-
lines=3
|
172 |
-
)
|
173 |
|
174 |
-
|
175 |
-
|
176 |
-
|
|
|
|
|
|
|
177 |
)
|
178 |
|
179 |
seed_input = gr.Slider(
|
@@ -184,53 +324,47 @@ with gr.Blocks(title="AI 이미지 생성기") as demo:
|
|
184 |
label="시드 값 (동일한 시드는 동일한 이미지 생성)"
|
185 |
)
|
186 |
|
187 |
-
|
188 |
-
generate_image_btn = gr.Button("이미지 생성", variant="primary")
|
189 |
|
190 |
-
with gr.Column(scale=
|
191 |
-
|
192 |
-
|
193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
)
|
195 |
|
196 |
-
status_text = gr.
|
197 |
-
label="상태
|
198 |
-
lines=2,
|
199 |
-
interactive=False
|
200 |
)
|
201 |
|
202 |
-
#
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
generate_prompt_btn.click(
|
210 |
-
fn=generate_prompt_only,
|
211 |
-
inputs=[topic_input],
|
212 |
-
outputs=[generated_prompt]
|
213 |
-
)
|
214 |
|
215 |
-
# 이미지 생성
|
216 |
-
|
217 |
-
fn=
|
218 |
-
inputs=[topic_input,
|
219 |
-
outputs=[
|
220 |
)
|
221 |
|
222 |
gr.Markdown("""
|
223 |
---
|
224 |
-
### 사용
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
- 한글 프롬프트는 자동으로 영어로 번역되어 전송됩니다.
|
232 |
-
- 시드 값을 고정하면 동일한 프롬프트로 동일한 이미지를 재생성할 수 있습니다.
|
233 |
-
- "수동 프롬프트 사용"을 체크하면 주제 기반 자동 생성을 건너뛰고 직접 입력한 프롬프트를 사용합니다.
|
234 |
""")
|
235 |
|
236 |
# 앱 실행
|
@@ -241,4 +375,4 @@ if __name__ == "__main__":
|
|
241 |
if not FRIENDLI_TOKEN:
|
242 |
print("경고: FRIENDLI_TOKEN 환경 변수가 설정되지 않았습니다.")
|
243 |
|
244 |
-
demo.launch(
|
|
|
3 |
import requests
|
4 |
import os
|
5 |
import json
|
6 |
+
import asyncio
|
7 |
+
import concurrent.futures
|
8 |
from io import BytesIO
|
9 |
from PIL import Image
|
10 |
+
from typing import List, Tuple, Dict
|
11 |
|
12 |
# 환경 변수에서 토큰 가져오기
|
13 |
REPLICATE_API_TOKEN = os.getenv("RAPI_TOKEN")
|
14 |
FRIENDLI_TOKEN = os.getenv("FRIENDLI_TOKEN")
|
15 |
|
16 |
+
# 스타일 정의
|
17 |
+
STYLE_TEMPLATES = {
|
18 |
+
"3D Style (Pixar-like)": {
|
19 |
+
"name": "3D Style",
|
20 |
+
"description": "Pixar-esque 3D render with volumetric lighting",
|
21 |
+
"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."
|
22 |
+
},
|
23 |
+
"Elegant SWOT Quadrant": {
|
24 |
+
"name": "SWOT Analysis",
|
25 |
+
"description": "Flat-design 4-grid layout with minimal shadows",
|
26 |
+
"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"
|
27 |
+
},
|
28 |
+
"Colorful Mind Map": {
|
29 |
+
"name": "Mind Map",
|
30 |
+
"description": "Hand-drawn educational style with vibrant colors",
|
31 |
+
"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"
|
32 |
+
},
|
33 |
+
"Business Workflow": {
|
34 |
+
"name": "Business Process",
|
35 |
+
"description": "End-to-end business workflow with clear phases",
|
36 |
+
"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"
|
37 |
+
},
|
38 |
+
"Industrial Design": {
|
39 |
+
"name": "Product Design",
|
40 |
+
"description": "Sleek industrial design concept sketch",
|
41 |
+
"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"
|
42 |
+
},
|
43 |
+
"3D Bubble Chart": {
|
44 |
+
"name": "Bubble Chart",
|
45 |
+
"description": "Clean 3D bubble visualization",
|
46 |
+
"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"
|
47 |
+
},
|
48 |
+
"Timeline Ribbon": {
|
49 |
+
"name": "Timeline",
|
50 |
+
"description": "Horizontal ribbon timeline with cyber-futuristic vibe",
|
51 |
+
"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"
|
52 |
+
},
|
53 |
+
"Risk Heat Map": {
|
54 |
+
"name": "Heat Map",
|
55 |
+
"description": "Risk assessment heat map with gradient colors",
|
56 |
+
"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"
|
57 |
+
},
|
58 |
+
"Pyramid/Funnel": {
|
59 |
+
"name": "Funnel Chart",
|
60 |
+
"description": "Multi-layer gradient funnel visualization",
|
61 |
+
"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"
|
62 |
+
},
|
63 |
+
"KPI Dashboard": {
|
64 |
+
"name": "Dashboard",
|
65 |
+
"description": "Dark-mode analytics dashboard with sci-fi interface",
|
66 |
+
"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"
|
67 |
+
},
|
68 |
+
"Value Chain": {
|
69 |
+
"name": "Value Chain",
|
70 |
+
"description": "Horizontal value chain with industrial look",
|
71 |
+
"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"
|
72 |
+
},
|
73 |
+
"Gantt Chart": {
|
74 |
+
"name": "Gantt Chart",
|
75 |
+
"description": "Hand-drawn style Gantt chart with playful colors",
|
76 |
+
"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"
|
77 |
+
},
|
78 |
+
"Mobile App Mockup": {
|
79 |
+
"name": "App Mockup",
|
80 |
+
"description": "Clean wireframe for mobile app design",
|
81 |
+
"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"
|
82 |
+
},
|
83 |
+
"Flowchart": {
|
84 |
+
"name": "Flowchart",
|
85 |
+
"description": "Vibrant flowchart with minimalistic icons",
|
86 |
+
"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"
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
def generate_prompt_with_llm(topic: str, style_example: str = None) -> str:
|
91 |
+
"""주제와 스타일 예제를 받아서 LLM을 사용해 이미지 프롬프트를 생성"""
|
92 |
url = "https://api.friendli.ai/dedicated/v1/chat/completions"
|
93 |
headers = {
|
94 |
"Authorization": f"Bearer {FRIENDLI_TOKEN}",
|
95 |
"Content-Type": "application/json"
|
96 |
}
|
97 |
|
98 |
+
# 강화된 시스템 프롬프트
|
99 |
+
system_prompt = """You are an expert image prompt engineer specializing in creating detailed, visually rich prompts for AI image generation.
|
100 |
+
|
101 |
+
Your task is to create prompts that:
|
102 |
+
1. Are highly specific and visual, describing exact details, colors, lighting, and composition
|
103 |
+
2. Include style references (e.g., "3D render", "hand-drawn", "flat design", "industrial sketch")
|
104 |
+
3. Specify technical details like resolution, aspect ratio, or rendering style when appropriate
|
105 |
+
4. Use descriptive adjectives for materials, textures, and atmospheres
|
106 |
+
5. Avoid abstract concepts - focus on concrete visual elements
|
107 |
+
|
108 |
+
Important guidelines:
|
109 |
+
- If given a style example, adapt the topic to match that specific visual style
|
110 |
+
- Maintain the technical vocabulary and visual descriptors from the style example
|
111 |
+
- Always output ONLY the prompt without any explanation or additional text
|
112 |
+
- Make prompts between 50-150 words for optimal results"""
|
113 |
+
|
114 |
+
user_message = f"Topic: {topic}"
|
115 |
+
if style_example:
|
116 |
+
user_message += f"\n\nStyle reference to follow:\n{style_example}\n\nAdapt the topic to match this exact visual style."
|
117 |
|
118 |
payload = {
|
119 |
"model": "dep89a2fld32mcm",
|
|
|
124 |
},
|
125 |
{
|
126 |
"role": "user",
|
127 |
+
"content": user_message
|
128 |
}
|
129 |
],
|
130 |
+
"max_tokens": 300,
|
131 |
"top_p": 0.8,
|
132 |
+
"temperature": 0.7,
|
133 |
"stream": False
|
134 |
}
|
135 |
|
|
|
143 |
except Exception as e:
|
144 |
return f"프롬프트 생성 중 오류 발생: {str(e)}"
|
145 |
|
146 |
+
def translate_to_english(text: str) -> str:
|
147 |
"""한글 텍스트를 영어로 번역 (LLM 사용)"""
|
|
|
148 |
if not any(ord('가') <= ord(char) <= ord('힣') for char in text):
|
149 |
+
return text
|
150 |
|
151 |
url = "https://api.friendli.ai/dedicated/v1/chat/completions"
|
152 |
headers = {
|
|
|
177 |
result = response.json()
|
178 |
return result['choices'][0]['message']['content'].strip()
|
179 |
else:
|
180 |
+
return text
|
181 |
except Exception as e:
|
182 |
+
return text
|
183 |
|
184 |
+
def generate_image(prompt: str, seed: int = 10) -> Tuple[Image.Image, str]:
|
185 |
"""Replicate API를 사용해 이미지 생성"""
|
186 |
try:
|
|
|
187 |
english_prompt = translate_to_english(prompt)
|
188 |
|
|
|
189 |
if not REPLICATE_API_TOKEN:
|
190 |
return None, "RAPI_TOKEN 환경변수가 설정되지 않았습니다."
|
191 |
|
192 |
client = replicate.Client(api_token=REPLICATE_API_TOKEN)
|
193 |
|
|
|
194 |
input_params = {
|
195 |
"seed": seed,
|
196 |
"prompt": english_prompt,
|
|
|
203 |
input=input_params
|
204 |
)
|
205 |
|
|
|
206 |
if output:
|
|
|
207 |
if isinstance(output, str) and output.startswith('http'):
|
208 |
response = requests.get(output)
|
209 |
img = Image.open(BytesIO(response.content))
|
210 |
+
return img, english_prompt
|
|
|
211 |
else:
|
212 |
img = Image.open(BytesIO(output.read()))
|
213 |
+
return img, english_prompt
|
214 |
else:
|
215 |
return None, "이미지 생성 실패"
|
216 |
|
217 |
except Exception as e:
|
218 |
+
return None, f"오류: {str(e)}"
|
219 |
|
220 |
+
def generate_images_parallel(topic: str, selected_styles: List[str], seed: int) -> List[Dict]:
|
221 |
+
"""선택된 스타일들에 대해 병렬로 이미지 생성"""
|
222 |
+
results = []
|
223 |
+
|
224 |
+
# 각 스타일에 대한 프롬프트 생성
|
225 |
+
prompts = []
|
226 |
+
for style_name in selected_styles:
|
227 |
+
if style_name in STYLE_TEMPLATES:
|
228 |
+
style_info = STYLE_TEMPLATES[style_name]
|
229 |
+
prompt = generate_prompt_with_llm(topic, style_info["example"])
|
230 |
+
prompts.append({
|
231 |
+
"style": style_name,
|
232 |
+
"prompt": prompt,
|
233 |
+
"style_info": style_info
|
234 |
+
})
|
235 |
+
|
236 |
+
# 병렬로 이미지 생성
|
237 |
+
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
|
238 |
+
future_to_style = {}
|
239 |
+
for prompt_data in prompts:
|
240 |
+
future = executor.submit(generate_image, prompt_data["prompt"], seed)
|
241 |
+
future_to_style[future] = prompt_data
|
242 |
+
|
243 |
+
for future in concurrent.futures.as_completed(future_to_style):
|
244 |
+
prompt_data = future_to_style[future]
|
245 |
+
try:
|
246 |
+
img, used_prompt = future.result()
|
247 |
+
results.append({
|
248 |
+
"style": prompt_data["style"],
|
249 |
+
"image": img,
|
250 |
+
"prompt": prompt_data["prompt"],
|
251 |
+
"used_prompt": used_prompt,
|
252 |
+
"success": img is not None
|
253 |
+
})
|
254 |
+
except Exception as e:
|
255 |
+
results.append({
|
256 |
+
"style": prompt_data["style"],
|
257 |
+
"image": None,
|
258 |
+
"prompt": prompt_data["prompt"],
|
259 |
+
"used_prompt": str(e),
|
260 |
+
"success": False
|
261 |
+
})
|
262 |
+
|
263 |
+
return results
|
264 |
+
|
265 |
+
def process_multiple_styles(topic: str, selected_styles: List[str], seed: int):
|
266 |
+
"""여러 스타일로 이미지 생성 처리"""
|
267 |
+
if not topic.strip():
|
268 |
+
return [], "주제를 입력해주세요."
|
269 |
+
|
270 |
+
if not selected_styles:
|
271 |
+
return [], "최소 하나의 스타일을 선택해주세요."
|
272 |
+
|
273 |
+
status = f"선택된 {len(selected_styles)}개 스타일로 이미지 생성 중..."
|
274 |
+
|
275 |
+
# 병렬로 이미지 생성
|
276 |
+
results = generate_images_parallel(topic, selected_styles, seed)
|
277 |
+
|
278 |
+
# 결과 정리
|
279 |
+
images = []
|
280 |
+
prompts_info = []
|
281 |
+
|
282 |
+
for result in results:
|
283 |
+
if result["success"]:
|
284 |
+
images.append((result["image"], result["style"]))
|
285 |
+
prompts_info.append(f"**{result['style']}**\n프롬프트: {result['prompt']}\n")
|
286 |
+
else:
|
287 |
+
prompts_info.append(f"**{result['style']}** - 생성 실패: {result['used_prompt']}\n")
|
288 |
+
|
289 |
+
final_status = f"총 {len(images)}개 이미지 생성 완료\n\n" + "\n".join(prompts_info)
|
290 |
+
|
291 |
+
return images, final_status
|
292 |
|
293 |
# Gradio 인터페이스 생성
|
294 |
+
with gr.Blocks(title="AI 멀티스타일 이미지 생성기", theme=gr.themes.Soft()) as demo:
|
295 |
gr.Markdown("""
|
296 |
+
# 🎨 AI 멀티스타일 이미지 생성기
|
297 |
|
298 |
+
주제를 입력하고 원하는 스타일을 선택하면, 각 스타일에 맞는 이미지를 동시에 생성합니다.
|
|
|
299 |
""")
|
300 |
|
301 |
with gr.Row():
|
302 |
with gr.Column(scale=1):
|
303 |
topic_input = gr.Textbox(
|
304 |
label="주제 입력",
|
305 |
+
placeholder="예: 우주를 여행하는 고양이, 미래의 도시, 혁신적인 제품 디자인",
|
306 |
lines=2
|
307 |
)
|
308 |
|
309 |
+
gr.Markdown("### 스타일 선택 (복수 선택 가능)")
|
|
|
|
|
|
|
|
|
310 |
|
311 |
+
# 스타일 체크박스 그룹
|
312 |
+
style_checkboxes = gr.CheckboxGroup(
|
313 |
+
choices=list(STYLE_TEMPLATES.keys()),
|
314 |
+
label="생성할 스타일",
|
315 |
+
value=["3D Style (Pixar-like)"],
|
316 |
+
info="각 스타일은 고유한 시각적 특성을 가지고 있습니다"
|
317 |
)
|
318 |
|
319 |
seed_input = gr.Slider(
|
|
|
324 |
label="시드 값 (동일한 시드는 동일한 이미지 생성)"
|
325 |
)
|
326 |
|
327 |
+
generate_btn = gr.Button("🚀 선택한 스타일로 이미지 생성", variant="primary", size="lg")
|
|
|
328 |
|
329 |
+
with gr.Column(scale=2):
|
330 |
+
# 갤러리로 여러 이미지 표시
|
331 |
+
output_gallery = gr.Gallery(
|
332 |
+
label="생성된 이미지들",
|
333 |
+
show_label=True,
|
334 |
+
elem_id="gallery",
|
335 |
+
columns=2,
|
336 |
+
rows=3,
|
337 |
+
object_fit="contain",
|
338 |
+
height="auto"
|
339 |
)
|
340 |
|
341 |
+
status_text = gr.Markdown(
|
342 |
+
label="생성 상태 및 프롬프트 정보"
|
|
|
|
|
343 |
)
|
344 |
|
345 |
+
# 스타일 설명 섹션
|
346 |
+
with gr.Accordion("📚 스타일 가이드", open=False):
|
347 |
+
style_guide_text = "### 사용 가능한 스타일:\n\n"
|
348 |
+
for style_name, style_info in STYLE_TEMPLATES.items():
|
349 |
+
style_guide_text += f"**{style_name}**: {style_info['description']}\n\n"
|
350 |
+
gr.Markdown(style_guide_text)
|
|
|
|
|
|
|
|
|
|
|
|
|
351 |
|
352 |
+
# 이미지 생성 이벤트
|
353 |
+
generate_btn.click(
|
354 |
+
fn=process_multiple_styles,
|
355 |
+
inputs=[topic_input, style_checkboxes, seed_input],
|
356 |
+
outputs=[output_gallery, status_text]
|
357 |
)
|
358 |
|
359 |
gr.Markdown("""
|
360 |
---
|
361 |
+
### 💡 사용 팁:
|
362 |
+
- **주제**: 구체적일수록 좋은 결과를 얻을 수 있습니다
|
363 |
+
- **스타일 조합**: 여러 스타일을 선택하면 다양한 시각적 표현을 비교할 수 있습니다
|
364 |
+
- **병렬 처리**: 선택한 모든 스타일의 이미지가 동시에 생성됩니다
|
365 |
+
- **시드 값**: 동일한 시드로 재현 가능한 결과를 얻을 수 있습니다
|
366 |
+
|
367 |
+
각 스타일은 전문적으로 큐레이션된 예제를 기반으로 프롬프트를 생성합니다.
|
|
|
|
|
|
|
368 |
""")
|
369 |
|
370 |
# 앱 실행
|
|
|
375 |
if not FRIENDLI_TOKEN:
|
376 |
print("경고: FRIENDLI_TOKEN 환경 변수가 설정되지 않았습니다.")
|
377 |
|
378 |
+
demo.launch(share
|