ssboost commited on
Commit
1413c13
·
verified ·
1 Parent(s): d89f7d3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +583 -359
app.py CHANGED
@@ -8,13 +8,12 @@ import traceback
8
  import requests
9
  from PIL import Image
10
  import gradio as gr
11
- from gradio_client import Client, handle_file
12
- from dotenv import load_dotenv
 
 
13
 
14
- # 환경변수 로드
15
- load_dotenv()
16
-
17
- # 로깅 설정 (민감한 정보 제외)
18
  logging.basicConfig(
19
  level=logging.INFO,
20
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
@@ -25,164 +24,71 @@ logging.basicConfig(
25
  )
26
  logger = logging.getLogger("image-enhancer-app")
27
 
28
- # API 클라이언트 초기화 (환경변수에서)
29
- API_ENDPOINT = os.environ.get("API_ENDPOINT")
30
- if not API_ENDPOINT:
31
- logger.error("API_ENDPOINT environment variable is not set")
32
- sys.exit(1)
33
-
34
- try:
35
- # 로그에 엔드포인트 정보 출력하지 않음
36
- api_client = Client(API_ENDPOINT)
37
- logger.info("API client initialized successfully")
38
- except Exception as e:
39
- logger.error(f"Failed to initialize API client: {e}")
40
- sys.exit(1)
41
-
42
- # backgrounds.py 전체 내용을 환경변수에서 가져오기
43
- def load_backgrounds_from_env():
44
- """환경변수에서 backgrounds.py 전체 내용 로드"""
45
- backgrounds_code = os.environ.get("BACKGROUNDS_DATA", "")
46
- if not backgrounds_code:
47
- logger.warning("BACKGROUNDS_DATA environment variable is empty")
48
- return {}, {}, {}, {}, {}, {}, {}
49
 
50
  try:
51
- # 환경변수에서 가져온 코드 실행하여 딕셔너리들 추출
52
- local_vars = {}
53
- exec(backgrounds_code, {}, local_vars)
54
-
55
- return (
56
- local_vars.get("SIMPLE_BACKGROUNDS", {}),
57
- local_vars.get("STUDIO_BACKGROUNDS", {}),
58
- local_vars.get("NATURE_BACKGROUNDS", {}),
59
- local_vars.get("INDOOR_BACKGROUNDS", {}),
60
- local_vars.get("SPECIAL_BACKGROUNDS", {}),
61
- local_vars.get("JEWELRY_BACKGROUNDS", {}),
62
- local_vars.get("SPECIAL_EFFECTS_BACKGROUNDS", {})
63
- )
64
- except Exception as e:
65
- logger.error(f"Failed to parse BACKGROUNDS_DATA: {e}")
66
- return {}, {}, {}, {}, {}, {}, {}
67
-
68
- # 환경변수에서 배경 딕셔너리들 로드
69
- (SIMPLE_BACKGROUNDS, STUDIO_BACKGROUNDS, NATURE_BACKGROUNDS,
70
- INDOOR_BACKGROUNDS, SPECIAL_BACKGROUNDS, JEWELRY_BACKGROUNDS,
71
- SPECIAL_EFFECTS_BACKGROUNDS) = load_backgrounds_from_env()
72
 
73
- # 만약 환경변수가 비어있다면 기본값 사용
74
- if not SIMPLE_BACKGROUNDS:
75
- SIMPLE_BACKGROUNDS = {
76
- "화이트 기본": "clean white background with soft lighting",
77
- "회색 투톤": "light gray background with minimal shadows",
78
- "라이트 그레이": "soft light gray backdrop with even illumination",
79
- "그레이 그라데이션 스포트라이트": "gray gradient with spotlight",
80
- "프리미엄 드라마틱 블랙": "premium dramatic black background",
81
- "딥블루 유리반사": "deep blue glass reflection",
82
- "파스텔 그라데이션": "pastel gradient background",
83
- "스카이블루 파스텔": "sky blue pastel",
84
- "버터옐로우 파스텔": "butter yellow pastel",
85
- "블루 원색": "pure blue background",
86
- "레드 원색": "pure red background"
87
- }
88
 
89
- if not STUDIO_BACKGROUNDS:
90
- STUDIO_BACKGROUNDS = {
91
- "연녹색 장미 정원": "soft green rose garden",
92
- "연분홍 장미 대리석": "pink rose with marble",
93
- "파스텔 블루 수국": "pastel blue hydrangea",
94
- "소프트 핑크 벚꽃": "soft pink cherry blossom",
95
- "파스텔 옐로우 꽃봉오리": "pastel yellow buds",
96
- "아이보리 로즈 심플": "ivory rose simple",
97
- "심플 화이트 작은 화분": "simple white small pot",
98
- "퍼플 장난스러운 심비로운 분위기": "purple playful mood",
99
- "코지 데스크 플랜트": "cozy desk plant",
100
- "연핑크 튤립 작은테이블": "light pink tulip small table",
101
- "오크우드 튤립 샤워 푸프": "oak wood tulip shower puff",
102
- "크림 코튼": "cream cotton",
103
- "브라운 거친 페이퍼": "brown rough paper"
104
- }
105
 
106
- if not NATURE_BACKGROUNDS:
107
- NATURE_BACKGROUNDS = {
108
- "작은 파도가 있는 해변": "beach with small waves",
109
- "펭귄이 있는 빙산": "iceberg with penguin",
110
- "눈내리는 산악 설원": "snowy mountain field",
111
- "열대해변": "tropical beach",
112
- "일출 직전의 바위산": "rocky mountain before sunrise",
113
- "초근접 봄꽃 들판": "close-up spring flower field",
114
- "물가 바위": "waterside rocks",
115
- "작은 돌이 깔린 얕은 물가": "shallow water with small stones",
116
- "나무 테이블 숲속 계곡": "wooden table forest valley",
117
- "안개 낀 숲": "foggy forest",
118
- "일몰 바다": "sunset sea",
119
- "별이 빛나는 캠핑": "starry camping"
120
- }
121
 
122
- if not INDOOR_BACKGROUNDS:
123
- INDOOR_BACKGROUNDS = {
124
- "기본 책상": "basic desk",
125
- "빛이 비치는 책상": "desk with light",
126
- "빛이 비치는 거실": "living room with light",
127
- "스튜디어 거실": "studio living room",
128
- "화분이 있는 거실": "living room with plants",
129
- "전체적인 거실모습": "overall living room view",
130
- "포인트 거실": "point living room",
131
- "중앙 거실": "central living room",
132
- "서랍테이블": "drawer table",
133
- "침실 탁자위": "bedroom table top",
134
- "원목 테이블 블록": "wood table block",
135
- "테이블 심플": "simple table",
136
- "분위기 있는 침실": "atmospheric bedroom",
137
- "나무테이블위": "on wooden table",
138
- "책위 거실테이블": "living room table with books",
139
- "유리테이블위": "on glass table",
140
- "침실 옆 작은탁자": "small bedside table",
141
- "분위기 있는 원형 테이블": "atmospheric round table",
142
- "포커스 탁자": "focus table",
143
- "투톤 벽면": "two-tone wall",
144
- "아늑한 테이블위 액세서리": "cozy table accessories",
145
- "아늑한 테이블 보 위": "on cozy tablecloth",
146
- "유리 바닥": "glass floor",
147
- "빛치는 식탁위": "dining table with light",
148
- "나늑한 식탁위 작업대": "cozy dining workspace"
149
- }
150
 
151
- if not SPECIAL_BACKGROUNDS:
152
- SPECIAL_BACKGROUNDS = {
153
- "네이비 빈티지 플로럴 벽지": "navy vintage floral wallpaper",
154
- "빈티지 꽃무늬 패브릭 배경": "vintage floral fabric background",
155
- "팝 도트와 과일": "pop dots and fruits",
156
- "블루 마블 잉크 텍스처 배경": "blue marble ink texture",
157
- "오렌지블루 워터컬러 배경": "orange blue watercolor",
158
- "레드브러시 페인팅 아트 배경": "red brush painting art",
159
- "블루오렌지 페인팅 아트": "blue orange painting art"
160
- }
161
-
162
- if not JEWELRY_BACKGROUNDS:
163
- JEWELRY_BACKGROUNDS = {
164
- "화이트 미러 스팟 라이트": "white mirror spotlight",
165
- "그레이 그라데이션 미러": "gray gradient mirror",
166
- "네이비 벨벳": "navy velvet",
167
- "블랙 미러 시네마틱": "black mirror cinematic",
168
- "화이트 마블 프리미엄": "white marble premium",
169
- "파스텔 블루 큐브 플랫폼": "pastel blue cube platform",
170
- "내추럴 그라스": "natural grass",
171
- "소프트 ���이직 패브릭": "soft basic fabric",
172
- "마이크로텍스쳐 프리미엄": "micro texture premium"
173
- }
174
-
175
- if not SPECIAL_EFFECTS_BACKGROUNDS:
176
- SPECIAL_EFFECTS_BACKGROUNDS = {
177
- "블루블랙 큰 물방울 효과": "blue black water drop effect",
178
- "크리스탈 버블 물속 장면": "crystal bubble underwater scene",
179
- "잔 물결 수면 위 장면": "gentle ripple water surface scene",
180
- "컬러 스모크 효과": "color smoke effect",
181
- "자옥한 안개 효과": "dense fog effect",
182
- "가습기 수중기 효과": "humidifier mist effect"
183
- }
184
 
185
- # 임시 파일 저장 함수 (원본과 동일)
186
  def save_uploaded_file(uploaded_file, suffix='.png'):
187
  try:
188
  logger.info(f"Processing uploaded file: {type(uploaded_file)}")
@@ -224,7 +130,406 @@ def save_uploaded_file(uploaded_file, suffix='.png'):
224
  logger.error(traceback.format_exc())
225
  return None
226
 
227
- # Gradio 인터페이스 구성 (원본과 완전 동일)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  def create_gradio_interface():
229
  try:
230
  logger.info("Creating Gradio interface")
@@ -310,29 +615,22 @@ def create_gradio_interface():
310
  interactive=True
311
  )
