Kims12 commited on
Commit
77a62b5
ยท
verified ยท
1 Parent(s): 5ff01f8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -172
app.py CHANGED
@@ -1,223 +1,126 @@
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
- import io
9
 
10
  from google import genai
11
  from google.genai import types
12
 
13
- # .env ํŒŒ์ผ์— ์ €์žฅ๋œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋กœ๋“œ (python-dotenv ์„ค์น˜ ํ•„์š”: pip install python-dotenv)
14
  from dotenv import load_dotenv
15
  load_dotenv()
16
 
17
- # ๋กœ๊น… ์„ค์ • (๋กœ๊ทธ ๋ ˆ๋ฒจ: DEBUG)
18
- logging.basicConfig(level=logging.DEBUG,
19
- format='%(asctime)s - %(levelname)s - %(message)s')
20
  logger = logging.getLogger(__name__)
21
 
22
-
23
  def save_binary_file(file_name, data):
24
- logger.debug(f"ํŒŒ์ผ์— ์ด์ง„ ๋ฐ์ดํ„ฐ ์ €์žฅ ์ค‘: {file_name}")
25
  with open(file_name, "wb") as f:
26
  f.write(data)
27
- logger.debug(f"ํŒŒ์ผ ์ €์žฅ ์™„๋ฃŒ: {file_name}")
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:
34
- # API ํ‚ค๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ ๋ถˆ๋Ÿฌ์˜ด
35
- effective_api_key = os.environ.get("GEMINI_API_KEY")
36
- if effective_api_key:
37
- logger.debug("ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ API ํ‚ค ๋ถˆ๋Ÿฌ์˜ด")
38
- else:
39
- logger.error("API ํ‚ค๊ฐ€ ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
40
- raise ValueError("API ํ‚ค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.")
41
-
42
- client = genai.Client(api_key=effective_api_key)
43
- logger.debug("Gemini ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์™„๋ฃŒ.")
44
-
45
- # ํ”„๋กฌํ”„ํŠธ ์ฒ˜๋ฆฌ
46
  if not prompt or not prompt.strip():
47
- prompt = "A creative and visually appealing image that captures imagination. Use vibrant colors and interesting composition."
48
 
49
- # ํ•œ๊ตญ์–ด ํ”„๋กฌํ”„ํŠธ์— ์˜์–ด ์„ค๋ช… ์ถ”๊ฐ€
50
- if not any(ord(c) < 128 for c in prompt):
51
- prompt += " Create a highly detailed, visually stunning image that captures the essence of the description."
52
-
53
- # ์ปจํ…์ธ  ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ
54
- parts = [
55
- types.Part.from_text(text=prompt)
56
- ]
 
 
 
 
 
 
 
 
 
 
57
 
58
- logger.debug(f"์ปจํ…์ธ  ๊ฐ์ฒด ์ƒ์„ฑ ์™„๋ฃŒ: {len(parts)} ์•„์ดํ…œ")
59
-
60
  # ์ƒ์„ฑ ์„ค์ •
61
  generate_content_config = types.GenerateContentConfig(
62
  temperature=1,
63
- top_p=0.95,
64
- top_k=40,
65
- max_output_tokens=8192,
66
- response_modalities=["image", "text"],
67
  )
68
- logger.debug(f"์ƒ์„ฑ ์„ค์ •: {generate_content_config}")
69
-
70
  with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
71
  temp_path = tmp.name
72
- logger.debug(f"์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ๋จ: {temp_path}")
73
-
74
- # ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ ์ˆ˜์‹ 
75
- response_stream = client.models.generate_content_stream(
76
- model=model,
77
- contents=[
78
- types.Content(
79
- role="user",
80
- parts=parts,
81
- ),
82
- ],
83
  config=generate_content_config,
84
  )
85
 
86
- logger.debug("์‘๋‹ต ์ŠคํŠธ๋ฆผ ์ฒ˜๋ฆฌ ์‹œ์ž‘...")
87
-
88
- # ์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€์™€ ํ…๏ฟฝ๏ฟฝํŠธ ์ถ”์ถœ
89
- image_saved = False
90
- response_text = ""
91
-
92
- for chunk in response_stream:
93
- logger.debug(f"chunk ์ˆ˜์‹ : {chunk}")
94
-
95
- # ์‘๋‹ต ๊ฒ€์ฆ
96
- if not hasattr(chunk, 'candidates') or not chunk.candidates or len(chunk.candidates) == 0:
97
- logger.warning("chunk์— candidates๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.")
98
- continue
99
-
100
- if not hasattr(chunk.candidates[0], 'content') or chunk.candidates[0].content is None:
101
- logger.warning("chunk.candidates[0]์— content๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.")
102
- continue
103
 
104
- if not hasattr(chunk.candidates[0].content, 'parts') or not chunk.candidates[0].content.parts:
105
- logger.warning("chunk.candidates[0].content์— parts๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.")
106
- continue
 
107
 
108
- for part in chunk.candidates[0].content.parts:
109
- if hasattr(part, 'text') and part.text:
110
- response_text += part.text
111
- logger.info(f"์ˆ˜์‹ ๋œ ํ…์ŠคํŠธ: {part.text}")
112
- elif hasattr(part, 'inline_data') and part.inline_data:
113
- save_binary_file(temp_path, part.inline_data.data)
114
- logger.info(f"MIME ํƒ€์ž… {part.inline_data.mime_type}์˜ ํŒŒ์ผ์ด ์ €์žฅ๋จ: {temp_path}")
115
- image_saved = True
116
-
117
- if not image_saved:
118
- logger.warning("์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
119
- return None, response_text or "์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ”„๋กฌํ”„ํŠธ๋กœ ์‹œ๋„ํ•ด๋ณด์„ธ์š”."
120
-
121
- logger.debug("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ.")
122
- return temp_path, response_text
123
-
124
  except Exception as e:
125
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
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:
143
- result_img = Image.open(result_path)
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)
 
 
1
  import os
 
2
  import tempfile
3
  from PIL import Image
4
  import gradio as gr
5
  import logging
 
6
 
7
  from google import genai
8
  from google.genai import types
9
 
10
+ # ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋กœ๋“œ
11
  from dotenv import load_dotenv
12
  load_dotenv()
13
 
14
+ # ๊ฐ„๋‹จํ•œ ๋กœ๊น… ์„ค์ •
15
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
16
  logger = logging.getLogger(__name__)
17
 
 
18
  def save_binary_file(file_name, data):
 
19
  with open(file_name, "wb") as f:
20
  f.write(data)
 
 
21
 
22
+ def process_images_with_prompt(image1, image2, image3, prompt):
23
+ """
24
+ 3๊ฐœ์˜ ์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜
25
+ """
26
  try:
27
+ # API ํ‚ค ํ™•์ธ
28
+ api_key = os.environ.get("GEMINI_API_KEY")
29
+ if not api_key:
30
+ return None, "API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
31
+
32
+ # Gemini ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
33
+ client = genai.Client(api_key=api_key)
34
+
35
+ # ํ”„๋กฌํ”„ํŠธ ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •
 
 
 
36
  if not prompt or not prompt.strip():
37
+ prompt = "์ด ์ด๋ฏธ์ง€๋“ค์„ ํ™œ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”."
38
 
39
+ # ์ปจํ…์ธ  ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ (์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ ๊ฒฐํ•ฉ)
40
+ parts = []
41
+
42
+ # ํ…์ŠคํŠธ ํ”„๋กฌํ”„ํŠธ ์ถ”๊ฐ€
43
+ parts.append(types.Part.from_text(text=prompt))
44
+
45
+ # ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
46
+ for img in [image1, image2, image3]:
47
+ if img is not None:
48
+ # PIL ์ด๋ฏธ์ง€๋ฅผ ๋ฐ”์ดํŠธ๋กœ ๋ณ€ํ™˜
49
+ with tempfile.NamedTemporaryFile(suffix=".png") as tmp:
50
+ img_path = tmp.name
51
+ img.save(img_path, format="PNG")
52
+ with open(img_path, "rb") as f:
53
+ image_bytes = f.read()
54
+
55
+ # ์ด๋ฏธ์ง€๋ฅผ ํŒŒํŠธ๋กœ ์ถ”๊ฐ€
56
+ parts.append(types.Part.from_data(data=image_bytes, mime_type="image/png"))
57
 
 
 
58
  # ์ƒ์„ฑ ์„ค์ •
59
  generate_content_config = types.GenerateContentConfig(
60
  temperature=1,
61
+ response_modalities=["image"],
 
 
 
62
  )
63
+
64
+ # ์ž„์‹œ ํŒŒ์ผ ์ƒ์„ฑ
65
  with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
66
  temp_path = tmp.name
67
+
68
+ # Gemini ๋ชจ๋ธ๋กœ ์š”์ฒญ ์ „์†ก
69
+ response = client.models.generate_content(
70
+ model="gemini-2.0-flash-exp-image-generation",
71
+ contents=[types.Content(role="user", parts=parts)],
 
 
 
 
 
 
72
  config=generate_content_config,
73
  )
74
 
75
+ # ์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ
76
+ for part in response.candidates[0].content.parts:
77
+ if hasattr(part, 'inline_data') and part.inline_data:
78
+ save_binary_file(temp_path, part.inline_data.data)
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ # ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜
81
+ result_img = Image.open(temp_path)
82
+ if result_img.mode == "RGBA":
83
+ result_img = result_img.convert("RGB")
84
 
85
+ return result_img, "์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค."
86
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  except Exception as e:
88
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
89
+ return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
+ # ๊ฐ„์†Œํ™”๋œ Gradio ์ธํ„ฐํŽ˜์ด์Šค
 
 
 
 
 
92
  with gr.Blocks() as demo:
93
+ gr.HTML("<h1>๊ฐ„๋‹จํ•œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ</h1><p>์ด๋ฏธ์ง€ 3๊ฐœ์™€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”</p>")
94
+
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  with gr.Row():
96
  with gr.Column():
97
+ # 3๊ฐœ์˜ ์ด๋ฏธ์ง€ ์ž…๋ ฅ
98
+ image1_input = gr.Image(type="pil", label="์ด๋ฏธ์ง€ 1", image_mode="RGB")
99
+ image2_input = gr.Image(type="pil", label="์ด๋ฏธ์ง€ 2", image_mode="RGB")
100
+ image3_input = gr.Image(type="pil", label="์ด๋ฏธ์ง€ 3", image_mode="RGB")
101
 
102
+ # ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ
103
  prompt_input = gr.Textbox(
104
  lines=3,
105
+ placeholder="์ด ์ด๋ฏธ์ง€๋“ค์„ ์–ด๋–ป๊ฒŒ ๋ณ€ํ™˜ํ• ์ง€ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”",
106
+ label="ํ”„๋กฌํ”„ํŠธ"
107
  )
108
+
109
+ # ์ƒ์„ฑ ๋ฒ„ํŠผ
110
  submit_btn = gr.Button("์ด๋ฏธ์ง€ ์ƒ์„ฑ")
111
 
112
  with gr.Column():
113
+ # ๊ฒฐ๊ณผ ์ถœ๋ ฅ
114
+ output_image = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€")
115
+ output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€")
116
+
117
+ # ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
118
  submit_btn.click(
119
+ fn=process_images_with_prompt,
120
+ inputs=[image1_input, image2_input, image3_input, prompt_input],
121
+ outputs=[output_image, output_text],
122
  )
123
 
124
+ # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  if __name__ == "__main__":
126
  demo.launch(share=True)