Kims12 commited on
Commit
395173a
·
verified ·
1 Parent(s): 649e6f6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +366 -365
app.py CHANGED
@@ -1,119 +1,385 @@
1
- if text_match:
2
- text_to_remove = text_match.group(1)
3
- prompt = f"첫 번째 이미지에서 '{text_to_remove}' 텍스트를 찾아 자연스럽게 제거해주세요. 텍스트가 있던 부분을 배경과 조화롭게 채워주세요."
4
- else:
5
- prompt = "첫 번째 이미지에서 모든 텍스트를 찾아 자연스럽게 제거해주세요. 깔끔한 이미지로 만들어주세요."
 
 
 
 
6
 
7
- elif "4. 옷바꾸기" in prompt:
8
- prompt = "첫 번째 이미지의 인물 의상을 두 번째 이미지의 의상으로 변경해주세요. 의상의 스타일과 색상은 두 번째 이미지를 따르되, 신체 비율과 포즈는 첫 번째 이미지를 유지해주세요."
 
9
 
10
- elif "5. 배경바꾸기" in prompt:
11
- prompt = " 번째 이미지의 배경을 두 번째 이미지의 배경으로 변경해주세요. 첫 번째 이미지의 주요 피사체는 유지하고, 두 번째 이미지의 배경과 조화롭게 합성해주세요."
12
 
13
- elif "6. 이미지 합성(상품포함)" in prompt:
14
- prompt = "첫 번째 이미지와 두 번째 이미지(또는 세 번째 이미지)를 자연스럽게 합성해주세요. 모든 이미지의 주요 요소를 포함하고, 특히 상품이 돋보이도록 조화롭게 통합해주세요."
15
 
16
- prompt += " 이미지를 생성해주세요. 이미지에 텍스트나 글자를 포함하지 마세요."
17
- return prompt
18
 
19
- def generate_with_images(prompt, images, variation_index=0):
 
 
 
 
 
 
 
 
 
20
  try:
21
- if not GEMINI_API_KEY:
22
- return None, "API 키가 설정되지 않았습니다. 환경변수를 확인해주세요."
 
 
23
 
24
- model = genai.GenerativeModel('gemini-2.0-flash-exp-image-generation')
25
- logger.info(f"Gemini API 요청 시작 - 프롬프트: {prompt}, 변형 인덱스: {variation_index}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- variation_suffixes = [
28
- " Create this as the first variation. Do not add any text, watermarks, or labels to the image.",
29
- " Create this as the second variation with more vivid colors. Do not add any text, watermarks, or labels to the image.",
30
- " Create this as the third variation with a more creative style. Do not add any text, watermarks, or labels to the image.",
31
- " Create this as the fourth variation with enhanced details. Do not add any text, watermarks, or labels to the image."
32
- ]
33
-
34
- if variation_index < len(variation_suffixes):
35
- prompt = prompt + variation_suffixes[variation_index]
36
- else:
37
- prompt = prompt + " Do not add any text, watermarks, or labels to the image."
38
 
39
- contents = [prompt]
40
- for idx, img in enumerate(images, 1):
41
- if img is not None:
42
- contents.append(img)
43
- logger.info(f"이미지 #{idx} 추가됨")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
 
 
 
 
 
 
45
  response = model.generate_content(
46
- contents=contents,
47
  generation_config=genai.GenerationConfig(
48
- temperature=1,
49
  top_p=0.95,
50
- top_k=40,
51
- max_output_tokens=8192
52
  )
53
  )
54
-
55
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
56
- temp_path = tmp.name
57
- result_text = ""
58
- image_found = False
59
-
60
- if hasattr(response, 'candidates') and response.candidates:
61
- candidate = response.candidates[0]
62
- if hasattr(candidate, 'content') and candidate.content:
63
- for part in candidate.content.parts:
64
- if hasattr(part, 'text') and part.text:
65
- result_text += part.text
66
- logger.info(f"응답 텍스트: {part.text}")
67
- elif hasattr(part, 'inline_data') and part.inline_data:
68
- save_binary_file(temp_path, part.inline_data.data)
69
- image_found = True
70
- logger.info("응답에서 이미지 추출 성공")
71
-
72
- if not image_found:
73
- return None, f"API에서 이미지를 생성하지 못했습니다. 응답 텍스트: {result_text}"
74
-
75
- result_img = Image.open(temp_path)
76
- if result_img.mode == "RGBA":
77
- result_img = result_img.convert("RGB")
78
-
79
- return result_img, f"이미지가 성공적으로 생성되었습니다. {result_text}"
80
  except Exception as e:
81
- logger.exception("이미지 생성 중 오류 발생:")
82
- return None, f"오류 발생: {str(e)}"
83
-
84
- def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0, max_retries=3):
85
- retry_count = 0
86
- last_error = None
87
-
88
- while retry_count < max_retries:
89
- try:
90
- images = [image1, image2, image3]
91
- valid_images = [img for img in images if img is not None]
92
- if not valid_images:
93
- return None, "적어도 하나의 이미지를 업로드해주세요.", ""
94
 
