ssboost commited on
Commit
b7d35a7
ยท
verified ยท
1 Parent(s): 4eadc73

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +379 -130
app.py CHANGED
@@ -5,11 +5,29 @@ import gradio as gr
5
  from io import BytesIO
6
  import tempfile
7
  import logging
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  # ๋กœ๊น… ์„ค์ • - INFO ๋ ˆ๋ฒจ๋กœ ๋ณ€๊ฒฝ
10
  logging.basicConfig(level=logging.INFO)
11
  logger = logging.getLogger(__name__)
12
 
 
 
 
 
 
 
 
13
  def adjust_brightness(image, value):
14
  """์ด๋ฏธ์ง€ ๋ฐ๊ธฐ ์กฐ์ ˆ"""
15
  value = float(value - 1) * 100 # 0-2 ๋ฒ”์œ„๋ฅผ -100์—์„œ +100์œผ๋กœ ๋ณ€ํ™˜
@@ -50,96 +68,221 @@ def adjust_temperature(image, value):
50
  b = np.clip(b, 0, 255)
51
  return cv2.merge([b, g, r])
52
 
53
- def adjust_tint(image, value):
54
- """์ด๋ฏธ์ง€ ์ƒ‰์กฐ ์กฐ์ ˆ"""
55
- hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
56
- h, s, v = cv2.split(hsv_image)
57
- h = cv2.add(h, int(value))
58
- h = np.clip(h, 0, 179) # Hue ๊ฐ’์€ 0-179 ๋ฒ”์œ„
59
- final_hsv = cv2.merge((h, s, v))
60
- return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
61
-
62
- def adjust_exposure(image, value):
63
- """์ด๋ฏธ์ง€ ๋…ธ์ถœ ์กฐ์ ˆ"""
64
- enhancer = ImageEnhance.Brightness(Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)))
65
- img_enhanced = enhancer.enhance(1 + float(value) / 5.0)
66
- return cv2.cvtColor(np.array(img_enhanced), cv2.COLOR_RGB2BGR)
67
-
68
- def adjust_vibrance(image, value):
69
- """์ด๋ฏธ์ง€ ํ™œ๊ธฐ ์กฐ์ ˆ"""
70
- img = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
71
- converter = ImageEnhance.Color(img)
72
- factor = 1 + (float(value) / 100.0)
73
- img = converter.enhance(factor)
74
- return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
75
-
76
- def adjust_color_mixer_blues(image, value):
77
- """์ด๋ฏธ์ง€ ์ปฌ๋Ÿฌ ๋ฏน์„œ (๋ธ”๋ฃจ) ์กฐ์ ˆ"""
78
- b, g, r = cv2.split(image)
79
- b = cv2.add(b, float(value))
80
- b = np.clip(b, 0, 255)
81
- return cv2.merge([b, g, r])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
- def adjust_shadows(image, value):
84
- """์ด๋ฏธ์ง€ ๊ทธ๋ฆผ์ž ์กฐ์ ˆ"""
85
- pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
86
- enhancer = ImageEnhance.Brightness(pil_image)
87
- factor = 1 + (float(value) / 100.0)
88
- pil_image = enhancer.enhance(factor)
89
- return cv2.cvtColor(np.array(pil_image), cv2.COLOR_BGR2RGB)
90
 
91
- def process_image(image, brightness, contrast, saturation, temperature, tint, exposure, vibrance, color_mixer_blues, shadows):
92
- """๋ชจ๋“  ์กฐ์ • ์‚ฌํ•ญ์„ ์ด๋ฏธ์ง€์— ์ ์šฉ"""
93
  if image is None:
94
  return None
95
-
96
- # PIL ์ด๋ฏธ์ง€๋ฅผ OpenCV ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜
97
- image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
98
 
99
- # ์กฐ์ • ์‚ฌํ•ญ ์ˆœ์ฐจ ์ ์šฉ
100
- image = adjust_brightness(image, brightness)
101
- image = adjust_contrast(image, contrast)
102
- image = adjust_saturation(image, saturation)
103
- image = adjust_temperature(image, temperature)
104
- image = adjust_tint(image, tint)
105
- image = adjust_exposure(image, exposure)
106
- image = adjust_vibrance(image, vibrance)
107
- image = adjust_color_mixer_blues(image, color_mixer_blues)
108
- image = adjust_shadows(image, shadows)
109
 
110
- # PIL ์ด๋ฏธ์ง€๋กœ ๋‹ค์‹œ ๋ณ€ํ™˜
111
- return Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
 
 
 
112
 
113
- def download_image(image, input_image_name):
114
- """์ด๋ฏธ์ง€๋ฅผ JPG ํ˜•์‹์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๊ฒฝ๋กœ ๋ฐ˜ํ™˜"""
115
- if image is None:
 
 
 
 
 
 
 
116
  return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
