Kims12 commited on
Commit
24551ab
ยท
verified ยท
1 Parent(s): 238d194

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +264 -24
app.py CHANGED
@@ -13,6 +13,7 @@ from transformers import pipeline # ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ import
13
 
14
  load_dotenv()
15
 
 
16
  # ๊ธฐ์กด ์ฝ”๋“œ ์œ ์ง€ (๋กœ๊น…, ํ•จ์ˆ˜ ๋“ฑ ๋ชจ๋“  ๊ธฐ๋Šฅ ์ฝ”๋“œ)
17
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
18
  logger = logging.getLogger(__name__)
@@ -42,6 +43,116 @@ def save_binary_file(file_name, data):
42
  with open(file_name, "wb") as f:
43
  f.write(data)
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  def translate_prompt_to_english(prompt):
46
  # ๊ธฐ์กด ํ•จ์ˆ˜ ์œ ์ง€
47
  if not re.search("[๊ฐ€-ํžฃ]", prompt):
@@ -196,7 +307,8 @@ def generate_with_images(prompt, images, variation_index=0):
196
  )
197
  )
198
 
199
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
 
200
  temp_path = tmp.name
201
  result_text = ""
202
  image_found = False
@@ -210,16 +322,22 @@ def generate_with_images(prompt, images, variation_index=0):
210
  logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
211
  if not image_found:
212
  return None, f"API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ํ…์ŠคํŠธ: {result_text}"
 
213
  result_img = Image.open(temp_path)
214
  if result_img.mode == "RGBA":
215
- result_img = result_img.convert("RGB")
216
- return result_img, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
 
 
 
 
 
217
  except Exception as e:
218
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
219
  return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
220
 
221
  def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0, max_retries=3):
222
- # ๊ธฐ์กด ํ•จ์ˆ˜ ์œ ์ง€
223
  retry_count = 0
224
  last_error = None
225
 
@@ -249,6 +367,7 @@ def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0
249
 
250
  result_img, status = generate_with_images(final_prompt, valid_images, variation_index)
251
  if result_img is not None:
 
252
  return result_img, status, final_prompt
253
  else:
254
  last_error = status
@@ -264,7 +383,7 @@ def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0
264
  return None, f"์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜({max_retries}ํšŒ) ์ดˆ๊ณผ ํ›„ ์‹คํŒจ: {last_error}", prompt
265
 
266
  def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progress()):
267
- # ๊ธฐ์กด ํ•จ์ˆ˜ ์œ ์ง€
268
  results = []
269
  statuses = []
270
  prompts = []
@@ -279,6 +398,7 @@ def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progres
279
  result_img, status, final_prompt = process_images_with_prompt(image1, image2, image3, prompt, i, max_retries)
280
 
281
  if result_img is not None:
 
282
  results.append(result_img)
283
  statuses.append(f"์ด๋ฏธ์ง€ #{i+1}: {status}")
284
  prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
@@ -299,6 +419,7 @@ def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progres
299
 
300
  return results[0], results[1], results[2], results[3], combined_status, combined_prompts
301
 
 
302
  # GFPGAN ๊ด€๋ จ ์ฝ”๋“œ ์œ ์ง€
303
  import sys
304
  from torchvision.transforms import functional
@@ -842,6 +963,7 @@ with gr.Blocks(css=custom_css) as demo:
842
  <p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-expand-alt"></i> ์ด๋ฏธ์ง€ ํ™•์žฅ</p>
843
  ์›๋ณธ ์ด๋ฏธ์ง€ ์ฃผ๋ณ€์— ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ถ”๊ฐ€ ์˜์—ญ์„ ์ƒ์„ฑํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.
844
  </div>
 
845
  </div>
846
  </div>
847
  </div>
@@ -882,22 +1004,55 @@ with gr.Blocks(css=custom_css) as demo:
882
  </p>
883
  </div>
884
 
 
885
  <div class="manual-section">