95
- if prompt and prompt.strip():
96
- processed_prompt = preprocess_prompt(prompt, image1, image2, image3)
97
- if re.search("[가-힣]", processed_prompt):
98
- final_prompt = translate_prompt_to_english(processed_prompt)
99
- else:
100
- final_prompt = processed_prompt
101
- else:
102
- if len(valid_images) == 1:
103
- final_prompt = "Please creatively transform this image into a more vivid and artistic version. Do not include any text or watermarks in the generated image."
104
- logger.info("Default prompt generated for single image")
105
- elif len(valid_images) == 2:
106
- final_prompt = "Please seamlessly composite these two images, integrating their key elements harmoniously into a single image. Do not include any text or watermarks in the generated image."
107
- logger.info("Default prompt generated for two images")
108
- else:
109
- final_prompt = "Please creatively composite these three images, combining their main elements into a cohesive and natural scene. Do not include any text or watermarks in the generated image."
110
- logger.info("Default prompt generated for three images")
111
 
112
- result_img, status = generate_with_images(final_prompt, valid_images, variation_index)
113
- if result_img is not None:
114
- return result_img, status, final_prompt
115
- else:
116
- last_error = status
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  retry_count += 1
118
  logger.warning(f"이미지 생성 실패, 재시도 {retry_count}/{max_retries}: {status}")
119
  time.sleep(1)
@@ -292,7 +558,7 @@ def create_app():
292
  "name": nature,
293
  "english": NATURE_BACKGROUNDS.get(nature, "natural environment")
294
  }