- # ํ•œ๊ตญ ์‹œ๊ฐ„ ํƒ€์ž„์Šคํƒฌํ”„ ์ƒ์„ฑ ํ•จ์ˆ˜
119
- from datetime import datetime, timedelta
120
- def get_korean_timestamp():
121
- korea_time = datetime.utcnow() + timedelta(hours=9)
122
- return korea_time.strftime('%Y%m%d_%H%M%S')
123
 
124
  timestamp = get_korean_timestamp()
125
- if input_image_name and hasattr(input_image_name, 'name'):
126
- base_name = input_image_name.name.split('.')[0] # ํŒŒ์ผ ๊ฐ์ฒด์—์„œ ์ด๋ฆ„ ์ถ”์ถœ
127
- else:
128
- base_name = "์ด๋ฏธ์ง€"
 
129
 
130
- file_name = f"[๋์žฅAI]๋์žฅํ•„ํ„ฐ_{base_name}_{timestamp}.jpg"
 
 
 
 
 
131
 
132
  # ํŒŒ์ผ ์ €์žฅ
133
  temp_file_path = tempfile.gettempdir() + "/" + file_name
134
- image.save(temp_file_path, format="JPEG")
135
  return temp_file_path
136
 
 
 
 
 
 
 
 
 
137
  def create_interface():
138
  css = """
139
  footer {
140
  visibility: hidden;
141
  }
142
- .download-button, .download-output {
143
  width: 100%;
144
  }
145
  .download-container {
@@ -149,110 +292,216 @@ def create_interface():
149
  width: 100%;
150
  }
151
  #gradio-app {
152
- margin: 0 !important; /* ๋ชจ๋“  ๋ฐฉํ–ฅ ์—ฌ๋ฐฑ ์ œ๊ฑฐ */
153
- text-align: left !important; /* ์™ผ์ชฝ ์ •๋ ฌ ๊ฐ•์ œ */
154
- padding: 20px !important; /* ํŒจ๋”ฉ ์ถ”๊ฐ€ */
155
  }
156
  .gradio-container {
157
- max-width: 100% !important; /* ๊ฐ€๋กœ ํญ ์ „์ฒด ์‚ฌ์šฉ */
158
- margin-left: 0 !important; /* ์™ผ์ชฝ ์ •๋ ฌ */
159
- padding: 20px !important; /* ํŒจ๋”ฉ ์ถ”๊ฐ€ */
160
  }
161
- .download-button {
162
  background-color: black !important;
163
  color: white !important;
164
  border: none !important;
165
  padding: 10px !important;
166
  font-size: 16px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  }
168
  """
169
 
170
  with gr.Blocks(theme=gr.themes.Soft(
171
  primary_hue=gr.themes.Color(
172
- c50="#FFF7ED", # ๊ฐ€์žฅ ๋ฐ์€ ์ฃผํ™ฉ
173
  c100="#FFEDD5",
174
  c200="#FED7AA",
175
  c300="#FDBA74",
176
  c400="#FB923C",
177
- c500="#F97316", # ๊ธฐ๋ณธ ์ฃผํ™ฉ
178
  c600="#EA580C",
179
  c700="#C2410C",
180
  c800="#9A3412",
181
- c900="#7C2D12", # ๊ฐ€์žฅ ์–ด๋‘์šด ์ฃผํ™ฉ
182
  c950="#431407",
183
  ),
184
- secondary_hue="zinc", # ๋ชจ๋˜ํ•œ ๋А๋‚Œ์˜ ํšŒ์ƒ‰ ๊ณ„์—ด
185
  neutral_hue="zinc",
186
  font=("Pretendard", "sans-serif")
187
  ), css=css) as interface:
188
 
 
 
 
189
  with gr.Row():
190
- # ์™ผ์ชฝ ์—ด: ๋น„์œจ 3
191
- with gr.Column(scale=3):
192
  # ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ
193
  input_image = gr.Image(type="pil", label="์ด๋ฏธ์ง€ ์—…๋กœ๋“œ")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
 
195
- # ์กฐ์ • ์Šฌ๋ผ์ด๋”
196
- brightness_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="๋ฐ๊ธฐ ์กฐ์ ˆ")
197
- contrast_slider = gr.Slider(0.5, 1.5, value=1.0, step=0.1, label="๋Œ€๋น„ ์กฐ์ ˆ")
198
- saturation_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="์ฑ„๋„ ์กฐ์ ˆ")
199
- temperature_slider = gr.Slider(-1.0, 1.0, value=0.0, step=0.1, label="์ƒ‰์˜จ๋„ ์กฐ์ ˆ")
200
- tint_slider = gr.Slider(-100, 100, value=0, step=1, label="์ƒ‰์กฐ ์กฐ์ ˆ")
201
- exposure_slider = gr.Slider(-5.0, 5.0, value=0.0, step=0.1, label="๋…ธ์ถœ ์กฐ์ ˆ")
202
- vibrance_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="ํ™œ๊ธฐ ์กฐ์ ˆ")
203
- color_mixer_blues_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="์ปฌ๋Ÿฌ ๋ฏน์„œ (๋ธ”๋ฃจ)")
204
- shadows_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="๊ทธ๋ฆผ์ž ์กฐ์ ˆ")
 
 
 
 
 
 
 
 
 
