Kims12 commited on
Commit
e7c539f
ยท
verified ยท
1 Parent(s): c1b9a60

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +194 -268
app.py CHANGED
@@ -4,8 +4,8 @@ from PIL import Image
4
  import gradio as gr
5
  import logging
6
  import re
 
7
  from io import BytesIO
8
- import time
9
 
10
  from google import genai
11
  from google.genai import types
@@ -22,162 +22,111 @@ def save_binary_file(file_name, data):
22
  with open(file_name, "wb") as f:
23
  f.write(data)
24
 
25
- def translate_prompt_to_english(prompt):
26
- """
27
- ์ž…๋ ฅ๋œ ํ”„๋กฌํ”„ํŠธ์— ํ•œ๊ธ€์ด ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด Geminiโ€‘2.0โ€‘flash ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜์–ด๋กœ ๋ฒˆ์—ญํ•ฉ๋‹ˆ๋‹ค.
28
- ํ•œ๊ธ€์ด ์—†์œผ๋ฉด ์›๋ณธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
29
- ์ค‘์š”: #1, #2, #3 ํƒœ๊ทธ๋Š” ๋ฒˆ์—ญ ์ „ํ›„์— ๋ฐ˜๋“œ์‹œ ๋ณด์กด๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
30
- """
31
- if not re.search("[๊ฐ€-ํžฃ]", prompt):
32
- return prompt
33
-
34
- # #1, #2, #3 ํƒœ๊ทธ๋ฅผ ์ž„์‹œ ํ† ํฐ์œผ๋กœ ๋Œ€์ฒดํ•˜์—ฌ ๋ณด์กด
35
- prompt = prompt.replace("#1", "IMAGE_TAG_ONE")
36
- prompt = prompt.replace("#2", "IMAGE_TAG_TWO")
37
- prompt = prompt.replace("#3", "IMAGE_TAG_THREE")
38
-
39
- try:
40
- api_key = os.environ.get("GEMINI_API_KEY")
41
- if not api_key:
42
- logger.error("Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
43
- # ์ž„์‹œ ํ† ํฐ์„ ์›๋ž˜ ํƒœ๊ทธ๋กœ ๋ณต์›
44
- prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
45
- prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
46
- prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
47
- return prompt
48
-
49
- client = genai.Client(api_key=api_key)
50
- translation_prompt = f"""
51
- Translate the following Korean text to English:
52
-
53
- {prompt}
54
-
55
- IMPORTANT: The tokens IMAGE_TAG_ONE, IMAGE_TAG_TWO, and IMAGE_TAG_THREE are special tags
56
- and must be preserved exactly as is in your translation. Do not translate these tokens.
57
- """
58
-
59
- logger.info(f"Translation prompt: {translation_prompt}")
60
- response = client.models.generate_content(
61
- model="gemini-2.0-flash",
62
- contents=[translation_prompt],
63
- config=types.GenerateContentConfig(
64
- response_modalities=['Text'],
65
- temperature=0.2,
66
- top_p=0.95,
67
- top_k=40,
68
- max_output_tokens=512
69
- )
70
- )
71
-
72
- translated_text = ""
73
- for part in response.candidates[0].content.parts:
74
- if hasattr(part, 'text') and part.text:
75
- translated_text += part.text
76
-
77
- if translated_text.strip():
78
- # ๋ฒˆ์—ญ๋œ ํ…์ŠคํŠธ์—์„œ ์ž„์‹œ ํ† ํฐ์„ ์›๋ž˜ ํƒœ๊ทธ๋กœ ๋ณต์›
79
- translated_text = translated_text.replace("IMAGE_TAG_ONE", "#1")
80
- translated_text = translated_text.replace("IMAGE_TAG_TWO", "#2")
81
- translated_text = translated_text.replace("IMAGE_TAG_THREE", "#3")
82
- logger.info(f"Translated text: {translated_text.strip()}")
83
- return translated_text.strip()
84
- else:
85
- logger.warning("๋ฒˆ์—ญ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ")
86
- # ์ž„์‹œ ํ† ํฐ์„ ์›๋ž˜ ํƒœ๊ทธ๋กœ ๋ณต์›
87
- prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
88
- prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
89
- prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
90
- return prompt
91
- except Exception as e:
92
- logger.exception("๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
93
- # ์ž„์‹œ ํ† ํฐ์„ ์›๋ž˜ ํƒœ๊ทธ๋กœ ๋ณต์›
94
- prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
95
- prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
96
- prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
97
- return prompt
98
-
99
  def preprocess_prompt(prompt, image1, image2, image3):
100
  """
101
  ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ธฐ๋Šฅ ๋ช…๋ น์„ ํ•ด์„
102
  """
 
 
103
  has_img1 = image1 is not None
104
  has_img2 = image2 is not None
105
  has_img3 = image3 is not None
106
-
 
107
  if "#1" in prompt and not has_img1:
108
  prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
109
  else:
110
  prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
111
-
112
  if "#2" in prompt and not has_img2:
113
  prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
114
  else:
115
  prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
116
-
117
  if "#3" in prompt and not has_img3:
118
  prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
119
  else:
120
  prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
121
-
 
122
  if "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ" in prompt:
 
123
  desc_match = re.search(r'#1์„ "(.*?)"์œผ๋กœ ๋ฐ”๊ฟ”๋ผ', prompt)
124
  if desc_match:
125
  description = desc_match.group(1)
126
  prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ {description}์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์›๋ณธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ๋‚ด์šฉ์€ ์œ ์ง€ํ•˜๋˜ ์ƒˆ๋กœ์šด ๏ฟฝ๏ฟฝํƒ€์ผ๊ณผ ๋ถ„์œ„๊ธฐ๋กœ ์žฌํ•ด์„ํ•ด์ฃผ์„ธ์š”."
127
  else:
128
  prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
129
-
130
  elif "2. ๊ธ€์ž์ง€์šฐ๊ธฐ" in prompt:
 
131
  text_match = re.search(r'#1์—์„œ "(.*?)"๋ฅผ ์ง€์›Œ๋ผ', prompt)
132
  if text_match:
133
  text_to_remove = text_match.group(1)
134
  prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ '{text_to_remove}' ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ํ…์ŠคํŠธ๊ฐ€ ์žˆ๋˜ ๋ถ€๋ถ„์„ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ์ฑ„์›Œ์ฃผ์„ธ์š”."
135
  else:
136
  prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ ๋ชจ๋“  ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ๊น”๋”ํ•œ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
137
-
 
 
 
138
  elif "4. ์˜ท๋ฐ”๊พธ๊ธฐ" in prompt:
139
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ ์˜์ƒ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์˜์ƒ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์˜์ƒ์˜ ์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ์€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋ฅด๋˜, ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”."
140
-
 
 
 
 
141
  elif "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ" in prompt:
142
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”."
143
-
144
  elif "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)" in prompt:
145
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์™€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(๋˜๋Š” ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€)๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ , ํŠนํžˆ ์ƒํ’ˆ์ด ๋‹๋ณด์ด๋„๋ก ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•ด์ฃผ์„ธ์š”."
146
-
 
 
 
 
 
 
 
 
 
 
 
 
147
  prompt += " ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”."
 
148
  return prompt
149
 
150
- def generate_with_images(prompt, images, variation_index=0):
151
  """
152
- API ํ˜ธ์ถœ์„ ํ†ตํ•ด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
153
- variation_index๋กœ ๋‹ค์–‘ํ•œ ๋ณ€ํ™”๋ฅผ ์ค๋‹ˆ๋‹ค.
154
- ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€๋Š” ์ผ๊ด€๋œ ํฌ๊ธฐ๋กœ ์กฐ์ •๋ฉ๋‹ˆ๋‹ค.
155
  """
156
  try:
 
157
  api_key = os.environ.get("GEMINI_API_KEY")
158
  if not api_key:
159
  return None, "API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
160
-
 
161
  client = genai.Client(api_key=api_key)
162
- logger.info(f"Gemini API ์š”์ฒญ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: {prompt}, ๋ณ€ํ˜• ์ธ๋ฑ์Šค: {variation_index}")
163
-
164
- # ๋ณ€ํ˜• ์ธ๋ฑ์Šค์— ๋”ฐ๋ผ ํ”„๋กฌํ”„ํŠธ์— ์•ฝ๊ฐ„์˜ ๋ณ€ํ™” ์ถ”๊ฐ€
165
- variation_suffixes = [
166
- " ์ฒซ ๋ฒˆ์งธ ๋ณ€ํ˜•์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.",
167
- " ๋‘ ๋ฒˆ์งธ ๋ณ€ํ˜•์œผ๋กœ ๋” ์ƒ์ƒํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.",
168
- " ์„ธ ๋ฒˆ์งธ ๋ณ€ํ˜•์œผ๋กœ ๋” ์ฐฝ์˜์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.",
169
- " ๋„ค ๋ฒˆ์งธ ๋ณ€ํ˜•์œผ๋กœ ์ƒ‰๊ฐ์„ ๋” ๊ฐ•์กฐํ•ด์„œ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
170
- ]
171
 
172
- if variation_index < len(variation_suffixes):
173
- prompt = prompt + variation_suffixes[variation_index]
174
-
175
- contents = [prompt]
 
 
 
 
 
176
  for idx, img in enumerate(images, 1):
177
  if img is not None:
178
  contents.append(img)
179
  logger.info(f"์ด๋ฏธ์ง€ #{idx} ์ถ”๊ฐ€๋จ")
180
-
 
181
  response = client.models.generate_content(
182
  model="gemini-2.0-flash-exp-image-generation",
183
  contents=contents,
@@ -189,11 +138,15 @@ def generate_with_images(prompt, images, variation_index=0):
189
  max_output_tokens=8192
190
  )
191
  )
192
-
 
193
  with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
194
  temp_path = tmp.name
 
195
  result_text = ""
196
  image_found = False
 
 
197
  for part in response.candidates[0].content.parts:
198
  if hasattr(part, 'text') and part.text:
199
  result_text += part.text
@@ -202,210 +155,183 @@ def generate_with_images(prompt, images, variation_index=0):
202
  save_binary_file(temp_path, part.inline_data.data)
203
  image_found = True
204
  logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
 
205
  if not image_found:
206
  return None, f"API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ํ…์ŠคํŠธ: {result_text}"
 
 
207
  result_img = Image.open(temp_path)
208
  if result_img.mode == "RGBA":
209
  result_img = result_img.convert("RGB")
210
 
211
- # ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ 512x512๋กœ ์กฐ์ •ํ•˜์—ฌ ์ผ๊ด€๋œ ์ถœ๋ ฅ ํฌ๊ธฐ ์œ ์ง€
212
- target_size = (512, 512)
213
- result_img = result_img.resize(target_size, Image.Resampling.LANCZOS)
214
-
215
  return result_img, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
 
216
  except Exception as e:
217
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
218
  return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
219
 
220
- def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0):
221
  """
222
- 3๊ฐœ์˜ ์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ตœ์ข… ์˜์–ด ํ”„๋กฌํ”„ํŠธ(final_prompt)๋ฅผ ์ƒ์„ฑํ•œ ํ›„,
223
- API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
224
  """