886
- <div class="manual-section-title"><i class="fas fa-question-circle"></i> ์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ</div>
887
- <p class="manual-text">
888
- <strong style="font-size: 1.1rem; color: #5561e9;">Q: ์–ด๋–ค ํ˜•์‹์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋‚˜์š”?</strong><br>
889
- A: JPG, PNG, WEBP ๋“ฑ ๋Œ€๋ถ€๋ถ„์˜ ์ผ๋ฐ˜์ ์ธ ์ด๋ฏธ์ง€ ํ˜•์‹์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.<br><br>
890
-
891
- <strong style="font-size: 1.1rem; color: #5561e9;">Q: ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฐ ์ฒ˜๋ฆฌ์— ์–ผ๋งˆ๋‚˜ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋‚˜์š”?</strong><br>
892
- A: ์ผ๋ฐ˜์ ์œผ๋กœ 10-30์ดˆ ์ •๋„ ์†Œ์š”๋˜๋ฉฐ, ์ด๋ฏธ์ง€ ํฌ๊ธฐ์™€ ๋ณต์žก๋„์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.<br><br>
893
-
894
- <strong style="font-size: 1.1rem; color: #5561e9;">Q: ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€๋ฅผ ์–ด๋–ป๊ฒŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‚˜์š”?</strong><br>
895
- A: ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€์— ๋งˆ์šฐ์Šค ์˜ค๋ฅธ์ชฝ ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ '์ด๋ฏธ์ง€ ์ €์žฅ' ์˜ต์…˜์„ ์„ ํƒํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
896
- </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
897
  </div>