205
 
206
- # ์˜ค๋ฅธ์ชฝ ์—ด: ๋น„์œจ 7
207
- with gr.Column(scale=7):
208
- # ์ฒ˜๋ฆฌ๋œ ์ด๋ฏธ์ง€ ์ถœ๋ ฅ
209
- output_image = gr.Image(type="pil", label="์ฒ˜๋ฆฌ๋œ ์ด๋ฏธ์ง€")
210
-
211
- # ๋ณ€ํ™˜๋œ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ
212
- with gr.Row(elem_classes="download-container"):
213
- download_button = gr.Button("JPG๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ", elem_classes="download-button")
214
- with gr.Row(elem_classes="download-container"):
215
- download_output = gr.File(label="JPG ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ", elem_classes="download-output")
216
-
217
- # ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ ์—ฐ๊ฒฐ
218
- inputs = [
219
- input_image,
220
- brightness_slider,
221
- contrast_slider,
222
- saturation_slider,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  temperature_slider,
224
- tint_slider,
225
- exposure_slider,
226
- vibrance_slider,
227
- color_mixer_blues_slider,
228
- shadows_slider
229
- ]
230
-
231
- input_components = [
232
  brightness_slider,
233
  contrast_slider,
234
  saturation_slider,
235
- temperature_slider,
236
- tint_slider,
237
- exposure_slider,
238
- vibrance_slider,
239
- color_mixer_blues_slider,
240
- shadows_slider
241
  ]
242
-
243
- for input_component in input_components:
 
244
  input_component.change(
245
- fn=process_image,
246
- inputs=inputs,
247
- outputs=output_image
248
  )