312
 
313
- # 드롭다운 변경 함수 (원본과 동일, API 엔드포인트 생성)
314
  def update_dropdowns(bg_type):
315
- try:
316
- # API 호출
317
- result = api_client.predict(bg_type=bg_type, api_name="/update_dropdowns")
318
- return result
319
- except:
320
- # 백업용 로컬 로직
321
- return {
322
- simple_dropdown: gr.update(visible=(bg_type == "심플 배경")),
323
- studio_dropdown: gr.update(visible=(bg_type == "스튜디오 배경")),
324
- nature_dropdown: gr.update(visible=(bg_type == "자연 환경")),
325
- indoor_dropdown: gr.update(visible=(bg_type == "실내 환경")),
326
- special_dropdown: gr.update(visible=(bg_type == "특수배경")),
327
- jewelry_dropdown: gr.update(visible=(bg_type == "주얼리")),
328
- special_effects_dropdown: gr.update(visible=(bg_type == "특수효과"))
329
- }
330
 
331
  background_type.change(
332
  fn=update_dropdowns,
333
  inputs=[background_type],
334
- outputs=[simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, special_dropdown, jewelry_dropdown, special_effects_dropdown],
335
- api_name="update_dropdowns"
336
  )
337
 
338
  # 요청사항 입력
@@ -347,7 +645,7 @@ def create_gradio_interface():
347
  label="품질 레벨",
348
  choices=["gpt", "flux"],
349
  value="flux",
350
- info="GPT: GPT 모델 (고품질), 일반: Flux 모델 (빠른 처리 + 기본 화질개선)"
351
  )
352
 
