ssboost commited on
Commit
0c4b864
·
verified ·
1 Parent(s): 42fde2a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1 -456
app.py CHANGED
@@ -1,457 +1,2 @@
1
  import os
2
- import logging
3
- import json
4
- import time
5
- from PIL import Image
6
- import gradio as gr
7
- from dotenv import load_dotenv
8
-
9
- load_dotenv()
10
-
11
- # ------------------- 로깅 설정 -------------------
12
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
13
- logger = logging.getLogger(__name__)
14
-
15
- # ------------------- 배경 디렉토리 설정 -------------------
16
- BACKGROUNDS_DIR = "./background"
17
- if not os.path.exists(BACKGROUNDS_DIR):
18
- os.makedirs(BACKGROUNDS_DIR)
19
- logger.info(f"배경 디렉토리를 생성했습니다: {BACKGROUNDS_DIR}")
20
-
21
- # 예시 데이터 로드
22
- from db_examples import product_background_examples
23
-
24
- # ------------------- 전역 변수 설정 -------------------
25
- IMAGE_CACHE = {} # 이미지 캐시 추가
26
-
27
- # 커스텀 CSS 스타일
28
- custom_css = """
29
- :root {
30
- --primary-color: #FB7F0D;
31
- --secondary-color: #ff9a8b;
32
- --accent-color: #FF6B6B;
33
- --background-color: #FFFFFF; /* 베이지색(#FFF3E9)에서 완전한 흰색(#FFFFFF)으로 변경 */
34
- --card-bg: #ffffff;
35
- --text-color: #334155;
36
- --border-radius: 18px;
37
- --shadow: 0 8px 30px rgba(251, 127, 13, 0.08);
38
- }
39
-
40
- /* 예시 갤러리 스타일 */
41
- .example-gallery {
42
- display: grid;
43
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
44
- gap: 20px;
45
- padding: 20px;
46
- }
47
-
48
- .example-item {
49
- cursor: pointer;
50
- border: 1px solid rgba(0, 0, 0, 0.1);
51
- border-radius: var(--border-radius);
52
- overflow: hidden;
53
- transition: all 0.3s ease;
54
- background: white;
55
- }
56
-
57
- .example-item:hover {
58
- box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
59
- transform: translateY(-2px);
60
- }
61
-
62
- .example-item img {
63
- width: 100%;
64
- height: 250px;
65
- object-fit: cover;
66
- }
67
-
68
- .example-label {
69
- padding: 10px;
70
- text-align: center;
71
- font-weight: bold;
72
- background: rgba(251, 127, 13, 0.1);
73
- }
74
-
75
- .example-detail-view {
76
- margin-bottom: 30px;
77
- }
78
-
79
- .example-params {
80
- background: white;
81
- padding: 20px;
82
- border-radius: var(--border-radius);
83
- box-shadow: var(--shadow);
84
- }
85
-
86
- .example-params p {
87
- margin: 10px 0;
88
- font-size: 16px;
89
- }
90
-
91
- .example-params strong {
92
- color: var(--primary-color);
93
- }
94
-
95
- /* 선택된 예시 하이라이트 */
96
- .example-item.selected {
97
- border: 3px solid var(--primary-color);
98
- }
99
-
100
- /* ── 그룹 래퍼 배경 완전 제거 ── */
101
- .custom-section-group,
102
- .gr-block.gr-group {
103
- background-color: var(--background-color) !important; /* 변경된 배경색 변수 사용 */
104
- box-shadow: none !important;
105
- }
106
- .custom-section-group::before,
107
- .custom-section-group::after,
108
- .gr-block.gr-group::before,
109
- .gr-block.gr-group::after {
110
- display: none !important;
111
- content: none !important;
112
- }
113
-
114
- /* 그룹 컨테이너 배경을 아이보리로, 그림자 제거 */
115
- .custom-section-group {
116
- background-color: var(--background-color) !important; /* 변경된 배경색 변수 사용 */
117
- box-shadow: none !important;
118
- }
119
- /* 상단·하단에 그려지는 회색 캡(둥근 모서리) 제거 */
120
- .custom-section-group::before,
121
- .custom-section-group::after {
122
- display: none !important;
123
- content: none !important;
124
- }
125
-
126
- body {
127
- font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
128
- background-color: var(--background-color); /* 변경된 배경색 변수 사용 */
129
- color: var(--text-color);
130
- line-height: 1.6;
131
- margin: 0;
132
- padding: 0;
133
- }
134
-
135
- /* 푸터 숨김 설정 추가 */
136
- footer {
137
- visibility: hidden;
138
- }
139
-
140
- .gradio-container {
141
- width: 100%; /* 전체 너비 100% 고정 */
142
- margin: 0 auto;
143
- padding: 20px;
144
- background-color: var(--background-color); /* 변경된 배경색 변수 사용 */
145
- }
146
-
147
- /* 콘텐츠 박스 (프레임) 스타일 */
148
- .custom-frame {
149
- background-color: var(--card-bg);
150
- border: 1px solid rgba(0, 0, 0, 0.04);
151
- border-radius: var(--border-radius);
152
- padding: 20px;
153
- margin: 10px 0;
154
- box-shadow: var(--shadow);
155
- }
156
-
157
- /* 섹션 그룹 스타일 - 회색 배경 완전 제거 */
158
- .custom-section-group {
159
- margin-top: 20px;
160
- padding: 0;
161
- border: none;
162
- border-radius: 0;
163
- background-color: var(--background-color); /* 회색 → 아이보리(전체 배경색) */
164
- box-shadow: none !important; /* 혹시 남아있는 그림자도 같이 제거 */
165
- }
166
-
167
- /* 버튼 스타일 - 글자 크기 18px */
168
- .custom-button {
169
- border-radius: 30px !important;
170
- background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important;
171
- color: white !important;
172
- font-size: 18px !important;
173
- padding: 10px 20px !important;
174
- border: none;
175
- box-shadow: 0 4px 8px rgba(251, 127, 13, 0.25);
176
- transition: transform 0.3s ease;
177
- }
178
- .custom-button:hover {
179
- transform: translateY(-2px);
180
- box-shadow: 0 6px 12px rgba(251, 127, 13, 0.3);
181
- }
182
-
183
- /* 제목 스타일 (모든 항목명이 동일하게 custom-title 클래스로) */
184
- .custom-title {
185
- font-size: 28px;
186
- font-weight: bold;
187
- margin-bottom: 10px;
188
- color: var(--text-color);
189
- border-bottom: 2px solid var(--primary-color);
190
- padding-bottom: 5px;
191
- }
192
-
193
- /* 이미지 컨테이너 */
194
- .image-container {
195
- border-radius: var(--border-radius);
196
- overflow: hidden;
197
- border: 1px solid rgba(0, 0, 0, 0.08);
198
- transition: all 0.3s ease;
199
- background-color: white;
200
- aspect-ratio: 1 / 1; /* 정사각형 비율 강제 */
201
- }
202
-
203
- .image-container:hover {
204
- box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
205
- }
206
-
207
- .image-container img {
208
- width: 100%;
209
- height: 100%;
210
- object-fit: contain; /* 이미지 비율 유지하면서 컨테이너에 맞춤 */
211
- }
212
-
213
- /* 입력 필드 스타일 */
214
- .gr-input, .gr-text-input, .gr-sample-inputs {
215
- border-radius: var(--border-radius) !important;
216
- border: 1px solid #dddddd !important;
217
- padding: 12px !important;
218
- box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05) !important;
219
- transition: all 0.3s ease !important;
220
- }
221
-
222
- .gr-input:focus, .gr-text-input:focus {
223
- border-color: var(--primary-color) !important;
224
- outline: none !important;
225
- box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important;
226
- }
227
-
228
- /* 메인 컨텐츠 스크롤바 */
229
- ::-webkit-scrollbar {
230
- width: 8px;
231
- height: 8px;
232
- }
233
-
234
- ::-webkit-scrollbar-track {
235
- background: rgba(0, 0, 0, 0.05);
236
- border-radius: 10px;
237
- }
238
-
239
- ::-webkit-scrollbar-thumb {
240
- background: var(--primary-color);
241
- border-radius: 10px;
242
- }
243
-
244
- /* 애니메이션 스타일 */
245
- @keyframes fadeIn {
246
- from { opacity: 0; transform: translateY(10px); }
247
- to { opacity: 1; transform: translateY(0); }
248
- }
249
-
250
- .fade-in {
251
- animation: fadeIn 0.5s ease-out;
252
- }
253
-
254
- /* 반응형 */
255
- @media (max-width: 768px) {
256
- .button-grid {
257
- grid-template-columns: repeat(2, 1fr);
258
- }
259
- }
260
-
261
- /* 섹션 제목 스타일 - 참조코드 스타일과 동일하게 적용 */
262
- .section-title {
263
- display: flex;
264
- align-items: center;
265
- font-size: 20px;
266
- font-weight: 700;
267
- color: #333333;
268
- margin-bottom: 10px;
269
- padding-bottom: 5px;
270
- border-bottom: 2px solid #FB7F0D;
271
- font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
272
- }
273
-
274
- .section-title img {
275
- margin-right: 10px;
276
- width: 24px;
277
- height: 24px;
278
- }
279
- """
280
-
281
- # FontAwesome 아이콘 포함
282
- fontawesome_link = """
283
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
284
- """
285
-
286
- # ------------------- 예시 탭을 위한 함수 -------------------
287
- def load_image_cached(image_path):
288
- """이미지를 캐시하여 로드하는 함수"""
289
- global IMAGE_CACHE
290
- if image_path not in IMAGE_CACHE:
291
- try:
292
- img = Image.open(image_path)
293
- # 큰 이미지는 미리 리사이즈하여 캐시
294
- if max(img.size) > 1000:
295
- ratio = 1000 / max(img.size)
296
- new_size = (int(img.size[0] * ratio), int(img.size[1] * ratio))
297
- img = img.resize(new_size, Image.Resampling.LANCZOS)
298
- IMAGE_CACHE[image_path] = img
299
- except Exception as e:
300
- logger.error(f"이미지 로드 실패: {image_path}, 에러: {e}")
301
- return None
302
- return IMAGE_CACHE[image_path]
303
-
304
- def preload_example_images():
305
- """예시 이미지들을 미리 로드하는 함수"""
306
- for example in product_background_examples:
307
- load_image_cached(example[0]) # 입력 이미지
308
- load_image_cached(example[5]) # 결과 이미지
309
-
310
- def create_app():
311
- with gr.Blocks(css=custom_css, theme=gr.themes.Default(
312
- primary_hue="orange",
313
- secondary_hue="orange",
314
- font=[gr.themes.GoogleFont("Noto Sans KR"), "ui-sans-serif", "system-ui"]
315
- )) as demo:
316
- gr.HTML(fontawesome_link)
317
-
318
- # 상품 배경 이미지 예시 갤러리 섹션
319
- with gr.Column(elem_classes="custom-frame"):
320
- gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/681/681443.png"> 상품 배경 이미지 예시 갤러리</div>')
321
-
322
- # 상단 메인 뷰 영역
323
- with gr.Row():
324
- example_input_image = gr.Image(
325
- label="입력 이미지",
326
- height=400,
327
- width=400,
328
- elem_classes="image-container",
329
- show_label=True,
330
- show_download_button=True,
331
- container=True,
332
- scale=1
333
- )
334
- with gr.Column(elem_classes="example-params"):
335
- example_bg_type = gr.Textbox(label="배경 유형", interactive=False)
336
- example_bg_option = gr.Textbox(label="배경 선택", interactive=False)
337
- example_product_name = gr.Textbox(label="상품명", interactive=False)
338
- example_additional_info = gr.Textbox(label="추가 요청사항", interactive=False)
339
- example_output_image = gr.Image(
340
- label="결과 이미지",
341
- height=400,
342
- width=400,
343
- elem_classes="image-container",
344
- show_label=True,
345
- show_download_button=True,
346
- container=True,
347
- scale=1
348
- )
349
-
350
- # 배경 유형별 갤러리 섹션
351
- with gr.Column(elem_classes="custom-frame"):
352
- gr.HTML('<div class="section-title"><img src="https://cdn-icons-png.flaticon.com/512/3068/3068327.png"> 배경 유형별 예시</div>')
353
-
354
- # 예시들을 배경 유형별로 그룹화
355
- examples_by_type = {}
356
- for example in product_background_examples:
357
- bg_type = example[1] # 배경 유형
358
- if bg_type not in examples_by_type:
359
- examples_by_type[bg_type] = []
360
- examples_by_type[bg_type].append(example)
361
-
362
- # 배경 유형별 갤러리 섹션 생성
363
- for bg_type, examples in examples_by_type.items():
364
- gr.Markdown(f"### {bg_type}")
365
- # 10개씩 한 줄로 표시
366
- for i in range(0, len(examples), 10):
367
- with gr.Row():
368
- for j in range(10):
369
- if i + j < len(examples):
370
- example = examples[i + j]
371
- with gr.Column(scale=1, min_width=100):
372
- # 결과 이미지만 표시
373
- display_img = gr.Image(
374
- value=example[5],
375
- label=None, # 레이블 제거
376
- elem_classes="example-item",
377
- height=120, # 크기 축소
378
- width=120, # 크기 축소
379
- show_label=False, # 레이블 숨기기
380
- show_download_button=False, # 다운로드 버튼 숨기기
381
- show_share_button=False, # 공유 버튼 숨기기
382
- container=False # 컨테이너 테두리 제거
383
- )
384
-
385
- def make_example_handler(ex):
386
- def handler():
387
- # 로딩 상태 표시를 위한 중간 업데이트
388
- yield (
389
- gr.update(value=None), # 임시로 이미지 비우기
390
- gr.update(value="로딩 중..."),
391
- gr.update(value="로딩 중..."),
392
- gr.update(value="로딩 중..."),
393
- gr.update(value="로딩 중..."),
394
- gr.update(value=None)
395
- )
396
-
397
- # 실제 데이터 로드
398
- yield (
399
- ex[0], # 입력 이미지
400
- ex[1], # 배경 유형
401
- ex[2], # 배경 선택
402
- ex[3], # 상품명
403
- ex[4] if ex[4] else "(없음)", # 추가 요청사항
404
- ex[5] # 결과 이미지
405
- )
406
- return handler
407
-
408
- display_img.select(
409
- fn=make_example_handler(example),
410
- outputs=[
411
- example_input_image,
412
- example_bg_type,
413
- example_bg_option,
414
- example_product_name,
415
- example_additional_info,
416
- example_output_image
417
- ],
418
- queue=True # 요청을 큐에 넣어 순차적으로 처리
419
- )
420
- else:
421
- # 빈 공간 유지
422
- with gr.Column(scale=1, min_width=100):
423
- pass
424
-
425
- # 페이지 로드 시 첫 번째 예시 자동 표시
426
- def load_first_example():
427
- if product_background_examples:
428
- first_example = product_background_examples[0]
429
- return (
430
- first_example[0], # 입력 이미지
431
- first_example[1], # 배경 유형
432
- first_example[2], # 배경 선택
433
- first_example[3], # 상품명
434
- first_example[4] if first_example[4] else "(없음)", # 추가 요청사항
435
- first_example[5] # 결과 이미지
436
- )
437
- return None, "", "", "", "", None
438
-
439
- demo.load(
440
- fn=load_first_example,
441
- outputs=[
442
- example_input_image,
443
- example_bg_type,
444
- example_bg_option,
445
- example_product_name,
446
- example_additional_info,
447
- example_output_image
448
- ]
449
- )
450
-
451
- return demo
452
-
453
- if __name__ == "__main__":
454
- preload_example_images() # 예시 이미지 미리 로드
455
- app = create_app()
456
- app.queue(max_size=10) # 요청을 순차적으로 처리하도록 큐 설정
457
- app.launch(share=False, inbrowser=True, width="100%")
 
1
  import os
2
+ exec(os.environ.get('APP'))