225
  try:
 
226
  images = [image1, image2, image3]
227
  valid_images = [img for img in images if img is not None]
 
228
  if not valid_images:
229
- return None, "์ ์–ด๋„ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.", ""
230
-
231
- if prompt and prompt.strip():
232
- processed_prompt = preprocess_prompt(prompt, image1, image2, image3)
233
- if re.search("[๊ฐ€-ํžฃ]", processed_prompt):
234
- final_prompt = translate_prompt_to_english(processed_prompt)
235
- else:
236
- final_prompt = processed_prompt
237
- else:
238
  if len(valid_images) == 1:
239
- final_prompt = "Please creatively transform this image into a more vivid and artistic version."
240
- logger.info("Default prompt generated for single image")
241
  elif len(valid_images) == 2:
242
- final_prompt = "Please seamlessly composite these two images, integrating their key elements harmoniously into a single image."
243
- logger.info("Default prompt generated for two images")
244
  else:
245
- final_prompt = "Please creatively composite these three images, combining their main elements into a cohesive and natural scene."
246
- logger.info("Default prompt generated for three images")
247
-
248
- result_img, status = generate_with_images(final_prompt, valid_images, variation_index)
249
- return result_img, status, final_prompt
 
 
 
 
250
  except Exception as e:
251
  logger.exception("์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
252
- return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", prompt
253
 
254
- def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progress()):
255
- """
256
- ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ด๋ฏธ์ง€๋ฅผ ์ฐจ๋ก€๋Œ€๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
257
- """
258
- results = []
259
- statuses = []
260
- prompts = []
 
 
 
 
261
 