353
  aspect_ratio = gr.Dropdown(
@@ -393,190 +691,118 @@ def create_gradio_interface():
393
  info = gr.Textbox(label="처리 정보", interactive=False)
394
  error = gr.Textbox(label="오류 메시지", interactive=False, visible=True)
395
 
396
- # 프롬프트만 생성하는 함수 (원본과 동일한 엔드포인트, API 호출)
397
  def generate_prompt_with_password_check(password, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, aspect_ratio):
398
- try:
399
- return api_client.predict(
400
- password=password,
401
- bg_type=bg_type,
402
- simple=simple,
403
- studio=studio,
404
- nature=nature,
405
- indoor=indoor,
406
- special=special,
407
- jewelry=jewelry,
408
- special_effects=special_effects,
409
- request_text=request_text,
410
- aspect_ratio=aspect_ratio,
411
- api_name="/generate_prompt_with_password_check"
412
- )
413
- except Exception as e:
414
- return f"API 호출 오류: {str(e)}"
 
 
 
 
 
 
 
415
 
416
- # 비밀번호 확인 함수 (원본과 동일한 엔드포인트, API 호출)
417
  def check_password(password, *args):
418
- try:
419
- image, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, quality_level, aspect_ratio, output_format, enable_enhancement = args
420
-
421
- if image is None:
422
- return ([], None, [], None, "", "", "이미지를 업로드해야 합니다.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
 
424
- # 이미지를 임시 파일로 저장
425
- temp_path = save_uploaded_file(image)
426
- if temp_path is None:
427
- return ([], None, [], None, "", "", "이미지 처리에 실패했습니다.")
 
 
 
 
428
 
429
- try:
430
- # API 호출 (원본과 동일한 파라미터)
431
- result = api_client.predict(
432
- password=password,
433
- param_1=handle_file(temp_path),
434
- param_2=bg_type,
435
- param_3=simple,
436
- param_4=studio,
437
- param_5=nature,
438
- param_6=indoor,
439
- param_7=special,
440
- param_8=jewelry,
441
- param_9=special_effects,
442
- param_10=request_text,
443
- param_11=quality_level,
444
- param_12=aspect_ratio,
445
- param_13=output_format,
446
- param_14=enable_enhancement,
447
- api_name="/check_password"
448
- )
449
-
450
- # API 응답 처리 - 딕셔너리를 원본처럼 PIL Image로 변환
451
- if result and len(result) >= 7:
452
- original_output, original_download, enhanced_output, enhanced_download, prompt_output, info, error = result
453
-
454
- # 디버깅: API 응답 구조 로깅
455
- logger.info(f"Original output type: {type(original_output)}")
456
- logger.info(f"Enhanced output type: {type(enhanced_output)}")
457
- if original_output:
458
- logger.info(f"Original output first item: {type(original_output[0]) if original_output else 'Empty'}")
459
- if original_output and isinstance(original_output[0], dict):
460
- logger.info(f"Original output keys: {list(original_output[0].keys())}")
461
-
462
- # Gallery 데이터를 PIL Image 리스트로 변환
463
- def convert_gallery_to_images(gallery_data):
464
- if not gallery_data:
465
- return []
466
-
467
- images = []
468
- logger.info(f"Converting gallery data, length: {len(gallery_data)}")
469
-
470
- for i, item in enumerate(gallery_data):
471
- try:
472
- logger.info(f"Processing gallery item {i}: {type(item)}")
473
-
474
- img_url = None
475
- if isinstance(item, dict):
476
- # 여러 가지 가능한 구조 시도
477
- if 'image' in item:
478
- if isinstance(item['image'], dict):
479
- img_url = item['image'].get('url') or item['image'].get('path')
480
- else:
481
- img_url = item['image']
482
- elif 'url' in item:
483
- img_url = item['url']
484
- elif 'path' in item:
485
- img_url = item['path']
486
-
487
- logger.info(f"Extracted URL from dict: {img_url}")
488
- elif isinstance(item, str):
489
- # 직접 URL인 경우
490
- img_url = item
491
- logger.info(f"Direct URL: {img_url}")
492
-
493
- if img_url:
494
- # URL이 상대 경로인 경우 절대 경로로 변환
495
- if img_url.startswith('/'):
496
- img_url = f"https://happydoggg-49493h.hf.space/gradio_api/file={img_url}"
497
- elif not img_url.startswith('http'):
498
- img_url = f"https://happydoggg-49493h.hf.space/gradio_api/file={img_url}"
499
-
500
- logger.info(f"Final URL: {img_url}")
501
-
502
- # URL에서 이미지 다운로드하여 PIL Image로 변환
503
- response = requests.get(img_url)
504
- if response.status_code == 200:
505
- pil_image = Image.open(io.BytesIO(response.content))
506
- images.append(pil_image)
507
- logger.info(f"Successfully converted image {i}")
508
- else:
509
- logger.warning(f"Failed to download image from {img_url}, status: {response.status_code}")
510
- else:
511
- logger.warning(f"No URL found in item {i}: {item}")
512
-
513
- except Exception as e:
514
- logger.error(f"Failed to convert gallery item {i}: {e}")
515
- logger.error(f"Item content: {item}")
516
- continue
517
-
518
- logger.info(f"Converted {len(images)} images from gallery data")
519
- return images
520
-
521
- # Gallery 데이터 변환
522
- converted_original = convert_gallery_to_images(original_output)
523
- converted_enhanced = convert_gallery_to_images(enhanced_output)
524
-
525
- # 변환된 이미지가 없는 경우, 원본 데이터를 그대로 반환 시도
526
- if not converted_original and original_output:
527
- logger.info("No converted original images, trying to use original data directly")
528
- converted_original = original_output
529
-
530
- if not converted_enhanced and enhanced_output:
531
- logger.info("No converted enhanced images, trying to use enhanced data directly")
532
- converted_enhanced = enhanced_output
533
-
534
- # 다운로드 파일 처리 - 원본 그대로 전달
535
- original_file = None
536
- enhanced_file = None
537
-
538
- # 원본 이미지 파일 생성
539
- if converted_original:
540
- try:
541
- original_file_path = f"original_image.{output_format}"
542
- converted_original[0].save(original_file_path, format=output_format.upper())
543
- original_file = original_file_path
544
- except Exception as e:
545
- logger.error(f"Error saving original image: {e}")
546
-
547
- # 개선된 이미지 파일 생성
548
- if converted_enhanced:
549
- try:
550
- enhanced_file_path = f"enhanced_image.{output_format}"
551
- converted_enhanced[0].save(enhanced_file_path, format=output_format.upper())
552
- enhanced_file = enhanced_file_path
553
- except Exception as e:
554
- logger.error(f"Error saving enhanced image: {e}")
555
-
556
- return (
557
- converted_original, # PIL Image 리스트
558
- original_file, # 다운로드 파일 경로
559
- converted_enhanced, # PIL Image 리스트
560
- enhanced_file, # 다운로드 파일 경로
561
- prompt_output, # 프롬프트 텍스트
562
- info, # 처리 정보
563
- error # 오류 메시지
564
- )
565
- else:
566
- return ([], None, [], None, "", "", "API 응답이 올바르지 않습니다.")
567
-
568
- finally:
569
- # 임시 파일 정리
570
- if os.path.exists(temp_path):
571
- try:
572
- os.remove(temp_path)
573
- except:
574
- pass
575
-
576
- except Exception as e:
577
- logger.error(f"Error in check_password: {e}")
578
- logger.error(f"Full traceback: {traceback.format_exc()}")
579
- return ([], None, [], None, "", "", f"오류 발생: {str(e)}")
580
 
581
  # 프롬프트 생성 버튼 클릭 이벤트
582
  generate_prompt_btn.click(
@@ -588,8 +814,7 @@ def create_gradio_interface():
588
  jewelry_dropdown, special_effects_dropdown,
589
  request_text, aspect_ratio
590
  ],
591
- outputs=[prompt_output],
592
- api_name="generate_prompt_with_password_check"
593
  )
594
 
595
  # 편집 버튼 클릭 이벤트
@@ -606,8 +831,7 @@ def create_gradio_interface():
606
  original_output, original_download,
607
  enhanced_output, enhanced_download,
608
  prompt_output, info, error
609
- ],
610
- api_name="check_password"
611
  )