249
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  # ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ๊ธฐ๋Šฅ
251
  download_button.click(
252
  fn=download_image,
253
- inputs=[output_image, input_image],
254
  outputs=download_output
255
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
 
257
  return interface
258
 
@@ -261,4 +510,4 @@ if __name__ == "__main__":
261
  logger.info("์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘")
262
  interface = create_interface()
263
  interface.queue()
264
- interface.launch()
 
5
  from io import BytesIO
6
  import tempfile
7
  import logging
8
+ import replicate
9
+ import requests
10
+ import os
11
+ from dotenv import load_dotenv
12
+ import base64
13
+ from datetime import datetime, timedelta
14
+ import uuid
15
+ import time
16
+
17
+ # .env ํŒŒ์ผ์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ (์—†์œผ๋ฉด ๋ฌด์‹œ)
18
+ load_dotenv()
19
 
20
  # ๋กœ๊น… ์„ค์ • - INFO ๋ ˆ๋ฒจ๋กœ ๋ณ€๊ฒฝ
21
  logging.basicConfig(level=logging.INFO)
22
  logger = logging.getLogger(__name__)
23
 
24
+ # Replicate API ํ† ํฐ ์„ค์ •
25
+ REPLICATE_API_TOKEN = os.getenv("REPLICATE_API_TOKEN", "์—ฌ๊ธฐ์—_API_ํ† ํฐ์„_์ž…๋ ฅํ•˜์„ธ์š”")
26
+ os.environ["REPLICATE_API_TOKEN"] = REPLICATE_API_TOKEN
27
+
28
+ # ์„ธ์…˜๋ณ„ ์ด๋ฏธ์ง€ ์ €์žฅ์šฉ ๋”•์…”๋„ˆ๋ฆฌ
29
+ session_images = {}
30
+
31
  def adjust_brightness(image, value):
32
  """์ด๋ฏธ์ง€ ๋ฐ๊ธฐ ์กฐ์ ˆ"""
33
  value = float(value - 1) * 100 # 0-2 ๋ฒ”์œ„๋ฅผ -100์—์„œ +100์œผ๋กœ ๋ณ€ํ™˜
 
68
  b = np.clip(b, 0, 255)
69
  return cv2.merge([b, g, r])
70
 
71
+ def remove_background(image, session_id):
72
+ """๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ๊ธฐ๋Šฅ - Replicate API ์‚ฌ์šฉ"""
73
+ logger.info(f"์„ธ์…˜ {session_id}: ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ์‹œ์ž‘")
74
+
75
+ # ์ž„์‹œ ํŒŒ์ผ๋กœ ์ด๋ฏธ์ง€ ์ €์žฅ
76
+ temp_file = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
77
+ temp_file_path = temp_file.name
78
+ temp_file.close()
79
+
80
+ # PIL ์ด๋ฏธ์ง€๋ฅผ ์ž„์‹œ ํŒŒ์ผ๋กœ ์ €์žฅ
81
+ image.save(temp_file_path, format="PNG")
82
+
83
+ try:
84
+ # Replicate API๋กœ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ์š”์ฒญ
85
+ output = replicate.run(
86
+ "851-labs/background-remover:a029dff38972b5fda4ec5d75d7d1cd25aeff621d2cf4946a41055d7db66b80bc",
87
+ input={
88
+ "image": open(temp_file_path, "rb"),
89
+ "format": "png",
90
+ "reverse": False,
91
+ "threshold": 0,
92
+ "background_type": "rgba"
93
+ }
94
+ )
95
+
96
+ # URL์—์„œ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ
97
+ response = requests.get(output)
98
+ if response.status_code == 200:
99
+ # ๋ฐ”์ดํŠธ ๋ฐ์ดํ„ฐ๋ฅผ PIL ์ด๋ฏธ์ง€๋กœ ๋ณ€ํ™˜
100
+ img = Image.open(BytesIO(response.content))
101
+
102
+ # PNG ํŒŒ์ผ๋กœ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๋œ ์ด๋ฏธ์ง€ ์ €์žฅ (๋งˆ์Šคํฌ์šฉ)
103
+ timestamp = get_korean_timestamp()
104
+ bg_removed_path = tempfile.gettempdir() + f"/bg_removed_{session_id}_{timestamp}.png"
105
+ img.save(bg_removed_path, format="PNG")
106
+
107
+ logger.info(f"์„ธ์…˜ {session_id}: ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ์™„๋ฃŒ: {bg_removed_path}")
108
+
109
+ # ์„ธ์…˜ ์ •๋ณด ์ €์žฅ
110
+ session_images[session_id]['bg_removed_image'] = img
111
+ session_images[session_id]['bg_removed_path'] = bg_removed_path
112
+
113
+ return img
114
+ else:
115
+ logger.error(f"์„ธ์…˜ {session_id}: API ์‘๋‹ต ์˜ค๋ฅ˜: {response.status_code}")
116
+ return image
117
+
118
+ except Exception as e:
119
+ logger.error(f"์„ธ์…˜ {session_id}: ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ์˜ค๋ฅ˜: {e}")
120
+ return image
121
+ finally:
122
+ # ์ž„์‹œ ํŒŒ์ผ ์‚ญ์ œ
123
+ if os.path.exists(temp_file_path):
124
+ os.unlink(temp_file_path)
125
 
126
+ def get_korean_timestamp():
127
+ """ํ•œ๊ตญ ์‹œ๊ฐ„ ํƒ€์ž„์Šคํƒฌํ”„ ์ƒ์„ฑ"""
128
+ korea_time = datetime.utcnow() + timedelta(hours=9)
129
+ return korea_time.strftime('%Y%m%d_%H%M%S')
 
 
 
130
 
131
+ def handle_upload(image, session_id=None):
132
+ """๏ฟฝ๏ฟฝ๋ฏธ์ง€ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ ๋ฐ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ"""
133
  if image is None:
134
  return None
 
 
 
135
 
136
+ # ์„ธ์…˜ ID ์ƒ์„ฑ ๋˜๋Š” ํ™•์ธ
137
+ if session_id is None or session_id not in session_images:
138
+ session_id = str(uuid.uuid4())
139
+ session_images[session_id] = {}
140
+
141
+ # ์›๋ณธ ์ด๋ฏธ์ง€ ์ €์žฅ
142
+ original_image = Image.fromarray(np.array(image))
143
+ session_images[session_id]['original_image'] = original_image
 
 
144
 
145
+ # ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ์‹คํ–‰
146
+ bg_removed_image = remove_background(original_image, session_id)
147
+
148
+ # ํ•„ํ„ฐ ์ ์šฉ ์ „ ๊ธฐ๋ณธ ์ด๋ฏธ์ง€ ๋ฐ˜ํ™˜ (ํ•„ํ„ฐ ์—†์ด ์›๋ณธ ์ด๋ฏธ์ง€)
149
+ return original_image, session_id
150
 
151
+ def process_image(session_id, temperature, brightness, contrast, saturation, filter_mode):
152
+ """๋ชจ๋“œ์— ๋”ฐ๋ฅธ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ"""
153
+ if session_id is None or session_id not in session_images:
154
+ logger.warning(f"์„ธ์…˜ ID๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค: {session_id}")
155
+ return None
156
+
157
+ original_image = session_images[session_id].get('original_image')
158
+ bg_removed_image = session_images[session_id].get('bg_removed_image')
159
+
160
+ if original_image is None:
161
  return None
162
+
163
+ if bg_removed_image is None:
164
+ logger.warning(f"์„ธ์…˜ {session_id}: ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๋œ ์ด๋ฏธ์ง€๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ „์ฒด ํ•„ํ„ฐ ๋ชจ๋“œ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.")
165
+ # ์ „์ฒด ํ•„ํ„ฐ๋กœ ์ฒ˜๋ฆฌ
166
+ cv_image = cv2.cvtColor(np.array(original_image), cv2.COLOR_RGB2BGR)
167
+ cv_image = adjust_temperature(cv_image, temperature)
168
+ cv_image = adjust_brightness(cv_image, brightness)
169
+ cv_image = adjust_contrast(cv_image, contrast)
170
+ cv_image = adjust_saturation(cv_image, saturation)
171
+ return Image.fromarray(cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB))
172
+
173
+ # ํ•„ํ„ฐ ๋ชจ๋“œ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์ฒ˜๋ฆฌ
174
+ if filter_mode == "์ „์ฒด ํ•„ํ„ฐ":
175
+ # ์›๋ณธ ์ด๋ฏธ์ง€์— ํ•„ํ„ฐ ์ ์šฉ
176
+ cv_image = cv2.cvtColor(np.array(original_image), cv2.COLOR_RGB2BGR)
177
+ cv_image = adjust_temperature(cv_image, temperature)
178
+ cv_image = adjust_brightness(cv_image, brightness)
179
+ cv_image = adjust_contrast(cv_image, contrast)
180
+ cv_image = adjust_saturation(cv_image, saturation)
181
+ return Image.fromarray(cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB))
182
+
183
+ elif filter_mode == "๋ฐฐ๊ฒฝ๋งŒ ํ•„ํ„ฐ ์ ์šฉ":
184
+ # ์›๋ณธ์— ํ•„ํ„ฐ ์ ์šฉ ํ›„ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๋œ ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ
185
+ cv_image = cv2.cvtColor(np.array(original_image), cv2.COLOR_RGB2BGR)
186
+ cv_image = adjust_temperature(cv_image, temperature)
187
+ cv_image = adjust_brightness(cv_image, brightness)
188
+ cv_image = adjust_contrast(cv_image, contrast)
189
+ cv_image = adjust_saturation(cv_image, saturation)
190
+ filtered_original = Image.fromarray(cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB))
191
+
192
+ # ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๋œ ์ด๋ฏธ์ง€๊ฐ€ RGBA ๋ชจ๋“œ์ธ์ง€ ํ™•์ธ
193
+ if bg_removed_image.mode != 'RGBA':
194
+ logger.warning(f"์„ธ์…˜ {session_id}: ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๋œ ์ด๋ฏธ์ง€๊ฐ€ RGBA ๋ชจ๋“œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.")
195
+ return filtered_original
196
+
197
+ # ํ•„ํ„ฐ๋ง๋œ ์ด๋ฏธ์ง€๋ฅผ RGBA๋กœ ๋ณ€ํ™˜ (์•ŒํŒŒ ์ฑ„๋„ ์ถ”๊ฐ€)
198
+ if filtered_original.mode != 'RGBA':
199
+ filtered_original = filtered_original.convert('RGBA')
200
+
201
+ # ํ•ฉ์„ฑ (๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๋œ ์ด๋ฏธ์ง€๋ฅผ ํ•„ํ„ฐ๋ง๋œ ์›๋ณธ ์œ„์— ๋ฐฐ์น˜)
202
+ result = Image.new('RGBA', filtered_original.size, (0, 0, 0, 0))
203
+ result.paste(filtered_original, (0, 0), None)
204
+ result.paste(bg_removed_image, (0, 0), bg_removed_image)
205
+
206
+ return result.convert('RGB')
207
+
208
+ elif filter_mode == "์ƒํ’ˆ๋งŒ ํ•„ํ„ฐ ์ ์šฉ":
209
+ # ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๋œ ์ด๋ฏธ์ง€๋งŒ ํ•„ํ„ฐ ์ ์šฉ
210
+ cv_image = cv2.cvtColor(np.array(bg_removed_image), cv2.COLOR_RGBA2BGRA)
211
+ # ์•ŒํŒŒ ์ฑ„๋„ ๋ถ„๋ฆฌ
212
+ b, g, r, a = cv2.split(cv_image)
213
+ # BGR ์ด๋ฏธ์ง€๋กœ ํ•ฉ์น˜๊ธฐ
214
+ bgr = cv2.merge([b, g, r])
215
+ # ํ•„ํ„ฐ ์ ์šฉ
216
+ bgr = adjust_temperature(bgr, temperature)
217
+ bgr = adjust_brightness(bgr, brightness)
218
+ bgr = adjust_contrast(bgr, contrast)
219
+ bgr = adjust_saturation(bgr, saturation)
220
+ # ๋‹ค์‹œ ์•ŒํŒŒ ์ฑ„๋„๊ณผ ํ•ฉ์น˜๊ธฐ
221
+ filtered_bgra = cv2.merge([bgr[:,:,0], bgr[:,:,1], bgr[:,:,2], a])
222
+ filtered_bg_removed = Image.fromarray(cv2.cvtColor(filtered_bgra, cv2.COLOR_BGRA2RGBA))
223
+
224
+ # ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๋œ ์ด๋ฏธ์ง€๊ฐ€ RGBA ๋ชจ๋“œ์ธ์ง€ ํ™•์ธ
225
+ if bg_removed_image.mode != 'RGBA':
226
+ logger.warning(f"์„ธ์…˜ {session_id}: ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๋œ ์ด๋ฏธ์ง€๊ฐ€ RGBA ๋ชจ๋“œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.")
227
+ # ํ•„ํ„ฐ๋งŒ ์ ์šฉํ•˜๊ณ  ๋ฐ˜ํ™˜
228
+ return filtered_bg_removed
229
+
230
+ # ์›๋ณธ ์ด๋ฏธ์ง€๋ฅผ RGBA๋กœ ๋ณ€ํ™˜
231
+ original_rgba = original_image.convert('RGBA')
232
+
233
+ # ์›๋ณธ ์ด๋ฏธ์ง€์™€ ํ•„ํ„ฐ๋ง๋œ ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ์ด๋ฏธ์ง€๋ฅผ ํ•ฉ์„ฑ
234
+ result = Image.new('RGBA', original_rgba.size, (0, 0, 0, 0))
235
+ result.paste(original_rgba, (0, 0), None)
236
+ result.paste(filtered_bg_removed, (0, 0), bg_removed_image)
237
+
238
+ return result.convert('RGB')
239
+
240
+ # ๊ธฐ๋ณธ๊ฐ’์€ ์ „์ฒด ํ•„ํ„ฐ
241
+ cv_image = cv2.cvtColor(np.array(original_image), cv2.COLOR_RGB2BGR)
242
+ cv_image = adjust_temperature(cv_image, temperature)
243
+ cv_image = adjust_brightness(cv_image, brightness)
244
+ cv_image = adjust_contrast(cv_image, contrast)
245
+ cv_image = adjust_saturation(cv_image, saturation)
246
+ return Image.fromarray(cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB))
247
 
