ssboost commited on
Commit
57eb886
ยท
verified ยท
1 Parent(s): 28cfe51

Update app.py

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