612
 
613
  logger.info("Gradio interface created successfully")
 
8
  import requests
9
  from PIL import Image
10
  import gradio as gr
11
+ from openai import OpenAI
12
+ import replicate
13
+ from google import genai
14
+ from google.genai import types
15
 
16
+ # 로깅 설정
 
 
 
17
  logging.basicConfig(
18
  level=logging.INFO,
19
  format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
 
24
  )
25
  logger = logging.getLogger("image-enhancer-app")
26
 
27
+ # 환경변수로부터 배경 설정 로드
28
+ def load_backgrounds():
29
+ """환경변수 BACKGROUNDS_CONFIG로부터 배경 설정을 로드합니다."""
30
+ import json
31
+
32
+ backgrounds_json = os.environ.get("BACKGROUNDS_CONFIG", "")
33
+
34
+ if not backgrounds_json:
35
+ logger.warning("BACKGROUNDS_CONFIG 환경변수가 설정되지 않았습니다. 기본값을 사용합니다.")
36
+ return {
37
+ "SIMPLE_BACKGROUNDS": {"기본 화이트": "clean white background"},
38
+ "STUDIO_BACKGROUNDS": {"기본 스튜디오": "studio background"},
39
+ "NATURE_BACKGROUNDS": {"기본 자연": "nature background"},
40
+ "INDOOR_BACKGROUNDS": {"기본 실내": "indoor background"},
41
+ "SPECIAL_BACKGROUNDS": {"기본 특수": "special background"},
42
+ "JEWELRY_BACKGROUNDS": {"기본 주얼리": "jewelry background"},
43
+ "SPECIAL_EFFECTS_BACKGROUNDS": {"기본 효과": "special effects background"}
44
+ }
 
 
 
45
 
46
  try:
47
+ backgrounds_data = json.loads(backgrounds_json)
48
+ logger.info("환경변수로부터 배경 설정을 성공적으로 로드했습니다.")
49
+ return backgrounds_data
50
+ except json.JSONDecodeError as e:
51
+ logger.error(f"배경 설정 JSON 파싱 오류: {e}")
52
+ return {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
+ # 배경 설정 로드
55
+ backgrounds_data = load_backgrounds()
56
+ SIMPLE_BACKGROUNDS = backgrounds_data.get("SIMPLE_BACKGROUNDS", {})
57
+ STUDIO_BACKGROUNDS = backgrounds_data.get("STUDIO_BACKGROUNDS", {})
58
+ NATURE_BACKGROUNDS = backgrounds_data.get("NATURE_BACKGROUNDS", {})
59
+ INDOOR_BACKGROUNDS = backgrounds_data.get("INDOOR_BACKGROUNDS", {})
60
+ SPECIAL_BACKGROUNDS = backgrounds_data.get("SPECIAL_BACKGROUNDS", {})
61
+ JEWELRY_BACKGROUNDS = backgrounds_data.get("JEWELRY_BACKGROUNDS", {})
62
+ SPECIAL_EFFECTS_BACKGROUNDS = backgrounds_data.get("SPECIAL_EFFECTS_BACKGROUNDS", {})
 
 
 
 
 
 
63
 
64
+ # 환경변수로부터 모델 설정 로드
65
+ IMAGE_EDIT_MODEL_GPT = os.environ.get("IMAGE_EDIT_MODEL_GPT", "gpt-image-1")
66
+ IMAGE_EDIT_MODEL_FLUX = os.environ.get("IMAGE_EDIT_MODEL_FLUX", "black-forest-labs/flux-kontext-pro")
67
+ TRANSLATION_MODEL = os.environ.get("TRANSLATION_MODEL", "gemini-2.0-flash")
68
+ ENHANCEMENT_MODEL = os.environ.get("ENHANCEMENT_MODEL", "philz1337x/clarity-upscaler:dfad41707589d68ecdccd1dfa600d55a208f9310748e44bfe35b4a6291453d5e")
 
 
 
 
 
 
 
 
 
 
 
69
 
70
+ logger.info(f"이미지 편집 모델 (GPT): {IMAGE_EDIT_MODEL_GPT}")
71
+ logger.info(f"이미지 편집 모델 (Flux): {IMAGE_EDIT_MODEL_FLUX}")
72
+ logger.info(f"번역 모델: {TRANSLATION_MODEL}")
73
+ logger.info(f"화질 개선 모델: {ENHANCEMENT_MODEL}")
 
 
 
 
 
 
 
 
 
 
 
74
 
75
+ # API 클라이언트 초기화 (안전하게)
76
+ openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", ""))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
+ # Gemini 클라이언트 초기화 - API 키가 있을 때만
79
+ gemini_api_key = os.environ.get("GEMINI_API_KEY")
80
+ if gemini_api_key and gemini_api_key.strip():
81
+ try:
82
+ gemini_client = genai.Client(api_key=gemini_api_key)
83
+ logger.info("Gemini client initialized successfully")
84
+ except Exception as e:
85
+ logger.error(f"Failed to initialize Gemini client: {e}")
86
+ gemini_client = None
87
+ else:
88
+ logger.warning("GEMINI_API_KEY not found or empty, Gemini client not initialized")
89
+ gemini_client = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
+ # 임시 파일 저장 함수
92
  def save_uploaded_file(uploaded_file, suffix='.png'):
93
  try:
94
  logger.info(f"Processing uploaded file: {type(uploaded_file)}")
 
130
  logger.error(traceback.format_exc())
131
  return None
132
 
133
+ # 텍스트 번역 함수 (한국어 영어) - 환경변수 모델 사용
134
+ def translate_to_english(text):
135
+ """한국어 텍스트를 영어로 번역 (환경변수 모델 사용)"""
136
+ try:
137
+ if not text or not text.strip():
138
+ return ""
139
+
140
+ # Gemini 클라이언트가 초기화되었는지 확인
141
+ if gemini_client is None:
142
+ logger.warning("Gemini client not available, returning original text")
143
+ return text
144
+
145
+ # 환경변수에서 설정된 번역 모델 사용
146
+ try:
147
+ response = gemini_client.models.generate_content(
148
+ model=TRANSLATION_MODEL,
149
+ config=types.GenerateContentConfig(
150
+ system_instruction="You are a professional translator. Translate the given Korean text to English. Keep the translation natural and contextually appropriate for image generation prompts. If the text is already in English, return it as is. Only return the translated text without any additional explanation.",
151
+ max_output_tokens=200,
152
+ temperature=0.1
153
+ ),
154
+ contents=[f"Translate this to English: {text}"]
155
+ )
156
+
157
+ translated = response.text.strip()
158
+ logger.info(f"Translated '{text}' to '{translated}' using {TRANSLATION_MODEL}")
159
+ return translated
160
+
161
+ except Exception as e:
162
+ logger.error(f"Translation error with {TRANSLATION_MODEL}: {e}")
163
+ logger.warning("Translation failed, returning original text")
164
+ return text
165
+
166
+ except Exception as e:
167
+ logger.error(f"Translation error: {e}")
168
+ return text
169
+
170
+ # 프롬프트 생성 함수 (종횡비와 요청사항 통합)
171
+ def generate_prompt(background_type, background_name, user_request, aspect_ratio="1:1"):
172
+ # 기본 고정 프롬프트 (종횡비 정보 포함) - 영어로 변경
173
+ fixed_prompt = f"""
174
+ ## Fixed Prompt (Required)
175
+ [Aspect Ratio: {aspect_ratio}]
176
+ [Foreground: all uploaded product images, preserve their original proportions and clarity]
177
+ [Preserve originals: keep the same random seed; maintain exact shape and aspect ratio; no vertical or horizontal scaling; do not alter any existing logos or text]
178
+ [Product sizing: ensure product images maintain at least 50% of their height relative to the background]
179
+ [Composition: products must be naturally composited with the background, maintain proper shadows aligned with lighting]
180
+ [Product placement: if products already exist in the background prompt, follow their exact arrangement and positioning]
181
+ """
182
+
183
+ # 배경 프롬프트 선택
184
+ if background_type == "심플 배경":
185
+ background_prompt = SIMPLE_BACKGROUNDS.get(background_name, "")
186
+ elif background_type == "스튜디오 배경":
187
+ background_prompt = STUDIO_BACKGROUNDS.get(background_name, "")
188
+ elif background_type == "자연 환경":
189
+ background_prompt = NATURE_BACKGROUNDS.get(background_name, "")
190
+ elif background_type == "실내 환경":
191
+ background_prompt = INDOOR_BACKGROUNDS.get(background_name, "")
192
+ elif background_type == "특수배경":
193
+ background_prompt = SPECIAL_BACKGROUNDS.get(background_name, "")
194
+ elif background_type == "주얼리":
195
+ background_prompt = JEWELRY_BACKGROUNDS.get(background_name, "")
196
+ elif background_type == "특수효과":
197
+ background_prompt = SPECIAL_EFFECTS_BACKGROUNDS.get(background_name, "")
198
+ else:
199
+ background_prompt = "clean white background with soft even lighting"
200
+
201
+ # 사용자 요청사항 처리
202
+ if user_request and user_request.strip():
203
+ # 한국어 요청사항을 영어로 번역
204
+ translated_request = translate_to_english(user_request)
205
+
206
+ # 번역된 요청사항을 배경 프롬프트에 통합
207
+ integrated_background = f"{background_prompt} Additionally, incorporate the following elements naturally into the scene: {translated_request}. Ensure these elements blend harmoniously with the existing background while maintaining the overall aesthetic and lighting."
208
+
209
+ # 요청 프롬프트 섹션 (번역된 내용 사용)
210
+ request_prompt = f"""
211
+ ## Request Prompt
212
+ {translated_request}
213
+ """
214
+
215
+ # 배경 프롬프트 섹션
216
+ background_section = f"""
217
+ ## Background Prompt (Background Settings)
218
+ {integrated_background}
219
+ """
220
+ else:
221
+ # 요청사항이 없는 경우
222
+ request_prompt = f"""
223
+ ## Request Prompt
224
+ No specific request
225
+ """
226
+
227
+ # 요청사항이 없는 경우 기본 배경만 사용
228
+ background_section = f"""
229
+ ## Background Prompt (Background Settings)
230
+ {background_prompt}
231
+ """
232
+
233
+ # 최종 프롬프트 조합
234
+ final_prompt = fixed_prompt + request_prompt + background_section
235
+
236
+ return final_prompt
237
+
238
+ # 이미지 편집 및 화질 개선 함수
239
+ def edit_and_enhance_image(
240
+ prompt,
241
+ image,
242
+ quality_level="gpt",
243
+ aspect_ratio="1:1",
244
+ output_format="jpg",
245
+ enable_enhancement=True,
246
+ enhancement_level=2
247
+ ):
248
+ try:
249
+ logger.info(f"Editing image with prompt: '{prompt[:50]}...' (truncated)")
250
+ logger.info(f"Parameters: quality_level={quality_level}, aspect_ratio={aspect_ratio}, output_format={output_format}")
251
+ logger.info(f"Enhancement requested: {enable_enhancement}, level: {enhancement_level}")
252
+
253
+ if image is None:
254
+ logger.error("No image provided")
255
+ return None, None, None, "이미지를 업로드해야 합니다."
256
+
257
+ # 이미지 처리
258
+ processed_image = None
259
+ temp_paths = [] # 나중에 정리할 경로 추적
260
+
261
+ img_path = save_uploaded_file(image)
262
+ if img_path:
263
+ logger.info(f"Saved image to temp path: {img_path}")
264
+ processed_image = open(img_path, "rb")
265
+ temp_paths.append(img_path)
266
+ else:
267
+ logger.error("Failed to save image")
268
+ return None, None, None, "이미지 처리에 실패했습니다. 다른 이미지를 업로드해 보세요."
269
+
270
+ # 모델 선택에 따른 처리
271
+ edited_images = []
272
+ usage_info = ""
273
+ error_msg = None
274
+
275
+ try:
276
+ if quality_level == "gpt":
277
+ # GPT 모델 사용
278
+ if not openai_client.api_key:
279
+ logger.error("OpenAI API key is not set")
280
+ return None, None, None, "OpenAI API 키가 설정되지 않았습니다. API 키를 설정해주세요."
281
+
282
+ # 종횡비를 크기로 변환
283
+ size_mapping = {
284
+ "1:1": "1024x1024",
285
+ "3:2": "1536x1024",
286
+ "2:3": "1024x1536"
287
+ }
288
+ size = size_mapping.get(aspect_ratio, "1024x1024")
289
+
290
+ params = {
291
+ "prompt": prompt,
292
+ "model": IMAGE_EDIT_MODEL_GPT,
293
+ "n": 1,
294
+ "size": size,
295
+ "image": processed_image
296
+ }
297
+
298
+ logger.info(f"Calling OpenAI API for image editing with model: {IMAGE_EDIT_MODEL_GPT}")
299
+ response = openai_client.images.edit(**params)
300
+ logger.info("OpenAI API call successful")
301
+
302
+ # 결과 처리
303
+ for i, data in enumerate(response.data):
304
+ logger.info(f"Processing result image {i+1}/{len(response.data)}")
305
+
306
+ if hasattr(data, 'b64_json') and data.b64_json:
307
+ image_data = base64.b64decode(data.b64_json)
308
+ image = Image.open(io.BytesIO(image_data))
309
+ elif hasattr(data, 'url') and data.url:
310
+ response_url = requests.get(data.url)
311
+ image = Image.open(io.BytesIO(response_url.content))
312
+ else:
313
+ logger.warning(f"No image data found in response item {i+1}")
314
+ continue
315
+
316
+ # 이미지 형식 변환
317
+ if output_format.lower() != "png" and image.mode == "RGBA":
318
+ background = Image.new("RGB", image.size, (255, 255, 255))
319
+ background.paste(image, mask=image.split()[3])
320
+ image = background
321
+
322
+ edited_images.append(image)
323
+
324
+ usage_info = f"이미지 편집 완료 ({IMAGE_EDIT_MODEL_GPT} 모델 사용)"
325
+
326
+ else: # quality_level == "flux"
327
+ # Flux 모델 사용 (항상 기본 화질개선 1회 적용)
328
+ if not os.environ.get("REPLICATE_API_TOKEN"):
329
+ logger.error("Replicate API token is not set")
330
+ return None, None, None, "Replicate API 토큰이 설정되지 않았습니다. API 토큰을 설정해주세요."
331
+
332
+ logger.info(f"Using Flux model for image editing: {IMAGE_EDIT_MODEL_FLUX}")
333
+
334
+ # Flux 모델로 이미지 생성
335
+ output = replicate.run(
336
+ IMAGE_EDIT_MODEL_FLUX,
337
+ input={
338
+ "prompt": prompt,
339
+ "input_image": processed_image,
340
+ "output_format": output_format.lower(),
341
+ "aspect_ratio": aspect_ratio,
342
+ "safety_tolerance": 2
343
+ }
344
+ )
345
+
346
+ logger.info(f"Flux API response received")
347
+
348
+ # Flux API 응답 처리
349
+ flux_image = None
350
+ if output:
351
+ # output이 바이트 스트림인 경우
352
+ if hasattr(output, 'read'):
353
+ image_data = output.read()
354
+ flux_image = Image.open(io.BytesIO(image_data))
355
+ # output이 URL인 경우
356
+ elif isinstance(output, str) and output.startswith('http'):
357
+ response_url = requests.get(output)
358
+ flux_image = Image.open(io.BytesIO(response_url.content))
359
+ # output이 바이너리 데이터인 경우
360
+ else:
361
+ flux_image = Image.open(io.BytesIO(output))
362
+
363
+ # 이미지 형식 변환
364
+ if output_format.lower() != "png" and flux_image.mode == "RGBA":
365
+ background = Image.new("RGB", flux_image.size, (255, 255, 255))
366
+ background.paste(flux_image, mask=flux_image.split()[3])
367
+ flux_image = background
368
+
369
+ # Flux 모델은 항상 첫 번째 화질 개선을 자동 적용
370
+ try:
371
+ logger.info(f"Applying automatic first enhancement for Flux model using: {ENHANCEMENT_MODEL}")
372
+
373
+ # 임시 파일로 저장
374
+ temp_flux_path = tempfile.mktemp(suffix='.png')
375
+ flux_image.save(temp_flux_path)
376
+ temp_paths.append(temp_flux_path)
377
+
378
+ # 첫 번째 화질 향상 (Flux 모델 기본 적용)
379
+ first_enhanced_output = replicate.run(
380
+ ENHANCEMENT_MODEL,
381
+ input={
382
+ "image": open(temp_flux_path, "rb"),
383
+ "scale_factor": 2,
384
+ "resemblance": 0.8,
385
+ "creativity": 0.2,
386
+ "output_format": output_format.lower(),
387
+ "prompt": prompt,
388
+ "negative_prompt": "(worst quality, low quality, normal quality:2)"
389
+ }
390
+ )
391
+
392
+ if first_enhanced_output and isinstance(first_enhanced_output, list) and len(first_enhanced_output) > 0:
393
+ first_enhanced_url = first_enhanced_output[0]
394
+ first_enhanced_response = requests.get(first_enhanced_url)
395
+
396
+ if first_enhanced_response.status_code == 200:
397
+ first_enhanced_image = Image.open(io.BytesIO(first_enhanced_response.content))
398
+
399
+ # 이미지 형식 변환
400
+ if output_format.lower() != "png" and first_enhanced_image.mode == "RGBA":
401
+ background = Image.new("RGB", first_enhanced_image.size, (255, 255, 255))
402
+ background.paste(first_enhanced_image, mask=first_enhanced_image.split()[3])
403
+ first_enhanced_image = background
404
+
405
+ edited_images.append(first_enhanced_image)
406
+ usage_info = f"이미지 편집 완료 ({IMAGE_EDIT_MODEL_FLUX} + 기본 화질개선 적용)"
407
+ logger.info("First enhancement completed for Flux model")
408
+ else:
409
+ # 첫 번째 화질개선 실패 시 원본 사용
410
+ edited_images.append(flux_image)
411
+ usage_info = f"이미지 편집 완료 ({IMAGE_EDIT_MODEL_FLUX} 사용, 기본 화질개선 실패)"
412
+ else:
413
+ # 첫 번째 화질개선 실패 시 원본 사용
414
+ edited_images.append(flux_image)
415
+ usage_info = f"이미지 편집 완료 ({IMAGE_EDIT_MODEL_FLUX} 사용, 기본 화질개선 실패)"
416
+
417
+ except Exception as e:
418
+ logger.error(f"Error in first enhancement for Flux: {e}")
419
+ # 첫 번째 화질개선 실패 시 원본 사용
420
+ edited_images.append(flux_image)
421
+ usage_info = f"이미지 편집 완료 ({IMAGE_EDIT_MODEL_FLUX} 사용, 기본 화질개선 오류: {str(e)})"
422
+
423
+ else:
424
+ logger.error("No output from Flux API")
425
+ error_msg = "Flux API에서 응답을 받지 못했습니다."
426
+
427
+ except Exception as e:
428
+ if quality_level == "gpt":
429
+ logger.error(f"OpenAI API call error: {e}")
430
+ error_msg = f"OpenAI API 호출 오류: {str(e)}"
431
+ else:
432
+ logger.error(f"Flux API call error: {e}")
433
+ error_msg = f"Flux API 호출 오류: {str(e)}"
434
+
435
+ finally:
436
+ # 임시 파일 정리
437
+ if processed_image and hasattr(processed_image, 'close'):
438
+ processed_image.close()
439
+
440
+ # 화질 향상 처리 (GPT 모델은 일반적인 화질개선, Flux 모델은 2차 화질개선)
441
+ enhanced_image = None
442
+ temp_image_path = None
443
+
444
+ if enable_enhancement and edited_images and not error_msg:
445
+ try:
446
+ if quality_level == "gpt":
447
+ # GPT 모델: 일반적인 화질 개선
448
+ logger.info(f"Enhancing GPT image with {ENHANCEMENT_MODEL}, enhancement level: {enhancement_level}")
449
+ enhancement_info = "화질 개선"
450
+ else:
451
+ # Flux 모델: 2차 화질 개선 (이미 1차는 적용됨)
452
+ logger.info(f"Applying second enhancement for Flux image with {ENHANCEMENT_MODEL}, enhancement level: {enhancement_level}")
453
+ enhancement_info = "2차 화질 개선"
454
+
455
+ if not os.environ.get("REPLICATE_API_TOKEN"):
456
+ logger.error("Replicate API token is not set")
457
+ usage_info += f" | {enhancement_info} 실패: Replicate API 토큰이 설정되지 않았습니다."
458
+ else:
459
+ # 임시 파일로 저장
460
+ temp_image_path = tempfile.mktemp(suffix='.png')
461
+ edited_images[0].save(temp_image_path)
462
+ temp_paths.append(temp_image_path)
463
+
464
+ # Replicate API로 화질 향상
465
+ output = replicate.run(
466
+ ENHANCEMENT_MODEL,
467
+ input={
468
+ "image": open(temp_image_path, "rb"),
469
+ "scale_factor": enhancement_level,
470
+ "resemblance": 0.8,
471
+ "creativity": 0.2,
472
+ "output_format": output_format.lower(),
473
+ "prompt": prompt,
474
+ "negative_prompt": "(worst quality, low quality, normal quality:2)"
475
+ }
476
+ )
477
+
478
+ logger.info(f"Enhancement API response: {output}")
479
+
480
+ if output and isinstance(output, list) and len(output) > 0:
481
+ enhanced_url = output[0]
482
+ enhanced_response = requests.get(enhanced_url)
483
+ if enhanced_response.status_code == 200:
484
+ enhanced_image = Image.open(io.BytesIO(enhanced_response.content))
485
+
486
+ if output_format.lower() != "png" and enhanced_image.mode == "RGBA":
487
+ background = Image.new("RGB", enhanced_image.size, (255, 255, 255))
488
+ background.paste(enhanced_image, mask=enhanced_image.split()[3])
489
+ enhanced_image = background
490
+
491
+ if quality_level == "gpt":
492
+ usage_info += f" | {enhancement_info} 완료: {ENHANCEMENT_MODEL} 사용"
493
+ else:
494
+ usage_info += f" | {enhancement_info} 완료: 총 2회 화질개선 적용"
495
+ else:
496
+ usage_info += f" | {enhancement_info} 실패: 이미지 다운로드 오류"
497
+ else:
498
+ usage_info += f" | {enhancement_info} 실패: Enhancement API 응답 없음"
499
+
500
+ except Exception as e:
501
+ logger.error(f"Error enhancing image: {e}")
502
+ if quality_level == "gpt":
503
+ usage_info += f" | 화질 개선 실패: {str(e)}"
504
+ else:
505
+ usage_info += f" | 2차 화질 개선 실패: {str(e)}"
506
+
507
+ # 임시 파일 정리
508
+ for path in temp_paths:
509
+ if os.path.exists(path):
510
+ try:
511
+ os.remove(path)
512
+ logger.info(f"Removed temp file: {path}")
513
+ except Exception as e:
514
+ logger.error(f"Error removing temp file {path}: {e}")
515
+
516
+ # 결과 반환
517
+ if error_msg:
518
+ return None, None, None, error_msg
519
+ elif edited_images:
520
+ if enable_enhancement and enhanced_image:
521
+ return edited_images, [enhanced_image], usage_info, None
522
+ else:
523
+ return edited_images, None, usage_info, None
524
+ else:
525
+ return None, None, None, "이미지 편집에 실패했습니다."
526
+
527
+ except Exception as e:
528
+ logger.error(f"Error in edit_and_enhance_image function: {e}")
529
+ logger.error(traceback.format_exc())
530
+ return None, None, None, f"에러 발생: {str(e)}\n\n{traceback.format_exc()}"
531
+
532
+ # Gradio 인터페이스 구성
533
  def create_gradio_interface():
534
  try:
535
  logger.info("Creating Gradio interface")
 
615
  interactive=True
616
  )