248
+ def download_image(image, session_id, format_type):
249
+ """์ด๋ฏธ์ง€๋ฅผ ์ง€์ •๋œ ํ˜•์‹์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๊ฒฝ๋กœ ๋ฐ˜ํ™˜"""
250
+ if image is None or session_id is None or session_id not in session_images:
251
+ return None
 
252
 
253
  timestamp = get_korean_timestamp()
254
+
255
+ # ์›๋ณธ ํŒŒ์ผ๋ช… ๊ฐ€์ ธ์˜ค๊ธฐ (์žˆ๋Š” ๊ฒฝ์šฐ)
256
+ original_name = "์ด๋ฏธ์ง€"
257
+ if 'original_name' in session_images[session_id]:
258
+ original_name = session_images[session_id]['original_name']
259
 
260
+ if format_type == "JPG":
261
+ file_name = f"[๋์žฅAI]๋์žฅํ•„ํ„ฐ_{original_name}_{timestamp}.jpg"
262
+ format_str = "JPEG"
263
+ else: # PNG
264
+ file_name = f"[๋์žฅAI]๋์žฅํ•„ํ„ฐ_{original_name}_{timestamp}.png"
265
+ format_str = "PNG"
266
 
267
  # ํŒŒ์ผ ์ €์žฅ
