Kims12 commited on
Commit
da0375c
Β·
verified Β·
1 Parent(s): e9db8f1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +234 -140
app.py CHANGED
@@ -17,6 +17,67 @@ load_dotenv()
17
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
18
  logger = logging.getLogger(__name__)
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  # ------------------- κΈ°λ³Έ 이미지 생성 κ΄€λ ¨ ν•¨μˆ˜ -------------------
21
  def save_binary_file(file_name, data):
22
  with open(file_name, "wb") as f:
@@ -86,32 +147,39 @@ def generate_with_images(prompt, images, variation_index=0):
86
  return None, "API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. ν™˜κ²½λ³€μˆ˜λ₯Ό ν™•μΈν•΄μ£Όμ„Έμš”."
87
  client = genai.Client(api_key=api_key)
88
  logger.info(f"Gemini API μš”μ²­ μ‹œμž‘ - ν”„λ‘¬ν”„νŠΈ: {prompt}, λ³€ν˜• 인덱슀: {variation_index}")
 
 
89
  variation_suffixes = [
90
- " Create this as the first variation. Do not add any text, watermarks, or labels to the image.",
91
- " Create this as the second variation with more vivid colors. Do not add any text, watermarks, or labels to the image.",
92
- " Create this as the third variation with a more creative style. Do not add any text, watermarks, or labels to the image.",
93
- " Create this as the fourth variation with enhanced details. Do not add any text, watermarks, or labels to the image."
94
  ]
 
95
  if variation_index < len(variation_suffixes):
96
  prompt = prompt + variation_suffixes[variation_index]
97
  else:
98
- prompt = prompt + " Do not add any text, watermarks, or labels to the image."
 
99
  contents = [prompt]
100
  for idx, img in enumerate(images, 1):
101
  if img is not None:
102
  contents.append(img)
103
  logger.info(f"이미지 #{idx} 좔가됨")
 
 
104
  response = client.models.generate_content(
105
  model="gemini-2.0-flash-exp-image-generation",
106
  contents=contents,
107
  config=types.GenerateContentConfig(
108
  response_modalities=['Text', 'Image'],
109
- temperature=1,
110
- top_p=0.95,
111
- top_k=40,
112
- max_output_tokens=8192
113
  )
114
  )
 
115
  with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp:
116
  temp_path = tmp.name
117
  result_text = ""
@@ -160,117 +228,21 @@ def process_images_with_prompt(image1, prompt, variation_index=0, max_retries=3)
160
  time.sleep(1)
161
  return None, f"μ΅œλŒ€ μž¬μ‹œλ„ 횟수({max_retries}회) 초과 ν›„ μ‹€νŒ¨: {last_error}", prompt
162
 