898
- </div>
899
  """)
900
-
901
  with gr.TabItem("โœจ ์ด์ปค๋จธ์Šค ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ", elem_classes="tab-content"):
902
  with gr.Row(equal_height=True):
903
  with gr.Column(scale=1):
@@ -928,6 +1083,8 @@ with gr.Blocks(css=custom_css) as demo:
928
  background_change_btn = gr.Button('๐Ÿ–ผ๏ธ ๋ฐฐ๊ฒฝ ๋ฐ”๊พธ๊ธฐ', elem_classes="custom-button")
929
  composite_product_btn = gr.Button('โœ‚๏ธ ๋ถ€๋ถ„ ์ง€์šฐ๊ธฐ', elem_classes="custom-button")
930
  outpainting_btn = gr.Button('๐Ÿ” ์ด๋ฏธ์ง€ ํ™•์žฅ', elem_classes="custom-button")
 
 
931
 
932
  submit_single_btn = gr.Button('โœจ ์ด๋ฏธ์ง€ ์ƒ์„ฑ (1์žฅ)', elem_classes="custom-button primary")
933
  submit_btn = gr.Button('โœจ ์ด๋ฏธ์ง€ ์ƒ์„ฑ (4์žฅ)', elem_classes="custom-button primary")
@@ -937,11 +1094,11 @@ with gr.Blocks(css=custom_css) as demo:
937
  gr.HTML('<div class="section-title"><i class="fas fa-images"></i> ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€</div>')
938
  with gr.Row():
939
  with gr.Column():
940
- output_image1 = gr.Image(label="์ด๋ฏธ์ง€ #1", elem_classes="image-container", height=400)
941
- output_image3 = gr.Image(label="์ด๋ฏธ์ง€ #3", elem_classes="image-container", height=400)
942
  with gr.Column():
943
- output_image2 = gr.Image(label="์ด๋ฏธ์ง€ #2", elem_classes="image-container", height=400)
944
- output_image4 = gr.Image(label="์ด๋ฏธ์ง€ #4", elem_classes="image-container", height=400)
945
 
946
  gr.HTML('<div class="section-title"><i class="fas fa-info-circle"></i> ๊ฒฐ๊ณผ ์ •๋ณด</div>')
947
  output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€", lines=2, elem_classes="custom-input")
@@ -959,14 +1116,15 @@ with gr.Blocks(css=custom_css) as demo:
959
  ["down/๋ชจ๋ธ.jpg", "down/์™€์ธ์ž”.png", None, "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด(#2์˜ ์™€์ธ์ž”)์„ ๋“ค๊ณ  ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์Šต"],
960
  ["down/๋ชจ๋ธ.jpg", "down/์นดํŽ˜์ „๊ฒฝ.png", None, "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2 ์นดํŽ˜)์—์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์žˆ๋Š” ๋ชจ์Šต"],
961
  ["down/์ƒ์–ด๋ ˆ๊ณ ๋ชจํ˜•.png", None, None, "(#1์˜ ๋ ˆ๊ณ ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋ฅผ ์ œ๊ฑฐํ•œ ํ›„, ๊ทธ ์ž๋ฆฌ๋ฅผ ์ฃผ๋ณ€ ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ฑ„์›Œ์ฃผ์„ธ์š”. ๋‹จ, ์ด๋ฏธ์ง€์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์˜ ์ฃผ์š” ์š”์†Œ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ํ•ด ํ•ด์•ผํ•œ๋‹ค."],
962
- ["down/์นดํŽ˜์ „๊ฒฝ.png", None, None, "(#1 ์ด๋ฏธ์ง€)๋ฅผ ์›๋ณธ๊ทธ๋Œ€๋กœ ์ค‘์•™์— ๋‘๊ณ  ๋น„์œจ๋กœ ์œ ์ง€ํ•œ ์ฒด ์œ„์•„๋ž˜ ๋ฐ ์ขŒ์šฐ๋กœ ํฌ๊ฒŒ ํ™•์žฅํ•˜๋ผ."]
 
963
  ]
964
 
965
  gr.Examples(
966
  examples=examples,
967
  inputs=[image1_input, image2_input, image3_input, prompt_input]
968
  )
969
-
970
  with gr.TabItem("๐Ÿ” ์ด๋ฏธ์ง€ ์—…์Šค์ผ€์ผ๋Ÿฌ", elem_classes="tab-content"):
971
  with gr.Row():
972
  with gr.Column(elem_classes="panel"):
@@ -1017,6 +1175,82 @@ with gr.Blocks(css=custom_css) as demo:
1017
  with gr.Row():
1018
  bg_remove_status = gr.Textbox(label="์ƒํƒœ", lines=2, elem_classes="custom-input")
1019
  bg_remove_btn = gr.Button('โœ‚๏ธ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐํ•˜๊ธฐ', elem_classes="custom-button primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1020
 
1021
  # ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
1022
  image_change_btn1.click(
@@ -1070,6 +1304,12 @@ with gr.Blocks(css=custom_css) as demo:
1070
  inputs=[],
1071
  outputs=prompt_input
1072
  )
 
 
 
 
 
 
1073
 
1074
  # ๋‹จ์ผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
1075
  submit_single_btn.click(
 
13
 
14
  load_dotenv()
15
 
16
+
17
  # ๊ธฐ์กด ์ฝ”๋“œ ์œ ์ง€ (๋กœ๊น…, ํ•จ์ˆ˜ ๋“ฑ ๋ชจ๋“  ๊ธฐ๋Šฅ ์ฝ”๋“œ)
18
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
19
  logger = logging.getLogger(__name__)
 
43
  with open(file_name, "wb") as f:
44
  f.write(data)
45
 
46
+
47
+ # ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ๊ธฐ๋Šฅ์„ ์œ„ํ•œ ์ถ”๊ฐ€ ์ฝ”๋“œ
48
+ import cv2
49
+ import numpy as np
50
+ from PIL import Image, ImageEnhance, ImageFilter
51
+ import tempfile
52
+ from datetime import datetime, timedelta
53
+
54
+ # ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜
55
+ def adjust_brightness(image, value):
56
+ """์ด๋ฏธ์ง€ ๋ฐ๊ธฐ ์กฐ์ ˆ"""
57
+ value = float(value - 1) * 100 # 0-2 ๋ฒ”์œ„๋ฅผ -100์—์„œ +100์œผ๋กœ ๋ณ€ํ™˜
58
+ hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
59
+ h, s, v = cv2.split(hsv)
60
+ v = cv2.add(v, value)
61
+ v = np.clip(v, 0, 255)
62
+ final_hsv = cv2.merge((h, s, v))
63
+ return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
64
+
65
+ def adjust_contrast(image, value):
66
+ """์ด๋ฏธ์ง€ ๋Œ€๋น„ ์กฐ์ ˆ"""
67
+ value = float(value)
68
+ return np.clip(image * value, 0, 255).astype(np.uint8)
69
+
70
+ def adjust_saturation(image, value):
71
+ """์ด๋ฏธ์ง€ ์ฑ„๋„ ์กฐ์ ˆ"""
72
+ value = float(value - 1) * 100 # 0-2 ๋ฒ”์œ„๋ฅผ -100์—์„œ +100์œผ๋กœ ๋ณ€ํ™˜
73
+ hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
74
+ h, s, v = cv2.split(hsv)
75
+ s = cv2.add(s, value)
76
+ s = np.clip(s, 0, 255)
77
+ final_hsv = cv2.merge((h, s, v))
78
+ return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
79
+
80
+ def adjust_temperature(image, value):
81
+ """์ด๋ฏธ์ง€ ์ƒ‰์˜จ๋„ ์กฐ์ ˆ (์ƒ‰์ƒ ๋ฐธ๋Ÿฐ์Šค)"""
82
+ value = float(value) * 30 # ํšจ๊ณผ ์Šค์ผ€์ผ ์กฐ์ ˆ
83
+ b, g, r = cv2.split(image)
84
+ if value > 0: # ๋”ฐ๋œปํ•˜๊ฒŒ
85
+ r = cv2.add(r, value)
86
+ b = cv2.subtract(b, value)
87
+ else: # ์ฐจ๊ฐ‘๊ฒŒ
88
+ r = cv2.add(r, value)
89
+ b = cv2.subtract(b, value)
90
+
91
+ r = np.clip(r, 0, 255)
92
+ b = np.clip(b, 0, 255)
93
+ return cv2.merge([b, g, r])
94
+
95
+ def adjust_tint(image, value):
96
+ """์ด๋ฏธ์ง€ ์ƒ‰์กฐ ์กฐ์ ˆ"""
97
+ hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
98
+ h, s, v = cv2.split(hsv_image)
99
+ h = cv2.add(h, int(value))
100
+ h = np.clip(h, 0, 179) # Hue ๊ฐ’์€ 0-179 ๋ฒ”์œ„
101
+ final_hsv = cv2.merge((h, s, v))
102
+ return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
103
+
104
+ def adjust_exposure(image, value):
105
+ """์ด๋ฏธ์ง€ ๋…ธ์ถœ ์กฐ์ ˆ"""
106
+ enhancer = ImageEnhance.Brightness(Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)))
107
+ img_enhanced = enhancer.enhance(1 + float(value) / 5.0)
108
+ return cv2.cvtColor(np.array(img_enhanced), cv2.COLOR_RGB2BGR)
109
+
110
+ def adjust_vibrance(image, value):
111
+ """์ด๋ฏธ์ง€ ํ™œ๊ธฐ ์กฐ์ ˆ"""
112
+ img = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
113
+ converter = ImageEnhance.Color(img)
114
+ factor = 1 + (float(value) / 100.0)
115
+ img = converter.enhance(factor)
116
+ return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
117
+
118
+ def adjust_color_mixer_blues(image, value):
119
+ """์ด๋ฏธ์ง€ ์ปฌ๋Ÿฌ ๋ฏน์„œ (๋ธ”๋ฃจ) ์กฐ์ ˆ"""
120
+ b, g, r = cv2.split(image)
121
+ b = cv2.add(b, float(value))
122
+ b = np.clip(b, 0, 255)
123
+ return cv2.merge([b, g, r])
124
+
125
+ def adjust_shadows(image, value):
126
+ """์ด๋ฏธ์ง€ ๊ทธ๋ฆผ์ž ์กฐ์ ˆ"""
127
+ pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
128
+ enhancer = ImageEnhance.Brightness(pil_image)
129
+ factor = 1 + (float(value) / 100.0)
130
+ pil_image = enhancer.enhance(factor)
131
+ return cv2.cvtColor(np.array(pil_image), cv2.COLOR_BGR2RGB)
132
+
133
+ def process_image(image, brightness, contrast, saturation, temperature, tint, exposure, vibrance, color_mixer_blues, shadows):
134
+ """๋ชจ๋“  ์กฐ์ • ์‚ฌํ•ญ์„ ์ด๋ฏธ์ง€์— ์ ์šฉ"""
135
+ if image is None:
136
+ return None
137
+
138
+ # PIL ์ด๋ฏธ์ง€๋ฅผ OpenCV ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜
139
+ image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
140
+
141
+ # ์กฐ์ • ์‚ฌํ•ญ ์ˆœ์ฐจ ์ ์šฉ
142
+ image = adjust_brightness(image, brightness)
143
+ image = adjust_contrast(image, contrast)
144
+ image = adjust_saturation(image, saturation)
145
+ image = adjust_temperature(image, temperature)
146
+ image = adjust_tint(image, tint)
147
+ image = adjust_exposure(image, exposure)
148
+ image = adjust_vibrance(image, vibrance)
149
+ image = adjust_color_mixer_blues(image, color_mixer_blues)
150
+ image = adjust_shadows(image, shadows)
151
+
152
+ # PIL ์ด๋ฏธ์ง€๋กœ ๋‹ค์‹œ ๋ณ€ํ™˜
153
+ return Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
154
+
155
+
156
  def translate_prompt_to_english(prompt):
157
  # ๊ธฐ์กด ํ•จ์ˆ˜ ์œ ์ง€
158
  if not re.search("[๊ฐ€-ํžฃ]", prompt):
 
307
  )
308
  )
309
 
310
+ # ์ž„์‹œ ํŒŒ์ผ์€ ํ•ญ์ƒ JPG ํ™•์žฅ์ž๋กœ ์ƒ์„ฑ
311
+ with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp:
312
  temp_path = tmp.name
313
  result_text = ""
314
  image_found = False
 
322
  logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
323
  if not image_found:
324
  return None, f"API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ํ…์ŠคํŠธ: {result_text}"
325
+
326
  result_img = Image.open(temp_path)
327
  if result_img.mode == "RGBA":
328
+ result_img = result_img.convert("RGB") # JPG๋Š” ํˆฌ๋ช…๋„๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ RGB๋กœ ๋ณ€ํ™˜
329
+
330
+ # ๋ณ€ํ™˜๋œ ์ด๋ฏธ์ง€๋ฅผ JPG๋กœ ์ €์žฅ
331
+ result_img.save(temp_path, format="JPEG", quality=95)
332
+
333
+ # ํŒŒ์ผ ๊ฒฝ๋กœ ๋ฐ˜ํ™˜
334
+ return temp_path, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
335
  except Exception as e:
336
  logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
337
  return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
338
 
339
  def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0, max_retries=3):
340
+ # ๊ธฐ์กด ํ•จ์ˆ˜ ๋‚ด์šฉ ์œ ์ง€
341
  retry_count = 0
342
  last_error = None
343
 
 
367
 
368
  result_img, status = generate_with_images(final_prompt, valid_images, variation_index)
369
  if result_img is not None:
370
+ # ์ด๋ฏธ ํŒŒ์ผ ๊ฒฝ๋กœ๊ฐ€ ๋ฐ˜ํ™˜๋˜๋ฏ€๋กœ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ ์—†์ด ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜
371
  return result_img, status, final_prompt
372
  else:
373
  last_error = status
 
383
  return None, f"์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜({max_retries}ํšŒ) ์ดˆ๊ณผ ํ›„ ์‹คํŒจ: {last_error}", prompt
384
 
385
  def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progress()):
386
+ # ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€๋“ค์ด JPG๋กœ ๋ณ€ํ™˜๋˜๋„๋ก ์ˆ˜์ •
387
  results = []
388
  statuses = []
389
  prompts = []
 
398
  result_img, status, final_prompt = process_images_with_prompt(image1, image2, image3, prompt, i, max_retries)
399
 
400
  if result_img is not None:
401
+ # ์ด๋ฏธ ํŒŒ์ผ ๊ฒฝ๋กœ์ด๋ฏ€๋กœ ์ถ”๊ฐ€ ์ฒ˜๋ฆฌ ์—†์ด ๊ทธ๋Œ€๋กœ ์ถ”๊ฐ€
402
  results.append(result_img)
403
  statuses.append(f"์ด๋ฏธ์ง€ #{i+1}: {status}")
404
  prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
 
419
 
420
  return results[0], results[1], results[2], results[3], combined_status, combined_prompts
421
 
422
+
423
  # GFPGAN ๊ด€๋ จ ์ฝ”๋“œ ์œ ์ง€
424
  import sys
425
  from torchvision.transforms import functional
 
963
  <p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-expand-alt"></i> ์ด๋ฏธ์ง€ ํ™•์žฅ</p>
964
  ์›๋ณธ ์ด๋ฏธ์ง€ ์ฃผ๋ณ€์— ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ถ”๊ฐ€ ์˜์—ญ์„ ์ƒ์„ฑํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.
965
  </div>
966
+
967
  </div>
968
  </div>
969
  </div>
 
1004
  </p>
1005
  </div>
1006
 
1007
+
1008
  <div class="manual-section">
1009
+ <div class="manual-section-title"><i class="fas fa-tools"></i> ์ฃผ์š” ๊ธฐ๋Šฅ</div>
1010
+ <div class="manual-text">
1011
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem;">
1012
+ <div>
1013
+ <p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-exchange-alt"></i> ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ</p>
1014
+ ํฌ์ฆˆ๋‚˜ ์Šคํƒ€์ผ์„ ๋ณ€๊ฒฝํ•˜๋ฉด์„œ ์›๋ณธ์˜ ์ฃผ์š” ์š”์†Œ๋Š” ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
1015
+ </div>
1016
+
1017
+ <div>
1018
+ <p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-eraser"></i> ๊ธ€์ž ์ง€์šฐ๊ธฐ/๋ณ€๊ฒฝ</p>
1019
+ ์ด๋ฏธ์ง€์˜ ํ…์ŠคํŠธ๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ํ…์ŠคํŠธ๋กœ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.
1020
+ </div>
1021
+
1022
+ <div>
1023
+ <p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-tshirt"></i> ๊ฐ€์ƒ ์ƒํ’ˆ์ฐฉ์šฉ</p>
1024
+ ๋ชจ๋ธ์—๊ฒŒ ๋‹ค๋ฅธ ์ด๋ฏธ์ง€์˜ ์ƒํ’ˆ์„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ฐฉ์šฉ์‹œํ‚ต๋‹ˆ๋‹ค.
1025
+ </div>
1026
+
1027
+ <div>
1028
+ <p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-image"></i> ๋ฐฐ๊ฒฝ ๋ฐ”๊พธ๊ธฐ</p>
1029
+ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๋ฉฐ ๋ฐฐ๊ฒฝ๋งŒ ๊ต์ฒดํ•ฉ๋‹ˆ๋‹ค.
1030
+ </div>
1031
+
1032
+ <div>
1033
+ <p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-cut"></i> ๋ถ€๋ถ„ ์ง€์šฐ๊ธฐ</p>
1034
+ ์ด๋ฏธ์ง€์˜ ํŠน์ • ๋ถ€๋ถ„์„ ์ง€์šฐ๊ณ  ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์กฐํ™”๋˜๋„๋ก ์ฑ„์›๋‹ˆ๋‹ค.
1035
+ </div>
1036
+
1037
+ <div>
1038
+ <p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-wine-glass"></i> ์ƒํ’ˆ๋“ค๊ณ  ์žˆ๊ธฐ</p>
1039
+ ๋ชจ๋ธ์ด ์ƒํ’ˆ์„ ๋“ค๊ณ  ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ํฌ์ฆˆ์˜ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
1040
+ </div>
1041
+
1042
+ <div>
1043
+ <p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-expand-alt"></i> ์ด๋ฏธ์ง€ ํ™•์žฅ</p>
1044
+ ์›๋ณธ ์ด๋ฏธ์ง€ ์ฃผ๋ณ€์— ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ถ”๊ฐ€ ์˜์—ญ์„ ์ƒ์„ฑํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค.
1045
+ </div>
1046
+
1047
+ <div>
1048
+ <p style="font-size: 1.1rem; font-weight: 600; color: #ff6b6b;"><i class="fas fa-utensils"></i> ์Œ์‹ ํ”Œ๋ ˆ์ดํŒ…</p>
1049
+ ์Œ์‹์„ ํŠน์ • ์šฉ๊ธฐ์— ๋‹ด์•„ ์ƒ์—…์ ์ธ ๊ฐ๋„๋กœ ์•„๋ฆ„๋‹ต๊ฒŒ ํ”Œ๋ ˆ์ดํŒ…ํ•ฉ๋‹ˆ๋‹ค.
1050
+ </div>
1051
+ </div>
1052
+ </div>
1053
  </div>
 
1054
  """)