268
  temp_file_path = tempfile.gettempdir() + "/" + file_name
269
+ image.save(temp_file_path, format=format_str)
270
  return temp_file_path
271
 
272
+ def reset_filters(session_id):
273
+ """ํ•„ํ„ฐ ์„ค์ • ์ดˆ๊ธฐํ™”"""
274
+ if session_id is None or session_id not in session_images:
275
+ return None, 0.0, 1.0, 1.0, 1.0
276
+
277
+ original_image = session_images[session_id].get('original_image')
278
+ return original_image, 0.0, 1.0, 1.0, 1.0
279
+
280
  def create_interface():
281
  css = """
282
  footer {
283
  visibility: hidden;
284
  }
285
+ .download-button, .download-output, .reset-button {
286
  width: 100%;
287
  }
288
  .download-container {
 
292
  width: 100%;
293
  }
294
  #gradio-app {
295
+ margin: 0 !important;
296
+ text-align: left !important;
297
+ padding: 0 !important;
298
  }
299
  .gradio-container {
300
+ max-width: 100% !important;
301
+ margin: 0 !important;
302
+ padding: 0 !important;
303
  }
304
+ .download-button, .reset-button {
305
  background-color: black !important;
306
  color: white !important;
307
  border: none !important;
308
  padding: 10px !important;
309
  font-size: 16px !important;
310
+ border-radius: 4px !important;
311
+ }
312
+ .reset-button {
313
+ background-color: #666 !important;
314
+ }
315
+ .filter-mode {
316
+ margin-top: 20px;
317
+ margin-bottom: 20px;
318
+ }
319
+ .input-panel {
320
+ padding: 20px;
321
+ }
322
+ .output-panel {
323
+ padding: 20px;
324
+ height: 100%;
325
+ display: flex;
326
+ flex-direction: column;
327
+ }
328
+ .output-image {
329
+ flex-grow: 1;
330
+ min-height: 400px;
331
+ }
332
+ .filter-section {
333
+ margin: 15px 0;
334
+ padding: 15px;
335
+ border: 1px solid #eee;
336
+ border-radius: 8px;
337
+ }
338
+ .session-id {
339
+ display: none;
340
  }
341
  """
