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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +69 -11
app.py CHANGED
@@ -3,6 +3,7 @@ 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
@@ -11,7 +12,7 @@ from google.genai import types
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
 
@@ -19,6 +20,14 @@ 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๊ฐœ์˜ ์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜
@@ -32,9 +41,12 @@ def process_images_with_prompt(image1, image2, image3, prompt):
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 = []
@@ -43,7 +55,8 @@ def process_images_with_prompt(image1, image2, image3, prompt):
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:
@@ -54,6 +67,11 @@ def process_images_with_prompt(image1, image2, image3, prompt):
54
 
55
  # ์ด๋ฏธ์ง€๋ฅผ ํŒŒํŠธ๋กœ ์ถ”๊ฐ€
56
  parts.append(types.Part.from_data(data=image_bytes, mime_type="image/png"))
 
 
 
 
 
57
 
58
  # ์ƒ์„ฑ ์„ค์ •
59
  generate_content_config = types.GenerateContentConfig(
@@ -66,6 +84,7 @@ def process_images_with_prompt(image1, image2, image3, prompt):
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)],
@@ -73,9 +92,15 @@ def process_images_with_prompt(image1, image2, image3, prompt):
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)
@@ -88,39 +113,72 @@ def process_images_with_prompt(image1, image2, image3, prompt):
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)
 
3
  from PIL import Image
4
  import gradio as gr
5
  import logging
6
+ import re
7
 
8
  from google import genai
9
  from google.genai import types
 
12
  from dotenv import load_dotenv
13
  load_dotenv()
14
 
15
+ # ๋กœ๊น… ์„ค์ •
16
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
17
  logger = logging.getLogger(__name__)
18
 
 
20
  with open(file_name, "wb") as f:
21
  f.write(data)
22
 
23
+ def preprocess_prompt(prompt):
24
+ """
25
+ ํ”„๋กฌํ”„ํŠธ์— ์žˆ๋Š” #1, #2, #3 ์ฐธ์กฐ๋ฅผ ์ ์ ˆํ•œ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜
26
+ """
27
+ # #1, #2, #3 ์ฐธ์กฐ๋ฅผ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜
28
+ processed_prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€").replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€").replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
29
+ return processed_prompt
30
+
31
  def process_images_with_prompt(image1, image2, image3, prompt):