1055
+
1056
  with gr.TabItem("โœจ ์ด์ปค๋จธ์Šค ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ", elem_classes="tab-content"):
1057
  with gr.Row(equal_height=True):
1058
  with gr.Column(scale=1):
 
1083
  background_change_btn = gr.Button('๐Ÿ–ผ๏ธ ๋ฐฐ๊ฒฝ ๋ฐ”๊พธ๊ธฐ', elem_classes="custom-button")
1084
  composite_product_btn = gr.Button('โœ‚๏ธ ๋ถ€๋ถ„ ์ง€์šฐ๊ธฐ', elem_classes="custom-button")
1085
  outpainting_btn = gr.Button('๐Ÿ” ์ด๋ฏธ์ง€ ํ™•์žฅ', elem_classes="custom-button")
1086
+ food_plating_btn = gr.Button('๐Ÿฝ๏ธ ์Œ์‹ํ”Œ๋ ˆ์ดํŒ…', elem_classes="custom-button") # ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ๋ฒ„ํŠผ
1087
+
1088
 
1089
  submit_single_btn = gr.Button('โœจ ์ด๋ฏธ์ง€ ์ƒ์„ฑ (1์žฅ)', elem_classes="custom-button primary")
1090
  submit_btn = gr.Button('โœจ ์ด๋ฏธ์ง€ ์ƒ์„ฑ (4์žฅ)', elem_classes="custom-button primary")
 