342
 
343
  with gr.Blocks(theme=gr.themes.Soft(
344
  primary_hue=gr.themes.Color(
345
+ c50="#FFF7ED",
346
  c100="#FFEDD5",
347
  c200="#FED7AA",
348
  c300="#FDBA74",
349
  c400="#FB923C",
350
+ c500="#F97316",
351
  c600="#EA580C",
352
  c700="#C2410C",
353
  c800="#9A3412",
354
+ c900="#7C2D12",
355
  c950="#431407",
356
  ),
357
+ secondary_hue="zinc",
358
  neutral_hue="zinc",
359
  font=("Pretendard", "sans-serif")
360
  ), css=css) as interface:
361
 
362
+ # ์„ธ์…˜ ID (์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด์ด์ง€ ์•Š์Œ)
363
+ session_id = gr.Textbox(visible=False, elem_classes="session-id")
364
+
365
  with gr.Row():
366
+ # ์™ผ์ชฝ ์—ด: ์ž…๋ ฅ ํŒจ๋„
367
+ with gr.Column(scale=1, elem_classes="input-panel"):
368
  # ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ
369
  input_image = gr.Image(type="pil", label="์ด๋ฏธ์ง€ ์—…๋กœ๋“œ")
370
+
371
+ # ํ•„ํ„ฐ ๋ชจ๋“œ ์„ ํƒ
372
+ with gr.Group(elem_classes="filter-section"):
373
+ filter_mode = gr.Radio(
374
+ ["์ „์ฒด ํ•„ํ„ฐ", "๋ฐฐ๊ฒฝ๋งŒ ํ•„ํ„ฐ ์ ์šฉ", "์ƒํ’ˆ๋งŒ ํ•„ํ„ฐ ์ ์šฉ"],
375
+ label="ํ•„ํ„ฐ ์ ์šฉ ๋ชจ๋“œ",
376
+ value="์ „์ฒด ํ•„ํ„ฐ",
377
+ elem_classes="filter-mode"
378
+ )
379
+
380
+ # ์กฐ์ • ์Šฌ๋ผ์ด๋” ๊ทธ๋ฃน (์ค‘์š” ์Šฌ๋ผ์ด๋”๋งŒ ๋‚จ๊น€)
381
+ with gr.Group(elem_classes="filter-section"):
382
+ gr.Markdown("### ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ์„ค์ •")
383
+
384
+ # ์ƒ‰์˜จ๋„๋ฅผ ๊ฐ€์žฅ ์œ„๋กœ ๋ฐฐ์น˜
385
+ temperature_slider = gr.Slider(-1.0, 1.0, value=0.0, step=0.1, label="์ƒ‰์˜จ๋„ ์กฐ์ ˆ")
386
+ brightness_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="๋ฐ๊ธฐ ์กฐ์ ˆ")
387
+ contrast_slider = gr.Slider(0.5, 1.5, value=1.0, step=0.1, label="๋Œ€๋น„ ์กฐ์ ˆ")
388
+ saturation_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="์ฑ„๋„ ์กฐ์ ˆ")
389
+
390
+ # ๋ฆฌ์…‹ ๋ฒ„ํŠผ
391
+ reset_button = gr.Button("ํ•„ํ„ฐ ์ดˆ๊ธฐํ™”", elem_classes="reset-button")
392
 
393
+ # ์˜ค๋ฅธ์ชฝ ์—ด: ์ถœ๋ ฅ ํŒจ๋„
394
+ with gr.Column(scale=1, elem_classes="output-panel"):
395
+ # ํ•„ํ„ฐ ์ ์šฉ๋œ ์ด๋ฏธ์ง€ ์ถœ๋ ฅ
396
+ filtered_output = gr.Image(type="pil", label="ํ•„ํ„ฐ ์ ์šฉ๋œ ์ด๋ฏธ์ง€", elem_classes="output-image")
397
+
398
+ # ์ด๋ฏธ์ง€ ์ €์žฅ ์˜ต์…˜ ๋ฐ ๋‹ค์šด๋กœ๋“œ
399
+ with gr.Row():
400
+ with gr.Column(scale=1):
401
+ format_select = gr.Radio(
402
+ ["JPG", "PNG"],
403
+ label="์ €์žฅ ํ˜•์‹",
404
+ value="JPG"
405
+ )
406
+
407
+ with gr.Column(scale=2):
408
+ download_button = gr.Button("์ด๋ฏธ์ง€ ๋ณ€ํ™˜ํ•˜๊ธฐ", elem_classes="download-button")
409
+
410
+ # ๋‹ค์šด๋กœ๋“œ ๋งํฌ
411
+ download_output = gr.File(label="๋ณ€ํ™˜๋œ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ")
412
 
