Kims12 commited on
Commit
ddd42e5
ยท
verified ยท
1 Parent(s): 0ae66bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +229 -78
app.py CHANGED
@@ -28,6 +28,7 @@ def save_binary_file(file_name, data):
28
 
29
 
30
  def generate_image_from_prompt(prompt, model="gemini-2.0-flash-exp-image-generation"):
 
31
  logger.debug(f"generate_image_from_prompt ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: '{prompt}'")
32
 
33
  try:
@@ -126,17 +127,224 @@ def generate_image_from_prompt(prompt, model="gemini-2.0-flash-exp-image-generat
126
  return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}" # ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ None๊ณผ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜
127
 
128
 
129
- def process_image_generation(person_pil, product_pil, background_pil, prompt):
130
- logger.debug(f"process_image_generation ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: '{prompt}'")
 
 
131
  try:
132
- # ์ด๋ฏธ์ง€๋“ค์ด ์ œ๊ณต๋˜์—ˆ๋‹ค๋ฉด ๊ธฐ์กด ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌ
133
- if person_pil is not None or product_pil is not None or background_pil is not None:
134
- return process_images_and_prompt(person_pil, product_pil, background_pil, prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
- # ์ด๋ฏธ์ง€ ์—†์ด ํ”„๋กฌํ”„ํŠธ๋งŒ ์žˆ๋Š” ๊ฒฝ์šฐ
137
- result_path, response_text = generate_image_from_prompt(prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
- # ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜ ๋ฐ ์ฒ˜๋ฆฌ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  if result_path:
141
  logger.debug(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ. ๊ฒฝ๋กœ: {result_path}")
142
  try:
@@ -144,80 +352,23 @@ def process_image_generation(person_pil, product_pil, background_pil, prompt):
144
  if result_img.mode == "RGBA":
145
  result_img = result_img.convert("RGB")
146
 
 
 
 
 
 
 
 
 
 
147
  return [result_img], response_text or "์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
148
  except Exception as e:
149
  logger.exception(f"๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
150
  return [], f"๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {str(e)}"
151
  else:
152
- logger.error("generate_image_from_prompt ํ•จ์ˆ˜์—์„œ None ๋ฐ˜ํ™˜๋จ.")
153
- return [], response_text or "์ด๋ฏธ์ง€ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ”„๋กฌํ”„ํŠธ๋กœ ์‹œ๋„ํ•ด๋ณด์„ธ์š”."
154
 
155
  except Exception as e:
156
- logger.exception("process_image_generation ํ•จ์ˆ˜์—์„œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
157
- return [], f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}" # ์˜ค๋ฅ˜ ์‹œ ๋นˆ ๋ฆฌ์ŠคํŠธ์™€ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜
158
-
159
-
160
- # --- Gradio ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌ์„ฑ (๊ธฐ์กด ์ฝ”๋“œ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€) ---
161
- with gr.Blocks() as demo:
162
- gr.HTML(
163
- """
164
- <div style='display: flex; align-items: center; justify-content: center; gap: 20px'>
165
- <div style="background-color: var(--block-background-fill); border-radius: 8px">
166
- <img src="https://www.gstatic.com/lamda/images/gemini_favicon_f069958c85030456e93de685481c559f160ea06b.png" style="width: 100px; height: 100px;">
167
- </div>
168
- <div>
169
- <h1>Gemini๋ฅผ ์ด์šฉํ•œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ</h1>
170
- <p>์‚ฌ๋žŒ, ์ƒํ’ˆ, ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€๋ฅผ ํ•ฉ์„ฑํ•˜๊ฑฐ๋‚˜ ํ…์ŠคํŠธ๋กœ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
171
- </div>
172
- </div>
173
- """
174
- )
175
- gr.Markdown("์‚ฌ๋žŒ ์ด๋ฏธ์ง€, ์ƒํ’ˆ ์ด๋ฏธ์ง€, ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ฑฐ๋‚˜ ํ…์ŠคํŠธ๋กœ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
176
-
177
- with gr.Row():
178
- with gr.Column():
179
- # ์ด๋ฏธ์ง€ ์ž…๋ ฅ ์„น์…˜ (์„ ํƒ ์‚ฌํ•ญ)
180
- person_input = gr.Image(type="pil", label="์‚ฌ๋žŒ ์ด๋ฏธ์ง€ (์„ ํƒ ์‚ฌํ•ญ)", image_mode="RGB")
181
- product_input = gr.Image(type="pil", label="์ƒํ’ˆ ์ด๋ฏธ์ง€ (์„ ํƒ ์‚ฌํ•ญ)", image_mode="RGB")
182
- background_input = gr.Image(type="pil", label="๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ (์„ ํƒ ์‚ฌํ•ญ)", image_mode="RGB")
183
-
184
- # ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ (ํ•„์ˆ˜)
185
- prompt_input = gr.Textbox(
186
- lines=3,
187
- placeholder="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์„ค๋ช…์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”. ์˜ˆ: '๊ฝƒ๋ฐญ์—์„œ ํ–‡๋น›์„ ๋ฐ›์œผ๋ฉฐ ๋ฏธ์†Œ ์ง“๋Š” ์ Š์€ ์—ฌ์„ฑ', 'ํ˜„๋Œ€์ ์ธ ๊ฑฐ์‹ค์—์„œ ๊ณ ๊ธ‰ ์Šค๋งˆํŠธํฐ์„ ์‚ฌ์šฉํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค๋งจ'",
188
- label="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์„ค๋ช… (ํ•„์ˆ˜)"
189
- )
190
- submit_btn = gr.Button("์ด๋ฏธ์ง€ ์ƒ์„ฑ")
191
-
192
- with gr.Column():
193
- output_gallery = gr.Gallery(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€")
194
- output_text = gr.Textbox(label="AI ์‘๋‹ต ํ…์ŠคํŠธ", visible=True)
195
-
196
- submit_btn.click(
197
- fn=process_image_generation,
198
- inputs=[person_input, product_input, background_input, prompt_input],
199
- outputs=[output_gallery, output_text],
200
- )
201
-
202
- gr.HTML("""
203
- <div style="margin-top: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 8px;">
204
- <h3>์‚ฌ์šฉ ๋ฐฉ๋ฒ•:</h3>
205
- <ul>
206
- <li><strong>ํ…์ŠคํŠธ๋กœ ์ด๋ฏธ์ง€ ์ƒ์„ฑ:</strong> ์„ค๋ช…๋งŒ ์ž…๋ ฅํ•˜๊ณ  ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด์„ธ์š”.</li>
207
- <li><strong>์ด๋ฏธ์ง€ ํ•ฉ์„ฑ:</strong> ์‚ฌ๋žŒ, ์ƒํ’ˆ, ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€๋ฅผ ์„ ํƒ์ ์œผ๋กœ ์—…๋กœ๋“œํ•˜๊ณ  ํ•ฉ์„ฑ ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.</li>
208
- <li><strong>ํ”„๋กฌํ”„ํŠธ ํŒ:</strong> ๊ตฌ์ฒด์ ์ด๊ณ  ์ƒ์ƒํ•œ ์„ค๋ช…์ผ์ˆ˜๋ก ๋” ์ข‹์€ ์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.</li>
209
- <li><strong>์˜ˆ์‹œ ํ”„๋กฌํ”„ํŠธ:</strong>
210
- <ul>
211
- <li>ํ•œ์ ํ•œ ์นดํŽ˜์—์„œ ๋…ธํŠธ๋ถ์œผ๋กœ ์ž‘์—…ํ•˜๋Š” ํฌ๋ฆฌ์—์ดํ„ฐ</li>
212
- <li>ํ•ด๋ณ€๊ฐ€์—์„œ ๊ณ ๊ธ‰ ์„ ๊ธ€๋ผ์Šค๋ฅผ ๋ผ๊ณ  ํฌ์ฆˆ ์ทจํ•˜๋Š” ๋ชจ๋ธ</li>
213
- <li>ํ˜„๋Œ€์ ์ธ ์ฃผ๋ฐฉ์—์„œ ์ตœ์‹  ๋ธ”๋ Œ๋”๋กœ ์Šค๋ฌด๋””๋ฅผ ๋งŒ๋“œ๋Š” ์š”๋ฆฌ์‚ฌ</li>
214
- </ul>
215
- </li>
216
- <li><strong>์–ธ์–ด ํŒ:</strong> ํ•œ๊ตญ์–ด์™€ ์˜์–ด๋ฅผ ํ˜ผํ•ฉํ•ด ์‚ฌ์šฉํ•˜๋ฉด ๋” ์ •ํ™•ํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</li>
217
- </ul>
218
- </div>
219
- """)
220
-
221
- # --- ์‹คํ–‰ ---
222
- if __name__ == "__main__":
223
- demo.launch(share=True)
 
28
 
29
 
30
  def generate_image_from_prompt(prompt, model="gemini-2.0-flash-exp-image-generation"):
31
+ """ํ…์ŠคํŠธ ํ”„๋กฌํ”„ํŠธ๋งŒ์œผ๋กœ ์ด๋ฏธ์ง€ ์ƒ์„ฑ"""
32
  logger.debug(f"generate_image_from_prompt ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: '{prompt}'")
33
 
34
  try:
 
127
  return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}" # ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ None๊ณผ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜
128
 
129
 
130
+ def merge_images(person_img_path, product_img_path, background_img_path, prompt, model="gemini-2.0-flash-exp-image-generation"):
131
+ """๊ธฐ์กด ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ํ•จ์ˆ˜ (์ด์ „ ์ฝ”๋“œ์™€ ๋™์ผ)"""
132
+ logger.debug(f"merge_images ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: '{prompt}'")
133
+
134
  try:
135
+ # API ํ‚ค๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๋ถˆ๋Ÿฌ์˜ด
136
+ effective_api_key = os.environ.get("GEMINI_API_KEY")
137
+ if effective_api_key:
138
+ logger.debug("ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ API ํ‚ค ๋ถˆ๋Ÿฌ์˜ด")
139
+ else:
140
+ logger.error("API ํ‚ค๊ฐ€ ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
141
+ raise ValueError("API ํ‚ค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")
142
+
143
+ client = genai.Client(api_key=effective_api_key)
144
+ logger.debug("Gemini ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์™„๋ฃŒ.")
145
+
146
+ # ์ด๋ฏธ์ง€ ํŒŒ์ผ ์—…๋กœ๋“œ
147
+ person_file = client.files.upload(file=person_img_path)
148
+ logger.debug(f"์‚ฌ๋žŒ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์™„๋ฃŒ: {person_file.uri}")
149
+
150
+ product_file = client.files.upload(file=product_img_path)
151
+ logger.debug(f"์ƒํ’ˆ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์™„๋ฃŒ: {product_file.uri}")
152
+
153
+ # ์ปจํ…์ธ  ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
154
+ parts = []
155
 
156
+ # ์‚ฌ๋žŒ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
157
+ parts.append(
158
+ types.Part.from_uri(
159
+ file_uri=person_file.uri,
160
+ mime_type=person_file.mime_type
161
+ )
162
+ )
163
+
164
+ # ์ƒํ’ˆ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
165
+ parts.append(
166
+ types.Part.from_uri(
167
+ file_uri=product_file.uri,
168
+ mime_type=product_file.mime_type
169
+ )
170
+ )
171
+
172
+ # ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€๊ฐ€ ์žˆ์œผ๋ฉด ์ถ”๊ฐ€
173
+ if background_img_path:
174
+ background_file = client.files.upload(file=background_img_path)
175
+ logger.debug(f"๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์™„๋ฃŒ: {background_file.uri}")
176
+ parts.append(
177
+ types.Part.from_uri(
178
+ file_uri=background_file.uri,
179
+ mime_type=background_file.mime_type
180
+ )
181
+ )
182
+ logger.debug("๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€๋จ")
183
+
184
+ # ๋งˆ์ง€๋ง‰์— ํ”„๋กฌํ”„ํŠธ ์ถ”๊ฐ€
185
+ parts.append(
186
+ types.Part.from_text(text=prompt)
187
+ )
188
+
189
+ logger.debug(f"์ปจํ…์ธ  ๊ฐ์ฒด ์ƒ์„ฑ ์™„๋ฃŒ: {len(parts)} ์•„์ดํ…œ")
190
+
191
+ # ์ƒ์„ฑ ์„ค์ •
192
+ generate_content_config = types.GenerateContentConfig(
193
+ temperature=1,
194
+ top_p=0.95,
195
+ top_k=40,
196
+ max_output_tokens=8192,
197
+ response_modalities=["image", "text"],
198
+ )
199
+ logger.debug(f"์ƒ์„ฑ ์„ค์ •: {generate_content_config}")
200
 
201
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
202
+ temp_path = tmp.name
203
+ logger.debug(f"์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ๋จ: {temp_path}")
204
+
205
+ # ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ ์ˆ˜์‹ 
206
+ response_stream = client.models.generate_content_stream(
207
+ model=model,
208
+ contents=[
209
+ types.Content(
210
+ role="user",
211
+ parts=parts,
212
+ ),
213
+ ],
214
+ config=generate_content_config,
215
+ )
216
+
217
+ logger.debug("์‘๋‹ต ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ์‹œ์ž‘...")
218
+
219
+ # ์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€์™€ ํ…์ŠคํŠธ ์ถ”์ถœ
220
+ image_saved = False
221
+ response_text = ""
222
+
223
+ for chunk in response_stream:
224
+ logger.debug(f"chunk ์ˆ˜์‹ : {chunk}")
225
+
226
+ # ์‘๋‹ต ๊ฒ€์ฆ
227
+ if not hasattr(chunk, 'candidates') or not chunk.candidates or len(chunk.candidates) == 0:
228
+ logger.warning("chunk์— candidates๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.")
229
+ continue
230
+
231
+ if not hasattr(chunk.candidates[0], 'content') or chunk.candidates[0].content is None:
232
+ logger.warning("chunk.candidates[0]์— content๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.")
233
+ continue
234
+
235
+ if not hasattr(chunk.candidates[0].content, 'parts') or not chunk.candidates[0].content.parts:
236
+ logger.warning("chunk.candidates[0].content์— parts๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.")
237
+ continue
238
+
239
+ for part in chunk.candidates[0].content.parts:
240
+ if hasattr(part, 'text') and part.text:
241
+ response_text += part.text
242
+ logger.info(f"์ˆ˜์‹ ๋œ ํ…์ŠคํŠธ: {part.text}")
243
+ elif hasattr(part, 'inline_data') and part.inline_data:
244
+ save_binary_file(temp_path, part.inline_data.data)
245
+ logger.info(f"MIME ํƒ€์ž… {part.inline_data.mime_type}์˜ ํŒŒ์ผ์ด ์ €์žฅ๋จ: {temp_path}")
246
+ image_saved = True
247
+
248
+ if not image_saved:
249
+ logger.warning("์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
250
+ return None, response_text or "์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ”„๋กฌํ”„ํŠธ๋‚˜ ์ด๋ฏธ์ง€๋กœ ์‹œ๋„ํ•ด๋ณด์„ธ์š”."
251
+
252
+ logger.debug("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ.")
253
+ return temp_path, response_text
254
+
255
+ except Exception as e:
256
+ logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
257
+ return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}" # ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ None๊ณผ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜
258
+
259
+
260
+ def process_images_and_prompt(person_pil, product_pil, background_pil, prompt):
261
+ """
262
+ ์ด๋ฏธ์ง€ ๋ฐ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜
263
+
264
+ ์ด๋ฏธ์ง€ ์ž…๋ ฅ์ด ์—†๋Š” ๊ฒฝ์šฐ ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ์ด๋ฏธ์ง€ ์ƒ์„ฑ์œผ๋กœ ์ „ํ™˜
265
+ ์ด๋ฏธ์ง€ ์ž…๋ ฅ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๊ธฐ์กด์˜ ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ๋กœ์ง ์œ ์ง€
266
+ """
267
+ logger.debug(f"process_images_and_prompt ํ•จ์ˆ˜ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: '{prompt}'")
268
+
269
+ try:
270
+ # ์ด๋ฏธ์ง€ ์ž…๋ ฅ์ด ๋ชจ๋‘ ์—†๋Š” ๊ฒฝ์šฐ ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
271
+ if person_pil is None and product_pil is None and background_pil is None:
272
+ logger.debug("์ด๋ฏธ์ง€ ์—†์Œ. ํ…์ŠคํŠธ ๊ธฐ๋ฐ˜ ์ด๋ฏธ์ง€ ์ƒ์„ฑ์œผ๋กœ ์ „ํ™˜")
273
+ result_path, response_text = generate_image_from_prompt(prompt)
274
+
275
+ if result_path:
276
+ try:
277
+ result_img = Image.open(result_path)
278
+ if result_img.mode == "RGBA":
279
+ result_img = result_img.convert("RGB")
280
+
281
+ return [result_img], response_text or "์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
282
+ except Exception as e:
283
+ logger.exception(f"๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
284
+ return [], f"๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {str(e)}"
285
+ else:
286
+ return [], response_text or "์ด๋ฏธ์ง€ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."
287
+
288
+ # ํ•„์ˆ˜ ์ด๋ฏธ์ง€ ํ™•์ธ (์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ๋ชจ๋“œ)
289
+ if person_pil is None:
290
+ return [], "์‚ฌ๋žŒ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”."
291
+ if product_pil is None:
292
+ return [], "์ƒํ’ˆ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”."
293
+
294
+ # ๊ธฐ๋ณธ ํ”„๋กฌํ”„ํŠธ ์„ค์ • (๋น„์–ด์žˆ๋Š” ๊ฒฝ์šฐ)
295
+ if not prompt or not prompt.strip():
296
+ if background_pil:
297
+ prompt = "์ด ๋ฐฐ๊ฒฝ์— ์ด ์‚ฌ๋žŒ์ด ์ด ์ƒํ’ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ์Šต์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณด์—ฌ์ฃผ์„ธ์š”. ์ƒํ’ˆ์„ ์ž˜ ๋ณด์ด๊ฒŒ ํ•ด์ฃผ์„ธ์š”. Create a natural composite image showing this person using this product in this background setting. Make sure the product is clearly visible."
298
+ else:
299
+ prompt = "์ด ์‚ฌ๋žŒ์ด ์ด ์ƒํ’ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ์Šต์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณด์—ฌ์ฃผ์„ธ์š”. ์ƒํ’ˆ์„ ์ž˜ ๋ณด์ด๊ฒŒ ํ•ด์ฃผ์„ธ์š”. Create a natural composite image showing this person using this product. Make sure the product is clearly visible."
300
+
301
+ # ํ”„๋กฌํ”„ํŠธ์— ์˜์–ด๊ฐ€ ์—†์œผ๋ฉด ์˜์–ด ํ”„๋กฌํ”„ํŠธ ์ถ”๊ฐ€ (๋” ๋‚˜์€ ๊ฒฐ๊ณผ๋ฅผ ์œ„ํ•ด)
302
+ if not any(ord(c) < 128 for c in prompt):
303
+ if background_pil:
304
+ prompt += " Create a realistic composite image of this person with this product in this background."
305
+ else:
306
+ prompt += " Create a realistic composite image of this person with this product."
307
+
308
+ # ์ด๋ฏธ์ง€ ์ €์žฅ
309
+ with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_person:
310
+ person_path = tmp_person.name
311
+ # RGB๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ €์žฅ
312
+ person_img = person_pil
313
+ if person_img.mode != 'RGB':
314
+ person_img = person_img.convert('RGB')
315
+ person_img.save(person_path, 'JPEG')
316
+ logger.debug(f"์‚ฌ๋žŒ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {person_path}")
317
+
318
+ with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_product:
319
+ product_path = tmp_product.name
320
+ # RGB๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ €์žฅ
321
+ product_img = product_pil
322
+ if product_img.mode != 'RGB':
323
+ product_img = product_img.convert('RGB')
324
+ product_img.save(product_path, 'JPEG')
325
+ logger.debug(f"์ƒํ’ˆ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {product_path}")
326
+
327
+ # ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ €์žฅ (์žˆ๋Š” ๊ฒฝ์šฐ)
328
+ background_path = None
329
+ if background_pil is not None:
330
+ with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_bg:
331
+ background_path = tmp_bg.name
332
+ # RGB๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ €์žฅ
333
+ background_img = background_pil
334
+ if background_img.mode != 'RGB':
335
+ background_img = background_img.convert('RGB')
336
+ background_img.save(background_path, 'JPEG')
337
+ logger.debug(f"๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ ์ €์žฅ ์™„๋ฃŒ: {background_path}")
338
+
339
+ # ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ์‹คํ–‰
340
+ result_path, response_text = merge_images(
341
+ person_img_path=person_path,
342
+ product_img_path=product_path,
343
+ background_img_path=background_path,
344
+ prompt=prompt
345
+ )
346
+
347
+ # ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜ ๋ฐ ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ
348
  if result_path:
349
  logger.debug(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ. ๊ฒฝ๋กœ: {result_path}")
350
  try:
 
352
  if result_img.mode == "RGBA":
353
  result_img = result_img.convert("RGB")
354
 
355
+ # ์ž„์‹œ ํŒŒ์ผ ์ •๋ฆฌ
356
+ try:
357
+ os.unlink(person_path)
358
+ os.unlink(product_path)
359
+ if background_path:
360
+ os.unlink(background_path)
361
+ except Exception as e:
362
+ logger.warning(f"์ž„์‹œ ํŒŒ์ผ ์‚ญ์ œ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
363
+
364
  return [result_img], response_text or "์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
365
  except Exception as e:
366
  logger.exception(f"๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
367
  return [], f"๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {str(e)}"
368
  else:
369
+ logger.error("merge_images ํ•จ์ˆ˜์—์„œ None ๋ฐ˜ํ™˜๋จ.")
370
+ return [], response_text or "์ด๋ฏธ์ง€ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ด๋ฏธ์ง€๋‚˜ ํ”„๋กฌํ”„ํŠธ๋กœ ์‹œ๋„ํ•ด๋ณด์„ธ์š”."
371
 
372
  except Exception as e:
373
+ logger.exception("process_images_and_prompt ํ•จ์ˆ˜์—์„œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
374
+ return [], f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}" # ์˜ค๋ฅ˜ ์‹œ ๋นˆ ๋ฆฌ์ŠคํŠธ์™€ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ๋ฐ˜ํ™˜