1094
  gr.HTML('<div class="section-title"><i class="fas fa-images"></i> ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€</div>')
1095
  with gr.Row():
1096
  with gr.Column():
1097
+ output_image1 = gr.Image(label="์ด๋ฏธ์ง€ #1", elem_classes="image-container", height=400, type="filepath")
1098
+ output_image3 = gr.Image(label="์ด๋ฏธ์ง€ #3", elem_classes="image-container", height=400, type="filepath")
1099
  with gr.Column():
1100
+ output_image2 = gr.Image(label="์ด๋ฏธ์ง€ #2", elem_classes="image-container", height=400, type="filepath")
1101
+ output_image4 = gr.Image(label="์ด๋ฏธ์ง€ #4", elem_classes="image-container", height=400, type="filepath")
1102
 
1103
  gr.HTML('<div class="section-title"><i class="fas fa-info-circle"></i> ๊ฒฐ๊ณผ ์ •๋ณด</div>')
1104
  output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€", lines=2, elem_classes="custom-input")
 
1116
  ["down/๋ชจ๋ธ.jpg", "down/์™€์ธ์ž”.png", None, "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด(#2์˜ ์™€์ธ์ž”)์„ ๋“ค๊ณ  ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์Šต"],
1117
  ["down/๋ชจ๋ธ.jpg", "down/์นดํŽ˜์ „๊ฒฝ.png", None, "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2 ์นดํŽ˜)์—์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์žˆ๋Š” ๋ชจ์Šต"],