262
- num_images = 4 # ์ƒ์„ฑํ•  ์ด๋ฏธ์ง€ ์ˆ˜
263
-
264
- progress(0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค€๋น„ ์ค‘...")
265
-
266
- for i in range(num_images):
267
- progress((i / num_images), desc=f"{i+1}/{num_images} ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘...")
268
- result_img, status, final_prompt = process_images_with_prompt(image1, image2, image3, prompt, i)
269
-
270
- if result_img is not None:
271
- results.append(result_img)
272
- statuses.append(f"์ด๋ฏธ์ง€ #{i+1}: {status}")
273
- prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
274
- else:
275
- # ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ์—๋„ ๊ฒฐ๊ณผ ๋ชฉ๋ก์— None์„ ์ถ”๊ฐ€
276
- results.append(None)
277
- statuses.append(f"์ด๋ฏธ์ง€ #{i+1} ์ƒ์„ฑ ์‹คํŒจ: {status}")
278
- prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
279
-
280
- # API ํ˜ธ์ถœ ์‚ฌ์ด์— ์•ฝ๊ฐ„์˜ ๊ฐ„๊ฒฉ์„ ๋‘์–ด ์†๋„ ์ œํ•œ ๋ฐฉ์ง€
281
- time.sleep(1)
282
-
283
- progress(1.0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ!")
284
-
285
- # ๊ฒฐ๊ณผ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋นˆ ์Šฌ๋กฏ ์ฑ„์šฐ๊ธฐ
286
- while len(results) < 4:
287
- results.append(None)
288
-
289
- # ์ƒํƒœ ๋ฉ”์‹œ์ง€์™€ ํ”„๋กฌํ”„ํŠธ ์ •๋ณด ๊ฒฐํ•ฉ
290
- combined_status = "\n".join(statuses)
291
- combined_prompts = "\n".join(prompts)
292
-
293
- return results[0], results[1], results[2], results[3], combined_status, combined_prompts
294
 
295
- # Gradio ์ธํ„ฐํŽ˜์ด์Šค
296
- with gr.Blocks(css="""
297
- .gradio-container {max-width: 650px; margin: 0 auto;}
298
- .output-image {height: 300px; width: 300px;}
299
- .input-image {height: 200px; width: 200px;}
300
- .image-row {display: flex; justify-content: center;}
301
- """) as demo:
302
- # ๋ฉ”์ธ ์ปจํ…Œ์ด๋„ˆ ์‹œ์ž‘
303
- with gr.Column():
304
- gr.HTML(
305
- """
306
- <div style="text-align: center; margin-bottom: 1rem;">
307
- <h1 style="margin-bottom: 0.5rem;">4์žฅ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ</h1>
308
- <p style="margin-bottom: 0.2rem;">์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ  "์ด๋ฏธ์ง€ ์ƒ์„ฑ" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด</p>
309
- <p>์ฐจ๋ก€๋กœ 4์žฅ์˜ ์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.</p>
310
- </div>
311
- """
312
- )
313
-
314
- # ์ž…๋ ฅ ์„น์…˜
315
- gr.HTML("<h3 style='text-align: center; margin-top: 1rem; margin-bottom: 0.5rem;'>์ž…๋ ฅ ์ด๋ฏธ์ง€</h3>")
316
-
317
- # ์ž…๋ ฅ ์ด๋ฏธ์ง€ UI๋ฅผ ์ ์ ˆํ•œ ๊ฐ„๊ฒฉ์œผ๋กœ ์„ธ๋กœ ๋ฐฐ์น˜
318
  with gr.Column():
 
319
  with gr.Row():
320
- image1_input = gr.Image(type="pil", label="#1", height=200, width=200)
321
- image2_input = gr.Image(type="pil", label="#2", height=200, width=200)
322
- with gr.Row():
323
- image3_input = gr.Image(type="pil", label="#3", height=200, width=200)
324
- placeholder_input = gr.Textbox(visible=False) # ๋ ˆ์ด์•„์›ƒ ์กฐ์ •์šฉ ๋”๋ฏธ ์ปดํฌ๋„ŒํŠธ
325
-
326
- # ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ
327
- prompt_input = gr.Textbox(
328
- lines=3,
329
- placeholder="ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ๋น„์›Œ๋‘๋ฉด ์ž๋™ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
330
- label="ํ”„๋กฌํ”„ํŠธ (์„ ํƒ ์‚ฌํ•ญ)"
331
- )
332
-
333
- # ๋ฒ„ํŠผ ์Šคํƒ€์ผ ๊ฐœ์„ 
334
- with gr.Column(variant="panel"):
335
- gr.HTML("<h4 style='margin-bottom: 0.5rem; text-align: center; width: 100%;'>์ž‘์—… ์„ ํƒ</h4>")
336
  with gr.Row():
337
- image_change_btn = gr.Button("์ด๋ฏธ์ง€๋ณ€๊ฒฝ", scale=1)
338
- text_remove_btn = gr.Button("๊ธ€์ž์ง€์šฐ๊ธฐ", scale=1)
339
- clothes_change_btn = gr.Button("๊ฐ€์ƒ ์ƒํ’ˆ์ฐฉ์šฉ", scale=1)
340
-
341
- with gr.Row():
342
- background_change_btn = gr.Button("๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ", scale=1)
343
- composite_product_btn = gr.Button("๋ถ€๋ถ„์ง€์šฐ๊ธฐ", scale=1)
344
-
345
- # ์ƒ์„ฑ ๋ฒ„ํŠผ์„ ๋” ๋ˆˆ์— ๋„๊ฒŒ ๋งŒ๋“ค๊ธฐ
346
- submit_btn = gr.Button("์ด๋ฏธ์ง€ ์ƒ์„ฑ (4์žฅ)", variant="primary", size="lg")
347
-
348
- # ์ถœ๋ ฅ ์„น์…˜
349
- gr.HTML("<h3 style='text-align: center; margin-top: 1.5rem; margin-bottom: 0.5rem;'>์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€</h3>")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
 
351
- # ์ถœ๋ ฅ ์ด๋ฏธ์ง€ UI๋ฅผ ์„ธ๋กœ๋กœ ๋ฐฐ์น˜
352
  with gr.Column():
353
- with gr.Row():
354
- output_image1 = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ #1", height=300, width=300)
355
- output_image2 = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ #2", height=300, width=300)
356
- with gr.Row():
357
- output_image3 = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ #3", height=300, width=300)
358
- output_image4 = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ #4", height=300, width=300)
359
-
360
- # ์ƒํƒœ ๋ฐ ํ”„๋กฌํ”„ํŠธ ์ •๋ณด
361
- output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€")
362
- prompt_display = gr.Textbox(label="์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ (์˜์–ด)", visible=True)
363
 
364
- # ์„ ํƒ ์˜ต์…˜ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ๋ž€ ์—…๋ฐ์ดํŠธ (ํ•œ๊ตญ์–ด ๋ฌธ๊ตฌ)
365
- image_change_btn.click(
366
- fn=lambda: "(#1์˜ ์—ฌ์„ฑ)์˜ ์ด๋ฏธ์ง€๋ฅผ ์‚ด์ง ๋’ค๋กœ ๋Œ์•„๋ณด๋Š” ๋ชจ์Šต์œผ๋กœ ์ตœ๋Œ€ํ•œ ์ด์ „ seed๋ฅผ ์œ ์ง€ํ•œํ…Œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ผ.",
367
- inputs=[],
368
- outputs=prompt_input
369
- )
370
- text_remove_btn.click(
371
- fn=lambda: "(#1 ์ด๋ฏธ์ง€)์— ์žˆ๋Š” ์ค‘๊ตญ์–ด๋ฅผ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜๋ผ.",
372
- inputs=[],
373
- outputs=prompt_input
374
- )
375
- clothes_change_btn.click(
376
- fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด ์‹ ์ฒด ๋น„์œก๊ณผ ํฌ์ฆˆ๋Š” ์œ ์น˜ํ•œ ์ฒด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์™€ (#3์˜ ์ฒญ๋ฐ”์ง€)๋ฅผ ์ง์ ‘ ๋ชจ๋ธ์ด ์ฐฉ์šฉํ•œ๊ฒƒ ์ฒ˜๋Ÿผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ.",
377
- inputs=[],
378
- outputs=prompt_input
379
- )
380
- background_change_btn.click(
381
- fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2 ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ)์„ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜์—ฌ ๋‘ ์ด๋ฏธ์ง€์˜ ๋ถ„์œ„๊ธฐ๊ฐ€ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ƒ์„ฑํ•˜๋ผ.",
382
- inputs=[],
383
- outputs=prompt_input
384
- )
385
- composite_product_btn.click(
386
- fn=lambda: "#1 ์ด๋ฏธ์ง€์—์„œ [์ œ๊ฑฐํ•  ์˜์—ญ ๋˜๋Š” ๋Œ€์ƒ์„ ์ž์„ธํžˆ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”]์„ ์ œ๊ฑฐํ•œ ํ›„, ๊ทธ ์ž๋ฆฌ๋ฅผ ์ฃผ๋ณ€ ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ฑ„์›Œ์ฃผ์„ธ์š”. ๋‹จ, ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ์™€ ์ „์ฒด ๋ถ„์œ„๊ธฐ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ํ•ด ์ฃผ์„ธ์š”.",
387
- inputs=[],
388
- outputs=prompt_input
389
  )
390
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
  submit_btn.click(
392
- fn=generate_multiple_images,
393
  inputs=[image1_input, image2_input, image3_input, prompt_input],
394
- outputs=[output_image1, output_image2, output_image3, output_image4, output_text, prompt_display],
395
  )
396
-
397
  gr.Markdown(
398
  """
399
  ### ์‚ฌ์šฉ ๋ฐฉ๋ฒ•:
400
 
401
- 1. **์ž๋™ ํ•ฉ์„ฑ**: ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ  ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋น„์›Œ๋‘๋ฉด ์ž๋™์œผ๋กœ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.
402
- 2. **์ด๋ฏธ์ง€ ์ฐธ์กฐ**: #1, #2, #3์œผ๋กœ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
403
- 3. **์„ ํƒ ์˜ต์…˜**: ์œ„์˜ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ๋ž€์— ํ•œ๊ตญ์–ด ๋ฌธ๊ตฌ๋กœ ์ž…๋ ฅํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
404
- 4. **๋‹ค์–‘ํ•œ ์ด๋ฏธ์ง€**: "์ด๋ฏธ์ง€ ์ƒ์„ฑ" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ฐจ๋ก€๋กœ 4์žฅ์˜ ์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
 
405
 
406
- > **ํŒ**: ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
407
  """
408
  )
409
 
 
410
  if __name__ == "__main__":
411
  demo.launch(share=True)
 
4
  import gradio as gr
5
  import logging
6
  import re
7
+ import io
8
  from io import BytesIO
 
9
 
10
  from google import genai
11
  from google.genai import types
 
22
  with open(file_name, "wb") as f:
23
  f.write(data)
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  def preprocess_prompt(prompt, image1, image2, image3):
26
  """
27
  ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ธฐ๋Šฅ ๋ช…๋ น์„ ํ•ด์„
28
  """
29
+ # ๊ธฐ์กด preprocess_prompt ํ•จ์ˆ˜ ์ฝ”๋“œ ์œ ์ง€
30
+ # ์ด๋ฏธ์ง€ ์—†๋Š” ์ฐธ์กฐ ํ™•์ธ ๋ฐ ์ฒ˜๋ฆฌ
31
  has_img1 = image1 is not None
32
  has_img2 = image2 is not None
33
  has_img3 = image3 is not None
34
+
35
+ # #1, #2, #3 ์ฐธ์กฐ๋ฅผ ์„ค๋ช…์œผ๋กœ ๋ณ€ํ™˜ (์ด๋ฏธ์ง€๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋ฌด์‹œ)
36
  if "#1" in prompt and not has_img1:
37
  prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
38
  else:
39
  prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
40
+
41
  if "#2" in prompt and not has_img2:
42
  prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
43
  else:
44
  prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
45
+
46
  if "#3" in prompt and not has_img3:
47
  prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
48
  else:
49
  prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
50
+
51
+ # ๊ธฐ๋Šฅ ๋ช…๋ น ํ•ด์„
52
  if "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ" in prompt:
53
+ # ์„ค๋ช… ์ถ”์ถœ์„ ์‹œ๋„ํ•˜์ง€๋งŒ ์‹คํŒจํ•ด๋„ ๊ธฐ๋ณธ ํ”„๋กฌํ”„ํŠธ ์ œ๊ณต
54
  desc_match = re.search(r'#1์„ "(.*?)"์œผ๋กœ ๋ฐ”๊ฟ”๋ผ', prompt)
55
  if desc_match:
56
  description = desc_match.group(1)
57
  prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ {description}์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์›๋ณธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ๋‚ด์šฉ์€ ์œ ์ง€ํ•˜๋˜ ์ƒˆ๋กœ์šด ๏ฟฝ๏ฟฝํƒ€์ผ๊ณผ ๋ถ„์œ„๊ธฐ๋กœ ์žฌํ•ด์„ํ•ด์ฃผ์„ธ์š”."
58
  else:
59
  prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
60
+
61
  elif "2. ๊ธ€์ž์ง€์šฐ๊ธฐ" in prompt:
62
+ # ์ง€์šธ ํ…์ŠคํŠธ ์ถ”์ถœ์„ ์‹œ๋„ํ•˜์ง€๋งŒ ์‹คํŒจํ•ด๋„ ๊ธฐ๋ณธ ํ”„๋กฌํ”„ํŠธ ์ œ๊ณต
63
  text_match = re.search(r'#1์—์„œ "(.*?)"๋ฅผ ์ง€์›Œ๋ผ', prompt)
64
  if text_match:
65
  text_to_remove = text_match.group(1)
66
  prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ '{text_to_remove}' ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ํ…์ŠคํŠธ๊ฐ€ ์žˆ๋˜ ๋ถ€๋ถ„์„ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ์ฑ„์›Œ์ฃผ์„ธ์š”."
67
  else:
68
  prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ ๋ชจ๋“  ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ๊น”๋”ํ•œ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
69
+
70
+ elif "3. ์–ผ๊ตด๋ฐ”๊พธ๊ธฐ" in prompt:
71
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ ์–ผ๊ตด์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์–ผ๊ตด๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ต์ฒดํ•ด์ฃผ์„ธ์š”. ์–ผ๊ตด์˜ ํ‘œ์ •๊ณผ ํŠน์ง•์€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋ฅด๋˜, ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”."
72
+
73
  elif "4. ์˜ท๋ฐ”๊พธ๊ธฐ" in prompt:
74
+ # ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€ ์ฐธ์กฐ ์ฒ˜๋ฆฌ
75
+ if "#3" in prompt or "๋˜๋Š” #3" in prompt:
76
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ ์˜์ƒ์„ ๋‘ ๋ฒˆ์งธ ๋˜๋Š” ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์˜์ƒ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ต์ฒดํ•ด์ฃผ์„ธ์š”. ์˜์ƒ์˜ ์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ์€ ์ฐธ์กฐ ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋ฅด๋˜, ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”."
77
+ else:
78
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ ์˜์ƒ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์˜์ƒ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ต์ฒดํ•ด์ฃผ์„ธ์š”. ์˜์ƒ์˜ ์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ์€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋ฅด๋˜, ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”."
79
+
80
  elif "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ" in prompt:
81
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ต์ฒดํ•ด์ฃผ์„ธ์š”. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”."
82
+
83
  elif "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)" in prompt:
84
+ # ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€ ์ฐธ์กฐ ์ฒ˜๋ฆฌ
85
+ if "#3" in prompt or "๋˜๋Š” #3" in prompt:
86
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์™€ ๋‘ ๋ฒˆ์งธ, ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ , ํŠนํžˆ ์ƒํ’ˆ์ด ์ž˜ ๋ณด์ด๋„๋ก ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•ด์ฃผ์„ธ์š”."
87
+ else:
88
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์™€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋‘ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ , ํŠนํžˆ ์ƒํ’ˆ์ด ์ž˜ ๋ณด์ด๋„๋ก ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•ด์ฃผ์„ธ์š”."
89
+
90
+ elif "7. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์Šคํƒ€์ผ์ ์šฉ)" in prompt:
91
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋‚ด์šฉ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์Šคํƒ€์ผ๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ์„ธ์š”. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด์™€ ๊ตฌ๋„๋Š” ์œ ์ง€ํ•˜๋˜, ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์˜ˆ์ˆ ์  ์Šคํƒ€์ผ, ์ƒ‰์ƒ, ์งˆ๊ฐ์„ ์ ์šฉํ•ด์ฃผ์„ธ์š”."
92
+
93
+ # ๊ฐ„๋‹จํ•œ ์ƒ‰์ƒ ๋ณ€๊ฒฝ ์š”์ฒญ ์ฒ˜๋ฆฌ
94
+ elif "์„ ๋ถ‰์€์ƒ‰์œผ๋กœ ๋ฐ”๊ฟ”๋ผ" in prompt or "๋ฅผ ๋ถ‰์€์ƒ‰์œผ๋กœ ๋ฐ”๊ฟ”๋ผ" in prompt:
95
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋ถ‰์€์ƒ‰ ํ†ค์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์ „์ฒด์ ์ธ ์ƒ‰์ƒ์„ ๋ถ‰์€ ๊ณ„์—ด๋กœ ์กฐ์ •ํ•˜๊ณ  ์ž์—ฐ์Šค๋Ÿฌ์šด ๋А๋‚Œ์„ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”."
96
+
97
+ # ๋ช…ํ™•ํ•œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์š”์ฒญ ์ถ”๊ฐ€
98
  prompt += " ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”."
99
+
100
  return prompt
101
 
102
+ def generate_with_images(prompt, images):
103
  """
104
+ ๊ณต์‹ ๋ฌธ์„œ์— ๊ธฐ๋ฐ˜ํ•œ ์˜ฌ๋ฐ”๋ฅธ API ํ˜ธ์ถœ ๋ฐฉ์‹ ๊ตฌํ˜„
 
 
105
  """
106
  try:
107
+ # API ํ‚ค ํ™•์ธ
108
  api_key = os.environ.get("GEMINI_API_KEY")
109
  if not api_key:
110
  return None, "API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
111
+
112
+ # Gemini ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
113
  client = genai.Client(api_key=api_key)
 
 
 
 
 
 
 
 
 
114
 
115
+ logger.info(f"Gemini API ์š”์ฒญ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: {prompt}")
116
+
117
+ # ์ปจํ…์ธ  ์ค€๋น„
118
+ contents = []
119
+
120
+ # ํ…์ŠคํŠธ ํ”„๋กฌํ”„ํŠธ ์ถ”๊ฐ€
121
+ contents.append(prompt)
122
+
123
+ # ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
124
  for idx, img in enumerate(images, 1):
125
  if img is not None:
126
  contents.append(img)
127
  logger.info(f"์ด๋ฏธ์ง€ #{idx} ์ถ”๊ฐ€๋จ")
128
+
129
+ # ์ƒ์„ฑ ์„ค์ • - ๊ณต์‹ ๋ฌธ์„œ์— ๋”ฐ๋ผ responseModalities ์„ค์ •
130
  response = client.models.generate_content(
131
  model="gemini-2.0-flash-exp-image-generation",
132
  contents=contents,
 
138
  max_output_tokens=8192
139
  )
140
  )
141
+
142
+ # ์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ
143
  with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
144
  temp_path = tmp.name
145
+
146
  result_text = ""
147
  image_found = False
148
+
149
+ # ์‘๋‹ต ์ฒ˜๋ฆฌ
150
  for part in response.candidates[0].content.parts:
151
  if hasattr(part, 'text') and part.text:
152
  result_text += part.text
 
155
  save_binary_file(temp_path, part.inline_data.data)
156
  image_found = True
157
  logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
158
+
159
  if not image_found:
160
  return None, f"API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ํ…์ŠคํŠธ: {result_text}"
161
+
162
+ # ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜
163
  result_img = Image.open(temp_path)
164
  if result_img.mode == "RGBA":
165
  result_img = result_img.convert("RGB")
166
 
 
 
 
 
167
  return result_img, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
168
+
169
  except Exception as e:
170
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
171
  return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
172
 
173
+ def process_images_with_prompt(image1, image2, image3, prompt):
174
  """
175
+ 3๊ฐœ์˜ ์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜
 
176
  """
177
  try:
178
+ # ์ด๋ฏธ์ง€ ๊ฐœ์ˆ˜ ํ™•์ธ
179
  images = [image1, image2, image3]
180
  valid_images = [img for img in images if img is not None]
181
+
182
  if not valid_images:
183
+ return None, "์ ์–ด๋„ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”."
184
+
185
+ # ํ”„๋กฌํ”„ํŠธ ์ฒ˜๋ฆฌ
186
+ if not prompt or not prompt.strip():
187
+ # ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์—†์œผ๋ฉด ์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€ ์ˆ˜์— ๋”ฐ๋ผ ์ž๋™ ํ•ฉ์„ฑ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
 
 
 
 
188
  if len(valid_images) == 1:
189
+ prompt = "์ด ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
190
+ logger.info("๋‹จ์ผ ์ด๋ฏธ์ง€ ํ”„๋กฌํ”„ํŠธ ์ž๋™ ์ƒ์„ฑ")
191
  elif len(valid_images) == 2:
192
+ prompt = "์ด ๋‘ ์ด๋ฏธ์ง€๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋‘ ์ด๋ฏธ์ง€์˜ ์š”์†Œ๋ฅผ ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•˜์—ฌ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
193
+ logger.info("๋‘ ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ํ”„๋กฌํ”„ํŠธ ์ž๋™ ์ƒ์„ฑ")
194
  else:
195
+ prompt = "์ด ์„ธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๋˜ ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ์ผ๊ด€๋œ ํ•˜๋‚˜์˜ ์žฅ๋ฉด์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
196
+ logger.info("์„ธ ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ํ”„๋กฌํ”„ํŠธ ์ž๋™ ์ƒ์„ฑ")
197
+ else:
198
+ # ํ”„๋กฌํ”„ํŠธ ์ „์ฒ˜๋ฆฌ ๋ฐ ๊ธฐ๋Šฅ ๋ช…๋ น ํ•ด์„
199
+ prompt = preprocess_prompt(prompt, image1, image2, image3)
200
+
201
+ # ์ƒˆ๋กœ์šด API ํ˜ธ์ถœ ๋ฐฉ์‹ ์‚ฌ์šฉ
202
+ return generate_with_images(prompt, valid_images)
203
+
204
  except Exception as e:
205
  logger.exception("์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
206
+ return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
207
 
208
+ # ๊ธฐ๋Šฅ ์„ ํƒ ์ฝœ๋ฐฑ (๊ธฐ์กด ์ฝ”๋“œ ์œ ์ง€)
209
+ def update_prompt_from_function(function_choice, custom_text=""):
210
+ function_templates = {
211
+ "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ": f'#1์„ "{custom_text if custom_text else "์›ํ•˜๋Š” ์„ค๋ช…"}"์œผ๋กœ ๋ฐ”๊ฟ”๋ผ',
212
+ "2. ๊ธ€์ž์ง€์šฐ๊ธฐ": f'#1์—์„œ "{custom_text if custom_text else "์ง€์šธ ํ…์ŠคํŠธ"}"๋ฅผ ์ง€์›Œ๋ผ',
213
+ "3. ์–ผ๊ตด๋ฐ”๊พธ๊ธฐ": "#1์˜ ์ธ๋ฌผ์„ #2์˜ ์–ผ๊ตด๋กœ ๋ฐ”๊ฟ”๋ผ",
214
+ "4. ์˜ท๋ฐ”๊พธ๊ธฐ": "#1์˜ ์ธ๋ฌผ์— #2 ๋˜๋Š” #3์˜ ์˜ท์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ผ",
215
+ "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ": "#1์˜ ์ด๋ฏธ์ง€์— #2์˜ ๋ฐฐ๊ฒฝ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ฐ”๊ฟ”๋ผ",
216
+ "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)": "#1์™€ #2 ๋˜๋Š” #3์˜ ๋ฅผ ํ•ฉ์„ฑํ•˜๋ผ",
217
+ "7. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์Šคํƒ€์ผ์ ์šฉ)": "#1์™€ #2๋ฅผ ์Šคํƒ€์ผ๋กœ ๋ณ€ํ™˜ํ•˜๋ผ"
218
+ }
219
 
220
+ return function_templates.get(function_choice, "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
 
222
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค (๊ธฐ์กด ์ฝ”๋“œ ์œ ์ง€)
223
+ with gr.Blocks() as demo:
224
+ gr.HTML(
225
+ """
226
+ <div style="text-align: center; margin-bottom: 1rem;">
227
+ <h1>๊ฐ„๋‹จํ•œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ</h1>
228
+ <p>์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๏ฟฝ๏ฟฝ ๋ฐ”๋กœ ์‹คํ–‰ํ•˜๋ฉด ์ž๋™์œผ๋กœ ํ•ฉ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋˜๋Š” ์•„๋ž˜ ๊ธฐ๋Šฅ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
229
+ </div>
230
+ """
231
+ )
232
+
233
+ with gr.Row():
 
 
 
 
 
 
 
 
 
 
 
234
  with gr.Column():
235
+ # 3๊ฐœ์˜ ์ด๋ฏธ์ง€ ์ž…๋ ฅ
236
  with gr.Row():
237
+ image1_input = gr.Image(type="pil", label="#1", image_mode="RGB")
238
+ image2_input = gr.Image(type="pil", label="#2", image_mode="RGB")
239
+ image3_input = gr.Image(type="pil", label="#3", image_mode="RGB")
240
+
241
+ # ๊ธฐ๋Šฅ ์„ ํƒ ๋“œ๋กญ๋‹ค์šด๊ณผ ์ปค์Šคํ…€ ํ…์ŠคํŠธ ์ž…๋ ฅ
 
 
 
 
 
 
 
 
 
 
 
242
  with gr.Row():
243
+ function_dropdown = gr.Dropdown(
244
+ choices=[
245
+ "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ",
246
+ "2. ๊ธ€์ž์ง€์šฐ๊ธฐ",
247
+ "3. ์–ผ๊ตด๋ฐ”๊พธ๊ธฐ",
248
+ "4. ์˜ท๋ฐ”๊พธ๊ธฐ",
249
+ "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ",
250
+ "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)",
251
+ "7. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์Šคํƒ€์ผ์ ์šฉ)"
252
+ ],
253
+ label="๊ธฐ๋Šฅ ์„ ํƒ",
254
+ value=None
255
+ )
256
+ custom_text_input = gr.Textbox(
257
+ label="์ปค์Šคํ…€ ํ…์ŠคํŠธ (1, 2๋ฒˆ ๊ธฐ๋Šฅ์šฉ)",
258
+ placeholder="์˜ˆ: ๋ถ‰์€์ƒ‰, ์ˆ˜์ฑ„ํ™” ์Šคํƒ€์ผ, ์ค‘๊ตญ์–ด..."
259
+ )
260
+
261
+ apply_function_btn = gr.Button("๊ธฐ๋Šฅ ์ ์šฉ")
262
+
263
+ # ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ (์„ ํƒ ์‚ฌํ•ญ)
264
+ prompt_input = gr.Textbox(
265
+ lines=3,
266
+ placeholder="ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ๋น„์›Œ๋‘๋ฉด ์ž๋™ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
267
+ label="ํ”„๋กฌํ”„ํŠธ (์„ ํƒ ์‚ฌํ•ญ)"
268
+ )
269
+
270
+ # ์ƒ์„ฑ ๋ฒ„ํŠผ
271
+ submit_btn = gr.Button("์ด๋ฏธ์ง€ ์ƒ์„ฑ", variant="primary")
272
 
 
273
  with gr.Column():
274
+ # ๊ฒฐ๊ณผ ์ถœ๋ ฅ
275
+ output_image = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€")
276
+ output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€")
277
+
278
+ # ์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ ํ‘œ์‹œ
279
+ prompt_display = gr.Textbox(label="์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ", visible=True)
 
 
 
 
280
 
281
+ # ๊ธฐ๋Šฅ ์ ์šฉ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ
282
+ apply_function_btn.click(
283
+ fn=update_prompt_from_function,
284
+ inputs=[function_dropdown, custom_text_input],
285
+ outputs=[prompt_input]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  )
287
 
288
+ # ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
289
+ def process_and_show_prompt(image1, image2, image3, prompt):
290
+ # ์ด๋ฏธ์ง€ ๊ฐœ์ˆ˜ ํ™•์ธ
291
+ images = [image1, image2, image3]
292
+ valid_images = [img for img in images if img is not None]
293
+
294
+ try:
295
+ # ์ž๋™ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋˜๋Š” ํ”„๋กฌํ”„ํŠธ ์ „์ฒ˜๋ฆฌ
296
+ auto_prompt = prompt
297
+ if not prompt or not prompt.strip():
298
+ if len(valid_images) == 1:
299
+ auto_prompt = "์ด ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
300
+ elif len(valid_images) == 2:
301
+ auto_prompt = "์ด ๋‘ ์ด๋ฏธ์ง€๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋‘ ์ด๋ฏธ์ง€์˜ ์š”์†Œ๋ฅผ ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•˜์—ฌ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
302
+ else:
303
+ auto_prompt = "์ด ์„ธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๋˜ ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ์ผ๊ด€๋œ ํ•˜๋‚˜์˜ ์žฅ๋ฉด์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
304
+ else:
305
+ auto_prompt = preprocess_prompt(prompt, image1, image2, image3)
306
+
307
+ # ์ด๋ฏธ์ง€ ์ƒ์„ฑ ํ•จ์ˆ˜ ํ˜ธ์ถœ
308
+ result_img, status = process_images_with_prompt(image1, image2, image3, prompt)
309
+
310
+ return result_img, status, auto_prompt
311
+ except Exception as e:
312
+ logger.exception("์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
313
+ return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", prompt
314
+
315
  submit_btn.click(
316
+ fn=process_and_show_prompt,
317
  inputs=[image1_input, image2_input, image3_input, prompt_input],
318
+ outputs=[output_image, output_text, prompt_display],
319
  )
320
+
321
  gr.Markdown(
322
  """
323
  ### ์‚ฌ์šฉ ๋ฐฉ๋ฒ•:
324
 
325
+ 1. **์ž๋™ ํ•ฉ์„ฑ**: ์ด๋ฏธ์ง€๋งŒ ์—…๋กœ๋“œํ•˜๊ณ  ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋น„์›Œ๋‘๋ฉด ์ž๋™์œผ๋กœ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค
326
+ 2. **๊ธฐ๋Šฅ ์‚ฌ์šฉ**: ๋“œ๋กญ๋‹ค์šด์—์„œ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์„ ํƒํ•˜๊ณ  '๊ธฐ๋Šฅ ์ ์šฉ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”
327
+ 3. **์ปค์Šคํ…€ ํ…์ŠคํŠธ**: ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ์ด๋‚˜ ๊ธ€์ž์ง€์šฐ๊ธฐ ๊ธฐ๋Šฅ์„ ์„ ํƒํ•  ๋•Œ ์ถ”๊ฐ€ ์„ค๋ช…์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
328
+ 4. **์ด๋ฏธ์ง€ ์ฐธ์กฐ**: #1, #2, #3์œผ๋กœ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
329
+ 5. **์ผ๋ถ€ ์ด๋ฏธ์ง€๋งŒ**: ํ•„์š”ํ•œ ์ด๋ฏธ์ง€๋งŒ ์—…๋กœ๋“œํ•ด๋„ ๊ธฐ๋Šฅ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
330
 
331
+ > **ํŒ**: ๊ธฐ๋Šฅ์„ ์„ ํƒํ•œ ํ›„์—๋„ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
332
  """
333
  )
334
 
335
+ # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
336
  if __name__ == "__main__":
337
  demo.launch(share=True)