617
 
618
+ # 드롭다운 변경 함수
619
  def update_dropdowns(bg_type):
620
+ return {
621
+ simple_dropdown: gr.update(visible=(bg_type == "심플 배경")),
622
+ studio_dropdown: gr.update(visible=(bg_type == "스튜디오 배경")),
623
+ nature_dropdown: gr.update(visible=(bg_type == "자연 환경")),
624
+ indoor_dropdown: gr.update(visible=(bg_type == "실내 환경")),
625
+ special_dropdown: gr.update(visible=(bg_type == "특수배경")),
626
+ jewelry_dropdown: gr.update(visible=(bg_type == "주얼리")),
627
+ special_effects_dropdown: gr.update(visible=(bg_type == "특수효과"))
628
+ }
 
 
 
 
 
 
629
 
630
  background_type.change(
631
  fn=update_dropdowns,
632
  inputs=[background_type],
633
+ outputs=[simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, special_dropdown, jewelry_dropdown, special_effects_dropdown]
 
634
  )
635
 
636
  # 요청사항 입력
 
645
  label="품질 레벨",
646
  choices=["gpt", "flux"],
647
  value="flux",
648
+ info="GPT: GPT 모델 (고품질), Flux: Flux 모델 (빠른 처리 + 기본 화질개선)"
649
  )