163
- # ------------------- μƒν’ˆ ν”„λ‘¬ν”„νŠΈ 생성 κ΄€λ ¨ ν•¨μˆ˜ -------------------
164
- # λ°°κ²½ JSON 파일 경둜 μ„€μ •
165
- BACKGROUNDS_DIR = "./background"
166
- if not os.path.exists(BACKGROUNDS_DIR):
167
- os.makedirs(BACKGROUNDS_DIR)
168
- logger.info(f"λ°°κ²½ 디렉토리λ₯Ό μƒμ„±ν–ˆμŠ΅λ‹ˆλ‹€: {BACKGROUNDS_DIR}")
169
- else:
170
- logger.info(f"λ°°κ²½ 디렉토리가 이미 μ‘΄μž¬ν•©λ‹ˆλ‹€: {BACKGROUNDS_DIR}")
171
-
172
- def load_background_json(filename):
173
- file_path = os.path.join(BACKGROUNDS_DIR, filename)
174
- try:
175
- with open(file_path, 'r', encoding='utf-8') as f:
176
- data = json.load(f)
177
- logger.info(f"{filename} νŒŒμΌμ„ μ„±κ³΅μ μœΌλ‘œ λ‘œλ“œν–ˆμŠ΅λ‹ˆλ‹€. {len(data)} ν•­λͺ© 포함.")
178
- return data
179
- except Exception as e:
180
- logger.warning(f"{filename} 파일 λ‘œλ“œ 쀑 였λ₯˜ λ°œμƒ: {str(e)}. 기본값을 μ‚¬μš©ν•©λ‹ˆλ‹€.")
181
- return {}
182
-
183
- SIMPLE_BACKGROUNDS = load_background_json("simple_backgrounds.json")
184
- STUDIO_BACKGROUNDS = load_background_json("studio_backgrounds.json")
185
- NATURE_BACKGROUNDS = load_background_json("nature_backgrounds.json")
186
- INDOOR_BACKGROUNDS = load_background_json("indoor_backgrounds.json")
187
- ABSTRACT_BACKGROUNDS = load_background_json("abstract_backgrounds.json")
188
-
189
- if not SIMPLE_BACKGROUNDS:
190
- SIMPLE_BACKGROUNDS = {"ν™”μ΄νŠΈ λ°°κ²½": "white background"}
191
- if not STUDIO_BACKGROUNDS:
192
- STUDIO_BACKGROUNDS = {"μ œν’ˆ 사진 μŠ€νŠœλ””μ˜€": "product photography studio"}
193
- if not NATURE_BACKGROUNDS:
194
- NATURE_BACKGROUNDS = {"μ—΄λŒ€ ν•΄λ³€": "tropical beach"}
195
- if not INDOOR_BACKGROUNDS:
196
- INDOOR_BACKGROUNDS = {"λͺ¨λ˜ 리빙룸": "modern living room"}
197
- if not ABSTRACT_BACKGROUNDS:
198
- ABSTRACT_BACKGROUNDS = {"λ„€μ˜¨ μ‘°λͺ…": "neon lights"}
199
-
200
- # ------------------- μ‹œμŠ€ν…œ μΈμŠ€νŠΈλŸ­μ…˜ μˆ˜μ • (μƒν’ˆ 포컀슀, κ³ ν™”μ§ˆ λ Œλ”λ§ 및 μƒν’ˆ ν…Œλ‘λ¦¬ λΉ› μ΅œμ†Œν™”) -------------------
201
- def generate_system_instruction():
202
- return """당신은 μƒν’ˆ μ΄λ―Έμ§€μ˜ 배경을 λ³€κ²½ν•˜κΈ° μœ„ν•œ κ³ ν’ˆμ§ˆ ν”„λ‘¬ν”„νŠΈλ₯Ό μƒμ„±ν•˜λŠ” μ „λ¬Έκ°€μž…λ‹ˆλ‹€.
203
- μ‚¬μš©μžκ°€ μ œκ³΅ν•˜λŠ” μƒν’ˆλͺ…, λ°°κ²½ μœ ν˜•, μΆ”κ°€ μš”μ²­μ‚¬ν•­μ„ λ°”νƒ•μœΌλ‘œ λ―Έλ“œμ €λ‹ˆ(Midjourney)에 μ‚¬μš©ν•  수 μžˆλŠ” μƒμ„Έν•˜κ³  전문적인 ν”„λ‘¬ν”„νŠΈλ₯Ό μ˜μ–΄λ‘œ μƒμ„±ν•΄μ£Όμ„Έμš”.
204
- λ‹€μŒ κ°€μ΄λ“œλΌμΈμ„ λ°˜λ“œμ‹œ μ€€μˆ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€:
205
- 1. μƒν’ˆμ„ "#1"둜 μ§€μ •ν•˜μ—¬ μ°Έμ‘°ν•©λ‹ˆλ‹€. (예: "skincare tube (#1)")
206
- 2. *** 맀우 μ€‘μš”: μƒν’ˆμ˜ μ›λž˜ νŠΉμ„±(λ””μžμΈ, 색상, ν˜•νƒœ, 둜고, νŒ¨ν‚€μ§€ λ“±)은 μ–΄λ–€ μƒν™©μ—μ„œλ„ μ ˆλŒ€ λ³€κ²½ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ***
207
- 3. *** μƒν’ˆμ˜ 본질적 νŠΉμ„±μ„ μœ μ§€ν•˜λ˜, μƒν’ˆμ— 포컀슀λ₯Ό 맞좰 λͺ¨λ“  μ„ΈλΆ€ 사항이 μ„ λͺ…ν•˜κ²Œ λ“œλŸ¬λ‚˜λ„λ‘ ν•˜λ©°, κ³ ν™”μ§ˆ(ultra high resolution)둜 λ Œλ”λ§λ˜μ–΄ ν™”μ§ˆ μ €ν•˜ 없이 ν‘œν˜„λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€. ***
208
- 4. 이미지 λΉ„μœ¨μ€ μ •ν™•νžˆ 1:1(μ •μ‚¬κ°ν˜•) ν˜•μ‹μœΌλ‘œ μ§€μ •ν•©λ‹ˆλ‹€. ν”„λ‘¬ν”„νŠΈμ— "square format", "1:1 ratio" λ˜λŠ” "aspect ratio 1:1"을 λͺ…μ‹œμ μœΌλ‘œ ν¬ν•¨ν•©λ‹ˆλ‹€.
209
- 5. μƒν’ˆμ€ λ°˜λ“œμ‹œ μ •μ‚¬κ°ν˜• κ΅¬λ„μ˜ 정쀑앙에 λ°°μΉ˜λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
210
- 6. μƒν’ˆμ„ μ΄λ―Έμ§€μ˜ μ£Όμš” 초점으둜 λΆ€κ°μ‹œν‚€κ³ , μƒν’ˆμ˜ λΉ„μœ¨μ΄ 전체 μ΄λ―Έμ§€μ—μ„œ 크게 μ°¨μ§€ν•˜λ„λ‘ ν•©λ‹ˆλ‹€.
211
- 7. μƒν’ˆ 이미지 컷아웃(#1)의 κΈ°λ³Έ ν˜•νƒœμ™€ 색상은 μœ μ§€ν•˜λ©΄μ„œ, μ„ νƒν•œ ν™˜κ²½μ— μžμ—°μŠ€λŸ½κ²Œ ν†΅ν•©λ˜λ„λ‘ ν•©λ‹ˆλ‹€.
212
- 8. κ³ κΈ‰μŠ€λŸ¬μš΄ 상업적 이미지λ₯Ό μœ„ν•œ λ‹€μŒ ν™˜κ²½ μš”μ†Œλ“€μ„ ν¬οΏ½οΏ½ν•˜μ„Έμš”:
213
- - μƒν’ˆκ³Ό μ–΄μšΈλ¦¬λŠ” μ£Όλ³€ ν™˜κ²½/λ°°κ²½ μš”μ†Œ
214
- - ν™˜κ²½μ˜ μ‘°λͺ… νš¨κ³ΌλŠ” λΆ€λ“œλŸ½κ³  μžμ—°μŠ€λŸ¬μš΄ μ‘°λͺ…λ§Œ μ μš©ν•˜λ©°, μƒν’ˆ ν…Œλ‘λ¦¬μ˜ λΉ› νš¨κ³ΌλŠ” μ΅œμ†Œν™”ν•©λ‹ˆλ‹€.
215
- - μžμ—°μŠ€λŸ¬μš΄ κ·Έλ¦Όμžμ™€ λΉ› ν‘œν˜„
216
- - μƒν’ˆμ˜ μš©λ„λ‚˜ μž₯점을 μ•”μ‹œν•˜λŠ” λ°°κ²½ μš”μ†Œ
217
- - ν”„λ‘œνŽ˜μ…”λ„ν•œ 상업 사진 효과 (피사계 심도, μ†Œν”„νŠΈ 포컀슀, μŠ€νŠœλ””μ˜€ μ‘°λͺ… λ“±)
218
- 9. ν”„λ‘¬ν”„νŠΈμ— λ‹€μŒ μš”μ†Œλ“€μ„ λͺ…μ‹œμ μœΌλ‘œ ν¬ν•¨ν•˜μ„Έμš”:
219
- - "highly detailed commercial photography"
220
- - "award-winning product photography"
221
- - "professional advertising imagery"
222
- - "studio quality"
223
- - "magazine advertisement quality"
224
- 10. λ°°κ²½ ν™˜κ²½ μš”μ†Œλ₯Ό μƒν’ˆ μΉ΄ν…Œκ³ λ¦¬μ— 맞게 μ„ νƒν•©λ‹ˆλ‹€.
225
- 11. μ‚¬μš©μžκ°€ μ œκ³΅ν•œ ꡬ체적인 λ°°κ²½κ³Ό μΆ”κ°€ μš”μ²­μ‚¬ν•­μ„ μ •ν™•νžˆ λ°˜μ˜ν•©λ‹ˆλ‹€.
226
- 12. ν”„λ‘¬ν”„νŠΈλŠ” λ―Έλ“œμ €λ‹ˆ AI에 μ΅œμ ν™”λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
227
- 13. ν”„λ‘¬ν”„νŠΈ 끝에 "--ar 1:1 --s 750 --q 2" νŒŒλΌλ―Έν„°λ₯Ό μΆ”κ°€ν•˜μ—¬ λ―Έλ“œμ €λ‹ˆμ—μ„œ κ³ ν’ˆμ§ˆ μ •μ‚¬κ°ν˜• λΉ„μœ¨μ„ κ°•μ œν•©λ‹ˆλ‹€.
228
- """
229
-
230
- def generate_prompt_with_gemini(product_name, background_info, additional_info=""):
231
- GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
232
- if not GEMINI_API_KEY:
233
- return "Gemini API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. ν™˜κ²½ λ³€μˆ˜ GEMINI_API_KEYλ₯Ό μ„€μ •ν•˜κ±°λ‚˜ μ½”λ“œμ— 직접 μž…λ ₯ν•˜μ„Έμš”."
234
- try:
235
- genai_generative.configure(api_key=GEMINI_API_KEY)
236
- prompt_request = f"""
237
- μƒν’ˆλͺ…: {product_name}
238
- λ°°κ²½ μœ ν˜•: {background_info.get('english', 'studio')}
239
- λ°°κ²½ μΉ΄ν…Œκ³ λ¦¬: {background_info.get('category', '')}
240
- λ°°κ²½ 이름: {background_info.get('name', '')}
241
- μΆ”κ°€ μš”μ²­μ‚¬ν•­: {additional_info}
242
- μ€‘μš” μš”κ΅¬μ‚¬ν•­:
243
- 1. μƒν’ˆμ΄ 크게 λΆ€κ°λ˜κ³  μ΄λ―Έμ§€μ—μ„œ 쀑심적인 μœ„μΉ˜λ₯Ό μ°¨μ§€ν•˜λ„λ‘ ν”„λ‘¬ν”„νŠΈλ₯Ό μƒμ„±ν•΄μ£Όμ„Έμš”.
244
- 2. μ΄λ―Έμ§€λŠ” μ •ν™•νžˆ 1:1 λΉ„μœ¨(μ •μ‚¬κ°ν˜•)이어야 ν•©λ‹ˆλ‹€.
245
- 3. μƒν’ˆμ€ μ •μ‚¬κ°ν˜• ν”„λ ˆμž„μ˜ 정쀑앙에 μœ„μΉ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.
246
- 4. μƒν’ˆμ˜ λ””μžμΈ, 색상, ν˜•νƒœ, 둜고 λ“± 본질적 νŠΉμ„±μ€ μ ˆλŒ€ μˆ˜μ •ν•˜μ§€ λ§ˆμ„Έμš”.
247
- 5. ν™˜κ²½κ³Όμ˜ μžμ—°μŠ€λŸ¬μš΄ 톡합을 μœ„ν•œ μ‘°λͺ… νš¨κ³Όμ™€ κ·Έλ¦ΌμžλŠ” μ μš©ν•˜λ˜, μƒν’ˆ ν…Œλ‘λ¦¬μ˜ λΉ› νš¨κ³ΌλŠ” μ΅œμ†Œν™”ν•©λ‹ˆλ‹€.
248
- 6. μƒν’ˆμ„ 더 λ‹λ³΄μ΄κ²Œ ν•˜λŠ” λ°°κ²½ ν™˜κ²½μ„ μ„€λͺ…ν•΄μ£Όμ„Έμš”.
249
- 7. κ³ κΈ‰μŠ€λŸ¬μš΄ 상업 κ΄‘κ³  ν’ˆμ§ˆμ˜ 이미지가 λ˜λ„λ‘ ν™˜κ²½ μ„€λͺ…을 ν•΄μ£Όμ„Έμš”.
250
- 8. ν”„λ‘¬ν”„νŠΈ 끝에 λ―Έλ“œμ €λ‹ˆ νŒŒλΌλ―Έν„° "--ar 1:1 --s 750 --q 2"λ₯Ό μΆ”κ°€ν•΄μ£Όμ„Έμš”.
251
- ν•œκ΅­μ–΄ μž…λ ₯ λ‚΄μš©μ„ μ˜μ–΄λ‘œ 적절히 λ²ˆμ—­ν•˜μ—¬ λ°˜μ˜ν•΄μ£Όμ„Έμš”.
252
- """
253
- model = genai_generative.GenerativeModel(
254
- 'gemini-2.0-flash',
255
- system_instruction=generate_system_instruction()
256
- )
257
- response = model.generate_content(
258
- prompt_request,
259
- generation_config=genai_generative.types.GenerationConfig(
260
- temperature=0.7,
261
- top_p=0.95,
262
- top_k=64,
263
- max_output_tokens=1024,
264
- )
265
- )
266
- response_text = response.text.strip()
267
- if "--ar 1:1" not in response_text:
268
- response_text = response_text.rstrip(".") + ". --ar 1:1 --s 750 --q 2"
269
- return response_text
270
- except Exception as e:
271
- return f"ν”„λ‘¬ν”„νŠΈ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}"
272
 
