Kims12 commited on
Commit
eeae49e
ยท
verified ยท
1 Parent(s): 4458d2d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +152 -127
app.py CHANGED
@@ -4,6 +4,7 @@ from PIL import Image
4
  import gradio as gr
5
  import logging
6
  import re
 
7
  from io import BytesIO
8
 
9
  from google import genai
@@ -21,115 +22,111 @@ def save_binary_file(file_name, data):
21
  with open(file_name, "wb") as f:
22
  f.write(data)
23
 
24
- def translate_prompt_to_english(prompt):
25
- """
26
- ์ž…๋ ฅ๋œ ํ”„๋กฌํ”„ํŠธ์— ํ•œ๊ธ€์ด ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด Geminiโ€‘2.0โ€‘flash ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜์–ด๋กœ ๋ฒˆ์—ญํ•ฉ๋‹ˆ๋‹ค.
27
- ํ•œ๊ธ€์ด ์—†์œผ๋ฉด ์›๋ณธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
28
- """
29
- if not re.search("[๊ฐ€-ํžฃ]", prompt):
30
- return prompt
31
- try:
32
- api_key = os.environ.get("GEMINI_API_KEY")
33
- if not api_key:
34
- logger.error("Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
35
- return prompt
36
- client = genai.Client(api_key=api_key)
37
- translation_prompt = f"Translate the following Korean text to English:\n\n{prompt}"
38
- logger.info(f"Translation prompt: {translation_prompt}")
39
- response = client.models.generate_content(
40
- model="gemini-2.0-flash",
41
- contents=[translation_prompt],
42
- config=types.GenerateContentConfig(
43
- response_modalities=['Text'],
44
- temperature=0.2,
45
- top_p=0.95,
46
- top_k=40,
47
- max_output_tokens=512
48
- )
49
- )
50
- translated_text = ""
51
- for part in response.candidates[0].content.parts:
52
- if hasattr(part, 'text') and part.text:
53
- translated_text += part.text
54
- if translated_text.strip():
55
- logger.info(f"Translated text: {translated_text.strip()}")
56
- return translated_text.strip()
57
- else:
58
- logger.warning("๋ฒˆ์—ญ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ")
59
- return prompt
60
- except Exception as e:
61
- logger.exception("๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
62
- return prompt
63
-
64
  def preprocess_prompt(prompt, image1, image2, image3):
65
  """
66
  ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ธฐ๋Šฅ ๋ช…๋ น์„ ํ•ด์„
67
  """
 
 
68
  has_img1 = image1 is not None
69
  has_img2 = image2 is not None
70
  has_img3 = image3 is not None
71
-
 
72
  if "#1" in prompt and not has_img1:
73
  prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
74
  else:
75
  prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
76
-
77
  if "#2" in prompt and not has_img2:
78
  prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
79
  else:
80
  prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
81
-
82
  if "#3" in prompt and not has_img3:
83
  prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
84
  else:
85
  prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
86
-
 
87
  if "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ" in prompt:
 
88
  desc_match = re.search(r'#1์„ "(.*?)"์œผ๋กœ ๋ฐ”๊ฟ”๋ผ', prompt)
89
  if desc_match:
90
  description = desc_match.group(1)
91
  prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ {description}์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์›๋ณธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ๋‚ด์šฉ์€ ์œ ์ง€ํ•˜๋˜ ์ƒˆ๋กœ์šด ์Šคํƒ€์ผ๊ณผ ๋ถ„์œ„๊ธฐ๋กœ ์žฌํ•ด์„ํ•ด์ฃผ์„ธ์š”."
92
  else:
93
  prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
94
-
95
  elif "2. ๊ธ€์ž์ง€์šฐ๊ธฐ" in prompt:
 
96
  text_match = re.search(r'#1์—์„œ "(.*?)"๋ฅผ ์ง€์›Œ๋ผ', prompt)
97
  if text_match:
98
  text_to_remove = text_match.group(1)
99
  prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ '{text_to_remove}' ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ํ…์ŠคํŠธ๊ฐ€ ์žˆ๋˜ ๋ถ€๋ถ„์„ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ์ฑ„์›Œ์ฃผ์„ธ์š”."
100
  else:
101
  prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ ๋ชจ๋“  ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ๊น”๋”ํ•œ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
102
-
 
 
 
103
  elif "4. ์˜ท๋ฐ”๊พธ๊ธฐ" in prompt:
104
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ ์˜์ƒ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์˜์ƒ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์˜์ƒ์˜ ์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ์€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋ฅด๋˜, ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”."
105
-
 
 
 
 
106
  elif "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ" in prompt:
107
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”."
108
-
109
  elif "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)" in prompt:
110
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์™€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(๋˜๋Š” ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€)๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ , ํŠนํžˆ ์ƒํ’ˆ์ด ๋‹๋ณด์ด๋„๋ก ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•ด์ฃผ์„ธ์š”."
111
-
 
 
 
 
 
 
 
 
 
 
 
 
112
  prompt += " ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”."
 
113
  return prompt
114
 
115
  def generate_with_images(prompt, images):
116
  """
117
- API ํ˜ธ์ถœ์„ ํ†ตํ•ด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
118
  """
119
  try:
 
120
  api_key = os.environ.get("GEMINI_API_KEY")
121
  if not api_key:
122
  return None, "API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
123
-
 
124
  client = genai.Client(api_key=api_key)
 
125
  logger.info(f"Gemini API ์š”์ฒญ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: {prompt}")
126
-
127
- contents = [prompt]
 
 
 
 
 
 
128
  for idx, img in enumerate(images, 1):
129
  if img is not None:
130
  contents.append(img)
131
  logger.info(f"์ด๋ฏธ์ง€ #{idx} ์ถ”๊ฐ€๋จ")
132
-
 
133
  response = client.models.generate_content(
134
  model="gemini-2.0-flash-exp-image-generation",
135
  contents=contents,
@@ -141,11 +138,15 @@ def generate_with_images(prompt, images):
141
  max_output_tokens=8192
142
  )
143
  )
144
-
 
145
  with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
146
  temp_path = tmp.name
 
147
  result_text = ""
148
  image_found = False
 
 
149
  for part in response.candidates[0].content.parts:
150
  if hasattr(part, 'text') and part.text:
151
  result_text += part.text
@@ -154,116 +155,140 @@ def generate_with_images(prompt, images):
154
  save_binary_file(temp_path, part.inline_data.data)
155
  image_found = True
156
  logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
 
157
  if not image_found:
158
  return None, f"API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ํ…์ŠคํŠธ: {result_text}"
 
 
159
  result_img = Image.open(temp_path)
160
  if result_img.mode == "RGBA":
161
  result_img = result_img.convert("RGB")
 
162
  return result_img, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
 
163
  except Exception as e:
164
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
165
  return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
166
 
167
  def process_images_with_prompt(image1, image2, image3, prompt):
168
  """
169
- 3๊ฐœ์˜ ์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ตœ์ข… ์˜์–ด ํ”„๋กฌํ”„ํŠธ(final_prompt)๋ฅผ ์ƒ์„ฑํ•œ ํ›„,
170
- API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
171
  """
172
  try:
 
173
  images = [image1, image2, image3]
174
  valid_images = [img for img in images if img is not None]
 
175
  if not valid_images:
176
- return None, "์ ์–ด๋„ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.", ""
177
-
178
- if prompt and prompt.strip():
179
- processed_prompt = preprocess_prompt(prompt, image1, image2, image3)
180
- if re.search("[๊ฐ€-ํžฃ]", processed_prompt):
181
- final_prompt = translate_prompt_to_english(processed_prompt)
182
- else:
183
- final_prompt = processed_prompt
184
- else:
185
  if len(valid_images) == 1:
186
- final_prompt = "Please creatively transform this image into a more vivid and artistic version."
187
  logger.info("Default prompt generated for single image")
188
  elif len(valid_images) == 2:
189
- final_prompt = "Please seamlessly composite these two images, integrating their key elements harmoniously into a single image."
190
  logger.info("Default prompt generated for two images")
191
  else:
192
- final_prompt = "Please creatively composite these three images, combining their main elements into a cohesive and natural scene."
193
  logger.info("Default prompt generated for three images")
194
-
195
- result_img, status = generate_with_images(final_prompt, valid_images)
196
- return result_img, status, final_prompt
 
 
 
 
197
  except Exception as e:
198
  logger.exception("์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
199
- return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", prompt
200
 
201
- def process_and_show_prompt(image1, image2, image3, prompt):
202
- try:
203
- result_img, status, final_prompt = process_images_with_prompt(image1, image2, image3, prompt)
204
- return result_img, status, final_prompt
205
- except Exception as e:
206
- logger.exception("์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
207
- return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", prompt
208
 
 
209
  with gr.Blocks() as demo:
210
  gr.HTML(
211
  """
212
  <div style="text-align: center; margin-bottom: 1rem;">
213
- <h1>Gemini for Image Editing</h1>
214
- <p>Upload an image and enter a prompt to generate outputs.</p>
215
  </div>
216
  """
217
  )
218
 
219
  with gr.Row():
220
  with gr.Column():
221
- image_input = gr.Image(
222
- type="pil",
223
- label="Upload Image",
224
- image_mode="RGBA",
225
- elem_id="image-input"
226
- )
227
- gemini_api_key = gr.Textbox(
228
- lines=1,
229
- placeholder="Enter Gemini API Key (optional)",
230
- label="Gemini API Key (optional)"
231
- )
232
  prompt_input = gr.Textbox(
233
- lines=2,
234
- placeholder="Enter prompt here...",
235
- label="Prompt"
236
  )
237
- submit_btn = gr.Button("Generate")
 
 
 
238
  with gr.Column():
239
- output_gallery = gr.Gallery(label="Generated Outputs")
240
- output_text = gr.Textbox(
241
- label="Gemini Output",
242
- placeholder="Text response will appear here if no image is generated."
243
- )
244
- prompt_display = gr.Textbox(label="Used Prompt (English)", visible=True)
245
-
246
- gr.Markdown("## Try these examples", elem_classes="gr-examples-header")
247
-
248
- examples = [
249
- ["down/1_in-1.png", "#1 ์ด๋ฏธ์ง€์˜ [์ฒญ์ƒ‰ ์ƒ์–ด๋ ˆ๊ณ ๋ฅผ ๊ฒ€์€ ๊ณ ๋ž˜๋ ˆ๊ณ ]์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ผ.", ""],
250
- ["down/2_in-1.png", "#1 ์ด๋ฏธ์ง€์˜ [์ค‘๊ตญ์–ด๋ฅผ ๋ชจ๋‘]๋ฅผ ์ œ๊ฑฐํ•˜๋ผ.", ""],
251
- ]
252
-
253
- gr.Examples(
254
- fn=lambda img, pr: process_and_show_prompt(img, None, None, pr),
255
- examples=examples,
256
- inputs=[image_input, prompt_input],
257
- outputs=[output_gallery, output_text, prompt_display],
258
- run_on_click=True,
259
- elem_id="examples-grid"
260
- )
261
 
262
- # submit_btn ํด๋ฆญ ์‹œ image2, image3๋Š” None์œผ๋กœ ์ „๋‹ฌ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  submit_btn.click(
264
- fn=lambda img, pr, key: process_and_show_prompt(img, None, None, pr),
265
- inputs=[image_input, prompt_input, gemini_api_key],
266
- outputs=[output_gallery, output_text, prompt_display],
 
 
 
 
 
 
 
 
 
 
 
 
267
  )
268
 
269
- demo.queue(max_size=50).launch()
 
 
 
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
 
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 = "Please creatively transform this image into a more vivid and artistic version."
190
  logger.info("Default prompt generated for single image")
191
  elif len(valid_images) == 2:
192
+ prompt = "Please seamlessly composite these two images, integrating their key elements harmoniously into a single image."
193
  logger.info("Default prompt generated for two images")
194
  else:
195
+ prompt = "Please creatively composite these three images, combining their main elements into a cohesive and natural scene."
196
  logger.info("Default prompt generated for three images")
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
 
210
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค
211
  with gr.Blocks() as demo:
212
  gr.HTML(
213
  """
214
  <div style="text-align: center; margin-bottom: 1rem;">
215
+ <h1>๊ฐ„๋‹จํ•œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ</h1>
216
+ <p>์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ  ๋ฐ”๋กœ ์‹คํ–‰ํ•˜๋ฉด ์ž๋™์œผ๋กœ ํ•ฉ์„ฑํ•ฉ๋‹ˆ๋‹ค.</p>
217
  </div>
218
  """
219
  )
220
 
221
  with gr.Row():
222
  with gr.Column():
223
+ # 3๊ฐœ์˜ ์ด๋ฏธ์ง€ ์ž…๋ ฅ
224
+ with gr.Row():
225
+ image1_input = gr.Image(type="pil", label="#1", image_mode="RGB")
226
+ image2_input = gr.Image(type="pil", label="#2", image_mode="RGB")
227
+ image3_input = gr.Image(type="pil", label="#3", image_mode="RGB")
228
+
229
+ # ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ (์„ ํƒ ์‚ฌํ•ญ)
 
 
 
 
230
  prompt_input = gr.Textbox(
231
+ lines=3,
232
+ placeholder="ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ๋น„์›Œ๋‘๋ฉด ์ž๋™ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
233
+ label="ํ”„๋กฌํ”„ํŠธ (์„ ํƒ ์‚ฌํ•ญ)"
234
  )
235
+
236
+ # ์ƒ์„ฑ ๋ฒ„ํŠผ
237
+ submit_btn = gr.Button("์ด๋ฏธ์ง€ ์ƒ์„ฑ", variant="primary")
238
+
239
  with gr.Column():
240
+ # ๊ฒฐ๊ณผ ์ถœ๋ ฅ
241
+ output_image = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€")
242
+ output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€")
243
+
244
+ # ์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ ํ‘œ์‹œ
245
+ prompt_display = gr.Textbox(label="์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ", visible=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
+ # ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
248
+ def process_and_show_prompt(image1, image2, image3, prompt):
249
+ # ์ด๋ฏธ์ง€ ๊ฐœ์ˆ˜ ํ™•์ธ
250
+ images = [image1, image2, image3]
251
+ valid_images = [img for img in images if img is not None]
252
+
253
+ try:
254
+ # ์ž๋™ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋˜๋Š” ํ”„๋กฌํ”„ํŠธ ์ „์ฒ˜๋ฆฌ
255
+ auto_prompt = prompt
256
+ if not prompt or not prompt.strip():
257
+ if len(valid_images) == 1:
258
+ auto_prompt = "Please creatively transform this image into a more vivid and artistic version."
259
+ elif len(valid_images) == 2:
260
+ auto_prompt = "Please seamlessly composite these two images, integrating their key elements harmoniously into a single image."
261
+ else:
262
+ auto_prompt = "Please creatively composite these three images, combining their main elements into a cohesive and natural scene."
263
+ else:
264
+ auto_prompt = preprocess_prompt(prompt, image1, image2, image3)
265
+
266
+ # ์ด๋ฏธ์ง€ ์ƒ์„ฑ ํ•จ์ˆ˜ ํ˜ธ์ถœ
267
+ result_img, status = process_images_with_prompt(image1, image2, image3, prompt)
268
+
269
+ return result_img, status, auto_prompt
270
+ except Exception as e:
271
+ logger.exception("์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
272
+ return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", prompt
273
+
274
  submit_btn.click(
275
+ fn=process_and_show_prompt,
276
+ inputs=[image1_input, image2_input, image3_input, prompt_input],
277
+ outputs=[output_image, output_text, prompt_display],
278
+ )
279
+
280
+ gr.Markdown(
281
+ """
282
+ ### ์‚ฌ์šฉ ๋ฐฉ๋ฒ•:
283
+
284
+ 1. **์ž๋™ ํ•ฉ์„ฑ**: ์ด๋ฏธ์ง€๋งŒ ์—…๋กœ๋“œํ•˜๊ณ  ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋น„์›Œ๋‘๋ฉด ์ž๋™์œผ๋กœ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.
285
+ 2. **์ด๋ฏธ์ง€ ์ฐธ์กฐ**: #1, #2, #3์œผ๋กœ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
286
+ 3. **์ผ๋ถ€ ์ด๋ฏธ์ง€๋งŒ**: ํ•„์š”ํ•œ ์ด๋ฏธ์ง€๋งŒ ์—…๋กœ๋“œํ•ด๋„ ๊ธฐ๋Šฅ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
287
+
288
+ > **ํŒ**: ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
289
+ """
290
  )
291
 
292
+ # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
293
+ if __name__ == "__main__":
294
+ demo.launch(share=True)