413
+ # ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ ๋ฐ ์ดˆ๊ธฐ ์ถœ๋ ฅ ์„ค์ •
414
+ def update_image_and_session(image):
415
+ if image is not None:
416
+ # ํŒŒ์ผ ์ด๋ฆ„ ์ €์žฅ (๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ)
417
+ original_name = "์ด๋ฏธ์ง€"
418
+ if hasattr(image, 'name'):
419
+ original_name = image.name.split('.')[0]
420
+
421
+ # ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๋ฐ ์„ธ์…˜ ์ƒ์„ฑ
422
+ result_image, new_session_id = handle_upload(image)
423
+
424
+ # ์›๋ณธ ํŒŒ์ผ๋ช… ์ €์žฅ
425
+ if new_session_id in session_images:
426
+ session_images[new_session_id]['original_name'] = original_name
427
+
428
+ return result_image, new_session_id
429
+ return None, None
430
+
431
+ input_image.change(
432
+ fn=update_image_and_session,
433
+ inputs=[input_image],
434
+ outputs=[filtered_output, session_id]
435
+ )
436
+
437
+ # ํ•„ํ„ฐ ์ ์šฉ ํ•จ์ˆ˜
438
+ def apply_filters(session_id, temperature, brightness, contrast, saturation, filter_mode):
439
+ return process_image(session_id, temperature, brightness, contrast, saturation, filter_mode)
440
+
441
+ # ํ•„ํ„ฐ ์ž…๋ ฅ ๋ณ€๊ฒฝ ์‹œ ์ฒ˜๋ฆฌ
442
+ filter_inputs = [
443
+ session_id,
444
  temperature_slider,
 
 
 
 
 
 
 
 
445
  brightness_slider,
446
  contrast_slider,
447
  saturation_slider,
448
+ filter_mode
 
 
 
 
 
449
  ]
450
+
451
+ # ์Šฌ๋ผ์ด๋” ๋ณ€๊ฒฝ ์‹œ ์ฒ˜๋ฆฌ
452
+ for input_component in [temperature_slider, brightness_slider, contrast_slider, saturation_slider]:
453
  input_component.change(
454
+ fn=apply_filters,
455
+ inputs=filter_inputs,
456
+ outputs=filtered_output
457
  )
458
+
459
+ # ํ•„ํ„ฐ ๋ชจ๋“œ ๋ณ€๊ฒฝ ์‹œ ์ฒ˜๋ฆฌ
460
+ filter_mode.change(
461
+ fn=apply_filters,
462
+ inputs=filter_inputs,
463
+ outputs=filtered_output
464
+ )
465
+
466
+ # ๋ฆฌ์…‹ ๋ฒ„ํŠผ ๊ธฐ๋Šฅ
467
+ reset_button.click(
468
+ fn=reset_filters,
469
+ inputs=[session_id],
470
+ outputs=[filtered_output, temperature_slider, brightness_slider, contrast_slider, saturation_slider]
471
+ )
472
+
473
  # ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ๊ธฐ๋Šฅ
474
  download_button.click(
475
  fn=download_image,
476
+ inputs=[filtered_output, session_id, format_select],
477
  outputs=download_output
478
  )
479
+
480
+ # ์„ธ์…˜ ์ •๋ฆฌ ํƒ€์ด๋จธ (30๋ถ„ ์ด์ƒ ๋œ ์„ธ์…˜ ์ œ๊ฑฐ)
481
+ def cleanup_sessions():
482
+ current_time = time.time()
483
+ to_remove = []
484
+
485
+ for sess_id in session_images:
486
+ if 'created_at' in session_images[sess_id]:
487
+ if current_time - session_images[sess_id]['created_at'] > 1800: # 30๋ถ„
488
+ to_remove.append(sess_id)
489
+
490
+ for sess_id in to_remove:
491
+ # ์ž„์‹œ ํŒŒ์ผ ์ œ๊ฑฐ
492
+ if 'bg_removed_path' in session_images[sess_id]:
493
+ path = session_images[sess_id]['bg_removed_path']
494
+ if os.path.exists(path):
495
+ os.unlink(path)
496
+
497
+ # ์„ธ์…˜ ์ •๋ณด ์ œ๊ฑฐ
498
+ del session_images[sess_id]
499
+
500
+ # 10๋ถ„๋งˆ๋‹ค ์‹คํ–‰
501
+ gr.set_interval(cleanup_sessions, 600)
502
+
503
+ # ์„ธ์…˜ ์ •๋ฆฌ ์‹œ์ž‘
504
+ interface.load(cleanup_sessions)
505
 
506
  return interface
507
 
 
510
  logger.info("์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘")
511
  interface = create_interface()
512
  interface.queue()
513
+ interface.launch()