Kims12 commited on
Commit
98fdfb4
ยท
verified ยท
1 Parent(s): e7c539f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +229 -184
app.py CHANGED
@@ -4,8 +4,8 @@ from PIL import Image
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
11
  from google.genai import types
@@ -22,111 +22,161 @@ def save_binary_file(file_name, data):
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,15 +188,11 @@ def generate_with_images(prompt, images):
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,183 +201,182 @@ def generate_with_images(prompt, images):
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 = "์ด ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
190
- logger.info("๋‹จ์ผ ์ด๋ฏธ์ง€ ํ”„๋กฌํ”„ํŠธ ์ž๋™ ์ƒ์„ฑ")
191
  elif len(valid_images) == 2:
192
- prompt = "์ด ๋‘ ์ด๋ฏธ์ง€๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋‘ ์ด๋ฏธ์ง€์˜ ์š”์†Œ๋ฅผ ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•˜์—ฌ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
193
- logger.info("๋‘ ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ํ”„๋กฌํ”„ํŠธ ์ž๋™ ์ƒ์„ฑ")
194
  else:
195
- prompt = "์ด ์„ธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๋˜ ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ์ผ๊ด€๋œ ํ•˜๋‚˜์˜ ์žฅ๋ฉด์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
196
- logger.info("์„ธ ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ํ”„๋กฌํ”„ํŠธ ์ž๋™ ์ƒ์„ฑ")
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
- def update_prompt_from_function(function_choice, custom_text=""):
210
- function_templates = {
211
- "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ": f'#1์„ "{custom_text if custom_text else "์›ํ•˜๋Š” ์„ค๋ช…"}"์œผ๋กœ ๋ฐ”๊ฟ”๋ผ',
212
- "2. ๊ธ€์ž์ง€์šฐ๊ธฐ": f'#1์—์„œ "{custom_text if custom_text else "์ง€์šธ ํ…์ŠคํŠธ"}"๋ฅผ ์ง€์›Œ๋ผ',
213
- "3. ์–ผ๊ตด๋ฐ”๊พธ๊ธฐ": "#1์˜ ์ธ๋ฌผ์„ #2์˜ ์–ผ๊ตด๋กœ ๋ฐ”๊ฟ”๋ผ",
214
- "4. ์˜ท๋ฐ”๊พธ๊ธฐ": "#1์˜ ์ธ๋ฌผ์— #2 ๋˜๋Š” #3์˜ ์˜ท์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ผ",
215
- "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ": "#1์˜ ์ด๋ฏธ์ง€์— #2์˜ ๋ฐฐ๊ฒฝ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ฐ”๊ฟ”๋ผ",
216
- "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)": "#1์™€ #2 ๋˜๋Š” #3์˜ ๋ฅผ ํ•ฉ์„ฑํ•˜๋ผ",
217
- "7. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์Šคํƒ€์ผ์ ์šฉ)": "#1์™€ #2๋ฅผ ์Šคํƒ€์ผ๋กœ ๋ณ€ํ™˜ํ•˜๋ผ"
218
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
- return function_templates.get(function_choice, "")
 
 
 
 
 
 
 
 
221
 
222
- # Gradio ์ธํ„ฐํŽ˜์ด์Šค (๊ธฐ์กด ์ฝ”๋“œ ์œ ์ง€)
223
  with gr.Blocks() as demo:
224
- gr.HTML(
225
  """
226
  <div style="text-align: center; margin-bottom: 1rem;">
227
- <h1>๊ฐ„๋‹จํ•œ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ</h1>
228
- <p>์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ  ๋ฐ”๋กœ ์‹คํ–‰ํ•˜๋ฉด ์ž๋™์œผ๋กœ ํ•ฉ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๋˜๋Š” ์•„๋ž˜ ๊ธฐ๋Šฅ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.</p>
 
229
  </div>
230
  """
231
  )
232
 
233
  with gr.Row():
234
  with gr.Column():
235
- # 3๊ฐœ์˜ ์ด๋ฏธ์ง€ ์ž…๋ ฅ
236
- with gr.Row():
237
- image1_input = gr.Image(type="pil", label="#1", image_mode="RGB")
238
- image2_input = gr.Image(type="pil", label="#2", image_mode="RGB")
239
- image3_input = gr.Image(type="pil", label="#3", image_mode="RGB")
240
-
241
- # ๊ธฐ๋Šฅ ์„ ํƒ ๋“œ๋กญ๋‹ค์šด๊ณผ ์ปค์Šคํ…€ ํ…์ŠคํŠธ ์ž…๋ ฅ
242
  with gr.Row():
243
- function_dropdown = gr.Dropdown(
244
- choices=[
245
- "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ",
246
- "2. ๊ธ€์ž์ง€์šฐ๊ธฐ",
247
- "3. ์–ผ๊ตด๋ฐ”๊พธ๊ธฐ",
248
- "4. ์˜ท๋ฐ”๊พธ๊ธฐ",
249
- "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ",
250
- "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)",
251
- "7. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์Šคํƒ€์ผ์ ์šฉ)"
252
- ],
253
- label="๊ธฐ๋Šฅ ์„ ํƒ",
254
- value=None
255
- )
256
- custom_text_input = gr.Textbox(
257
- label="์ปค์Šคํ…€ ํ…์ŠคํŠธ (1, 2๋ฒˆ ๊ธฐ๋Šฅ์šฉ)",
258
- placeholder="์˜ˆ: ๋ถ‰์€์ƒ‰, ์ˆ˜์ฑ„ํ™” ์Šคํƒ€์ผ, ์ค‘๊ตญ์–ด..."
259
- )
260
-
261
- apply_function_btn = gr.Button("๊ธฐ๋Šฅ ์ ์šฉ")
262
-
263
- # ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ (์„ ํƒ ์‚ฌํ•ญ)
264
  prompt_input = gr.Textbox(
265
  lines=3,
266
  placeholder="ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ๋น„์›Œ๋‘๋ฉด ์ž๋™ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
267
  label="ํ”„๋กฌํ”„ํŠธ (์„ ํƒ ์‚ฌํ•ญ)"
268
  )
269
-
270
- # ์ƒ์„ฑ ๋ฒ„ํŠผ
271
- submit_btn = gr.Button("์ด๋ฏธ์ง€ ์ƒ์„ฑ", variant="primary")
272
-
 
 
 
 
 
273
  with gr.Column():
274
- # ๊ฒฐ๊ณผ ์ถœ๋ ฅ
275
- output_image = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€")
276
- output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€")
277
-
278
- # ์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ ํ‘œ์‹œ
279
- prompt_display = gr.Textbox(label="์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ", visible=True)
280
 
281
- # ๊ธฐ๋Šฅ ์ ์šฉ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ
282
- apply_function_btn.click(
283
- fn=update_prompt_from_function,
284
- inputs=[function_dropdown, custom_text_input],
285
- outputs=[prompt_input]
286
- )
287
 
288
- # ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
289
- def process_and_show_prompt(image1, image2, image3, prompt):
290
- # ์ด๋ฏธ์ง€ ๊ฐœ์ˆ˜ ํ™•์ธ
291
- images = [image1, image2, image3]
292
- valid_images = [img for img in images if img is not None]
293
-
294
- try:
295
- # ์ž๋™ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋˜๋Š” ํ”„๋กฌํ”„ํŠธ ์ „์ฒ˜๋ฆฌ
296
- auto_prompt = prompt
297
- if not prompt or not prompt.strip():
298
- if len(valid_images) == 1:
299
- auto_prompt = "์ด ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
300
- elif len(valid_images) == 2:
301
- auto_prompt = "์ด ๋‘ ์ด๋ฏธ์ง€๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋‘ ์ด๋ฏธ์ง€์˜ ์š”์†Œ๋ฅผ ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•˜์—ฌ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
302
- else:
303
- auto_prompt = "์ด ์„ธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๋˜ ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ์ผ๊ด€๋œ ํ•˜๋‚˜์˜ ์žฅ๋ฉด์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
304
- else:
305
- auto_prompt = preprocess_prompt(prompt, image1, image2, image3)
306
-
307
- # ์ด๋ฏธ์ง€ ์ƒ์„ฑ ํ•จ์ˆ˜ ํ˜ธ์ถœ
308
- result_img, status = process_images_with_prompt(image1, image2, image3, prompt)
309
-
310
- return result_img, status, auto_prompt
311
- except Exception as e:
312
- logger.exception("์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
313
- return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", prompt
 
 
 
314
 
315
  submit_btn.click(
316
- fn=process_and_show_prompt,
317
  inputs=[image1_input, image2_input, image3_input, prompt_input],
318
- outputs=[output_image, output_text, prompt_display],
319
  )
320
-
321
  gr.Markdown(
322
  """
323
  ### ์‚ฌ์šฉ ๋ฐฉ๋ฒ•:
324
 
325
- 1. **์ž๋™ ํ•ฉ์„ฑ**: ์ด๋ฏธ์ง€๋งŒ ์—…๋กœ๋“œํ•˜๊ณ  ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋น„์›Œ๋‘๋ฉด ์ž๋™์œผ๋กœ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค
326
- 2. **๊ธฐ๋Šฅ ์‚ฌ์šฉ**: ๋“œ๋กญ๋‹ค์šด์—์„œ ์›ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์„ ํƒํ•˜๊ณ  '๊ธฐ๋Šฅ ์ ์šฉ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”
327
- 3. **์ปค์Šคํ…€ ํ…์ŠคํŠธ**: ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ์ด๋‚˜ ๊ธ€์ž์ง€์šฐ๊ธฐ ๊ธฐ๋Šฅ์„ ์„ ํƒํ•  ๋•Œ ์ถ”๊ฐ€ ์„ค๋ช…์„ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
328
- 4. **์ด๋ฏธ์ง€ ์ฐธ์กฐ**: #1, #2, #3์œผ๋กœ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
329
- 5. **์ผ๋ถ€ ์ด๋ฏธ์ง€๋งŒ**: ํ•„์š”ํ•œ ์ด๋ฏธ์ง€๋งŒ ์—…๋กœ๋“œํ•ด๋„ ๊ธฐ๋Šฅ ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
330
 
331
- > **ํŒ**: ๊ธฐ๋Šฅ์„ ์„ ํƒํ•œ ํ›„์—๋„ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
332
  """
333
  )
334
 
335
- # ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
336
  if __name__ == "__main__":
337
  demo.launch(share=True)
 
4
  import gradio as gr
5
  import logging
6
  import re
 
7
  from io import BytesIO
8
+ import time
9
 
10
  from google import genai
11
  from google.genai import types
 
22
  with open(file_name, "wb") as f:
23
  f.write(data)
24
 
25
+ def translate_prompt_to_english(prompt):
26
+ """
27
+ ์ž…๋ ฅ๋œ ํ”„๋กฌํ”„ํŠธ์— ํ•œ๊ธ€์ด ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด Geminiโ€‘2.0โ€‘flash ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜์–ด๋กœ ๋ฒˆ์—ญํ•ฉ๋‹ˆ๋‹ค.
28
+ ํ•œ๊ธ€์ด ์—†์œผ๋ฉด ์›๋ณธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
29
+ ์ค‘์š”: #1, #2, #3 ํƒœ๊ทธ๋Š” ๋ฒˆ์—ญ ์ „ํ›„์— ๋ฐ˜๋“œ์‹œ ๋ณด์กด๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
30
+ """
31
+ if not re.search("[๊ฐ€-ํžฃ]", prompt):
32
+ return prompt
33
+
34
+ # #1, #2, #3 ํƒœ๊ทธ๋ฅผ ์ž„์‹œ ํ† ํฐ์œผ๋กœ ๋Œ€์ฒดํ•˜์—ฌ ๋ณด์กด
35
+ prompt = prompt.replace("#1", "IMAGE_TAG_ONE")
36
+ prompt = prompt.replace("#2", "IMAGE_TAG_TWO")
37
+ prompt = prompt.replace("#3", "IMAGE_TAG_THREE")
38
+
39
+ try:
40
+ api_key = os.environ.get("GEMINI_API_KEY")
41
+ if not api_key:
42
+ logger.error("Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
43
+ # ์ž„์‹œ ํ† ํฐ์„ ์›๋ž˜ ํƒœ๊ทธ๋กœ ๋ณต์›
44
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
45
+ prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
46
+ prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
47
+ return prompt
48
+
49
+ client = genai.Client(api_key=api_key)
50
+ translation_prompt = f"""
51
+ Translate the following Korean text to English:
52
+
53
+ {prompt}
54
+
55
+ IMPORTANT: The tokens IMAGE_TAG_ONE, IMAGE_TAG_TWO, and IMAGE_TAG_THREE are special tags
56
+ and must be preserved exactly as is in your translation. Do not translate these tokens.
57
+ """
58
+
59
+ logger.info(f"Translation prompt: {translation_prompt}")
60
+ response = client.models.generate_content(
61
+ model="gemini-2.0-flash",
62
+ contents=[translation_prompt],
63
+ config=types.GenerateContentConfig(
64
+ response_modalities=['Text'],
65
+ temperature=0.2,
66
+ top_p=0.95,
67
+ top_k=40,
68
+ max_output_tokens=512
69
+ )
70
+ )
71
+
72
+ translated_text = ""
73
+ for part in response.candidates[0].content.parts:
74
+ if hasattr(part, 'text') and part.text:
75
+ translated_text += part.text
76
+
77
+ if translated_text.strip():
78
+ # ๋ฒˆ์—ญ๋œ ํ…์ŠคํŠธ์—์„œ ์ž„์‹œ ํ† ํฐ์„ ์›๋ž˜ ํƒœ๊ทธ๋กœ ๋ณต์›
79
+ translated_text = translated_text.replace("IMAGE_TAG_ONE", "#1")
80
+ translated_text = translated_text.replace("IMAGE_TAG_TWO", "#2")
81
+ translated_text = translated_text.replace("IMAGE_TAG_THREE", "#3")
82
+ logger.info(f"Translated text: {translated_text.strip()}")
83
+ return translated_text.strip()
84
+ else:
85
+ logger.warning("๋ฒˆ์—ญ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ")
86
+ # ์ž„์‹œ ํ† ํฐ์„ ์›๋ž˜ ํƒœ๊ทธ๋กœ ๋ณต์›
87
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
88
+ prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
89
+ prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
90
+ return prompt
91
+ except Exception as e:
92
+ logger.exception("๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
93
+ # ์ž„์‹œ ํ† ํฐ์„ ์›๋ž˜ ํƒœ๊ทธ๋กœ ๋ณต์›
94
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
95
+ prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
96
+ prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
97
+ return prompt
98
+
99
  def preprocess_prompt(prompt, image1, image2, image3):
100
  """
101
  ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ธฐ๋Šฅ ๋ช…๋ น์„ ํ•ด์„
102
  """
 
 
103
  has_img1 = image1 is not None
104
  has_img2 = image2 is not None
105
  has_img3 = image3 is not None
106
+
 
107
  if "#1" in prompt and not has_img1:
108
  prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
109
  else:
110
  prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
111
+
112
  if "#2" in prompt and not has_img2:
113
  prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
114
  else:
115
  prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
116
+
117
  if "#3" in prompt and not has_img3:
118
  prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
119
  else:
120
  prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
121
+
 
122
  if "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ" in prompt:
 
123
  desc_match = re.search(r'#1์„ "(.*?)"์œผ๋กœ ๋ฐ”๊ฟ”๋ผ', prompt)
124
  if desc_match:
125
  description = desc_match.group(1)
126
  prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ {description}์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์›๋ณธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ๋‚ด์šฉ์€ ์œ ์ง€ํ•˜๋˜ ์ƒˆ๋กœ์šด ๏ฟฝ๏ฟฝํƒ€์ผ๊ณผ ๋ถ„์œ„๊ธฐ๋กœ ์žฌํ•ด์„ํ•ด์ฃผ์„ธ์š”."
127
  else:
128
  prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
129
+
130
  elif "2. ๊ธ€์ž์ง€์šฐ๊ธฐ" in prompt:
 
131
  text_match = re.search(r'#1์—์„œ "(.*?)"๋ฅผ ์ง€์›Œ๋ผ', prompt)
132
  if text_match:
133
  text_to_remove = text_match.group(1)
134
  prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ '{text_to_remove}' ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ํ…์ŠคํŠธ๊ฐ€ ์žˆ๋˜ ๋ถ€๋ถ„์„ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ์ฑ„์›Œ์ฃผ์„ธ์š”."
135
  else:
136
  prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ ๋ชจ๋“  ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ๊น”๋”ํ•œ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
137
+
 
 
 
138
  elif "4. ์˜ท๋ฐ”๊พธ๊ธฐ" in prompt:
139
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ ์˜์ƒ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์˜์ƒ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์˜์ƒ์˜ ์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ์€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋ฅด๋˜, ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”."
140
+
 
 
 
 
141
  elif "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ" in prompt:
142
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”."
143
+
144
  elif "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)" in prompt:
145
+ prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์™€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(๋˜๋Š” ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€)๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ , ํŠนํžˆ ์ƒํ’ˆ์ด ๋‹๋ณด์ด๋„๋ก ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•ด์ฃผ์„ธ์š”."
146
+
 
 
 
 
 
 
 
 
 
 
 
 
147
  prompt += " ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”."
 
148
  return prompt
149
 
150
+ def generate_with_images(prompt, images, variation_index=0):
151
  """
152
+ API ํ˜ธ์ถœ์„ ํ†ตํ•ด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
153
+ variation_index๋กœ ๋‹ค์–‘ํ•œ ๋ณ€ํ™”๋ฅผ ์ค๋‹ˆ๋‹ค.
154
  """
155
  try:
 
156
  api_key = os.environ.get("GEMINI_API_KEY")
157
  if not api_key:
158
  return None, "API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
159
+
 
160
  client = genai.Client(api_key=api_key)
161
+ logger.info(f"Gemini API ์š”์ฒญ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: {prompt}, ๋ณ€ํ˜• ์ธ๋ฑ์Šค: {variation_index}")
162
+
163
+ # ๋ณ€ํ˜• ์ธ๋ฑ์Šค์— ๋”ฐ๋ผ ํ”„๋กฌํ”„ํŠธ์— ์•ฝ๊ฐ„์˜ ๋ณ€ํ™” ์ถ”๊ฐ€
164
+ variation_suffixes = [
165
+ " ์ฒซ ๋ฒˆ์งธ ๋ณ€ํ˜•์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.",
166
+ " ๋‘ ๋ฒˆ์งธ ๋ณ€ํ˜•์œผ๋กœ ๋” ์ƒ์ƒํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.",
167
+ " ์„ธ ๋ฒˆ์งธ ๋ณ€ํ˜•์œผ๋กœ ๋” ์ฐฝ์˜์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.",
168
+ " ๋„ค ๋ฒˆ์งธ ๋ณ€ํ˜•์œผ๋กœ ์ƒ‰๊ฐ์„ ๋” ๊ฐ•์กฐํ•ด์„œ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
169
+ ]
170
 
171
+ if variation_index < len(variation_suffixes):
172
+ prompt = prompt + variation_suffixes[variation_index]
173
+
174
+ contents = [prompt]
 
 
 
 
 
175
  for idx, img in enumerate(images, 1):
176
  if img is not None:
177
  contents.append(img)
178
  logger.info(f"์ด๋ฏธ์ง€ #{idx} ์ถ”๊ฐ€๋จ")
179
+
 
180
  response = client.models.generate_content(
181
  model="gemini-2.0-flash-exp-image-generation",
182
  contents=contents,
 
188
  max_output_tokens=8192
189
  )
190
  )
191
+
 
192
  with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
193
  temp_path = tmp.name
 
194
  result_text = ""
195
  image_found = False
 
 
196
  for part in response.candidates[0].content.parts:
197
  if hasattr(part, 'text') and part.text:
198
  result_text += part.text
 
201
  save_binary_file(temp_path, part.inline_data.data)
202
  image_found = True
203
  logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
 
204
  if not image_found:
205
  return None, f"API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ํ…์ŠคํŠธ: {result_text}"
 
 
206
  result_img = Image.open(temp_path)
207
  if result_img.mode == "RGBA":
208
  result_img = result_img.convert("RGB")
 
209
  return result_img, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
 
210
  except Exception as e:
211
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
212
  return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
213
 
214
+ def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0):
215
  """
216
+ 3๊ฐœ์˜ ์ด๋ฏธ์ง€์™€ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ตœ์ข… ์˜์–ด ํ”„๋กฌํ”„ํŠธ(final_prompt)๋ฅผ ์ƒ์„ฑํ•œ ํ›„,
217
+ API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
218
  """
219
  try:
 
220
  images = [image1, image2, image3]
221
  valid_images = [img for img in images if img is not None]
 
222
  if not valid_images:
223
+ return None, "์ ์–ด๋„ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.", ""
224
+
225
+ if prompt and prompt.strip():
226
+ processed_prompt = preprocess_prompt(prompt, image1, image2, image3)
227
+ if re.search("[๊ฐ€-ํžฃ]", processed_prompt):
228
+ final_prompt = translate_prompt_to_english(processed_prompt)
229
+ else:
230
+ final_prompt = processed_prompt
231
+ else:
232
  if len(valid_images) == 1:
233
+ final_prompt = "Please creatively transform this image into a more vivid and artistic version."
234
+ logger.info("Default prompt generated for single image")
235
  elif len(valid_images) == 2:
236
+ final_prompt = "Please seamlessly composite these two images, integrating their key elements harmoniously into a single image."
237
+ logger.info("Default prompt generated for two images")
238
  else:
239
+ final_prompt = "Please creatively composite these three images, combining their main elements into a cohesive and natural scene."
240
+ logger.info("Default prompt generated for three images")
241
+
242
+ result_img, status = generate_with_images(final_prompt, valid_images, variation_index)
243
+ return result_img, status, final_prompt
 
 
 
 
244
  except Exception as e:
245
  logger.exception("์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
246
+ return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}", prompt
247
 
248
+ def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progress()):
249
+ """
250
+ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ด๋ฏธ์ง€๋ฅผ ์ฐจ๋ก€๋Œ€๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
251
+ """
252
+ results = []
253
+ statuses = []
254
+ prompts = []
255
+
256
+ num_images = 4 # ์ƒ์„ฑํ•  ์ด๋ฏธ์ง€ ์ˆ˜
257
+
258
+ progress(0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค€๋น„ ์ค‘...")
259
+
260
+ for i in range(num_images):
261
+ progress((i / num_images), desc=f"{i+1}/{num_images} ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘...")
262
+ result_img, status, final_prompt = process_images_with_prompt(image1, image2, image3, prompt, i)
263
+
264
+ if result_img is not None:
265
+ results.append(result_img)
266
+ statuses.append(f"์ด๋ฏธ์ง€ #{i+1}: {status}")
267
+ prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
268
+ else:
269
+ # ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ์—๋„ ๊ฒฐ๊ณผ ๋ชฉ๋ก์— None์„ ์ถ”๊ฐ€
270
+ results.append(None)
271
+ statuses.append(f"์ด๋ฏธ์ง€ #{i+1} ์ƒ์„ฑ ์‹คํŒจ: {status}")
272
+ prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
273
+
274
+ # API ํ˜ธ์ถœ ์‚ฌ์ด์— ์•ฝ๊ฐ„์˜ ๊ฐ„๊ฒฉ์„ ๋‘์–ด ์†๋„ ์ œํ•œ ๋ฐฉ์ง€
275
+ time.sleep(1)
276
+
277
+ progress(1.0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ!")
278
 
279
+ # ๊ฒฐ๊ณผ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋นˆ ์Šฌ๋กฏ ์ฑ„์šฐ๊ธฐ
280
+ while len(results) < 4:
281
+ results.append(None)
282
+
283
+ # ์ƒํƒœ ๋ฉ”์‹œ์ง€์™€ ํ”„๋กฌํ”„ํŠธ ์ •๋ณด ๊ฒฐํ•ฉ
284
+ combined_status = "\n".join(statuses)
285
+ combined_prompts = "\n".join(prompts)
286
+
287
+ return results[0], results[1], results[2], results[3], combined_status, combined_prompts
288
 
289
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค
290
  with gr.Blocks() as demo:
291
+ gr.HTML(
292
  """
293
  <div style="text-align: center; margin-bottom: 1rem;">
294
+ <h1>4์žฅ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ (์˜ˆ์ œ ํฌํ•จ)</h1>
295
+ <p>์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ  "์ด๋ฏธ์ง€ ์ƒ์„ฑ" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ฐจ๋ก€๋กœ 4์žฅ์˜ ์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.</p>
296
+ <p>๋ชจ๋“  ์ด๋ฏธ์ง€๋Š” ๊ท ์ผํ•œ ํฌ๊ธฐ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.</p>
297
  </div>
298
  """
299
  )
300
 
301
  with gr.Row():
302
  with gr.Column():
 
 
 
 
 
 
 
303
  with gr.Row():
304
+ image1_input = gr.Image(type="pil", label="#1", image_mode="RGB", height=250, width=250)
305
+ image2_input = gr.Image(type="pil", label="#2", image_mode="RGB", height=250, width=250)
306
+ image3_input = gr.Image(type="pil", label="#3", image_mode="RGB", height=250, width=250)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  prompt_input = gr.Textbox(
308
  lines=3,
309
  placeholder="ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ๋น„์›Œ๋‘๋ฉด ์ž๋™ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.",
310
  label="ํ”„๋กฌํ”„ํŠธ (์„ ํƒ ์‚ฌํ•ญ)"
311
  )
312
+ with gr.Row():
313
+ image_change_btn = gr.Button("์ด๋ฏธ์ง€๋ณ€๊ฒฝ")
314
+ text_remove_btn = gr.Button("๊ธ€์ž์ง€์šฐ๊ธฐ")
315
+ clothes_change_btn = gr.Button("๊ฐ€์ƒ ์ƒํ’ˆ์ฐฉ์šฉ")
316
+ background_change_btn = gr.Button("๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ")
317
+ composite_product_btn = gr.Button("๋ถ€๋ถ„์ง€์šฐ๊ธฐ")
318
+ submit_btn = gr.Button("์ด๋ฏธ์ง€ ์ƒ์„ฑ (4์žฅ)", variant="primary")
319
+
320
+ with gr.Row():
321
  with gr.Column():
322
+ output_image1 = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ #1", height=300, width=300)
323
+ with gr.Column():
324
+ output_image2 = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ #2", height=300, width=300)
 
 
 
325
 
326
+ with gr.Row():
327
+ with gr.Column():
328
+ output_image3 = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ #3", height=300, width=300)
329
+ with gr.Column():
330
+ output_image4 = gr.Image(label="์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ #4", height=300, width=300)
 
331
 
332
+ output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€")
333
+ prompt_display = gr.Textbox(label="์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ (์˜์–ด)", visible=True)
334
+
335
+ # ์„ ํƒ ์˜ต์…˜ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ๋ž€ ์—…๋ฐ์ดํŠธ (ํ•œ๊ตญ์–ด ๋ฌธ๊ตฌ)
336
+ image_change_btn.click(
337
+ fn=lambda: "#1 ์ด๋ฏธ์ง€์˜ [๋‹ค๋ฅธ ๋ชจ์Šต]์œผ๋กœ ๋ณ€๊ฒฝํ•˜๋ผ.",
338
+ inputs=[],
339
+ outputs=prompt_input
340
+ )
341
+ text_remove_btn.click(
342
+ fn=lambda: "#1 ์ด๋ฏธ์ง€์˜ [์ค‘๊ตญ์–ด๋ฅผ ๋ชจ๋‘]๋ฅผ ์ œ๊ฑฐํ•˜๋ผ.",
343
+ inputs=[],
344
+ outputs=prompt_input
345
+ )
346
+ clothes_change_btn.click(
347
+ fn=lambda: "#1์ด๋ฏธ์ง€์—์„œ [์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์œ ์ง€ํ•œ ์ฒด] ์˜์ƒ[์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ์„]#2, #3์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”.",
348
+ inputs=[],
349
+ outputs=prompt_input
350
+ )
351
+ background_change_btn.click(
352
+ fn=lambda: "#1 ์ด๋ฏธ์ง€์— #2 ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ ๊ต์ฒดํ•˜๋ผ [๋‹จ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜์—ฌ ๋‘ ์ด๋ฏธ์ง€์˜ ๋ถ„์œ„๊ธฐ๊ฐ€ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”].",
353
+ inputs=[],
354
+ outputs=prompt_input
355
+ )
356
+ composite_product_btn.click(
357
+ fn=lambda: "#1 ์ด๋ฏธ์ง€์—์„œ [์ œ๊ฑฐํ•  ์˜์—ญ ๋˜๋Š” ๋Œ€์ƒ์„ ์ž์„ธํžˆ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”]์„ ์ œ๊ฑฐํ•œ ํ›„, ๊ทธ ์ž๋ฆฌ๋ฅผ ์ฃผ๋ณ€ ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ฑ„์›Œ์ฃผ์„ธ์š”. ๋‹จ, ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ์™€ ์ „์ฒด ๋ถ„์œ„๊ธฐ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ํ•ด ์ฃผ์„ธ์š”.",
358
+ inputs=[],
359
+ outputs=prompt_input
360
+ )
361
 
362
  submit_btn.click(
363
+ fn=generate_multiple_images,
364
  inputs=[image1_input, image2_input, image3_input, prompt_input],
365
+ outputs=[output_image1, output_image2, output_image3, output_image4, output_text, prompt_display],
366
  )
367
+
368
  gr.Markdown(
369
  """
370
  ### ์‚ฌ์šฉ ๋ฐฉ๋ฒ•:
371
 
372
+ 1. **์ž๋™ ํ•ฉ์„ฑ**: ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ  ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋น„์›Œ๋‘๋ฉด ์ž๋™์œผ๋กœ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค.
373
+ 2. **์ด๋ฏธ์ง€ ์ฐธ์กฐ**: #1, #2, #3์œผ๋กœ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
374
+ 3. **์„ ํƒ ์˜ต์…˜**: ์œ„์˜ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ๋ž€์— ํ•œ๊ตญ์–ด ๋ฌธ๊ตฌ๋กœ ์ž…๋ ฅํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
375
+ 4. **๋‹ค์–‘ํ•œ ์ด๋ฏธ์ง€**: "์ด๋ฏธ์ง€ ์ƒ์„ฑ" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜๋ฉด ์ฐจ๋ก€๋กœ 4์žฅ์˜ ์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
 
376
 
377
+ > **ํŒ**: ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
378
  """
379
  )
380
 
 
381
  if __name__ == "__main__":
382
  demo.launch(share=True)