1118
  ["down/์ƒ์–ด๋ ˆ๊ณ ๋ชจํ˜•.png", None, None, "(#1์˜ ๋ ˆ๊ณ ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋ฅผ ์ œ๊ฑฐํ•œ ํ›„, ๊ทธ ์ž๋ฆฌ๋ฅผ ์ฃผ๋ณ€ ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ฑ„์›Œ์ฃผ์„ธ์š”. ๋‹จ, ์ด๋ฏธ์ง€์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์˜ ์ฃผ์š” ์š”์†Œ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ํ•ด ํ•ด์•ผํ•œ๋‹ค."],
1119
+ ["down/์นดํŽ˜์ „๊ฒฝ.png", None, None, "(#1 ์ด๋ฏธ์ง€)๋ฅผ ์›๋ณธ๊ทธ๋Œ€๋กœ ์ค‘์•™์— ๋‘๊ณ  ๋น„์œจ๋กœ ์œ ์ง€ํ•œ ์ฒด ์œ„์•„๋ž˜ ๋ฐ ์ขŒ์šฐ๋กœ ํฌ๊ฒŒ ํ™•์žฅํ•˜๋ผ."],
1120
+ ["down/์ƒ๋Ÿฌ๋“œ.png", "down/์šฉ๊ธฐ.png", None, "(#1์˜ ์ƒ๋Ÿฌ๋“œ)๊ฐ€ ์Œ์‹๋งŒ์œผ๋กœ ๋ฐ˜๋“œ์‹œ(#2์˜ ๋™์ผํ•œ ์šฉ๊ธฐ)์— ๊ฝ‰ ์ฐจ๋„๋ก ์ƒ์—…์ ์ธ ๊ฐ๋„๋กœ ์–ด์šธ๋ฆฌ๋Š” ์†Œํ’ˆ๊ณผ ํ•จ๊ป˜ ํ”Œ๋ ˆ์ดํŒ…ํ•œ ๋ชจ์Šต"]
1121
  ]