273
- def get_selected_background_info(bg_type, simple, studio, nature, indoor, abstract):
 
 
274
  if bg_type == "μ‹¬ν”Œ λ°°κ²½":
275
  return {
276
  "category": "μ‹¬ν”Œ λ°°κ²½",
@@ -295,6 +267,18 @@ def get_selected_background_info(bg_type, simple, studio, nature, indoor, abstra
295
  "name": indoor,
296
  "english": INDOOR_BACKGROUNDS.get(indoor, "indoor environment")
297
  }
 
 
 
 
 
 
 
 
 
 
 
 
298
  elif bg_type == "좔상/특수 λ°°κ²½":
299
  return {
300
  "category": "좔상/특수 λ°°κ²½",
@@ -308,24 +292,114 @@ def get_selected_background_info(bg_type, simple, studio, nature, indoor, abstra
308
  "english": "white background"
309
  }
310
 
311
- # --- ν”„λ‘¬ν”„νŠΈμ—μ„œ 였직 Midjourney ν”„λ‘¬ν”„νŠΈ ν…μŠ€νŠΈλ§Œ μΆ”μΆœν•˜λŠ” ν•¨μˆ˜ ---
312
- def filter_prompt_only(prompt):
313
- idx = prompt.find("Highly detailed commercial photography")
314
- if idx != -1:
315
- prompt_text = prompt[idx:].strip()
316
- end_idx = prompt_text.rfind("--ar 1:1 --s 750 --q 2")
317
- if end_idx != -1:
318
- end_idx += len("--ar 1:1 --s 750 --q 2")
319
- prompt_text = prompt_text[:end_idx].strip()
320
- return prompt_text
321
- return prompt.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
322
 
323
  # ------------------- 단일 이미지 생성 ν•¨μˆ˜ -------------------
324
- def generate_product_image(image, bg_type, simple, studio, nature, indoor, abstract, product_name, additional_info):
325
  if image is None:
326
  return None, "이미지λ₯Ό μ—…λ‘œλ“œν•΄μ£Όμ„Έμš”.", "이미지λ₯Ό μ—…λ‘œλ“œ ν›„ ν”„λ‘¬ν”„νŠΈλ₯Ό μƒμ„±ν•΄μ£Όμ„Έμš”."
327
  product_name = product_name.strip() or "μ œν’ˆ"
328
- background_info = get_selected_background_info(bg_type, simple, studio, nature, indoor, abstract)
329
  generated_prompt = generate_prompt_with_gemini(product_name, background_info, additional_info)
330
  if "Gemini API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€" in generated_prompt:
331
  warning_msg = (
@@ -341,11 +415,11 @@ def generate_product_image(image, bg_type, simple, studio, nature, indoor, abstr
341
  return result_image, status, final_prompt
342
 
343
  # ------------------- 4μž₯ 이미지 생성 ν•¨μˆ˜ -------------------
344
- def generate_product_images(image, bg_type, simple, studio, nature, indoor, abstract, product_name, additional_info):
345
  if image is None:
346
  return None, None, None, None, "이미지λ₯Ό μ—…λ‘œλ“œν•΄μ£Όμ„Έμš”.", "이미지λ₯Ό μ—…λ‘œλ“œ ν›„ ν”„λ‘¬ν”„νŠΈλ₯Ό μƒμ„±ν•΄μ£Όμ„Έμš”."
347
  product_name = product_name.strip() or "μ œν’ˆ"
348
- background_info = get_selected_background_info(bg_type, simple, studio, nature, indoor, abstract)
349
  generated_prompt = generate_prompt_with_gemini(product_name, background_info, additional_info)
350
  if "Gemini API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€" in generated_prompt:
351
  warning_msg = (
@@ -377,9 +451,9 @@ def create_app():
377
  )
378
  with gr.Row():
379
  with gr.Column(scale=1):
380
- product_name = gr.Textbox(label="μƒν’ˆλͺ… (ν•œκ΅­μ–΄ μž…λ ₯)", placeholder="예: μŠ€ν‚¨μΌ€μ–΄ 튜브, ν…€λΈ”λŸ¬ λ“±", interactive=True)
381
  background_type = gr.Radio(
382
- choices=["μ‹¬ν”Œ λ°°κ²½", "μŠ€νŠœλ””μ˜€ λ°°κ²½", "μžμ—° ν™˜κ²½", "μ‹€λ‚΄ ν™˜κ²½", "좔상/특수 λ°°κ²½"],
383
  label="λ°°κ²½ μœ ν˜•",
384
  value="μ‹¬ν”Œ λ°°κ²½"
385
  )
@@ -411,6 +485,20 @@ def create_app():
411
  visible=False,
412
  interactive=True
413
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  abstract_dropdown = gr.Dropdown(
415
  choices=list(ABSTRACT_BACKGROUNDS.keys()),
416
  value=list(ABSTRACT_BACKGROUNDS.keys())[0] if ABSTRACT_BACKGROUNDS else None,
@@ -430,12 +518,14 @@ def create_app():
430
  studio_dropdown: gr.update(visible=(bg_type == "μŠ€νŠœλ””μ˜€ λ°°κ²½")),
431
  nature_dropdown: gr.update(visible=(bg_type == "μžμ—° ν™˜κ²½")),
432
  indoor_dropdown: gr.update(visible=(bg_type == "μ‹€λ‚΄ ν™˜κ²½")),
 
 
433
  abstract_dropdown: gr.update(visible=(bg_type == "좔상/특수 λ°°κ²½"))
434
  }
435
  background_type.change(
436
  fn=update_dropdowns,
437
  inputs=[background_type],
438
- outputs=[simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, abstract_dropdown]
439
  )
440
  image_input = gr.Image(label="μƒν’ˆ 이미지 μ—…λ‘œλ“œ", type="pil")
441
  with gr.Row():
@@ -455,17 +545,21 @@ def create_app():
455
  prompt_output_multi = gr.Textbox(label="μƒμ„±λœ ν”„λ‘¬ν”„νŠΈ (μ˜μ–΄)", lines=6)
456
  single_btn.click(
457
  fn=generate_product_image,
458
- inputs=[image_input, background_type, simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, abstract_dropdown, product_name, additional_info],
459
  outputs=[image_output, status_output, prompt_output]
460
  )
461
  multi_btn.click(
462
  fn=generate_product_images,
463
- inputs=[image_input, background_type, simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, abstract_dropdown, product_name, additional_info],
464
  outputs=[image_output1, image_output2, image_output3, image_output4, status_output_multi, prompt_output_multi]
465
  )
466
  return demo
467
 
 
468
  if __name__ == "__main__":
 
 
 
469
  app = create_app()
470
  app.queue()
471
- app.launch()
 
17
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
18
  logger = logging.getLogger(__name__)
19
 
20
+ # ------------------- λ°°κ²½ 디렉토리 μ„€μ • -------------------
21
+ BACKGROUNDS_DIR = "./background"
22
+ if not os.path.exists(BACKGROUNDS_DIR):
23
+ os.makedirs(BACKGROUNDS_DIR)
24
+ logger.info(f"λ°°κ²½ 디렉토리λ₯Ό μƒμ„±ν–ˆμŠ΅λ‹ˆλ‹€: {BACKGROUNDS_DIR}")
25
+
26
+ # ------------------- μ „μ—­ λ³€μˆ˜ μ„€μ • -------------------
27
+ SIMPLE_BACKGROUNDS = {}
28
+ STUDIO_BACKGROUNDS = {}
29
+ NATURE_BACKGROUNDS = {}
30
+ INDOOR_BACKGROUNDS = {}
31
+ TECHNOLOGY_BACKGROUNDS = {}
32
+ COLORFUL_PATTERN_BACKGROUNDS = {}
33
+ ABSTRACT_BACKGROUNDS = {}
34
+
35
+ # ------------------- λ°°κ²½ JSON 파일 λ‘œλ“œ ν•¨μˆ˜ -------------------
36
+ def load_background_json(filename):
37
+ file_path = os.path.join(BACKGROUNDS_DIR, filename)
38
+ try:
39
+ with open(file_path, 'r', encoding='utf-8') as f:
40
+ data = json.load(f)
41
+ logger.info(f"{filename} νŒŒμΌμ„ μ„±κ³΅μ μœΌλ‘œ λ‘œλ“œν–ˆμŠ΅λ‹ˆλ‹€. {len(data)} ν•­λͺ© 포함.")
42
+ return data
43
+ except FileNotFoundError:
44
+ logger.info(f"{filename} 파일이 μ—†μŠ΅λ‹ˆλ‹€.")
45
+ return {}
46
+ except Exception as e:
47
+ logger.warning(f"{filename} 파일 λ‘œλ“œ 쀑 였λ₯˜ λ°œμƒ: {str(e)}.")
48
+ return {}
49
+
50
+ # ------------------- λ°°κ²½ μ΄ˆκΈ°ν™” ν•¨μˆ˜ -------------------
51
+ def initialize_backgrounds():
52
+ global SIMPLE_BACKGROUNDS, STUDIO_BACKGROUNDS, NATURE_BACKGROUNDS, INDOOR_BACKGROUNDS
53
+ global TECHNOLOGY_BACKGROUNDS, COLORFUL_PATTERN_BACKGROUNDS, ABSTRACT_BACKGROUNDS
54
+
55
+ SIMPLE_BACKGROUNDS = load_background_json("simple_backgrounds.json")
56
+ STUDIO_BACKGROUNDS = load_background_json("studio_backgrounds.json")
57
+ NATURE_BACKGROUNDS = load_background_json("nature_backgrounds.json")
58
+ INDOOR_BACKGROUNDS = load_background_json("indoor_backgrounds.json")
59
+ TECHNOLOGY_BACKGROUNDS = load_background_json("technology_backgrounds.json")
60
+ COLORFUL_PATTERN_BACKGROUNDS = load_background_json("colorful_pattern_backgrounds.json")
61
+ ABSTRACT_BACKGROUNDS = load_background_json("abstract_backgrounds.json")
62
+
63
+ # κΈ°λ³Έκ°’ μ„€μ • (JSON 파일이 μ—†κ±°λ‚˜ λΉ„μ–΄μžˆλŠ” 경우)
64
+ if not SIMPLE_BACKGROUNDS:
65
+ SIMPLE_BACKGROUNDS = {"ν™”μ΄νŠΈ λ°°κ²½": "white background"}
66
+ if not STUDIO_BACKGROUNDS:
67
+ STUDIO_BACKGROUNDS = {"λ―Έλ‹ˆλ©€ ν”Œλž«λ ˆμ΄": "minimalist flat lay with clean white background"}
68
+ if not NATURE_BACKGROUNDS:
69
+ NATURE_BACKGROUNDS = {"μ—΄λŒ€ ν•΄λ³€": "tropical beach with crystal clear water"}
70
+ if not INDOOR_BACKGROUNDS:
71
+ INDOOR_BACKGROUNDS = {"λ―Έλ‹ˆλ©€ μŠ€μΉΈλ””λ‚˜λΉ„μ•ˆ κ±°μ‹€": "minimalist Scandinavian living room"}
72
+ if not TECHNOLOGY_BACKGROUNDS:
73
+ TECHNOLOGY_BACKGROUNDS = {"λ‹€μ΄λ‚˜λ―Ή μŠ€ν”Œλž˜μ‹œ": "dynamic water splash interaction with product"}
74
+ if not COLORFUL_PATTERN_BACKGROUNDS:
75
+ COLORFUL_PATTERN_BACKGROUNDS = {"ν™”λ €ν•œ 꽃 νŒ¨ν„΄": "vibrant floral pattern backdrop"}
76
+ if not ABSTRACT_BACKGROUNDS:
77
+ ABSTRACT_BACKGROUNDS = {"λ„€μ˜¨ μ‘°λͺ…": "neon lights abstract background"}
78
+
79
+ logger.info("λͺ¨λ“  λ°°κ²½ μ˜΅μ…˜ λ‘œλ“œ μ™„λ£Œ")
80
+
81
  # ------------------- κΈ°λ³Έ 이미지 생성 κ΄€λ ¨ ν•¨μˆ˜ -------------------
82
  def save_binary_file(file_name, data):
83
  with open(file_name, "wb") as f:
 
147
  return None, "API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. ν™˜κ²½λ³€μˆ˜λ₯Ό ν™•μΈν•΄μ£Όμ„Έμš”."
148
  client = genai.Client(api_key=api_key)
149
  logger.info(f"Gemini API μš”μ²­ μ‹œμž‘ - ν”„λ‘¬ν”„νŠΈ: {prompt}, λ³€ν˜• 인덱슀: {variation_index}")
150
+
151
+ # ν–₯μƒλœ λ³€ν˜• 접미사
152
  variation_suffixes = [
153
+ " Create this as a professional studio product shot with precise focus on the product details. Do not add any text, watermarks, or labels to the image.",
154
+ " Create this as a high-contrast artistic studio shot with dramatic lighting and shadows. Do not add any text, watermarks, or labels to the image.",
155
+ " Create this as a soft-lit elegantly styled product shot with complementary elements. Do not add any text, watermarks, or labels to the image.",
156
+ " Create this as a high-definition product photography with perfect color accuracy and detail preservation. Do not add any text, watermarks, or labels to the image."
157
  ]
158
+
159
  if variation_index < len(variation_suffixes):
160
  prompt = prompt + variation_suffixes[variation_index]
161
  else:
162
+ prompt = prompt + " Create as high-end commercial product photography. Do not add any text, watermarks, or labels to the image."
163
+
164
  contents = [prompt]
165
  for idx, img in enumerate(images, 1):
166
  if img is not None:
167
  contents.append(img)
168
  logger.info(f"이미지 #{idx} 좔가됨")
169
+
170
+ # ν–₯μƒλœ 생성 μ„€μ • - 더 높은 temperature둜 μ°½μ˜μ„± 증가, 더 높은 max_output_tokens
171
  response = client.models.generate_content(
172
  model="gemini-2.0-flash-exp-image-generation",
173
  contents=contents,
174
  config=types.GenerateContentConfig(
175
  response_modalities=['Text', 'Image'],
176
+ temperature=1.05, # μ•½κ°„ 높은 μ˜¨λ„λ‘œ 더 λ‹€μ–‘ν•œ κ²°κ³Ό
177
+ top_p=0.97, # μ•½κ°„ λ†’μž„
178
+ top_k=50, # λ‹€μ–‘μ„± 증가
179
+ max_output_tokens=10240 # 더 높은 토큰 ν•œλ„
180
  )
181
  )
182
+
183
  with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp:
184
  temp_path = tmp.name
185
  result_text = ""
 
228
  time.sleep(1)
229
  return None, f"μ΅œλŒ€ μž¬μ‹œλ„ 횟수({max_retries}회) 초과 ν›„ μ‹€νŒ¨: {last_error}", prompt
230
 
231
+ # ------------------- ν”„λ‘¬ν”„νŠΈμ—μ„œ 였직 Midjourney ν”„λ‘¬ν”„νŠΈ ν…μŠ€νŠΈλ§Œ μΆ”μΆœν•˜λŠ” ν•¨μˆ˜ -------------------
232
+ def filter_prompt_only(prompt):
233
+ idx = prompt.find("Highly detailed commercial photography")
234
+ if idx != -1:
235
+ prompt_text = prompt[idx:].strip()
236
+ end_idx = prompt_text.rfind("--ar 1:1 --s 750 --q 2")
237
+ if end_idx != -1:
238
+ end_idx += len("--ar 1:1 --s 750 --q 2")
239
+ prompt_text = prompt_text[:end_idx].strip()
240
+ return prompt_text
241
+ return prompt.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
+ # ------------------- μ„ νƒλœ λ°°κ²½ 정보 κ°€μ Έμ˜€κΈ° ν•¨μˆ˜ -------------------
244
+ def get_selected_background_info(bg_type, simple, studio, nature, indoor, tech, colorful, abstract):
245
+ """μ„ νƒλœ λ°°κ²½ 정보λ₯Ό κ°€μ Έμ˜€λŠ” ν•¨μˆ˜"""
246
  if bg_type == "μ‹¬ν”Œ λ°°κ²½":
247
  return {
248
  "category": "μ‹¬ν”Œ λ°°κ²½",
 
267
  "name": indoor,
268
  "english": INDOOR_BACKGROUNDS.get(indoor, "indoor environment")
269
  }
270
+ elif bg_type == "ν…Œν¬λ†€λ‘œμ§€ λ°°κ²½":
271
+ return {
272
+ "category": "ν…Œν¬λ†€λ‘œμ§€ λ°°κ²½",
273
+ "name": tech,
274
+ "english": TECHNOLOGY_BACKGROUNDS.get(tech, "technology environment")
275
+ }
276
+ elif bg_type == "μ»¬λŸ¬ν’€ νŒ¨ν„΄ λ°°κ²½":
277
+ return {
278
+ "category": "μ»¬λŸ¬ν’€ νŒ¨ν„΄ λ°°κ²½",
279
+ "name": colorful,
280
+ "english": COLORFUL_PATTERN_BACKGROUNDS.get(colorful, "colorful pattern background")
281
+ }
282
  elif bg_type == "좔상/특수 λ°°κ²½":
283
  return {
284
  "category": "좔상/특수 λ°°κ²½",
 
292
  "english": "white background"
293
  }
294
 
295
+ # ------------------- ν–₯μƒλœ μ‹œμŠ€ν…œ μΈμŠ€νŠΈλŸ­μ…˜ 생성 ν•¨μˆ˜ -------------------
296
+ def generate_enhanced_system_instruction():
297
+ """ν–₯μƒλœ μ‹œμŠ€ν…œ μΈμŠ€νŠΈλŸ­μ…˜ 생성 ν•¨μˆ˜"""
298
+ return """당신은 μƒν’ˆ μ΄λ―Έμ§€μ˜ 배경을 λ³€κ²½ν•˜κΈ° μœ„ν•œ 졜고 ν’ˆμ§ˆμ˜ ν”„λ‘¬ν”„νŠΈλ₯Ό μƒμ„±ν•˜λŠ” μ „λ¬Έκ°€μž…λ‹ˆλ‹€.
299
+ μ‚¬μš©μžκ°€ μ œκ³΅ν•˜λŠ” μƒν’ˆλͺ…, λ°°κ²½ μœ ν˜•, μΆ”κ°€ μš”μ²­μ‚¬ν•­μ„ λ°”νƒ•μœΌλ‘œ λ―Έλ“œμ €λ‹ˆ(Midjourney)에 μ‚¬μš©ν•  수 μžˆλŠ” μƒμ„Έν•˜κ³  전문적인 ν”„λ‘¬ν”„νŠΈλ₯Ό μ˜μ–΄λ‘œ μƒμ„±ν•΄μ£Όμ„Έμš”.
300
+
301
+ λ‹€μŒ κ°€μ΄λ“œλΌμΈμ„ λ°˜λ“œμ‹œ μ€€μˆ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€:
302
+
303
+ 1. μƒν’ˆμ„ "#1"둜 μ§€μ •ν•˜μ—¬ μ°Έμ‘°ν•©λ‹ˆλ‹€. (예: "skincare tube (#1)")
304
+
305
+ 2. *** 맀우 μ€‘μš”: μƒν’ˆμ˜ μ›λž˜ νŠΉμ„±(λ””μžμΈ, 색상, ν˜•νƒœ, 둜고, νŒ¨ν‚€μ§€ λ“±)은 μ–΄λ–€ μƒν™©μ—μ„œλ„ μ ˆλŒ€ λ³€κ²½ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ***
306
+
307
+ 3. *** μƒν’ˆμ˜ 본질적 νŠΉμ„±μ„ μœ μ§€ν•˜λ˜, μƒν’ˆμ— 포컀슀λ₯Ό 맞좰 λͺ¨λ“  μ„ΈλΆ€ 사항이 μ„ λͺ…ν•˜κ²Œ λ“œλŸ¬λ‚˜λ„λ‘ ν•˜λ©°,
308
+ 8K 해상도(8K resolution), μ˜€λ²„μƒ€ν”„λ‹ μ—†λŠ” μ΄ˆκ³ ν™”μ§ˆ(ultra high definition without oversharpening)둜 λ Œλ”λ§λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€. ***
309
+
310
+ 4. 이미지 λΉ„μœ¨μ€ μ •ν™•νžˆ 1:1(μ •μ‚¬κ°ν˜•) ν˜•μ‹μœΌλ‘œ μ§€μ •ν•©λ‹ˆλ‹€. ν”„λ‘¬ν”„νŠΈμ— "square format", "1:1 ratio" λ˜λŠ” "aspect ratio 1:1"을 λͺ…μ‹œμ μœΌλ‘œ ν¬ν•¨ν•©λ‹ˆλ‹€.
311
+
312
+ 5. μƒν’ˆμ€ λ°˜λ“œμ‹œ μ •μ‚¬κ°ν˜• κ΅¬λ„μ˜ 정쀑앙에 λ°°μΉ˜λ˜μ–΄μ•Ό ν•˜λ©°, μ μ ˆν•œ 크기둜 ν‘œν˜„ν•˜μ—¬ λ””ν…ŒμΌμ΄ μ™„λ²½ν•˜κ²Œ 보이도둝 ν•©λ‹ˆλ‹€.
313
+
314
+ 6. μƒν’ˆμ„ μ΄λ―Έμ§€μ˜ μ£Όμš” 초점으둜 λΆ€κ°μ‹œν‚€κ³ , μƒν’ˆμ˜ λΉ„μœ¨μ΄ 전체 μ΄λ―Έμ§€μ—μ„œ 60-70% 이상 μ°¨μ§€ν•˜λ„λ‘ ν•©λ‹ˆλ‹€.
315
+
316
+ 7. μ‘°λͺ… μ„€λͺ…을 맀우 ꡬ체적으둜 ν•΄μ£Όμ„Έμš”. 예: "soft directional lighting from left side", "dramatic rim lighting", "diffused natural light through windows"
317
+
318
+ 8. 배경의 재질과 μ§ˆκ°μ„ μƒμ„Ένžˆ μ„€λͺ…ν•΄μ£Όμ„Έμš”. 예: "polished marble surface", "rustic wooden table with visible grain", "matte concrete wall with subtle texture"
319
+
320
+ 9. ν”„λ‘¬ν”„νŠΈμ— λ‹€μŒ μš”μ†Œλ“€μ„ λͺ…μ‹œμ μœΌλ‘œ ν¬ν•¨ν•˜λ˜, μ‚¬μš© λ§₯락에 μ μ ˆν•˜κ²Œ λ³€ν˜•ν•˜μ„Έμš”:
321
+ - "award-winning product photography"
322
+ - "magazine-worthy commercial product shot"
323
+ - "professional advertising imagery with perfect exposure"
324
+ - "studio lighting with color-accurate rendering"
325
+ - "8K ultra high definition product showcase"
326
+ - "commercial product photography with precise detail rendering"
327
+ - "ultra high definition"
328
+ - "crystal clear details"
329
+
330
+ 10. μ‚¬μš©μžκ°€ μ œκ³΅ν•œ ꡬ체적인 λ°°κ²½κ³Ό μΆ”κ°€ μš”μ²­μ‚¬ν•­μ„ ν”„λ‘¬ν”„νŠΈμ— μ •ν™•νžˆ λ°˜μ˜ν•˜κ³  ν™•μž₯ν•©λ‹ˆλ‹€.
331
+
332
+ 11. ν”„λ‘¬ν”„νŠΈ 끝에 "--ar 1:1 --s 750 --q 2 --v 5.2" νŒŒλΌλ―Έν„°λ₯Ό μΆ”κ°€ν•˜μ—¬ λ―Έλ“œμ €λ‹ˆμ—μ„œ κ³ ν’ˆμ§ˆ μ •μ‚¬κ°ν˜• λΉ„μœ¨μ„ κ°•μ œν•©λ‹ˆλ‹€.
333
+ """
334
+
335
+ # ------------------- ν”„λ‘¬ν”„νŠΈ 생성 ν•¨μˆ˜ -------------------
336
+ def generate_prompt_with_gemini(product_name, background_info, additional_info=""):
337
+ """ν–₯μƒλœ ν”„λ‘¬ν”„νŠΈ 생성 ν•¨μˆ˜"""
338
+ GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
339
+ if not GEMINI_API_KEY:
340
+ return "Gemini API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. ν™˜κ²½ λ³€μˆ˜ GEMINI_API_KEYλ₯Ό μ„€μ •ν•˜κ±°λ‚˜ μ½”λ“œμ— 직접 μž…λ ₯ν•˜μ„Έμš”."
341
+
342
+ try:
343
+ genai_generative.configure(api_key=GEMINI_API_KEY)
344
+
345
+ # 더 μƒμ„Έν•œ ν”„λ‘¬ν”„νŠΈ μš”μ²­ ν…œν”Œλ¦Ώ
346
+ prompt_request = f"""
347
+ μƒν’ˆλͺ…: {product_name}
348
+ λ°°κ²½ μœ ν˜•: {background_info.get('english', 'studio')}
349
+ λ°°κ²½ μΉ΄ν…Œκ³ λ¦¬: {background_info.get('category', '')}
350
+ λ°°κ²½ 이름: {background_info.get('name', '')}
351
+ μΆ”κ°€ μš”μ²­μ‚¬ν•­: {additional_info}
352
+
353
+ μ€‘μš” μš”κ΅¬μ‚¬ν•­:
354
+ 1. μƒν’ˆ(#1)이 이미지 κ΅¬λ„μ—μ„œ 쀑심적인 μœ„μΉ˜λ₯Ό μ°¨μ§€ν•˜λ©° μ μ ˆν•œ 크기(μ΄λ―Έμ§€μ˜ 60-70%)둜 ν‘œν˜„λ˜λ„λ‘ ν”„λ‘¬ν”„νŠΈλ₯Ό μƒμ„±ν•΄μ£Όμ„Έμš”.
355
+ 2. μ΄λ―Έμ§€λŠ” μ •ν™•νžˆ 1:1 λΉ„μœ¨(μ •μ‚¬κ°ν˜•)이어야 ν•©λ‹ˆλ‹€.
356
+ 3. μƒν’ˆμ˜ λ””μžμΈ, 색상, ν˜•νƒœ, 둜고 λ“± 본질적 νŠΉμ„±μ€ μ ˆλŒ€ μˆ˜μ •ν•˜μ§€ λ§ˆμ„Έμš”.
357
+ 4. ꡬ체적인 μ‘°λͺ… 기법을 μƒμ„Ένžˆ λͺ…μ‹œν•΄μ£Όμ„Έμš”:
358
+ - μ •ν™•ν•œ μ‘°λͺ… μœ„μΉ˜ (예: "45-degree key light from upper left")
359
+ - μ‘°λͺ… ν’ˆμ§ˆ (예: "soft diffused light", "hard directional light")
360
+ - μ‘°λͺ… 강도와 μƒ‰μ˜¨λ„ (예: "warm tungsten key light with cool blue fill")
361
+ - λ°˜μ‚¬μ™€ 그림자 처리 방식 (예: "controlled specular highlights with soft shadow transitions")
362
+ 5. μƒν’ˆμ„ 더 λ‹λ³΄μ΄κ²Œ ν•˜λŠ” 보쑰 μš”μ†Œ(props)λ₯Ό μžμ—°μŠ€λŸ½κ²Œ ν™œμš©ν•˜λ˜, μƒν’ˆμ΄ 항상 주인곡이어야 ν•©λ‹ˆλ‹€.
363
+ 6. λ°°κ²½ 재질과 ν‘œλ©΄ μ§ˆκ°μ„ ꡬ체적으둜 μ„€λͺ…ν•˜κ³ , μƒν’ˆκ³Όμ˜ μƒν˜Έμž‘μš© 방식을 λͺ…μ‹œν•΄μ£Όμ„Έμš”.
364
+ 7. 색상 ꡬ성(color palette, color harmonies)을 λͺ…ν™•νžˆ ν•΄μ£Όμ„Έμš”.
365
+ 8. κ³ κΈ‰μŠ€λŸ¬μš΄ 상업 κ΄‘κ³  ν’ˆμ§ˆμ˜ 이미지가 λ˜λ„λ‘ ν”„λ‘¬ν”„νŠΈλ₯Ό μž‘μ„±ν•΄μ£Όμ„Έμš”.
366
+ 9. ν”„λ‘¬ν”„νŠΈ 끝에 λ―Έλ“œμ €λ‹ˆ νŒŒλΌλ―Έν„° "--ar 1:1 --s 750 --q 2 --v 5.2"λ₯Ό μΆ”κ°€ν•΄μ£Όμ„Έμš”.
367
+
368
+ ν•œκ΅­μ–΄ μž…λ ₯ λ‚΄μš©μ„ 전문적인 μ˜μ–΄λ‘œ λ²ˆμ—­ν•˜μ—¬ λ°˜μ˜ν•΄μ£Όμ„Έμš”.
369
+ """
370
+ # ν–₯μƒλœ μ‹œμŠ€ν…œ μΈμŠ€νŠΈλŸ­μ…˜ μ‚¬μš©
371
+ model = genai_generative.GenerativeModel(
372
+ 'gemini-2.0-flash',
373
+ system_instruction=generate_enhanced_system_instruction()
374
+ )
375
+
376
+ # 더 창의적인 κ²°κ³Όλ₯Ό μœ„ν•œ 생성 μ„€μ • μ‘°μ •
377
+ response = model.generate_content(
378
+ prompt_request,
379
+ generation_config=genai_generative.types.GenerationConfig(
380
+ temperature=0.8, # 더 창의적인 κ²°κ³Όλ₯Ό μœ„ν•΄ μ˜¨λ„ 상ν–₯
381
+ top_p=0.97,
382
+ top_k=64,
383
+ max_output_tokens=1600, # 더 μƒμ„Έν•œ ν”„λ‘¬ν”„νŠΈ ν—ˆμš©
384
+ )
385
+ )
386
+
387
+ response_text = response.text.strip()
388
+
389
+ # λ―Έλ“œμ €λ‹ˆ νŒŒλΌλ―Έν„°κ°€ 없을 경우 μΆ”κ°€
390
+ if "--ar 1:1" not in response_text:
391
+ response_text = response_text.rstrip(".") + ". --ar 1:1 --s 750 --q 2 --v 5.2"
392
+
393
+ return response_text
394
+ except Exception as e:
395
+ return f"ν”„λ‘¬ν”„νŠΈ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€: {str(e)}"
396
 
397
  # ------------------- 단일 이미지 생성 ν•¨μˆ˜ -------------------
398
+ def generate_product_image(image, bg_type, simple, studio, nature, indoor, tech, colorful, abstract, product_name, additional_info):
399
  if image is None:
400
  return None, "이미지λ₯Ό μ—…λ‘œλ“œν•΄μ£Όμ„Έμš”.", "이미지λ₯Ό μ—…λ‘œλ“œ ν›„ ν”„λ‘¬ν”„νŠΈλ₯Ό μƒμ„±ν•΄μ£Όμ„Έμš”."
401
  product_name = product_name.strip() or "μ œν’ˆ"
402
+ background_info = get_selected_background_info(bg_type, simple, studio, nature, indoor, tech, colorful, abstract)
403
  generated_prompt = generate_prompt_with_gemini(product_name, background_info, additional_info)
404
  if "Gemini API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€" in generated_prompt:
405
  warning_msg = (
 
415
  return result_image, status, final_prompt
416
 
417
  # ------------------- 4μž₯ 이미지 생성 ν•¨μˆ˜ -------------------
418
+ def generate_product_images(image, bg_type, simple, studio, nature, indoor, tech, colorful, abstract, product_name, additional_info):
419
  if image is None:
420
  return None, None, None, None, "이미지λ₯Ό μ—…λ‘œλ“œν•΄μ£Όμ„Έμš”.", "이미지λ₯Ό μ—…λ‘œλ“œ ν›„ ν”„λ‘¬ν”„νŠΈλ₯Ό μƒμ„±ν•΄μ£Όμ„Έμš”."
421
  product_name = product_name.strip() or "μ œν’ˆ"
422
+ background_info = get_selected_background_info(bg_type, simple, studio, nature, indoor, tech, colorful, abstract)
423
  generated_prompt = generate_prompt_with_gemini(product_name, background_info, additional_info)
424
  if "Gemini API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€" in generated_prompt:
425
  warning_msg = (
 
451
  )
452
  with gr.Row():
453
  with gr.Column(scale=1):
454
+ product_name = gr.Textbox(label="μƒν’ˆλͺ… (ν•œκ΅­μ–΄ μž…λ ₯)", placeholder="예: μŠ€ν‚¨μΌ€μ–΄ 튜브, μŠ€λ§ˆνŠΈμ›ŒμΉ˜, ν–₯수, μš΄λ™ν™” λ“±", interactive=True)
455
  background_type = gr.Radio(
456
+ choices=["μ‹¬ν”Œ λ°°κ²½", "μŠ€νŠœλ””μ˜€ λ°°κ²½", "μžμ—° ν™˜κ²½", "μ‹€λ‚΄ ν™˜κ²½", "ν…Œν¬λ†€λ‘œμ§€ λ°°κ²½", "μ»¬λŸ¬ν’€ νŒ¨ν„΄ λ°°κ²½", "좔상/특수 λ°°κ²½"],
457
  label="λ°°κ²½ μœ ν˜•",
458
  value="μ‹¬ν”Œ λ°°κ²½"
459
  )
 
485
  visible=False,
486
  interactive=True
487
  )
488
+ tech_dropdown = gr.Dropdown(
489
+ choices=list(TECHNOLOGY_BACKGROUNDS.keys()),
490
+ value=list(TECHNOLOGY_BACKGROUNDS.keys())[0] if TECHNOLOGY_BACKGROUNDS else None,
491
+ label="ν…Œν¬λ†€λ‘œμ§€ λ°°κ²½ 선택",
492
+ visible=False,
493
+ interactive=True
494
+ )
495
+ colorful_dropdown = gr.Dropdown(
496
+ choices=list(COLORFUL_PATTERN_BACKGROUNDS.keys()),
497
+ value=list(COLORFUL_PATTERN_BACKGROUNDS.keys())[0] if COLORFUL_PATTERN_BACKGROUNDS else None,
498
+ label="μ»¬λŸ¬ν’€ νŒ¨ν„΄ λ°°κ²½ 선택",
499
+ visible=False,
500
+ interactive=True
501
+ )
502
  abstract_dropdown = gr.Dropdown(
503
  choices=list(ABSTRACT_BACKGROUNDS.keys()),
504
  value=list(ABSTRACT_BACKGROUNDS.keys())[0] if ABSTRACT_BACKGROUNDS else None,
 
518
  studio_dropdown: gr.update(visible=(bg_type == "μŠ€νŠœλ””μ˜€ λ°°κ²½")),
519
  nature_dropdown: gr.update(visible=(bg_type == "μžμ—° ν™˜κ²½")),
520
  indoor_dropdown: gr.update(visible=(bg_type == "μ‹€λ‚΄ ν™˜κ²½")),
521
+ tech_dropdown: gr.update(visible=(bg_type == "ν…Œν¬λ†€λ‘œμ§€ λ°°κ²½")),
522
+ colorful_dropdown: gr.update(visible=(bg_type == "μ»¬λŸ¬ν’€ νŒ¨ν„΄ λ°°κ²½")),
523
  abstract_dropdown: gr.update(visible=(bg_type == "좔상/특수 λ°°κ²½"))
524
  }
525
  background_type.change(
526
  fn=update_dropdowns,
527
  inputs=[background_type],
528
+ outputs=[simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, tech_dropdown, colorful_dropdown, abstract_dropdown]
529
  )
530
  image_input = gr.Image(label="μƒν’ˆ 이미지 μ—…λ‘œλ“œ", type="pil")
531
  with gr.Row():
 
545
  prompt_output_multi = gr.Textbox(label="μƒμ„±λœ ν”„λ‘¬ν”„νŠΈ (μ˜μ–΄)", lines=6)
546
  single_btn.click(
547
  fn=generate_product_image,
548
+ inputs=[image_input, background_type, simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, tech_dropdown, colorful_dropdown, abstract_dropdown, product_name, additional_info],
549
  outputs=[image_output, status_output, prompt_output]
550
  )
551
  multi_btn.click(
552
  fn=generate_product_images,
553
+ inputs=[image_input, background_type, simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, tech_dropdown, colorful_dropdown, abstract_dropdown, product_name, additional_info],
554
  outputs=[image_output1, image_output2, image_output3, image_output4, status_output_multi, prompt_output_multi]
555
  )
556
  return demo
557
 
558
+ # ------------------- 메인 μ‹€ν–‰ ν•¨μˆ˜ -------------------
559
  if __name__ == "__main__":
560
+ # λ°°κ²½ μ˜΅μ…˜ μ΄ˆκΈ°ν™”
561
+ initialize_backgrounds()
562
+ # μ•± 생성 및 μ‹€ν–‰
563
  app = create_app()
564
  app.queue()
565
+ app.launch()