295
- elif bg_type == "실내 환경":
296
  return {
297
  "category": "실내 환경",
298
  "name": indoor,
@@ -404,269 +670,4 @@ API 키 설정 방법:
404
 
405
  if __name__ == "__main__":
406
  app = create_app()
407
- app.launch()import gradio as gr
408
- import google.generativeai as genai
409
- from PIL import Image
410
- import os
411
- import json
412
- import tempfile
413
- import re
414
- import time
415
- import logging
416
-
417
- # 로깅 설정
418
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
419
- logger = logging.getLogger(__name__)
420
-
421
- # Gemini API 키 설정 (환경 변수에서 가져오거나 직접 입력)
422
- GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
423
-
424
- # Gemini API 초기화
425
- genai.configure(api_key=GEMINI_API_KEY)
426
-
427
- # 배경 JSON 파일 경로 설정 - 상대 경로 사용
428
- BACKGROUNDS_DIR = "./background"
429
-
430
- # 디버깅을 위한 정보 출력
431
- print(f"현재 작업 디렉토리: {os.getcwd()}")
432
- print(f"사용 중인 배경 디렉토리 경로: {BACKGROUNDS_DIR}")
433
-
434
- # JSON 파일이 존재하지 않을 경우 디렉토리 생성
435
- if not os.path.exists(BACKGROUNDS_DIR):
436
- os.makedirs(BACKGROUNDS_DIR)
437
- print(f"배경 디렉토리를 생성했습니다: {BACKGROUNDS_DIR}")
438
- else:
439
- print(f"배경 디렉토리가 이미 존재합니다: {BACKGROUNDS_DIR}")
440
- try:
441
- for file in os.listdir(BACKGROUNDS_DIR):
442
- print(f"발견된 파일: {file}")
443
- except Exception as e:
444
- print(f"디렉토리 내용을 나열하는 중 오류 발생: {str(e)}")
445
-
446
- # JSON 파일 로드 함수
447
- def load_background_json(filename):
448
- file_path = os.path.join(BACKGROUNDS_DIR, filename)
449
- try:
450
- with open(file_path, 'r', encoding='utf-8') as f:
451
- data = json.load(f)
452
- print(f"{filename} 파일을 성공적으로 로드했습니다. {len(data)} 항목 포함.")
453
- return data
454
- except FileNotFoundError:
455
- print(f"경고: {filename} 파일을 찾을 수 없습니다. 기본값을 사용합니다.")
456
- return {}
457
- except json.JSONDecodeError:
458
- print(f"경고: {filename} 파일의 JSON 형식이 올바르지 않습니다. 기본값을 사용합니다.")
459
- return {}
460
- except Exception as e:
461
- print(f"경고: {filename} 파일 로드 중 오류 발생: {str(e)}. 기본값을 사용합니다.")
462
- return {}
463
-
464
- # 배경 데이터 로드
465
- SIMPLE_BACKGROUNDS = load_background_json("simple_backgrounds.json")
466
- STUDIO_BACKGROUNDS = load_background_json("studio_backgrounds.json")
467
- NATURE_BACKGROUNDS = load_background_json("nature_backgrounds.json")
468
- INDOOR_BACKGROUNDS = load_background_json("indoor_backgrounds.json")
469
- ABSTRACT_BACKGROUNDS = load_background_json("abstract_backgrounds.json")
470
-
471
- # 배경이 로드되지 않은 경우 기본값 설정
472
- if not SIMPLE_BACKGROUNDS:
473
- SIMPLE_BACKGROUNDS = {"화이트 배경": "white background"}
474
- if not STUDIO_BACKGROUNDS:
475
- STUDIO_BACKGROUNDS = {"제품 사진 스튜디오": "product photography studio"}
476
- if not NATURE_BACKGROUNDS:
477
- NATURE_BACKGROUNDS = {"열대 해변": "tropical beach"}
478
- if not INDOOR_BACKGROUNDS:
479
- INDOOR_BACKGROUNDS = {"모던 리빙룸": "modern living room"}
480
- if not ABSTRACT_BACKGROUNDS:
481
- ABSTRACT_BACKGROUNDS = {"네온 조명": "neon lights"}
482
-
483
- def generate_system_instruction():
484
- return """당신은 상품 이미지의 배경을 변경하기 위한 고품질 프롬프트를 생성하는 전문가입니다.
485
- 사용자가 제공하는 상품명, 배경 유형, 추가 요청사항을 바탕으로 미드저니(Midjourney)에 사용할 수 있는
486
- 상세하고 전문적인 프롬프트를 영어로 생성해주세요.
487
- 다음 가이드라인을 반드시 따라야 합니다:
488
-
489
- 1. 상품을 "#1"로 지정하여 참조합니다. (예: "skincare tube (#1)")
490
-
491
- 2. *** 매우 중요: 상품의 원래 특성(디자인, 색상, 형태, 로고, 패키지 등)은 어떤 상황에서도 절대 변경하지 않습니다. ***
492
-
493
- 3. *** 상품의 본질적 특성은 유지하되, 자연스러운 환경 통합을 위한 조명과 그림자는 허용합니다: ***
494
- - 상품 자체의 색상, 디자인, 형태, 텍스처는 절대 수정하지 않습니다.
495
- - 환경과 자연스럽게 어울리는 그림자, 주변 조명 효과는 허용됩니다.
496
- - 상품에 물방울, 응축, 금, 은과 같은 추가 요소나 물리적 효과는 적용하지 않습니다.
497
- - 환경에 어울리는 자연스러운 빛 반사, 주변 조명, 그림자는 사실적 통합감을 위해 적용할 수 있습니다.
498
-
499
- 4. 이미지 비율은 정확히 1:1(정사각형) 형식으로 지정합니다. 프롬프트에 "square format", "1:1 ratio" 또는 "aspect ratio 1:1"을 명시적으로 포함합니다.
500
-
501
- 5. 상품은 반드시 정사각형 구도의 정중앙에 배치되어야 합니다.
502
-
503
- 6. 상품을 이미지의 주요 초점으로 부각시키고, 상품의 비율이 전체 이미지에서 크게 차지하도록 합니다.
504
-
505
- 7. 상품 이미지 컷아웃(#1)의 기본 형태와 색상은 유지하면서, 선택한 환경에 자연스럽게 통합되도록 합니다.
506
-
507
- 8. 고급스러운 상업적 이미지를 위한 다음 환경 요소들을 포함하세요:
508
- - 상품과 어울리는 주변 환경/배경 요소를 추가합니다. 예를 들어, 화장품 주변에 꽃이나 허브, 음료 제품 옆에 과일, 전자제품 근처에 현대적 소품 등.
509
- - 환경의 조명 효과(림 라이트, 백라이트, 소프트박스 등)를 설명합니다.
510
- - 상품이 환경에 자연스럽게 존재하는 것처럼 보이도록 적절한 그림자와 빛 표현을 포함합니다.
511
- - 상품의 용도나 장점을 간접적으로 암시하는 배경 요소를 포함합니다.
512
- - 프로페셔널한 상업 사진 효과(선택적 피사계 심도, 소프트 포커스, 스튜디오 조명 등)를 명시합니다.
513
-
514
- 9. 프롬프트에 다음 요소들을 명시적으로 포함하세요:
515
- - "highly detailed commercial photography"
516
- - "award-winning product photography"
517
- - "professional advertising imagery"
518
- - "studio quality"
519
- - "magazine advertisement quality"
520
-
521
- 10. 배경 환경 요소를 상품 카테고리에 맞게 선택합니다:
522
- - 스킨케어 제품: 깨끗한 욕실 선반, 우아한 화장대, 스파 같은 환경 등
523
- - 음료 제품: 세련된 테이블, 파티 환경, 야외 피크닉 장면 등
524
- - 전자 제품: 세련된 작업 공간, 현대적인 거실, 미니멀한 책상 등
525
- - 패션/의류: 세련된 쇼룸, 도시 거리, 엘레강스한 라이프스타일 환경 등
526
- - 식품 제품: 깔끔한 주방, 식탁, 요리 환경 등
527
-
528
- 11. 사용자가 제공한 구체적인 배경과 추가 요청사항을 정확히 반영합니다.
529
-
530
- 12. 프롬프트는 미드저니 AI에 최적화되어야 합니다.
531
-
532
- 13. 프롬프트 끝에 "--ar 1:1 --s 750 --q 2" 파라미터를 추가하여 미드저니에서 고품질 정사각형 비율을 강제합니다.
533
-
534
- 출력 형식은 영어로 된 단일 단락의 상세한 프롬프트여야 하며, 끝에 미드저니 파라미터가 포함되어야 합니다.
535
- """
536
-
537
- def generate_prompt_with_gemini(product_name, background_info, additional_info=""):
538
- if not GEMINI_API_KEY:
539
- return "Gemini API 키가 설정되지 않았습니다. 환경 변수 GEMINI_API_KEY를 설정하거나 코드에 직접 입력하세요."
540
- try:
541
- prompt_request = f"""
542
- 상품명: {product_name}
543
- 배경 유형: {background_info.get('english', 'studio')}
544
- 배경 카테고리: {background_info.get('category', '')}
545
- 배경 이름: {background_info.get('name', '')}
546
- 추가 요청사항: {additional_info}
547
-
548
- 중요 요구사항:
549
- 1. 상품이 크게 부각되고 이미지에서 중심적인 위치를 차지하도록 프롬프트를 생성해주세요.
550
- 2. 이미지는 정확히 1:1 비율(정사각형)이어야 합니다.
551
- 3. 상품은 정사각형 프레임의 정중앙에 위치해야 합니다.
552
- 4. 상품의 디자인, 색상, 형태, 로고 등 본질적 특성은 절대 수정하지 마세요.
553
- 5. 환경과의 자연스러운 통합을 위한 조명 효과와 그림자는 포함해주세요.
554
- 6. 상품을 더 돋보이게 하는 배경 환경을 설명해주세요.
555
- 7. 고급스러운 상업 광고 품질의 이미지가 되도록 환경 설명을 해주세요.
556
- 8. 프롬프트 끝에 미드저니 파라미터 "--ar 1:1 --s 750 --q 2"를 추가해주세요.
557
-
558
- 한국어 입력 내용을 영어로 적절히 번역하여 반영해주세요.
559
- """
560
- model = genai.GenerativeModel(
561
- 'gemini-2.0-flash',
562
- system_instruction=generate_system_instruction()
563
- )
564
- response = model.generate_content(
565
- prompt_request,
566
- generation_config=genai.GenerationConfig(
567
- temperature=0.7,
568
- top_p=0.95,
569
- top_k=64,
570
- max_output_tokens=1024,
571
- )
572
- )
573
- response_text = response.text.strip()
574
- if "--ar 1:1" not in response_text:
575
- response_text = response_text.rstrip(".") + ". --ar 1:1 --s 750 --q 2"
576
- return response_text
577
- except Exception as e:
578
- return f"프롬프트 생성 중 오류가 발생했습니다: {str(e)}"
579
-
580
- # 이미지 생성에 필요한 함수들
581
- def save_binary_file(file_name, data):
582
- with open(file_name, "wb") as f:
583
- f.write(data)
584
-
585
- def translate_prompt_to_english(prompt):
586
- if not re.search("[가-힣]", prompt):
587
- return prompt
588
-
589
- prompt = prompt.replace("#1", "IMAGE_TAG_ONE")
590
- prompt = prompt.replace("#2", "IMAGE_TAG_TWO")
591
- prompt = prompt.replace("#3", "IMAGE_TAG_THREE")
592
-
593
- try:
594
- if not GEMINI_API_KEY:
595
- logger.error("Gemini API 키가 설정되지 않았습니다.")
596
- prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
597
- prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
598
- prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
599
- return prompt
600
-
601
- model = genai.GenerativeModel('gemini-2.0-flash')
602
- translation_prompt = f"""
603
- Translate the following Korean text to English:
604
-
605
- {prompt}
606
-
607
- IMPORTANT: The tokens IMAGE_TAG_ONE, IMAGE_TAG_TWO, and IMAGE_TAG_THREE are special tags
608
- and must be preserved exactly as is in your translation. Do not translate these tokens.
609
- """
610
-
611
- logger.info(f"Translation prompt: {translation_prompt}")
612
- response = model.generate_content(
613
- translation_prompt,
614
- generation_config=genai.GenerationConfig(
615
- temperature=0.2,
616
- top_p=0.95,
617
- top_k=40,
618
- max_output_tokens=512
619
- )
620
- )
621
-
622
- translated_text = response.text
623
-
624
- if translated_text.strip():
625
- translated_text = translated_text.replace("IMAGE_TAG_ONE", "#1")
626
- translated_text = translated_text.replace("IMAGE_TAG_TWO", "#2")
627
- translated_text = translated_text.replace("IMAGE_TAG_THREE", "#3")
628
- logger.info(f"Translated text: {translated_text.strip()}")
629
- return translated_text.strip()
630
- else:
631
- logger.warning("번역 결과가 없습니다. 원본 프롬프트 사용")
632
- prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
633
- prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
634
- prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
635
- return prompt
636
- except Exception as e:
637
- logger.exception("번역 중 오류 발생:")
638
- prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
639
- prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
640
- prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
641
- return prompt
642
-
643
- def preprocess_prompt(prompt, image1, image2, image3):
644
- has_img1 = image1 is not None
645
- has_img2 = image2 is not None
646
- has_img3 = image3 is not None
647
-
648
- if "#1" in prompt and not has_img1:
649
- prompt = prompt.replace("#1", "첫 번째 이미지(없음)")
650
- else:
651
- prompt = prompt.replace("#1", "첫 번째 이미지")
652
-
653
- if "#2" in prompt and not has_img2:
654
- prompt = prompt.replace("#2", "두 번째 이미지(없음)")
655
- else:
656
- prompt = prompt.replace("#2", "두 번째 이미지")
657
-
658
- if "#3" in prompt and not has_img3:
659
- prompt = prompt.replace("#3", "세 번째 이미지(없음)")
660
- else:
661
- prompt = prompt.replace("#3", "세 번째 이미지")
662
-
663
- if "1. 이미지 변경" in prompt:
664
- desc_match = re.search(r'#1을 "(.*?)"으로 바꿔라', prompt)
665
- if desc_match:
666
- description = desc_match.group(1)
667
- prompt = f"첫 번째 이미지를 {description}으로 변경해주세요. 원본 이미지의 주요 내용은 유지하되 새로운 스타일과 분위기로 재해석해주세요."
668
- else:
669
- prompt = "첫 번째 이미지를 창의적으로 변형해주세요. 더 생생하고 예술적인 버전으로 만들어주세요."
670
-
671
- elif "2. 글자지우기" in prompt:
672
- text_match = re.search(r'#1에서 "(.*?)"를 지워라', prompt)
 
1
+ import gradio as gr
2
+ import google.generativeai as genai
3
+ from PIL import Image
4
+ import os
5
+ import json
6
+ import tempfile
7
+ import re
8
+ import time
9
+ import logging
10
 
11
+ # 로깅 설정
12
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
13
+ logger = logging.getLogger(__name__)
14
 
15
+ # Gemini API 설정 (환경 변수에서 가져오거나 직접 입력)
16
+ GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
17
 
18
+ # Gemini API 초기화
19
+ genai.configure(api_key=GEMINI_API_KEY)
20
 
21
+ # 배경 JSON 파일 경로 설정 - 상대 경로 사용
22
+ BACKGROUNDS_DIR = "./background"
23
 
24
+ # 디버깅을 위한 정보 출력
25
+ print(f"현재 작업 디렉토리: {os.getcwd()}")
26
+ print(f"사용 중인 배경 디렉토리 경로: {BACKGROUNDS_DIR}")
27
+
28
+ # JSON 파일이 존재하지 않을 경우 디렉토리 생성
29
+ if not os.path.exists(BACKGROUNDS_DIR):
30
+ os.makedirs(BACKGROUNDS_DIR)
31
+ print(f"배경 디렉토리를 생성했습니다: {BACKGROUNDS_DIR}")
32
+ else:
33
+ print(f"배경 디렉토리가 이미 존재합니다: {BACKGROUNDS_DIR}")
34
  try:
35
+ for file in os.listdir(BACKGROUNDS_DIR):
36
+ print(f"발견된 파일: {file}")
37
+ except Exception as e:
38
+ print(f"디렉토리 내용을 나열하는 중 오류 발생: {str(e)}")
39
 
40
+ # JSON 파일 로드 함수
41
+ def load_background_json(filename):
42
+ file_path = os.path.join(BACKGROUNDS_DIR, filename)
43
+ try:
44
+ with open(file_path, 'r', encoding='utf-8') as f:
45
+ data = json.load(f)
46
+ print(f"{filename} 파일을 성공적으로 로드했습니다. {len(data)} 항목 포함.")
47
+ return data
48
+ except FileNotFoundError:
49
+ print(f"경고: {filename} 파일을 찾을 수 없습니다. 기본값을 사용합니다.")
50
+ return {}
51
+ except json.JSONDecodeError:
52
+ print(f"경고: {filename} 파일의 JSON 형식이 올바르지 않습니다. 기본값을 사용합니다.")
53
+ return {}
54
+ except Exception as e:
55
+ print(f"경고: {filename} 파일 로드 중 오류 발생: {str(e)}. 기본값을 사용합니다.")
56
+ return {}
57
 
58
+ # 배경 데이터 로드
59
+ SIMPLE_BACKGROUNDS = load_background_json("simple_backgrounds.json")
60
+ STUDIO_BACKGROUNDS = load_background_json("studio_backgrounds.json")
61
+ NATURE_BACKGROUNDS = load_background_json("nature_backgrounds.json")
62
+ INDOOR_BACKGROUNDS = load_background_json("indoor_backgrounds.json")
63
+ ABSTRACT_BACKGROUNDS = load_background_json("abstract_backgrounds.json")
 
 
 
 
 
64
 
65
+ # 배경이 로드되지 않은 경우 기본값 설정
66
+ if not SIMPLE_BACKGROUNDS:
67
+ SIMPLE_BACKGROUNDS = {"화이트 배경": "white background"}
68
+ if not STUDIO_BACKGROUNDS:
69
+ STUDIO_BACKGROUNDS = {"제품 사진 스튜디오": "product photography studio"}
70
+ if not NATURE_BACKGROUNDS:
71
+ NATURE_BACKGROUNDS = {"열대 해변": "tropical beach"}
72
+ if not INDOOR_BACKGROUNDS:
73
+ INDOOR_BACKGROUNDS = {"모던 리빙룸": "modern living room"}
74
+ if not ABSTRACT_BACKGROUNDS:
75
+ ABSTRACT_BACKGROUNDS = {"네온 조명": "neon lights"}
76
+
77
+ def generate_system_instruction():
78
+ return """당신은 상품 이미지의 배경을 변경하기 위한 고품질 프롬프트를 생성하는 전문가입니다.
79
+ 사용자가 제공하는 상품명, 배경 유형, 추가 요청사항을 바탕으로 미드저니(Midjourney)에 사용할 수 있는
80
+ 상세하고 전문적인 프롬프트를 영어로 생성해주세요.
81
+ 다음 가이드라인을 반드시 따라야 합니다:
82
+
83
+ 1. 상품을 "#1"로 지정하여 참조합니다. (예: "skincare tube (#1)")
84
+
85
+ 2. *** 매우 중요: 상품의 원래 특성(디자인, 색상, 형태, 로고, 패키지 등)은 어떤 상황에서도 절대 변경하지 않습니다. ***
86
+
87
+ 3. *** 상품의 본질적 특성은 유지하되, 자연스러운 환경 통합을 위한 조명과 그림자는 허용합니다: ***
88
+ - 상품 자체의 색상, 디자인, 형태, 텍스처는 절대 수정하지 않습니다.
89
+ - 환경과 자연스럽게 어울리는 그림자, 주변 조명 효과는 허용됩니다.
90
+ - 상품에 물방울, 응축, 금, 은과 같은 추가 요소나 물리적 효과는 적용하지 않습니다.
91
+ - 환경에 어울리는 자연스러운 빛 반사, 주변 조명, 그림자는 사실적 통합감을 위해 적용할 수 있습니다.
92
+
93
+ 4. 이미지 비율은 정확히 1:1(정사각형) 형식으로 지정합니다. 프롬프트에 "square format", "1:1 ratio" 또는 "aspect ratio 1:1"을 명시적으로 포함합니다.
94
+
95
+ 5. 상품은 반드시 정사각형 구도의 정중앙에 배치되어야 합니다.
96
+
97
+ 6. 상품을 이미지의 주요 초점으로 부각시키고, 상품의 비율이 전체 이미지에서 크게 차지하도록 합니다.
98
+
99
+ 7. 상품 이미지 컷아웃(#1)의 기본 형태와 색상은 유지하면서, 선택한 환경에 자연스럽게 통합되도록 합니다.
100
+
101
+ 8. 고급스러운 상업적 이미지를 위한 다음 환경 요소들을 포함하세요:
102
+ - 상품과 어울리는 주변 환경/배경 요소를 추가합니다. 예를 들어, 화장품 주변에 꽃이나 허브, 음료 제품 옆에 과일, 전자제품 근처에 현대적 소품 등.
103
+ - 환경의 조명 효과(림 라이트, 백라이트, 소프트박스 등)를 설명합니다.
104
+ - 상품이 환경에 자연스럽게 존재하는 것처럼 보이도록 적절한 그림자와 빛 표현을 포함합니다.
105
+ - 상품의 용도나 장점을 간접적으로 암시하는 배경 요소를 포함합니다.
106
+ - 프로페셔널한 상업 사진 효과(선택적 피사계 심도, 소프트 포커스, 스튜디오 조명 등)를 명시합니다.
107
+
108
+ 9. 프롬프트에 다음 요소들을 명시적으로 포함하세요:
109
+ - "highly detailed commercial photography"
110
+ - "award-winning product photography"
111
+ - "professional advertising imagery"
112
+ - "studio quality"
113
+ - "magazine advertisement quality"
114
+
115
+ 10. 배경 환경 요소를 상품 카테고리에 맞게 선택합니다:
116
+ - 스킨케어 제품: 깨끗한 욕실 선반, 우아한 화장대, 스파 같은 환경 등
117
+ - 음료 제품: 세련된 테이블, 파티 환경, 야외 피크닉 장면 등
118
+ - 전자 제품: 세련된 작업 공간, 현대적인 거실, 미니멀한 책상 등
119
+ - 패션/의류: 세련된 쇼룸, 도시 거리, 엘레강스한 라이프스타일 환경 등
120
+ - 식품 제품: 깔끔한 주방, 식탁, 요리 환경 등
121
+
122
+ 11. 사용자가 제공한 구체적인 배경과 추가 요청사항을 정확히 반영합니다.
123
+
124
+ 12. 프롬프트는 미드저니 AI에 최적화되어야 합니다.
125
+
126
+ 13. 프롬프트 끝에 "--ar 1:1 --s 750 --q 2" 파라미터를 추가하여 미드저니에서 고품질 정사각형 비율을 강제합니다.
127
+
128
+ 출력 형식은 영어로 된 단일 단락의 상세한 프롬프트여야 하며, 끝에 미드저니 파라미터가 포함되어야 합니다.
129
+ """
130
+
131
+ def generate_prompt_with_gemini(product_name, background_info, additional_info=""):
132
+ if not GEMINI_API_KEY:
133
+ return "Gemini API 키가 설정되지 않았습니다. 환경 변수 GEMINI_API_KEY를 설정하거나 코드에 직접 입력하세요."
134
+ try:
135
+ prompt_request = f"""
136
+ 상품명: {product_name}
137
+ 배경 유형: {background_info.get('english', 'studio')}
138
+ 배경 카테고리: {background_info.get('category', '')}
139
+ 배경 이름: {background_info.get('name', '')}
140
+ 추가 요청사항: {additional_info}
141
+
142
+ 중요 요구사항:
143
+ 1. 상품이 크게 부각되고 이미지에서 중심적인 위치를 차지하도록 프롬프트를 생성해주세요.
144
+ 2. 이미지는 정확히 1:1 비율(정사각형)이어야 합니다.
145
+ 3. 상품은 정사각형 프레임의 정중앙에 위치해야 합니다.
146
+ 4. 상품의 디자인, 색상, 형태, 로고 등 본질적 특성은 절대 수정하지 마세요.
147
+ 5. 환경과의 자연스러운 통합을 위한 조명 효과와 그림자는 포함해주세요.
148
+ 6. 상품을 더 돋보이게 하는 배경 환경을 설명해주세요.
149
+ 7. 고급스러운 상업 광고 품질의 이미지가 되도록 환경 설명을 해주세요.
150
+ 8. 프롬프트 끝에 미드저니 파라미터 "--ar 1:1 --s 750 --q 2"를 추가해주세요.
151
 
152
+ 한국어 입력 내용을 영어로 적절히 번역하여 반영해주세요.
153
+ """
154
+ model = genai.GenerativeModel(
155
+ 'gemini-2.0-flash',
156
+ system_instruction=generate_system_instruction()
157
+ )
158
  response = model.generate_content(
159
+ prompt_request,
160
  generation_config=genai.GenerationConfig(
161
+ temperature=0.7,
162
  top_p=0.95,
163
+ top_k=64,
164
+ max_output_tokens=1024,
165
  )
166
  )
167
+ response_text = response.text.strip()
168
+ if "--ar 1:1" not in response_text:
169
+ response_text = response_text.rstrip(".") + ". --ar 1:1 --s 750 --q 2"
170
+ return response_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  except Exception as e:
172
+ return f"프롬프트 생성 중 오류가 발생했습니다: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
+ # 이미지 생성에 필요한 함수들
175
+ def save_binary_file(file_name, data):
176
+ with open(file_name, "wb") as f:
177
+ f.write(data)
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
+ def translate_prompt_to_english(prompt):
180
+ if not re.search("[가-힣]", prompt):
181
+ return prompt
182
+
183
+ prompt = prompt.replace("#1", "IMAGE_TAG_ONE")
184
+ prompt = prompt.replace("#2", "IMAGE_TAG_TWO")
185
+ prompt = prompt.replace("#3", "IMAGE_TAG_THREE")
186
+
187
+ try:
188
+ if not GEMINI_API_KEY:
189
+ logger.error("Gemini API 키가 설정되지 않았습니다.")
190
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
191
+ prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
192
+ prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
193
+ return prompt
194
+
195
+ model = genai.GenerativeModel('gemini-2.0-flash')
196
+ translation_prompt = f"""
197
+ Translate the following Korean text to English:
198
+
199
+ {prompt}
200
+
201
+ IMPORTANT: The tokens IMAGE_TAG_ONE, IMAGE_TAG_TWO, and IMAGE_TAG_THREE are special tags
202
+ and must be preserved exactly as is in your translation. Do not translate these tokens.
203
+ """
204
+
205
+ logger.info(f"Translation prompt: {translation_prompt}")
206
+ response = model.generate_content(
207
+ translation_prompt,
208
+ generation_config=genai.GenerationConfig(
209
+ temperature=0.2,
210
+ top_p=0.95,
211
+ top_k=40,
212
+ max_output_tokens=512
213
+ )
214
+ )
215
+
216
+ translated_text = response.text
217
+
218
+ if translated_text.strip():
219
+ translated_text = translated_text.replace("IMAGE_TAG_ONE", "#1")
220
+ translated_text = translated_text.replace("IMAGE_TAG_TWO", "#2")
221
+ translated_text = translated_text.replace("IMAGE_TAG_THREE", "#3")
222
+ logger.info(f"Translated text: {translated_text.strip()}")
223
+ return translated_text.strip()
224
+ else:
225
+ logger.warning("번역 결과가 없습니다. 원본 프롬프트 사용")
226
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
227
+ prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
228
+ prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
229
+ return prompt
230
+ except Exception as e:
231
+ logger.exception("번역 중 오류 발생:")
232
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
233
+ prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
234
+ prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
235
+ return prompt
236
+
237
+ def preprocess_prompt(prompt, image1, image2, image3):
238
+ has_img1 = image1 is not None
239
+ has_img2 = image2 is not None
240
+ has_img3 = image3 is not None
241
+
242
+ if "#1" in prompt and not has_img1:
243
+ prompt = prompt.replace("#1", "첫 번째 이미지(없음)")
244
+ else:
245
+ prompt = prompt.replace("#1", "첫 번째 이미지")
246
+
247
+ if "#2" in prompt and not has_img2:
248
+ prompt = prompt.replace("#2", "두 번째 이미지(없음)")
249
+ else:
250
+ prompt = prompt.replace("#2", "두 번째 이미지")
251
+
252
+ if "#3" in prompt and not has_img3:
253
+ prompt = prompt.replace("#3", "세 번째 이미지(없음)")
254
+ else:
255
+ prompt = prompt.replace("#3", "세 번째 이미지")
256
+
257
+ if "1. 이미지 변경" in prompt:
258
+ desc_match = re.search(r'#1을 "(.*?)"으로 바꿔라', prompt)
259
+ if desc_match:
260
+ description = desc_match.group(1)
261
+ prompt = f"첫 번째 이미지를 {description}으로 변경해주세요. 원본 이미지의 주요 내용은 유지하되 새로운 스타일과 분위기로 재해석해주세요."
262
+ else:
263
+ prompt = "첫 번째 이미지를 창의적으로 변형해주세요. 더 생생하고 예술적인 버전으로 만들어주세요."
264
+
265
+ elif "2. 글자지우기" in prompt:
266
+ text_match = re.search(r'#1에서 "(.*?)"를 지워라', prompt)
267
+ if text_match:
268
+ text_to_remove = text_match.group(1)
269
+ prompt = f"첫 번째 이미지에서 '{text_to_remove}' 텍스트를 찾아 자연스럽게 제거해주세요. 텍스트가 있던 부분을 배경과 조화롭게 채워주세요."
270
+ else:
271
+ prompt = "첫 번째 이미지에서 모든 텍스트를 찾아 자연스럽게 제거해주세요. 깔끔한 이미지로 만들어주세요."
272
+
273
+ elif "4. 옷바꾸기" in prompt:
274
+ prompt = "첫 번째 이미지의 인물 의상을 두 번째 이미지의 의상으로 변경해주세요. 의상의 스타일과 색상은 두 번째 이미지를 따르되, 신체 비율과 포즈는 첫 번째 이미지를 유지해주세요."
275
+
276
+ elif "5. 배경바꾸기" in prompt:
277
+ prompt = "첫 번째 이미지의 배경을 두 번째 이미지의 배경으로 변경해주세요. 첫 번째 이미지의 ���요 피사체는 유지하고, 두 번째 이미지의 배경과 조화롭게 합성해주세요."
278
+
279
+ elif "6. 이미지 합성(상품포함)" in prompt:
280
+ prompt = "첫 번째 이미지와 두 번째 이미지(또는 세 번째 이미지)를 자연스럽게 합성해주세요. 모든 이미지의 주요 요소를 포함하고, 특히 상품이 돋보이도록 조화롭게 통합해주세요."
281
+
282
+ prompt += " 이미지를 생성해주세요. 이미지에 텍스트나 글자를 포함하지 마세요."
283
+ return prompt
284
+
285
+ def generate_with_images(prompt, images, variation_index=0):
286
+ try:
287
+ if not GEMINI_API_KEY:
288
+ return None, "API 키가 설정되지 않았습니다. 환경변수를 확인해주세요."
289
+
290
+ model = genai.GenerativeModel('gemini-2.0-flash-exp-image-generation')
291
+ logger.info(f"Gemini API 요청 시작 - 프롬프트: {prompt}, 변형 인덱스: {variation_index}")
292
+
293
+ variation_suffixes = [
294
+ " Create this as the first variation. Do not add any text, watermarks, or labels to the image.",
295
+ " Create this as the second variation with more vivid colors. Do not add any text, watermarks, or labels to the image.",
296
+ " Create this as the third variation with a more creative style. Do not add any text, watermarks, or labels to the image.",
297
+ " Create this as the fourth variation with enhanced details. Do not add any text, watermarks, or labels to the image."
298
+ ]
299
+
300
+ if variation_index < len(variation_suffixes):
301
+ prompt = prompt + variation_suffixes[variation_index]
302
+ else:
303
+ prompt = prompt + " Do not add any text, watermarks, or labels to the image."
304
+
305
+ contents = [prompt]
306
+ for idx, img in enumerate(images, 1):
307
+ if img is not None:
308
+ contents.append(img)
309
+ logger.info(f"이미지 #{idx} 추가됨")
310
+
311
+ response = model.generate_content(
312
+ contents=contents,
313
+ generation_config=genai.GenerationConfig(
314
+ temperature=1,
315
+ top_p=0.95,
316
+ top_k=40,
317
+ max_output_tokens=8192
318
+ )
319
+ )
320
+
321
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
322
+ temp_path = tmp.name
323
+ result_text = ""
324
+ image_found = False
325
+
326
+ if hasattr(response, 'candidates') and response.candidates:
327
+ candidate = response.candidates[0]
328
+ if hasattr(candidate, 'content') and candidate.content:
329
+ for part in candidate.content.parts:
330
+ if hasattr(part, 'text') and part.text:
331
+ result_text += part.text
332
+ logger.info(f"응답 텍스트: {part.text}")
333
+ elif hasattr(part, 'inline_data') and part.inline_data:
334
+ save_binary_file(temp_path, part.inline_data.data)
335
+ image_found = True
336
+ logger.info("응답에서 이미지 추출 성공")
337
+
338
+ if not image_found:
339
+ return None, f"API에서 이미지를 생성하지 못했습니다. 응답 텍스트: {result_text}"
340
+
341
+ result_img = Image.open(temp_path)
342
+ if result_img.mode == "RGBA":
343
+ result_img = result_img.convert("RGB")
344
+
345
+ return result_img, f"이미지가 성공적으로 생성되었습니다. {result_text}"
346
+ except Exception as e:
347
+ logger.exception("이미지 생성 중 오류 발생:")
348
+ return None, f"오류 발생: {str(e)}"
349
+
350
+ def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0, max_retries=3):
351
+ retry_count = 0
352
+ last_error = None
353
+
354
+ while retry_count < max_retries:
355
+ try:
356
+ images = [image1, image2, image3]
357
+ valid_images = [img for img in images if img is not None]
358
+ if not valid_images:
359
+ return None, "적어도 하나의 이미지를 업로드해주세요.", ""
360
+
361
+ if prompt and prompt.strip():
362
+ processed_prompt = preprocess_prompt(prompt, image1, image2, image3)
363
+ if re.search("[가-힣]", processed_prompt):
364
+ final_prompt = translate_prompt_to_english(processed_prompt)
365
+ else:
366
+ final_prompt = processed_prompt
367
+ else:
368
+ if len(valid_images) == 1:
369
+ final_prompt = "Please creatively transform this image into a more vivid and artistic version. Do not include any text or watermarks in the generated image."
370
+ logger.info("Default prompt generated for single image")
371
+ elif len(valid_images) == 2:
372
+ final_prompt = "Please seamlessly composite these two images, integrating their key elements harmoniously into a single image. Do not include any text or watermarks in the generated image."
373
+ logger.info("Default prompt generated for two images")
374
+ else:
375
+ final_prompt = "Please creatively composite these three images, combining their main elements into a cohesive and natural scene. Do not include any text or watermarks in the generated image."
376
+ logger.info("Default prompt generated for three images")
377
+
378
+ result_img, status = generate_with_images(final_prompt, valid_images, variation_index)
379
+ if result_img is not None:
380
+ return result_img, status, final_prompt
381
+ else:
382
+ last_error = status
383
  retry_count += 1
384
  logger.warning(f"이미지 생성 실패, 재시도 {retry_count}/{max_retries}: {status}")
385
  time.sleep(1)
 
558
  "name": nature,
559
  "english": NATURE_BACKGROUNDS.get(nature, "natural environment")
560
  }
561
+ elif bg_type == "실내 환경":
562
  return {
563
  "category": "실내 환경",
564
  "name": indoor,
 
670
 
671
  if __name__ == "__main__":
672
  app = create_app()
673
+ app.launch()