1122
 
1123
  gr.Examples(
1124
  examples=examples,
1125
  inputs=[image1_input, image2_input, image3_input, prompt_input]
1126
  )
1127
+
1128
  with gr.TabItem("๐Ÿ” ์ด๋ฏธ์ง€ ์—…์Šค์ผ€์ผ๋Ÿฌ", elem_classes="tab-content"):
1129
  with gr.Row():
1130
  with gr.Column(elem_classes="panel"):
 
1175
  with gr.Row():
1176
  bg_remove_status = gr.Textbox(label="์ƒํƒœ", lines=2, elem_classes="custom-input")
1177
  bg_remove_btn = gr.Button('โœ‚๏ธ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐํ•˜๊ธฐ', elem_classes="custom-button primary")
1178
+
1179
+ # ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ํƒญ UI ๊ฐœ์„ 
1180
+ with gr.TabItem("๐Ÿ–Œ๏ธ ์ด๋ฏธ์ง€ ํ•„ํ„ฐ", elem_classes="tab-content"):
1181
+ with gr.Column(elem_classes="panel"):
1182
+ gr.HTML('<div class="section-title"><i class="fas fa-sliders-h"></i> ์ด๋ฏธ์ง€ ํ•„ํ„ฐ</div>')
1183
+ gr.HTML("""
1184
+ <p style="margin-bottom: 1rem; font-size: 1.05rem;">
1185
+ ์ด๋ฏธ์ง€ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜์—ฌ ์ƒ‰์ƒ๊ณผ ํ†ค์„ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.
1186
+ </p>
1187
+ <div class="tip-box">
1188
+ <i class="fas fa-lightbulb"></i> <strong>ํŒ:</strong> ์Šฌ๋ผ์ด๋”๋ฅผ ์กฐ์ ˆํ•˜์—ฌ ์ด๋ฏธ์ง€์˜ ๋ฐ๊ธฐ, ๋Œ€๋น„, ์ฑ„๋„ ๋“ฑ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
1189
+ </div>
1190
+ """)
1191
+
1192
+ # ์ž…๋ ฅ/์ถœ๋ ฅ ์ด๋ฏธ์ง€ ์„น์…˜
1193
+ with gr.Row():
1194
+ with gr.Column():
1195
+ gr.HTML('<div class="section-title"><i class="fas fa-upload"></i> ์ž…๋ ฅ ์ด๋ฏธ์ง€</div>')
1196
+ filter_input_image = gr.Image(type="pil", label="์—…๋กœ๋“œ", elem_classes="image-container")
1197
+
1198
+ with gr.Column():
1199
+ gr.HTML('<div class="section-title"><i class="fas fa-image"></i> ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€</div>')
1200
+ filter_output_image = gr.Image(type="pil", label="ํ•„ํ„ฐ ์ ์šฉ ๊ฒฐ๊ณผ", elem_classes="image-container")
1201
+
1202
+ # ํ•„ํ„ฐ ์กฐ์ ˆ ์Šฌ๋ผ์ด๋”๋“ค
1203
+ gr.HTML('<div class="section-title"><i class="fas fa-sliders-h"></i> ํ•„ํ„ฐ ์กฐ์ ˆ</div>')
1204
+ with gr.Group():
1205
+ brightness_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="๋ฐ๊ธฐ ์กฐ์ ˆ")
1206
+ contrast_slider = gr.Slider(0.5, 1.5, value=1.0, step=0.1, label="๋Œ€๋น„ ์กฐ์ ˆ")
1207
+ saturation_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="์ฑ„๋„ ์กฐ์ ˆ")
1208
+ temperature_slider = gr.Slider(-1.0, 1.0, value=0.0, step=0.1, label="์ƒ‰์˜จ๋„ ์กฐ์ ˆ")
1209
+ tint_slider = gr.Slider(-100, 100, value=0, step=1, label="์ƒ‰์กฐ ์กฐ์ ˆ")
1210
+ exposure_slider = gr.Slider(-5.0, 5.0, value=0.0, step=0.1, label="๋…ธ์ถœ ์กฐ์ ˆ")
1211
+ vibrance_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="ํ™œ๊ธฐ ์กฐ์ ˆ")
1212
+ color_mixer_blues_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="์ปฌ๋Ÿฌ ๋ฏน์„œ (๋ธ”๋ฃจ)")
1213
+ shadows_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="๊ทธ๋ฆผ์ž ์กฐ์ ˆ")
1214
+
1215
+ # ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ ์—ฐ๊ฒฐ
1216
+ inputs = [
1217
+ filter_input_image,
1218
+ brightness_slider,
1219
+ contrast_slider,
1220
+ saturation_slider,
1221
+ temperature_slider,
1222
+ tint_slider,
1223
+ exposure_slider,
1224
+ vibrance_slider,
1225
+ color_mixer_blues_slider,
1226
+ shadows_slider
1227
+ ]
1228
+
1229
+ input_components = [
1230
+ brightness_slider,
1231
+ contrast_slider,
1232
+ saturation_slider,
1233
+ temperature_slider,
1234
+ tint_slider,
1235
+ exposure_slider,
1236
+ vibrance_slider,
1237
+ color_mixer_blues_slider,
1238
+ shadows_slider
1239
+ ]
1240
+
1241
+ for input_component in input_components:
1242
+ input_component.change(
1243
+ fn=process_image,
1244
+ inputs=inputs,
1245
+ outputs=filter_output_image
1246
+ )
1247
+
1248
+ filter_input_image.change(
1249
+ fn=lambda x, *args: process_image(x, *args) if x is not None else None,
1250
+ inputs=inputs,
1251
+ outputs=filter_output_image
1252
+ )
1253
+
1254
 
1255
  # ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
1256
  image_change_btn1.click(
 
1304
  inputs=[],
1305
  outputs=prompt_input
1306
  )
1307
+
1308
+ food_plating_btn.click(
1309
+ fn=lambda: "(#1์˜ ์ƒ๋Ÿฌ๋“œ)๊ฐ€ ์Œ์‹๋งŒ์œผ๋กœ ๋ฐ˜๋“œ์‹œ(#2์˜ ๋™์ผํ•œ ์šฉ๊ธฐ)์— ๊ฝ‰ ์ฐจ๋„๋ก ์ƒ์—…์ ์ธ ๊ฐ๋„๋กœ ์–ด์šธ๋ฆฌ๋Š” ์†Œํ’ˆ๊ณผ ํ•จ๊ป˜ ํ”Œ๋ ˆ์ดํŒ…ํ•œ ๋ชจ์Šต",
1310
+ inputs=[],
1311
+ outputs=prompt_input
1312
+ )
1313
 
1314
  # ๋‹จ์ผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
1315
  submit_single_btn.click(