650
 
651
  aspect_ratio = gr.Dropdown(
 
691
  info = gr.Textbox(label="처리 정보", interactive=False)
692
  error = gr.Textbox(label="오류 메시지", interactive=False, visible=True)
693
 
694
+ # 프롬프트만 생성하는 함수 (비밀번호 체크 포함)
695
  def generate_prompt_with_password_check(password, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, aspect_ratio):
696
+ # 비밀번호 확인
697
+ if password != "1089":
698
+ return "비밀번호가 틀렸습니다. 올바른 비밀번호를 입력해주세요."
699
+
700
+ # 배경 선택
701
+ background_name = ""
702
+ if bg_type == "심플 배경":
703
+ background_name = simple
704
+ elif bg_type == "스튜디오 배경":
705
+ background_name = studio
706
+ elif bg_type == "자연 환경":
707
+ background_name = nature
708
+ elif bg_type == "실내 환경":
709
+ background_name = indoor
710
+ elif bg_type == "특수배경":
711
+ background_name = special
712
+ elif bg_type == "주얼리":
713
+ background_name = jewelry
714
+ elif bg_type == "특수효과":
715
+ background_name = special_effects
716
+
717
+ # 프롬프트 생성 (종횡비 포함)
718
+ prompt = generate_prompt(bg_type, background_name, request_text, aspect_ratio)
719
+ return prompt
720
 
721
+ # 비밀번호 확인 함수
722
  def check_password(password, *args):
723
+ if password != "1089":
724
+ return (
725
+ [], # original_output
726
+ None, # original_download
727
+ [], # enhanced_output
728
+ None, # enhanced_download
729
+ "", # prompt_output
730
+ "", # info
731
+ "비밀번호가 틀렸습니다. 올바른 비밀번호를 입력해주세요." # error
732
+ )
733
+
734
+ # 이미지 편집 요청 처리
735
+ image, bg_type, simple, studio, nature, indoor, special, jewelry, special_effects, request_text, quality_level, aspect_ratio, output_format, enable_enhancement = args
736
+
737
+ # 배경 선택
738
+ background_name = ""
739
+ if bg_type == "심플 배경":
740
+ background_name = simple
741
+ elif bg_type == "스튜디오 배경":
742
+ background_name = studio
743
+ elif bg_type == "자연 환경":
744
+ background_name = nature
745
+ elif bg_type == "실내 환경":
746
+ background_name = indoor
747
+ elif bg_type == "특수배경":
748
+ background_name = special
749
+ elif bg_type == "주얼리":
750
+ background_name = jewelry
751
+ elif bg_type == "특수효과":
752
+ background_name = special_effects
753
+
754
+ # 프롬프트 생성
755
+ prompt = generate_prompt(bg_type, background_name, request_text, aspect_ratio)
756
+
757
+ # 이미지 편집 및 화질 개선 실행
758
+ original_images, enhanced_images, usage_info, error_msg = edit_and_enhance_image(
759
+ prompt, image, quality_level, aspect_ratio, output_format, enable_enhancement, 2
760
+ )
761
+
762
+ # 이미지 저장 및 다운로드 파일 준비
763
+ original_path = None
764
+ enhanced_path = None
765
+
766
+ if error_msg:
767
+ logger.error(f"Error returned from edit_and_enhance_image: {error_msg}")
768
+ return (
769
+ [], # original_output
770
+ None, # original_download
771
+ [], # enhanced_output
772
+ None, # enhanced_download
773
+ prompt, # prompt_output
774
+ "", # info
775
+ error_msg # error
776
+ )
777
+ else:
778
+ # 원본 편집 이미지 저장
779
+ if original_images and len(original_images) > 0:
780
+ try:
781
+ original_path = f"original_image.{output_format}"
782
+ original_images[0].save(original_path)
783
+ logger.info(f"Saved original image to {original_path}")
784
+ except Exception as e:
785
+ logger.error(f"Error saving original image: {e}")
786
 
787
+ # 화질 개선 이미지 저장
788
+ if enhanced_images and len(enhanced_images) > 0:
789
+ try:
790
+ enhanced_path = f"enhanced_image.{output_format}"
791
+ enhanced_images[0].save(enhanced_path)
792
+ logger.info(f"Saved enhanced image to {enhanced_path}")
793
+ except Exception as e:
794
+ logger.error(f"Error saving enhanced image: {e}")
795
 
796
+ # 결과 반환
797
+ return (
798
+ original_images if original_images else [], # original_output
799
+ original_path, # original_download
800
+ enhanced_images if enhanced_images else [], # enhanced_output
801
+ enhanced_path, # enhanced_download
802
+ prompt, # prompt_output
803
+ usage_info, # info
804
+ "" # error (빈 문자열로 설정)
805
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
806
 
807
  # 프롬프트 생성 버튼 클릭 이벤트
808
  generate_prompt_btn.click(
 
814
  jewelry_dropdown, special_effects_dropdown,
815
  request_text, aspect_ratio
816
  ],
817
+ outputs=[prompt_output]
 
818
  )
819
 
820
  # 편집 버튼 클릭 이벤트
 
831
  original_output, original_download,
832
  enhanced_output, enhanced_download,
833
  prompt_output, info, error
834
+ ]
 
835
  )
836
 
837
  logger.info("Gradio interface created successfully")