Kims12 commited on
Commit
811d552
ยท
verified ยท
1 Parent(s): 1e9bbd5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -108
app.py CHANGED
@@ -1,12 +1,9 @@
1
  import json
2
  import os
3
  import time
4
- import uuid
5
  import tempfile
6
  from PIL import Image
7
  import gradio as gr
8
- import base64
9
- import mimetypes
10
  import logging
11
  from io import BytesIO
12
 
@@ -30,8 +27,8 @@ def save_binary_file(file_name, data):
30
  logger.debug(f"ํŒŒ์ผ ์ €์žฅ ์™„๋ฃŒ: {file_name}")
31
 
32
 
33
- def generate(text, original_image_path, background_image_path=None, style_image_path=None, model="gemini-2.0-flash-exp-image-generation"):
34
- logger.debug(f"generate ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ…์ŠคํŠธ: '{text}', ์›๋ณธ ํŒŒ์ผ๋ช…: '{original_image_path}', ๋ชจ๋ธ: '{model}'")
35
 
36
  try:
37
  # API ํ‚ค๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๋ถˆ๋Ÿฌ์˜ด
@@ -46,33 +43,12 @@ def generate(text, original_image_path, background_image_path=None, style_image_
46
  logger.debug("Gemini ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์™„๋ฃŒ.")
47
 
48
  # PIL ์ด๋ฏธ์ง€ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
49
- original_img = Image.open(original_image_path)
 
50
 
51
  # ์ปจํ…์ธ  ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ (๊ณต์‹ ๋ฌธ์„œ ๋ฐฉ์‹๋Œ€๋กœ)
52
- contents = []
53
-
54
- # ํ…์ŠคํŠธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ช…ํ™•ํ•˜๊ฒŒ ์ž‘์„ฑ
55
- prompt = text
56
- if background_image_path and "๋ฐฐ๊ฒฝ" not in text.lower():
57
- prompt += " ์›๋ณธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€๋กœ ์™„์ „ํžˆ ๊ต์ฒดํ•ด ์ฃผ์„ธ์š”. ์ด๋ฏธ์ง€๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ฃผ์„ธ์š”."
58
- if style_image_path and "์Šคํƒ€์ผ" not in text.lower():
59
- prompt += " ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์Šคํƒ€์ผ์„ ์ „์ฒด์ ์œผ๋กœ ์ ์šฉํ•ด ์ฃผ์„ธ์š”."
60
-
61
- contents.append(prompt)
62
- contents.append(original_img)
63
-
64
- # ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€ (์žˆ๋Š” ๊ฒฝ์šฐ)
65
- if background_image_path:
66
- background_img = Image.open(background_image_path)
67
- contents.append(background_img)
68
- logger.debug("๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€๋จ")
69
-
70
- # ์Šคํƒ€์ผ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€ (์žˆ๋Š” ๊ฒฝ์šฐ)
71
- if style_image_path:
72
- style_img = Image.open(style_image_path)
73
- contents.append(style_img)
74
- logger.debug("์Šคํƒ€์ผ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€๋จ")
75
-
76
  logger.debug(f"์ปจํ…์ธ  ๊ฐ์ฒด ์ƒ์„ฑ ์™„๋ฃŒ: {len(contents)} ์•„์ดํ…œ")
77
 
78
  # ์ƒ์„ฑ ์„ค์ •
@@ -81,7 +57,7 @@ def generate(text, original_image_path, background_image_path=None, style_image_
81
  top_p=0.95,
82
  top_k=40,
83
  max_output_tokens=8192,
84
- response_modalities=["text", "image"], # ๋ฌธ์„œ์— ๋งž๊ฒŒ ๋Œ€์†Œ๋ฌธ์ž ์ˆ˜์ •
85
  )
86
  logger.debug(f"์ƒ์„ฑ ์„ค์ •: {generate_content_config}")
87
 
@@ -89,7 +65,7 @@ def generate(text, original_image_path, background_image_path=None, style_image_
89
  temp_path = tmp.name
90
  logger.debug(f"์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ๋จ: {temp_path}")
91
 
92
- # ์ŠคํŠธ๋ฆฌ๋ฐ ๋Œ€์‹  ๋‹จ์ผ ์š”์ฒญ์œผ๋กœ ๋ณ€๊ฒฝ (์ด๋ฏธ์ง€ ์ƒ์„ฑ์— ๋” ์ ํ•ฉ)
93
  response = client.models.generate_content(
94
  model=model,
95
  contents=contents,
@@ -98,12 +74,14 @@ def generate(text, original_image_path, background_image_path=None, style_image_
98
 
99
  logger.debug("์‘๋‹ต ์ฒ˜๋ฆฌ ์‹œ์ž‘...")
100
 
101
- # ์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ
102
  image_saved = False
 
 
103
  for part in response.candidates[0].content.parts:
104
  if hasattr(part, 'text') and part.text:
 
105
  logger.info(f"์ˆ˜์‹ ๋œ ํ…์ŠคํŠธ: {part.text}")
106
- print(part.text)
107
  elif hasattr(part, 'inline_data') and part.inline_data:
108
  save_binary_file(temp_path, part.inline_data.data)
109
  logger.info(f"MIME ํƒ€์ž… {part.inline_data.mime_type}์˜ ํŒŒ์ผ์ด ์ €์žฅ๋จ: {temp_path}")
@@ -111,84 +89,67 @@ def generate(text, original_image_path, background_image_path=None, style_image_
111
 
112
  if not image_saved:
113
  logger.warning("์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
114
- return None
115
 
116
  logger.debug("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ.")
117
- return temp_path
118
 
119
  except Exception as e:
120
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
121
- return None # ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ None ๋ฐ˜ํ™˜
122
 
123
 
124
- def process_image_and_prompt(original_pil, prompt, background_pil=None, style_pil=None):
125
- logger.debug(f"process_image_and_prompt ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: '{prompt}'")
126
  try:
127
- # ์›๋ณธ ์ด๋ฏธ์ง€ ์ €์žฅ
128
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
129
- original_path = tmp.name
130
- original_pil.save(original_path)
131
- logger.debug(f"์›๋ณธ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {original_path}")
132
-
133
- # ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ €์žฅ (์ œ๊ณต๋œ ๊ฒฝ์šฐ)
134
- background_path = None
135
- if background_pil is not None:
136
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_bg:
137
- background_path = tmp_bg.name
138
- background_pil.save(background_path)
139
- logger.debug(f"๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {background_path}")
140
-
141
- # ์Šคํƒ€์ผ ์ด๋ฏธ์ง€ ์ €์žฅ (์ œ๊ณต๋œ ๊ฒฝ์šฐ)
142
- style_path = None
143
- if style_pil is not None:
144
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_style:
145
- style_path = tmp_style.name
146
- style_pil.save(style_path)
147
- logger.debug(f"์Šคํƒ€์ผ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {style_path}")
148
-
149
- # ํ”„๋กฌํ”„ํŠธ ๋ณด๊ฐ• (์˜์–ด์™€ ํ•œ๊ตญ์–ด ๋‘ ์–ธ์–ด๋กœ ์ œ๊ณต)
150
- if prompt and not prompt.strip():
151
- if background_path and style_path:
152
- prompt = "์›๋ณธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋กœ ๊ต์ฒดํ•˜๊ณ  ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์Šคํƒ€์ผ์„ ์ ์šฉํ•ด ์ฃผ์„ธ์š”. Please replace the background with the second image while keeping the person, and apply the style of the third image."
153
- elif background_path:
154
- prompt = "์›๋ณธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋กœ ๊ต์ฒดํ•ด ์ฃผ์„ธ์š”. Please replace the background with the second image while keeping the person."
155
- elif style_path:
156
- prompt = "์›๋ณธ ์ด๋ฏธ์ง€์— ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์Šคํƒ€์ผ์„ ์ ์šฉํ•ด ์ฃผ์„ธ์š”. Please apply the style of the second image to the original image."
157
-
158
- model = "gemini-2.0-flash-exp-image-generation"
159
-
160
- gemma_edited_image_path = generate(
161
- text=prompt,
162
- original_image_path=original_path,
163
- background_image_path=background_path,
164
- style_image_path=style_path,
165
- model=model
166
  )
167
 
168
- if gemma_edited_image_path:
169
- logger.debug(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ. ๊ฒฝ๋กœ: {gemma_edited_image_path}")
170
- result_img = Image.open(gemma_edited_image_path)
 
171
  if result_img.mode == "RGBA":
172
  result_img = result_img.convert("RGB")
173
 
174
  # ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ
175
  try:
176
- os.unlink(original_path)
177
- if background_path:
178
- os.unlink(background_path)
179
- if style_path:
180
- os.unlink(style_path)
181
  except Exception as e:
182
  logger.warning(f"์ž„์‹œ ํŒŒ์ผ ์‚ญ์ œ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
183
 
184
- return [result_img]
185
  else:
186
- logger.error("generate ํ•จ์ˆ˜์—์„œ None ๋ฐ˜ํ™˜๋จ.")
187
- return [] # ์˜ค๋ฅ˜ ์‹œ ๋นˆ ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜
188
 
189
  except Exception as e:
190
- logger.exception("process_image_and_prompt ํ•จ์ˆ˜์—์„œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
191
- return [] # ์˜ค๋ฅ˜ ์‹œ ๋นˆ ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜
192
 
193
 
194
  # --- Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ ---
@@ -200,46 +161,47 @@ with gr.Blocks() as demo:
200
  <img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" style="width: 100px; height: 100px;">
201
  </div>
202
  <div>
203
- <h1>Gemini๋ฅผ ์ด์šฉํ•œ ์ด๋ฏธ์ง€ ํŽธ์ง‘</h1>
204
  <p>Gemini API ํ‚ค๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜(GEMINI_API_KEY)๋กœ ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.</p>
205
  </div>
206
  </div>
207
  """
208
  )
209
- gr.Markdown("์›๋ณธ, ๋ฐฐ๊ฒฝ, ์Šคํƒ€์ผ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ , ํŽธ์ง‘ํ•  ๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”.")
210
 
211
  with gr.Row():
212
  with gr.Column():
213
- original_input = gr.Image(type="pil", label="์›๋ณธ ์ด๋ฏธ์ง€", image_mode="RGB")
214
- background_input = gr.Image(type="pil", label="๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€", image_mode="RGB")
215
- style_input = gr.Image(type="pil", label="์Šคํƒ€์ผ ์ด๋ฏธ์ง€", image_mode="RGB")
216
  prompt_input = gr.Textbox(
217
  lines=2,
218
- placeholder="ํŽธ์ง‘ํ•  ๋‚ด์šฉ์„ ์ž…๋ ฅํ•˜์„ธ์š”...",
219
- label="ํŽธ์ง‘ ํ”„๋กฌํ”„ํŠธ"
220
  )
221
- submit_btn = gr.Button("์ด๋ฏธ์ง€ ํŽธ์ง‘ ์‹คํ–‰")
222
  with gr.Column():
223
- output_gallery = gr.Gallery(label="ํŽธ์ง‘ ๊ฒฐ๊ณผ")
224
- output_text = gr.Textbox(label="API ์‘๋‹ต ํ…์ŠคํŠธ", visible=True)
225
 
226
  submit_btn.click(
227
- fn=process_image_and_prompt,
228
- inputs=[original_input, prompt_input, background_input, style_input],
229
- outputs=output_gallery,
230
  )
231
 
232
  gr.HTML("""
233
  <div style="margin-top: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 8px;">
234
  <h3>์‚ฌ์šฉ ํŒ:</h3>
235
  <ul>
236
- <li><strong>ํ”„๋กฌํ”„ํŠธ ์ž‘์„ฑ ์˜ˆ์‹œ:</strong> "์›๋ณธ ์ธ๋ฌผ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋กœ ๊ต์ฒดํ•ด ์ฃผ์„ธ์š”."</li>
237
- <li><strong>๋ฐฐ๊ฒฝ ๊ต์ฒด ๋ช…ํ™•ํ™”:</strong> "๋ฐฐ๊ฒฝ๋งŒ ์™„์ „ํžˆ ๊ต์ฒดํ•˜๊ณ  ์›๋ณธ ์ธ๋ฌผ์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•ด ์ฃผ์„ธ์š”."</li>
238
- <li><strong>์Šคํƒ€์ผ ์ ์šฉ:</strong> "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์˜ˆ์ˆ  ์Šคํƒ€์ผ์„ ์ „์ฒด ์ด๋ฏธ์ง€์— ์ ์šฉํ•ด ์ฃผ์„ธ์š”."</li>
239
  <li><strong>์˜์–ด ํ”„๋กฌํ”„ํŠธ:</strong> ๋” ๋‚˜์€ ๊ฒฐ๊ณผ๋ฅผ ์œ„ํ•ด ์˜์–ด์™€ ํ•œ๊ตญ์–ด๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด ๋ณด์„ธ์š”.</li>
 
240
  </ul>
241
  </div>
242
  """)
243
 
244
  # --- ์‹คํ–‰ ---
245
- demo.launch(share=True)
 
 
1
  import json
2
  import os
3
  import time
 
4
  import tempfile
5
  from PIL import Image
6
  import gradio as gr
 
 
7
  import logging
8
  from io import BytesIO
9
 
 
27
  logger.debug(f"ํŒŒ์ผ ์ €์žฅ ์™„๋ฃŒ: {file_name}")
28
 
29
 
30
+ def merge_product_and_person(person_img_path, product_img_path, prompt, model="gemini-2.0-flash-exp-image-generation"):
31
+ logger.debug(f"merge_product_and_person ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: '{prompt}'")
32
 
33
  try:
34
  # API ํ‚ค๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๋ถˆ๋Ÿฌ์˜ด
 
43
  logger.debug("Gemini ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์™„๋ฃŒ.")
44
 
45
  # PIL ์ด๋ฏธ์ง€ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
46
+ person_img = Image.open(person_img_path)
47
+ product_img = Image.open(product_img_path)
48
 
49
  # ์ปจํ…์ธ  ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ (๊ณต์‹ ๋ฌธ์„œ ๋ฐฉ์‹๋Œ€๋กœ)
50
+ # ์ˆœ์„œ: ์‚ฌ๋žŒ ์ด๋ฏธ์ง€, ์ƒํ’ˆ ์ด๋ฏธ์ง€, ํ”„๋กฌํ”„ํŠธ ํ…์ŠคํŠธ
51
+ contents = [person_img, product_img, prompt]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  logger.debug(f"์ปจํ…์ธ  ๊ฐ์ฒด ์ƒ์„ฑ ์™„๋ฃŒ: {len(contents)} ์•„์ดํ…œ")
53
 
54
  # ์ƒ์„ฑ ์„ค์ •
 
57
  top_p=0.95,
58
  top_k=40,
59
  max_output_tokens=8192,
60
+ response_modalities=["text", "image"],
61
  )
62
  logger.debug(f"์ƒ์„ฑ ์„ค์ •: {generate_content_config}")
63
 
 
65
  temp_path = tmp.name
66
  logger.debug(f"์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ๋จ: {temp_path}")
67
 
68
+ # ๋‹จ์ผ ์š”์ฒญ์œผ๋กœ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
69
  response = client.models.generate_content(
70
  model=model,
71
  contents=contents,
 
74
 
75
  logger.debug("์‘๋‹ต ์ฒ˜๋ฆฌ ์‹œ์ž‘...")
76
 
77
+ # ์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€์™€ ํ…์ŠคํŠธ ์ถ”์ถœ
78
  image_saved = False
79
+ response_text = ""
80
+
81
  for part in response.candidates[0].content.parts:
82
  if hasattr(part, 'text') and part.text:
83
+ response_text += part.text
84
  logger.info(f"์ˆ˜์‹ ๋œ ํ…์ŠคํŠธ: {part.text}")
 
85
  elif hasattr(part, 'inline_data') and part.inline_data:
86
  save_binary_file(temp_path, part.inline_data.data)
87
  logger.info(f"MIME ํƒ€์ž… {part.inline_data.mime_type}์˜ ํŒŒ์ผ์ด ์ €์žฅ๋จ: {temp_path}")
 
89
 
90
  if not image_saved:
91
  logger.warning("์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
92
+ return None, response_text
93
 
94
  logger.debug("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ.")
95
+ return temp_path, response_text
96
 
97
  except Exception as e:
98
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
99
+ return None, str(e) # ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ None๊ณผ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜
100
 
101
 
102
+ def process_images_and_prompt(person_pil, product_pil, prompt):
103
+ logger.debug(f"process_images_and_prompt ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: '{prompt}'")
104
  try:
105
+ # ๊ธฐ๋ณธ ํ”„๋กฌํ”„ํŠธ ์„ค์ • (๋น„์–ด์žˆ๋Š” ๊ฒฝ์šฐ)
106
+ if not prompt or not prompt.strip():
107
+ prompt = "์ด ์‚ฌ๋žŒ์ด ์ด ์ƒํ’ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ์Šต์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณด์—ฌ์ฃผ์„ธ์š”. ์ƒํ’ˆ์„ ์ž˜ ๋ณด์ด๊ฒŒ ํ•ด์ฃผ์„ธ์š”. Make a natural composite image showing this person using this product. Make sure the product is clearly visible."
108
+
109
+ # ํ”„๋กฌํ”„ํŠธ์— ์˜์–ด๊ฐ€ ์—†์œผ๋ฉด ์˜์–ด ํ”„๋กฌํ”„ํŠธ ์ถ”๊ฐ€ (๋” ๋‚˜์€ ๊ฒฐ๊ณผ๋ฅผ ์œ„ํ•ด)
110
+ if not any(ord(c) < 128 for c in prompt):
111
+ prompt += " Create a realistic composite image of this person with this product."
112
+
113
+ # ์ด๋ฏธ์ง€ ์ €์žฅ
114
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_person:
115
+ person_path = tmp_person.name
116
+ person_pil.save(person_path)
117
+ logger.debug(f"์‚ฌ๋žŒ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {person_path}")
118
+
119
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_product:
120
+ product_path = tmp_product.name
121
+ product_pil.save(product_path)
122
+ logger.debug(f"์ƒํ’ˆ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {product_path}")
123
+
124
+ # ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ์‹คํ–‰
125
+ result_path, response_text = merge_product_and_person(
126
+ person_img_path=person_path,
127
+ product_img_path=product_path,
128
+ prompt=prompt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  )
130
 
131
+ # ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜ ๋ฐ ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ
132
+ if result_path:
133
+ logger.debug(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ. ๊ฒฝ๋กœ: {result_path}")
134
+ result_img = Image.open(result_path)
135
  if result_img.mode == "RGBA":
136
  result_img = result_img.convert("RGB")
137
 
138
  # ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ
139
  try:
140
+ os.unlink(person_path)
141
+ os.unlink(product_path)
 
 
 
142
  except Exception as e:
143
  logger.warning(f"์ž„์‹œ ํŒŒ์ผ ์‚ญ์ œ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
144
 
145
+ return [result_img], response_text
146
  else:
147
+ logger.error("merge_product_and_person ํ•จ์ˆ˜์—์„œ None ๋ฐ˜ํ™˜๋จ.")
148
+ return [], response_text # ์˜ค๋ฅ˜ ์‹œ ๋นˆ ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜
149
 
150
  except Exception as e:
151
+ logger.exception("process_images_and_prompt ํ•จ์ˆ˜์—์„œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
152
+ return [], str(e) # ์˜ค๋ฅ˜ ์‹œ ๋นˆ ๋ฆฌ์ŠคํŠธ์™€ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜
153
 
154
 
155
  # --- Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ ---
 
161
  <img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" style="width: 100px; height: 100px;">
162
  </div>
163
  <div>
164
+ <h1>Gemini๋ฅผ ์ด์šฉํ•œ ์ƒํ’ˆ-์‚ฌ๋žŒ ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ</h1>
165
  <p>Gemini API ํ‚ค๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜(GEMINI_API_KEY)๋กœ ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.</p>
166
  </div>
167
  </div>
168
  """
169
  )
170
+ gr.Markdown("์‚ฌ๋žŒ ์ด๋ฏธ์ง€์™€ ์ƒํ’ˆ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ , ์–ด๋–ป๊ฒŒ ํ•ฉ์„ฑํ• ์ง€ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.")
171
 
172
  with gr.Row():
173
  with gr.Column():
174
+ person_input = gr.Image(type="pil", label="์‚ฌ๋žŒ ์ด๋ฏธ์ง€", image_mode="RGB")
175
+ product_input = gr.Image(type="pil", label="์ƒํ’ˆ ์ด๋ฏธ์ง€", image_mode="RGB")
 
176
  prompt_input = gr.Textbox(
177
  lines=2,
178
+ placeholder="ํ•ฉ์„ฑ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”. (์˜ˆ: '์ด ์‚ฌ๋žŒ์ด ์ƒํ’ˆ์„ ๋“ค๊ณ  ์žˆ๋Š” ๋ชจ์Šต' ๋˜๋Š” '์ด ์‚ฌ๋žŒ์ด ์ด ์ƒํ’ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ์Šต')",
179
+ label="ํ•ฉ์„ฑ ๋ฐฉ๋ฒ• ์„ค๋ช…"
180
  )
181
+ submit_btn = gr.Button("์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ์‹คํ–‰")
182
  with gr.Column():
183
+ output_gallery = gr.Gallery(label="ํ•ฉ์„ฑ ๊ฒฐ๊ณผ")
184
+ output_text = gr.Textbox(label="AI ์‘๋‹ต ํ…์ŠคํŠธ", visible=True)
185
 
186
  submit_btn.click(
187
+ fn=process_images_and_prompt,
188
+ inputs=[person_input, product_input, prompt_input],
189
+ outputs=[output_gallery, output_text],
190
  )
191
 
192
  gr.HTML("""
193
  <div style="margin-top: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 8px;">
194
  <h3>์‚ฌ์šฉ ํŒ:</h3>
195
  <ul>
196
+ <li><strong>์ž์—ฐ์Šค๋Ÿฌ์šด ํ•ฉ์„ฑ:</strong> "์ด ์‚ฌ๋žŒ์ด ์ด ์ƒํ’ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์Šต์„ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."</li>
197
+ <li><strong>ํŠน์ • ์œ„์น˜ ์ง€์ •:</strong> "์ด ์‚ฌ๋žŒ์ด ์ด ์ƒํ’ˆ์„ ์†์— ๋“ค๊ณ  ์žˆ๋Š” ๋ชจ์Šต์„ ๋ณด์—ฌ์ฃผ์„ธ์š”."</li>
198
+ <li><strong>์ƒํ’ˆ ๊ฐ•์กฐ:</strong> "์ด ์‚ฌ๋žŒ์ด ์ด ์ƒํ’ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ์Šต์„ ๋ณด์—ฌ์ฃผ๋˜, ์ƒํ’ˆ์ด ์ž˜ ๋ณด์ด๋„๋ก ํ•ด์ฃผ์„ธ์š”."</li>
199
  <li><strong>์˜์–ด ํ”„๋กฌํ”„ํŠธ:</strong> ๋” ๋‚˜์€ ๊ฒฐ๊ณผ๋ฅผ ์œ„ํ•ด ์˜์–ด์™€ ํ•œ๊ตญ์–ด๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด ๋ณด์„ธ์š”.</li>
200
+ <li><strong>๊ตฌ์ฒด์ ์ธ ์ง€์‹œ:</strong> ํ•ฉ์„ฑ ๋ฐฉ๋ฒ•์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช…ํ• ์ˆ˜๋ก ๋” ์ข‹์€ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</li>
201
  </ul>
202
  </div>
203
  """)
204
 
205
  # --- ์‹คํ–‰ ---
206
+ if __name__ == "__main__":
207
+ demo.launch(share=True)