32
  """
33
  3๊ฐœ์˜ ์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜
 
41
  # Gemini ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
42
  client = genai.Client(api_key=api_key)
43
 
44
+ # ํ”„๋กฌํ”„ํŠธ ์ฒ˜๋ฆฌ
45
  if not prompt or not prompt.strip():
46
  prompt = "์ด ์ด๋ฏธ์ง€๋“ค์„ ํ™œ์šฉํ•˜์—ฌ ์ƒˆ๋กœ์šด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”."
47
+ else:
48
+ # #1, #2, #3 ์ฐธ์กฐ ์ฒ˜๋ฆฌ
49
+ prompt = preprocess_prompt(prompt)
50
 
51
  # ์ปจํ…์ธ  ๋ฆฌ์ŠคํŠธ ์ƒ์„ฑ (์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ ๊ฒฐํ•ฉ)
52
  parts = []
 
55
  parts.append(types.Part.from_text(text=prompt))
56
 
57
  # ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
58
+ images_added = 0
59
+ for idx, img in enumerate([image1, image2, image3], 1):
60
  if img is not None:
61
  # PIL ์ด๋ฏธ์ง€๋ฅผ ๋ฐ”์ดํŠธ๋กœ ๋ณ€ํ™˜
62
  with tempfile.NamedTemporaryFile(suffix=".png") as tmp:
 
67
 
68
  # ์ด๋ฏธ์ง€๋ฅผ ํŒŒํŠธ๋กœ ์ถ”๊ฐ€
69
  parts.append(types.Part.from_data(data=image_bytes, mime_type="image/png"))
70
+ images_added += 1
71
+ logger.info(f"์ด๋ฏธ์ง€ #{idx} ์ถ”๊ฐ€๋จ")
72
+
73
+ if images_added == 0:
74
+ return None, "์ ์–ด๋„ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”."
75
 
76
  # ์ƒ์„ฑ ์„ค์ •
77
  generate_content_config = types.GenerateContentConfig(
 
84
  temp_path = tmp.name
85
 
86
  # Gemini ๋ชจ๋ธ๋กœ ์š”์ฒญ ์ „์†ก
87
+ logger.info(f"Gemini API ์š”์ฒญ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: {prompt}")
88
  response = client.models.generate_content(
89
  model="gemini-2.0-flash-exp-image-generation",
90
  contents=[types.Content(role="user", parts=parts)],
 
92
  )
93
 
94
  # ์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ
95
+ image_found = False
96
  for part in response.candidates[0].content.parts:
97
  if hasattr(part, 'inline_data') and part.inline_data:
98
  save_binary_file(temp_path, part.inline_data.data)
99
+ image_found = True
100
+ logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
101
+
102
+ if not image_found:
103
+ return None, "API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ”„๋กฌํ”„ํŠธ๋กœ ์‹œ๋„ํ•ด๋ณด์„ธ์š”."
104
 
105
  # ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜
106
  result_img = Image.open(temp_path)
 
113
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
114
  return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
115
 
116
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค
117
  with gr.Blocks() as demo:
118
+ gr.HTML(
119
+ """
120
+ <div style="text-align: center; margin-bottom: 1rem;">
121
+ <h1>๊ฐ„๋‹จํ•œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ</h1>
122
+ <p>์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”. #1, #2, #3์œผ๋กœ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
123
+ </div>
124
+ """
125
+ )
126
 
127
  with gr.Row():
128
  with gr.Column():
129
  # 3๊ฐœ์˜ ์ด๋ฏธ์ง€ ์ž…๋ ฅ
130
+ with gr.Row():
131
+ image1_input = gr.Image(type="pil", label="#1", image_mode="RGB")
132
+ image2_input = gr.Image(type="pil", label="#2", image_mode="RGB")
133
+ image3_input = gr.Image(type="pil", label="#3", image_mode="RGB")
134
 
135
  # ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ
136
  prompt_input = gr.Textbox(
137
  lines=3,
138
+ placeholder="ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”. ์˜ˆ: '#1๊ณผ #2๋ฅผ ํ•ฉ์„ฑํ•ด ์ฃผ์„ธ์š”' ๋˜๋Š” '#1์˜ ์ธ๋ฌผ์„ #3์˜ ๋ฐฐ๊ฒฝ์— ๋„ฃ์–ด์ฃผ์„ธ์š”'",
139
  label="ํ”„๋กฌํ”„ํŠธ"
140
  )
141
 
142
+ # ์˜ˆ์‹œ ํ”„๋กฌํ”„ํŠธ ๋ฒ„ํŠผ๋“ค
143
+ with gr.Row():
144
+ prompt1_btn = gr.Button("์˜ˆ์‹œ: #1 + #2 ํ•ฉ์„ฑ")
145
+ prompt2_btn = gr.Button("์˜ˆ์‹œ: #1์˜ ์ธ๋ฌผ + #2์˜ ๋ฐฐ๊ฒฝ")
146
+ prompt3_btn = gr.Button("์˜ˆ์‹œ: #3 ์Šคํƒ€์ผ๋กœ #1 ๋ณ€ํ™˜")
147
+
148
  # ์ƒ์„ฑ ๋ฒ„ํŠผ
149
+ submit_btn = gr.Button("์ด๋ฏธ์ง€ ์ƒ์„ฑ", variant="primary")
150
 
151
  with gr.Column():
152
  # ๊ฒฐ๊ณผ ์ถœ๋ ฅ
153
  output_image = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€")
154
  output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€")
155
 
156
+ # ์˜ˆ์‹œ ํ”„๋กฌํ”„ํŠธ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
157
+ prompt1_btn.click(lambda: "#1๊ณผ #2๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด ์ฃผ์„ธ์š”", outputs=prompt_input)
158
+ prompt2_btn.click(lambda: "#1์˜ ์ธ๋ฌผ์„ #2์˜ ๋ฐฐ๊ฒฝ์— ๋„ฃ์–ด์ฃผ์„ธ์š”", outputs=prompt_input)
159
+ prompt3_btn.click(lambda: "#3์˜ ์Šคํƒ€์ผ๋กœ #1์„ ๋ณ€ํ™˜ํ•ด ์ฃผ์„ธ์š”", outputs=prompt_input)
160
+
161
+ # ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
162
  submit_btn.click(
163
  fn=process_images_with_prompt,
164
  inputs=[image1_input, image2_input, image3_input, prompt_input],
165
  outputs=[output_image, output_text],
166
  )
167
 
168
+ gr.HTML(
169
+ """
170
+ <div style="margin-top: 1rem; padding: 1rem; background-color: #f8f9fa; border-radius: 0.5rem;">
171
+ <h3>์‚ฌ์šฉ ๋ฐฉ๋ฒ•:</h3>
172
+ <ul>
173
+ <li>์ด๋ฏธ์ง€๋ฅผ #1, #2, #3 ์Šฌ๋กฏ์— ์—…๋กœ๋“œํ•˜์„ธ์š”</li>
174
+ <li>ํ”„๋กฌํ”„ํŠธ์—์„œ #1, #2, #3์œผ๋กœ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค</li>
175
+ <li>์˜ˆ์‹œ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค</li>
176
+ <li>๋ชจ๋“  ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•  ํ•„์š”๋Š” ์—†์œผ๋ฉฐ, ํ•„์š”ํ•œ ์ด๋ฏธ์ง€๋งŒ ์—…๋กœ๋“œํ•˜์„ธ์š”</li>
177
+ </ul>
178
+ </div>
179
+ """
180
+ )
181
+
182
  # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
183
  if __name__ == "__main__":
184
  demo.launch(share=True)