Kims12 commited on
Commit
42d38fe
ยท
verified ยท
1 Parent(s): 25faada

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +242 -341
app.py CHANGED
@@ -1,372 +1,273 @@
1
- import gradio as gr
2
- import google.generativeai as genai
3
- from PIL import Image
4
  import os
5
- import json
6
-
7
- # Gemini API ํ‚ค ์„ค์ • (ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ์ง์ ‘ ์ž…๋ ฅ)
8
- GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
 
 
9
 
10
- # Gemini API ์ดˆ๊ธฐํ™”
11
- genai.configure(api_key=GEMINI_API_KEY)
 
12
 
13
- # ๋ฐฐ๊ฒฝ JSON ํŒŒ์ผ ๊ฒฝ๋กœ ์„ค์ • - ์ƒ๋Œ€ ๊ฒฝ๋กœ ์‚ฌ์šฉ
14
- BACKGROUNDS_DIR = "./background"
15
 
16
- # ๋””๋ฒ„๊น…์„ ์œ„ํ•œ ์ •๋ณด ์ถœ๋ ฅ
17
- print(f"ํ˜„์žฌ ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ: {os.getcwd()}")
18
- print(f"์‚ฌ์šฉ ์ค‘์ธ ๋ฐฐ๊ฒฝ ๋””๋ ‰ํ† ๋ฆฌ ๊ฒฝ๋กœ: {BACKGROUNDS_DIR}")
19
 
20
- # JSON ํŒŒ์ผ์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
21
- if not os.path.exists(BACKGROUNDS_DIR):
22
- os.makedirs(BACKGROUNDS_DIR)
23
- print(f"๋ฐฐ๊ฒฝ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค: {BACKGROUNDS_DIR}")
24
- else:
25
- print(f"๋ฐฐ๊ฒฝ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์ด๋ฏธ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค: {BACKGROUNDS_DIR}")
26
- try:
27
- for file in os.listdir(BACKGROUNDS_DIR):
28
- print(f"๋ฐœ๊ฒฌ๋œ ํŒŒ์ผ: {file}")
29
- except Exception as e:
30
- print(f"๋””๋ ‰ํ† ๋ฆฌ ๋‚ด์šฉ์„ ๋‚˜์—ดํ•˜๋Š” ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
31
 
32
- # JSON ํŒŒ์ผ ๋กœ๋“œ ํ•จ์ˆ˜
33
- def load_background_json(filename):
34
- file_path = os.path.join(BACKGROUNDS_DIR, filename)
 
 
 
35
  try:
36
- with open(file_path, 'r', encoding='utf-8') as f:
37
- data = json.load(f)
38
- print(f"{filename} ํŒŒ์ผ์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋กœ๋“œํ–ˆ์Šต๋‹ˆ๋‹ค. {len(data)} ํ•ญ๋ชฉ ํฌํ•จ.")
39
- return data
40
- except FileNotFoundError:
41
- print(f"๊ฒฝ๊ณ : {filename} ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.")
42
- return {}
43
- except json.JSONDecodeError:
44
- print(f"๊ฒฝ๊ณ : {filename} ํŒŒ์ผ์˜ JSON ํ˜•์‹์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.")
45
- return {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  except Exception as e:
47
- print(f"๊ฒฝ๊ณ : {filename} ํŒŒ์ผ ๋กœ๋“œ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}. ๊ธฐ๋ณธ๊ฐ’์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.")
48
- return {}
49
-
50
- # ๋ฐฐ๊ฒฝ ๋ฐ์ดํ„ฐ ๋กœ๋“œ
51
- SIMPLE_BACKGROUNDS = load_background_json("simple_backgrounds.json")
52
- STUDIO_BACKGROUNDS = load_background_json("studio_backgrounds.json")
53
- NATURE_BACKGROUNDS = load_background_json("nature_backgrounds.json")
54
- INDOOR_BACKGROUNDS = load_background_json("indoor_backgrounds.json")
55
- ABSTRACT_BACKGROUNDS = load_background_json("abstract_backgrounds.json")
56
-
57
- # ๋ฐฐ๊ฒฝ์ด ๋กœ๋“œ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ๊ธฐ๋ณธ๊ฐ’ ์„ค์ •
58
- if not SIMPLE_BACKGROUNDS:
59
- SIMPLE_BACKGROUNDS = {"ํ™”์ดํŠธ ๋ฐฐ๊ฒฝ": "white background"}
60
- if not STUDIO_BACKGROUNDS:
61
- STUDIO_BACKGROUNDS = {"์ œํ’ˆ ์‚ฌ์ง„ ์ŠคํŠœ๋””์˜ค": "product photography studio"}
62
- if not NATURE_BACKGROUNDS:
63
- NATURE_BACKGROUNDS = {"์—ด๋Œ€ ํ•ด๋ณ€": "tropical beach"}
64
- if not INDOOR_BACKGROUNDS:
65
- INDOOR_BACKGROUNDS = {"๋ชจ๋˜ ๋ฆฌ๋น™๋ฃธ": "modern living room"}
66
- if not ABSTRACT_BACKGROUNDS:
67
- ABSTRACT_BACKGROUNDS = {"๋„ค์˜จ ์กฐ๋ช…": "neon lights"}
68
 
69
- def generate_system_instruction():
70
- return """๋‹น์‹ ์€ ์ƒํ’ˆ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ์œ„ํ•œ ๊ณ ํ’ˆ์งˆ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
71
- ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ƒํ’ˆ๋ช…, ๋ฐฐ๊ฒฝ ์œ ํ˜•, ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์„ ๋ฐ”ํƒ•์œผ๋กœ ๋ฏธ๋“œ์ €๋‹ˆ(Midjourney)์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”
72
- ์ƒ์„ธํ•˜๊ณ  ์ „๋ฌธ์ ์ธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์˜์–ด๋กœ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.
73
- ๋‹ค์Œ ๊ฐ€์ด๋“œ๋ผ์ธ์„ ๋ฐ˜๋“œ์‹œ ๋”ฐ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค:
74
 
75
- 1. ์ƒํ’ˆ์„ "#1"๋กœ ์ง€์ •ํ•˜์—ฌ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. (์˜ˆ: "skincare tube (#1)")
 
 
 
76
 
77
- 2. *** ๋งค์šฐ ์ค‘์š”: ์ƒํ’ˆ์˜ ์›๋ž˜ ํŠน์„ฑ(๋””์ž์ธ, ์ƒ‰์ƒ, ํ˜•ํƒœ, ๋กœ๊ณ , ํŒจํ‚ค์ง€ ๋“ฑ)์€ ์–ด๋–ค ์ƒํ™ฉ์—์„œ๋„ ์ ˆ๋Œ€ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ***
 
78
 
79
- 3. *** ์ƒํ’ˆ์˜ ๋ณธ์งˆ์  ํŠน์„ฑ์€ ์œ ์ง€ํ•˜๋˜, ์ž์—ฐ์Šค๋Ÿฌ์šด ํ™˜๊ฒฝ ํ†ตํ•ฉ์„ ์œ„ํ•œ ์กฐ๋ช…๊ณผ ๊ทธ๋ฆผ์ž๋Š” ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค: ***
80
- - ์ƒํ’ˆ ์ž์ฒด์˜ ์ƒ‰์ƒ, ๋””์ž์ธ, ํ˜•ํƒœ, ํ…์Šค์ฒ˜๋Š” ์ ˆ๋Œ€ ์ˆ˜์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
81
- - ํ™˜๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šธ๋ฆฌ๋Š” ๊ทธ๋ฆผ์ž, ์ฃผ๋ณ€ ์กฐ๋ช… ํšจ๊ณผ๋Š” ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค.
82
- - ์ƒํ’ˆ์— ๋ฌผ๋ฐฉ์šธ, ์‘์ถ•, ๊ธˆ, ์€๊ณผ ๊ฐ™์€ ์ถ”๊ฐ€ ์š”์†Œ๋‚˜ ๋ฌผ๋ฆฌ์  ํšจ๊ณผ๋Š” ์ ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
83
- - ํ™˜๊ฒฝ์— ์–ด์šธ๋ฆฌ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋น› ๋ฐ˜์‚ฌ, ์ฃผ๋ณ€ ์กฐ๋ช…, ๊ทธ๋ฆผ์ž๋Š” ์‚ฌ์‹ค์  ํ†ตํ•ฉ๊ฐ์„ ์œ„ํ•ด ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
84
-
85
- 4. ์ด๋ฏธ์ง€ ๋น„์œจ์€ ์ •ํ™•ํžˆ 1:1(์ •์‚ฌ๊ฐํ˜•) ํ˜•์‹์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กฌํ”„ํŠธ์— "square format", "1:1 ratio" ๋˜๋Š” "aspect ratio 1:1"์„ ๋ช…์‹œ์ ์œผ๋กœ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
86
-
87
- 5. ์ƒํ’ˆ์€ ๋ฐ˜๋“œ์‹œ ์ •์‚ฌ๊ฐํ˜• ๊ตฌ๋„์˜ ์ •์ค‘์•™์— ๋ฐฐ์น˜๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
88
-
89
- 6. ์ƒํ’ˆ์„ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์ดˆ์ ์œผ๋กœ ๋ถ€๊ฐ์‹œํ‚ค๊ณ , ์ƒํ’ˆ์˜ ๋น„์œจ์ด ์ „์ฒด ์ด๋ฏธ์ง€์—์„œ ํฌ๊ฒŒ ์ฐจ์ง€ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
90
-
91
- 7. ์ƒํ’ˆ ์ด๋ฏธ์ง€ ์ปท์•„์›ƒ(#1)์˜ ๊ธฐ๋ณธ ํ˜•ํƒœ์™€ ์ƒ‰์ƒ์€ ์œ ์ง€ํ•˜๋ฉด์„œ, ์„ ํƒํ•œ ํ™˜๊ฒฝ์— ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ†ตํ•ฉ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
92
-
93
- 8. ๊ณ ๊ธ‰์Šค๋Ÿฌ์šด ์ƒ์—…์  ์ด๋ฏธ์ง€๋ฅผ ์œ„ํ•œ ๋‹ค์Œ ํ™˜๊ฒฝ ์š”์†Œ๋“ค์„ ํฌํ•จํ•˜์„ธ์š”:
94
- - ์ƒํ’ˆ๊ณผ ์–ด์šธ๋ฆฌ๋Š” ์ฃผ๋ณ€ ํ™˜๊ฒฝ/๋ฐฐ๊ฒฝ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํ™”์žฅํ’ˆ ์ฃผ๋ณ€์— ๊ฝƒ์ด๋‚˜ ํ—ˆ๋ธŒ, ์Œ๋ฃŒ ์ œํ’ˆ ์˜†์— ๊ณผ์ผ, ์ „์ž์ œํ’ˆ ๊ทผ์ฒ˜์— ํ˜„๋Œ€์  ์†Œํ’ˆ ๋“ฑ.
95
- - ํ™˜๊ฒฝ์˜ ์กฐ๋ช… ํšจ๊ณผ(๋ฆผ ๋ผ์ดํŠธ, ๋ฐฑ๋ผ์ดํŠธ, ์†Œํ”„ํŠธ๋ฐ•์Šค ๋“ฑ)๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.
96
- - ์ƒํ’ˆ์ด ํ™˜๊ฒฝ์— ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์กด์žฌํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋„๋ก ์ ์ ˆํ•œ ๊ทธ๋ฆผ์ž์™€ ๋น› ํ‘œํ˜„์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
97
- - ์ƒํ’ˆ์˜ ์šฉ๋„๋‚˜ ์žฅ์ ์„ ๊ฐ„์ ‘์ ์œผ๋กœ ์•”์‹œํ•˜๋Š” ๋ฐฐ๊ฒฝ ์š”์†Œ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
98
- - ํ”„๋กœํŽ˜์…”๋„ํ•œ ์ƒ์—… ์‚ฌ์ง„ ํšจ๊ณผ(์„ ํƒ์  ํ”ผ์‚ฌ๊ณ„ ์‹ฌ๋„, ์†Œํ”„ํŠธ ํฌ์ปค์Šค, ์ŠคํŠœ๋””์˜ค ์กฐ๋ช… ๋“ฑ)๋ฅผ ๋ช…์‹œํ•ฉ๋‹ˆ๋‹ค.
99
-
100
- 9. ํ”„๋กฌํ”„ํŠธ์— ๋‹ค์Œ ์š”์†Œ๋“ค์„ ๋ช…์‹œ์ ์œผ๋กœ ํฌํ•จํ•˜์„ธ์š”:
101
- - "highly detailed commercial photography"
102
- - "award-winning product photography"
103
- - "professional advertising imagery"
104
- - "studio quality"
105
- - "magazine advertisement quality"
106
-
107
- 10. ๋ฐฐ๊ฒฝ ํ™˜๊ฒฝ ์š”์†Œ๋ฅผ ์ƒํ’ˆ ์นดํ…Œ๊ณ ๋ฆฌ์— ๋งž๊ฒŒ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค:
108
- - ์Šคํ‚จ์ผ€์–ด ์ œํ’ˆ: ๊นจ๋—ํ•œ ์š•์‹ค ์„ ๋ฐ˜, ์šฐ์•„ํ•œ ํ™”์žฅ๋Œ€, ์ŠคํŒŒ ๊ฐ™์€ ํ™˜๊ฒฝ ๋“ฑ
109
- - ์Œ๋ฃŒ ์ œํ’ˆ: ์„ธ๋ จ๋œ ํ…Œ์ด๋ธ”, ํŒŒํ‹ฐ ํ™˜๊ฒฝ, ์•ผ์™ธ ํ”ผํฌ๋‹‰ ์žฅ๋ฉด ๋“ฑ
110
- - ์ „์ž ์ œํ’ˆ: ์„ธ๋ จ๋œ ์ž‘์—… ๊ณต๊ฐ„, ํ˜„๋Œ€์ ์ธ ๊ฑฐ์‹ค, ๋ฏธ๋‹ˆ๋ฉ€ํ•œ ์ฑ…์ƒ ๋“ฑ
111
- - ํŒจ์…˜/์˜๋ฅ˜: ์„ธ๋ จ๋œ ์‡ผ๋ฃธ, ๋„์‹œ ๊ฑฐ๋ฆฌ, ์—˜๋ ˆ๊ฐ•์Šคํ•œ ๋ผ์ดํ”„์Šคํƒ€์ผ ํ™˜๊ฒฝ ๋“ฑ
112
- - ์‹ํ’ˆ ์ œํ’ˆ: ๊น”๋”ํ•œ ์ฃผ๋ฐฉ, ์‹ํƒ, ์š”๋ฆฌ ํ™˜๊ฒฝ ๋“ฑ
113
-
114
- 11. ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ๊ตฌ์ฒด์ ์ธ ๋ฐฐ๊ฒฝ๊ณผ ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ์„ ์ •ํ™•ํžˆ ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.
115
-
116
- 12. ํ”„๋กฌํ”„ํŠธ๋Š” ๋ฏธ๋“œ์ €๋‹ˆ AI์— ์ตœ์ ํ™”๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
117
-
118
- 13. ํ”„๋กฌํ”„ํŠธ ๋์— "--ar 1:1 --s 750 --q 2" ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฏธ๋“œ์ €๋‹ˆ์—์„œ ๊ณ ํ’ˆ์งˆ ์ •์‚ฌ๊ฐํ˜• ๋น„์œจ์„ ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค.
119
 
120
- ์ถœ๋ ฅ ํ˜•์‹์€ ์˜์–ด๋กœ ๋œ ๋‹จ์ผ ๋‹จ๋ฝ์˜ ์ƒ์„ธํ•œ ํ”„๋กฌํ”„ํŠธ์—ฌ์•ผ ํ•˜๋ฉฐ, ๋์— ๋ฏธ๋“œ์ €๋‹ˆ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
121
- """
122
 
123
- def generate_prompt_with_gemini(product_name, background_info, additional_info=""):
124
- if not GEMINI_API_KEY:
125
- return "Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ GEMINI_API_KEY๋ฅผ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ ์ฝ”๋“œ์— ์ง์ ‘ ์ž…๋ ฅํ•˜์„ธ์š”."
126
- try:
127
- prompt_request = f"""
128
- ์ƒํ’ˆ๋ช…: {product_name}
129
- ๋ฐฐ๊ฒฝ ์œ ํ˜•: {background_info.get('english', 'studio')}
130
- ๋ฐฐ๊ฒฝ ์นดํ…Œ๊ณ ๋ฆฌ: {background_info.get('category', '')}
131
- ๋ฐฐ๊ฒฝ ์ด๋ฆ„: {background_info.get('name', '')}
132
- ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ: {additional_info}
 
133
 
134
- ์ค‘์š” ์š”๊ตฌ์‚ฌํ•ญ:
135
- 1. ์ƒํ’ˆ์ด ํฌ๊ฒŒ ๋ถ€๊ฐ๋˜๊ณ  ์ด๋ฏธ์ง€์—์„œ ์ค‘์‹ฌ์ ์ธ ์œ„์น˜๋ฅผ ์ฐจ์ง€ํ•˜๋„๋ก ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.
136
- 2. ์ด๋ฏธ์ง€๋Š” ์ •ํ™•ํžˆ 1:1 ๋น„์œจ(์ •์‚ฌ๊ฐํ˜•)์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
137
- 3. ์ƒํ’ˆ์€ ์ •์‚ฌ๊ฐํ˜• ํ”„๋ ˆ์ž„์˜ ์ •์ค‘์•™์— ์œ„์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
138
- 4. ์ƒํ’ˆ์˜ ๋””์ž์ธ, ์ƒ‰์ƒ, ํ˜•ํƒœ, ๋กœ๊ณ  ๋“ฑ ๋ณธ์งˆ์  ํŠน์„ฑ์€ ์ ˆ๋Œ€ ์ˆ˜์ •ํ•˜์ง€ ๋งˆ์„ธ์š”.
139
- 5. ํ™˜๊ฒฝ๊ณผ์˜ ์ž์—ฐ์Šค๋Ÿฌ์šด ํ†ตํ•ฉ์„ ์œ„ํ•œ ์กฐ๋ช… ํšจ๊ณผ์™€ ๊ทธ๋ฆผ์ž๋Š” ํฌํ•จํ•ด์ฃผ์„ธ์š”.
140
- 6. ์ƒํ’ˆ์„ ๋” ๋‹๋ณด์ด๊ฒŒ ํ•˜๋Š” ๋ฐฐ๊ฒฝ ํ™˜๊ฒฝ์„ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.
141
- 7. ๊ณ ๊ธ‰์Šค๋Ÿฌ์šด ์ƒ์—… ๊ด‘๊ณ  ํ’ˆ์งˆ์˜ ์ด๋ฏธ์ง€๊ฐ€ ๋˜๋„๋ก ํ™˜๊ฒฝ ์„ค๋ช…์„ ํ•ด์ฃผ์„ธ์š”.
142
- 8. ํ”„๋กฌํ”„ํŠธ ๋์— ๋ฏธ๋“œ์ €๋‹ˆ ํŒŒ๋ผ๋ฏธํ„ฐ "--ar 1:1 --s 750 --q 2"๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.
143
 
144
- ํ•œ๊ตญ์–ด ์ž…๋ ฅ ๋‚ด์šฉ์„ ์˜์–ด๋กœ ์ ์ ˆํžˆ ๋ฒˆ์—ญํ•˜์—ฌ ๋ฐ˜์˜ํ•ด์ฃผ์„ธ์š”.
145
- """
146
- model = genai.GenerativeModel(
147
- 'gemini-2.0-flash',
148
- system_instruction=generate_system_instruction()
149
- )
150
- response = model.generate_content(
151
- prompt_request,
152
- generation_config=genai.types.GenerationConfig(
153
- temperature=0.7,
154
  top_p=0.95,
155
- top_k=64,
156
- max_output_tokens=1024,
157
  )
158
  )
159
- response_text = response.text.strip()
160
- if "--ar 1:1" not in response_text:
161
- response_text = response_text.rstrip(".") + ". --ar 1:1 --s 750 --q 2"
162
- return response_text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  except Exception as e:
164
- return f"ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
 
165
 
166
- def create_app():
167
- with gr.Blocks(title="๊ณ ๊ธ‰ ์ƒํ’ˆ ์ด๋ฏธ์ง€ ๋ฐฐ๊ฒฝ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ๊ธฐ") as demo:
168
- gr.Markdown("# ๊ณ ๊ธ‰ ์ƒํ’ˆ ์ด๋ฏธ์ง€ ๋ฐฐ๊ฒฝ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ๊ธฐ")
169
- gr.Markdown("์ƒํ’ˆ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๊ณ  ์˜ต์…˜์„ ์„ ํƒํ•˜๋ฉด ๊ณ ํ’ˆ์งˆ ์ƒ์—…์šฉ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๏ฟฝ๏ฟฝ.")
170
-
171
- # ์ƒํ’ˆ๋ช… ์„น์…˜
172
- with gr.Row():
173
- product_name = gr.Textbox(label="์ƒํ’ˆ๋ช… (ํ•œ๊ตญ์–ด ์ž…๋ ฅ)", placeholder="์˜ˆ: ์Šคํ‚จ์ผ€์–ด ํŠœ๋ธŒ, ํ…€๋ธ”๋Ÿฌ ๋“ฑ", interactive=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
- # ๋ฐฐ๊ฒฝ ์œ ํ˜•์„ ์„ ํƒํ•˜๊ธฐ ์œ„ํ•œ ๋ผ๋””์˜ค ๋ฒ„ํŠผ
176
- background_type = gr.Radio(
177
- choices=["์‹ฌํ”Œ ๋ฐฐ๊ฒฝ", "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ", "์ž์—ฐ ํ™˜๊ฒฝ", "์‹ค๋‚ด ํ™˜๊ฒฝ", "์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ"],
178
- label="๋ฐฐ๊ฒฝ ์œ ํ˜•",
179
- value="์‹ฌํ”Œ ๋ฐฐ๊ฒฝ"
180
- )
 
 
181
 
182
- # ๊ฐ ๋ฐฐ๊ฒฝ ์œ ํ˜•์— ๋งž๋Š” ๋“œ๋กญ๋‹ค์šด ์ปดํฌ๋„ŒํŠธ๋“ค (์ฒ˜์Œ์—๋Š” ์‹ฌํ”Œ ๋ฐฐ๊ฒฝ๋งŒ ํ‘œ์‹œ)
183
- with gr.Row():
184
- # ์ขŒ์ธก ์ปฌ๋Ÿผ - ์ž…๋ ฅ ์ปจํŠธ๋กค
185
- with gr.Column(scale=1):
186
- image_input = gr.Image(label="์ƒํ’ˆ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ", type="pil")
187
-
188
- # ์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์„ ํƒ ๋“œ๋กญ๋‹ค์šด
189
- simple_dropdown = gr.Dropdown(
190
- choices=list(SIMPLE_BACKGROUNDS.keys()),
191
- value=list(SIMPLE_BACKGROUNDS.keys())[0] if SIMPLE_BACKGROUNDS else None,
192
- label="์‹ฌํ”Œ ๋ฐฐ๊ฒฝ ์„ ํƒ",
193
- visible=True,
194
- interactive=True
195
- )
196
-
197
- # ์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ ์„ ํƒ ๋“œ๋กญ๋‹ค์šด
198
- studio_dropdown = gr.Dropdown(
199
- choices=list(STUDIO_BACKGROUNDS.keys()),
200
- value=list(STUDIO_BACKGROUNDS.keys())[0] if STUDIO_BACKGROUNDS else None,
201
- label="์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ ์„ ํƒ",
202
- visible=False,
203
- interactive=True
204
- )
205
-
206
- # ์ž์—ฐ ํ™˜๊ฒฝ ์„ ํƒ ๋“œ๋กญ๋‹ค์šด
207
- nature_dropdown = gr.Dropdown(
208
- choices=list(NATURE_BACKGROUNDS.keys()),
209
- value=list(NATURE_BACKGROUNDS.keys())[0] if NATURE_BACKGROUNDS else None,
210
- label="์ž์—ฐ ํ™˜๊ฒฝ ์„ ํƒ",
211
- visible=False,
212
- interactive=True
213
- )
214
-
215
- # ์‹ค๋‚ด ํ™˜๊ฒฝ ์„ ํƒ ๋“œ๋กญ๋‹ค์šด
216
- indoor_dropdown = gr.Dropdown(
217
- choices=list(INDOOR_BACKGROUNDS.keys()),
218
- value=list(INDOOR_BACKGROUNDS.keys())[0] if INDOOR_BACKGROUNDS else None,
219
- label="์‹ค๋‚ด ํ™˜๊ฒฝ ์„ ํƒ",
220
- visible=False,
221
- interactive=True
222
- )
223
-
224
- # ์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ ์„ ํƒ ๋“œ๋กญ๋‹ค์šด
225
- abstract_dropdown = gr.Dropdown(
226
- choices=list(ABSTRACT_BACKGROUNDS.keys()),
227
- value=list(ABSTRACT_BACKGROUNDS.keys())[0] if ABSTRACT_BACKGROUNDS else None,
228
- label="์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ ์„ ํƒ",
229
- visible=False,
230
- interactive=True
231
- )
232
 
233
- # ์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ
234
- additional_info = gr.Textbox(
235
- label="์ถ”๊ฐ€ ์š”์ฒญ์‚ฌํ•ญ (์„ ํƒ์‚ฌํ•ญ)",
236
- placeholder="์˜ˆ: ๊ณ ๊ธ‰์Šค๋Ÿฌ์šด ๋А๋‚Œ, ๋ฐ์€ ์กฐ๋ช…, ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ณด์กฐ์ ์ธ ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š” ๋“ฑ",
237
  lines=3,
238
- interactive=True
 
239
  )
240
 
241
- submit_btn = gr.Button("ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ", variant="primary")
 
 
242
 
243
- # ์šฐ์ธก ์ปฌ๋Ÿผ - ์ถœ๋ ฅ ๊ฒฐ๊ณผ
244
- with gr.Column(scale=1):
245
- prompt_output = gr.Textbox(label="์ƒ์„ฑ๋œ ํ”„๋กฌํ”„ํŠธ", lines=10)
246
- image_preview = gr.Image(label="์—…๋กœ๋“œ๋œ ์ด๋ฏธ์ง€ (#1)", type="pil")
247
- preview_html = gr.HTML("ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์‹œ ์—ฌ๊ธฐ์— ๋ฏธ๋ฆฌ๋ณด๊ธฐ๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.")
248
-
249
- # ๋ฐฐ๊ฒฝ ์œ ํ˜•์— ๋”ฐ๋ผ ๋“œ๋กญ๋‹ค์šด ํ‘œ์‹œ ์—…๋ฐ์ดํŠธ
250
- def update_dropdowns(bg_type):
251
- return {
252
- simple_dropdown: gr.update(visible=(bg_type == "์‹ฌํ”Œ ๋ฐฐ๊ฒฝ")),
253
- studio_dropdown: gr.update(visible=(bg_type == "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ")),
254
- nature_dropdown: gr.update(visible=(bg_type == "์ž์—ฐ ํ™˜๊ฒฝ")),
255
- indoor_dropdown: gr.update(visible=(bg_type == "์‹ค๋‚ด ํ™˜๊ฒฝ")),
256
- abstract_dropdown: gr.update(visible=(bg_type == "์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ"))
257
- }
258
-
259
- # ๋ฐฐ๊ฒฝ ์œ ํ˜• ๋ณ€๊ฒฝ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
260
- background_type.change(
261
- fn=update_dropdowns,
262
- inputs=[background_type],
263
- outputs=[simple_dropdown, studio_dropdown, nature_dropdown, indoor_dropdown, abstract_dropdown]
264
- )
265
-
266
- # ์„ ํƒ๋œ ๋ฐฐ๊ฒฝ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
267
- def get_selected_background_info(bg_type, simple, studio, nature, indoor, abstract):
268
- if bg_type == "์‹ฌํ”Œ ๋ฐฐ๊ฒฝ":
269
- return {
270
- "category": "์‹ฌํ”Œ ๋ฐฐ๊ฒฝ",
271
- "name": simple,
272
- "english": SIMPLE_BACKGROUNDS.get(simple, "white background")
273
- }
274
- elif bg_type == "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ":
275
- return {
276
- "category": "์ŠคํŠœ๋””์˜ค ๋ฐฐ๊ฒฝ",
277
- "name": studio,
278
- "english": STUDIO_BACKGROUNDS.get(studio, "product photography studio")
279
- }
280
- elif bg_type == "์ž์—ฐ ํ™˜๊ฒฝ":
281
- return {
282
- "category": "์ž์—ฐ ํ™˜๊ฒฝ",
283
- "name": nature,
284
- "english": NATURE_BACKGROUNDS.get(nature, "natural environment")
285
- }
286
- elif bg_type == "์‹ค๋‚ด ํ™˜๊ฒฝ":
287
- return {
288
- "category": "์‹ค๋‚ด ํ™˜๊ฒฝ",
289
- "name": indoor,
290
- "english": INDOOR_BACKGROUNDS.get(indoor, "indoor environment")
291
- }
292
- elif bg_type == "์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ":
293
- return {
294
- "category": "์ถ”์ƒ/ํŠน์ˆ˜ ๋ฐฐ๊ฒฝ",
295
- "name": abstract,
296
- "english": ABSTRACT_BACKGROUNDS.get(abstract, "abstract background")
297
- }
298
- else:
299
- return {
300
- "category": "๊ธฐ๋ณธ ๋ฐฐ๊ฒฝ",
301
- "name": "ํ™”์ดํŠธ ๋ฐฐ๊ฒฝ",
302
- "english": "white background"
303
- }
304
-
305
- # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ํ•จ์ˆ˜
306
- def generate_output(image, bg_type, simple, studio, nature, indoor, abstract, product_text, additional_text):
307
- if image is None:
308
- gr.Warning("์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.")
309
- return "์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.", None, "์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œ ํ›„ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”."
310
-
311
- product_text = product_text.strip() or "์ œํ’ˆ"
312
-
313
- # ๋ฐฐ๊ฒฝ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
314
- background_info = get_selected_background_info(bg_type, simple, studio, nature, indoor, abstract)
315
-
316
- try:
317
- prompt = generate_prompt_with_gemini(product_text, background_info, additional_text)
318
-
319
- if not GEMINI_API_KEY:
320
- gr.Warning("Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ‚ค๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„ ์„ค์ •ํ•ด์ฃผ์„ธ์š”.")
321
- prompt = """
322
- [Gemini API ํ‚ค ๋ˆ„๋ฝ]
323
- API ํ‚ค ์„ค์ • ๋ฐฉ๋ฒ•:
324
- 1. ํ™˜๊ฒฝ ๋ณ€์ˆ˜: export GEMINI_API_KEY="your-api-key"
325
- 2. ์ฝ”๋“œ ๋‚ด ์ง์ ‘ ์ž…๋ ฅ: GEMINI_API_KEY = "your-api-key"
326
- ํ‚ค ๋ฐœ๊ธ‰: https://makersuite.google.com/
327
- """
328
- return prompt, image, "API ํ‚ค๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค."
329
 
330
- # ํ”„๋กฌํ”„ํŠธ ์š”์•ฝ HTML ์ƒ์„ฑ
331
- preview = f"""
332
- <div style="padding:10px; border:1px solid #ddd; border-radius:8px; margin-top:10px;">
333
- <h3>ํ”„๋กฌํ”„ํŠธ ์š”์•ฝ</h3>
334
- <p><strong>์ด ๊ธธ์ด:</strong> {len(prompt)} ๊ธ€์ž</p>
335
- <p><strong>๋ฐฐ๊ฒฝ:</strong> {background_info['category']} &gt; {background_info['name']}</p>
336
- <p><strong>์ฃผ์š” ์š”์†Œ:</strong> {", ".join([kw for kw in ["commercial photography", "product", "square format", "centered", "detailed"] if kw.lower() in prompt.lower()])}</p>
337
- <p><strong>๋ฏธ๋“œ์ €๋‹ˆ ํŒŒ๋ผ๋ฏธํ„ฐ:</strong> {" ".join([param for param in ["--ar 1:1", "--s 750", "--q 2"] if param in prompt])}</p>
338
- </div>
339
- """
340
-
341
- return prompt, image, preview
342
- except Exception as e:
343
- error_msg = f"ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
344
- gr.Error(error_msg)
345
- return error_msg, image, "์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."
346
-
347
- # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
348
- submit_btn.click(
349
- fn=generate_output,
350
- inputs=[
351
- image_input,
352
- background_type,
353
- simple_dropdown,
354
- studio_dropdown,
355
- nature_dropdown,
356
- indoor_dropdown,
357
- abstract_dropdown,
358
- product_name,
359
- additional_info
360
- ],
361
- outputs=[
362
- prompt_output,
363
- image_preview,
364
- preview_html
365
- ]
366
- )
367
 
368
- return demo
 
 
 
 
 
369
 
370
- if __name__ == "__main__":
371
- app = create_app()
372
- app.launch()
 
 
 
 
1
  import os
2
+ import tempfile
3
+ from PIL import Image
4
+ import gradio as gr
5
+ import logging
6
+ import re
7
+ import time
8
 
9
+ from google import genai
10
+ from google.genai import types
11
+ from dotenv import load_dotenv
12
 
13
+ load_dotenv()
 
14
 
15
+ # Logging setup
16
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
17
+ logger = logging.getLogger(__name__)
18
 
19
+ def save_binary_file(file_name, data):
20
+ with open(file_name, "wb") as f:
21
+ f.write(data)
 
 
 
 
 
 
 
 
22
 
23
+ def translate_prompt_to_english(prompt):
24
+ if not re.search("[๊ฐ€-ํžฃ]", prompt):
25
+ return prompt
26
+
27
+ prompt = prompt.replace("#1", "IMAGE_TAG_ONE")
28
+
29
  try:
30
+ api_key = os.environ.get("GEMINI_API_KEY")
31
+ if not api_key:
32
+ logger.error("Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
33
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
34
+ return prompt
35
+
36
+ client = genai.Client(api_key=api_key)
37
+ translation_prompt = f"""
38
+ Translate the following Korean text to English:
39
+
40
+ {prompt}
41
+
42
+ IMPORTANT: The token IMAGE_TAG_ONE is a special tag
43
+ and must be preserved exactly as is in your translation. Do not translate this token.
44
+ """
45
+
46
+ logger.info(f"Translation prompt: {translation_prompt}")
47
+ response = client.models.generate_content(
48
+ model="gemini-2.0-flash",
49
+ contents=[translation_prompt],
50
+ config=types.GenerateContentConfig(
51
+ response_modalities=['Text'],
52
+ temperature=0.2,
53
+ top_p=0.95,
54
+ top_k=40,
55
+ max_output_tokens=512
56
+ )
57
+ )
58
+
59
+ translated_text = ""
60
+ for part in response.candidates[0].content.parts:
61
+ if hasattr(part, 'text') and part.text:
62
+ translated_text += part.text
63
+
64
+ if translated_text.strip():
65
+ translated_text = translated_text.replace("IMAGE_TAG_ONE", "#1")
66
+ logger.info(f"Translated text: {translated_text.strip()}")
67
+ return translated_text.strip()
68
+ else:
69
+ logger.warning("๋ฒˆ์—ญ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ")
70
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
71
+ return prompt
72
  except Exception as e:
73
+ logger.exception("๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
74
+ prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
75
+ return prompt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
+ def preprocess_prompt(prompt, image1):
78
+ has_img1 = image1 is not None
 
 
 
79
 
80
+ if "#1" in prompt and not has_img1:
81
+ prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
82
+ else:
83
+ prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
84
 
85
+ prompt += " ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”. ์ด๋ฏธ์ง€์— ํ…์ŠคํŠธ๋‚˜ ๊ธ€์ž๋ฅผ ํฌํ•จํ•˜์ง€ ๋งˆ์„ธ์š”."
86
+ return prompt
87
 
88
+ def generate_with_images(prompt, images, variation_index=0):
89
+ try:
90
+ api_key = os.environ.get("GEMINI_API_KEY")
91
+ if not api_key:
92
+ return None, "API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
+ client = genai.Client(api_key=api_key)
95
+ logger.info(f"Gemini API ์š”์ฒญ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: {prompt}, ๋ณ€ํ˜• ์ธ๋ฑ์Šค: {variation_index}")
96
 
97
+ variation_suffixes = [
98
+ " Create this as the first variation. Do not add any text, watermarks, or labels to the image.",
99
+ " Create this as the second variation with more vivid colors. Do not add any text, watermarks, or labels to the image.",
100
+ " Create this as the third variation with a more creative style. Do not add any text, watermarks, or labels to the image.",
101
+ " Create this as the fourth variation with enhanced details. Do not add any text, watermarks, or labels to the image."
102
+ ]
103
+
104
+ if variation_index < len(variation_suffixes):
105
+ prompt = prompt + variation_suffixes[variation_index]
106
+ else:
107
+ prompt = prompt + " Do not add any text, watermarks, or labels to the image."
108
 
109
+ contents = [prompt]
110
+ for idx, img in enumerate(images, 1):
111
+ if img is not None:
112
+ contents.append(img)
113
+ logger.info(f"์ด๋ฏธ์ง€ #{idx} ์ถ”๊ฐ€๋จ")
 
 
 
 
114
 
115
+ response = client.models.generate_content(
116
+ model="gemini-2.0-flash-exp-image-generation",
117
+ contents=contents,
118
+ config=types.GenerateContentConfig(
119
+ response_modalities=['Text', 'Image'],
120
+ temperature=1,
 
 
 
 
121
  top_p=0.95,
122
+ top_k=40,
123
+ max_output_tokens=8192
124
  )
125
  )
126
+
127
+ # ์ž„์‹œ ํŒŒ์ผ์€ ํ•ญ์ƒ JPG ํ™•์žฅ์ž๋กœ ์ƒ์„ฑ
128
+ with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp:
129
+ temp_path = tmp.name
130
+ result_text = ""
131
+ image_found = False
132
+ for part in response.candidates[0].content.parts:
133
+ if hasattr(part, 'text') and part.text:
134
+ result_text += part.text
135
+ logger.info(f"์‘๋‹ต ํ…์ŠคํŠธ: {part.text}")
136
+ elif hasattr(part, 'inline_data') and part.inline_data:
137
+ save_binary_file(temp_path, part.inline_data.data)
138
+ image_found = True
139
+ logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
140
+ if not image_found:
141
+ return None, f"API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ํ…์ŠคํŠธ: {result_text}"
142
+
143
+ result_img = Image.open(temp_path)
144
+ if result_img.mode == "RGBA":
145
+ result_img = result_img.convert("RGB") # JPG๋Š” ํˆฌ๋ช…๋„๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ RGB๋กœ ๋ณ€ํ™˜
146
+
147
+ # ๋ณ€ํ™˜๋œ ์ด๋ฏธ์ง€๋ฅผ JPG๋กœ ์ €์žฅ
148
+ result_img.save(temp_path, format="JPEG", quality=95)
149
+
150
+ # ํŒŒ์ผ ๊ฒฝ๋กœ ๋ฐ˜ํ™˜
151
+ return temp_path, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
152
  except Exception as e:
153
+ logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
154
+ return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
155
 
156
+ def process_images_with_prompt(image1, prompt, variation_index=0, max_retries=3):
157
+ retry_count = 0
158
+ last_error = None
159
+
160
+ while retry_count < max_retries:
161
+ try:
162
+ images = [image1]
163
+ valid_images = [img for img in images if img is not None]
164
+ if not valid_images:
165
+ return None, "์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.", ""
166
+
167
+ if prompt and prompt.strip():
168
+ processed_prompt = preprocess_prompt(prompt, image1)
169
+ if re.search("[๊ฐ€-ํžฃ]", processed_prompt):
170
+ final_prompt = translate_prompt_to_english(processed_prompt)
171
+ else:
172
+ final_prompt = processed_prompt
173
+ else:
174
+ 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."
175
+ logger.info("Default prompt generated for single image")
176
+
177
+ result_img, status = generate_with_images(final_prompt, valid_images, variation_index)
178
+ if result_img is not None:
179
+ return result_img, status, final_prompt
180
+ else:
181
+ last_error = status
182
+ retry_count += 1
183
+ logger.warning(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹คํŒจ, ์žฌ์‹œ๋„ {retry_count}/{max_retries}: {status}")
184
+ time.sleep(1)
185
+ except Exception as e:
186
+ last_error = str(e)
187
+ retry_count += 1
188
+ logger.exception(f"์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ, ์žฌ์‹œ๋„ {retry_count}/{max_retries}:")
189
+ time.sleep(1)
190
+
191
+ return None, f"์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜({max_retries}ํšŒ) ์ดˆ๊ณผ ํ›„ ์‹คํŒจ: {last_error}", prompt
192
+
193
+ def generate_multiple_images(image1, prompt, progress=gr.Progress()):
194
+ results = []
195
+ statuses = []
196
+ prompts = []
197
+
198
+ num_images = 4
199
+ max_retries = 3
200
+
201
+ progress(0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค€๋น„ ์ค‘...")
202
+
203
+ for i in range(num_images):
204
+ progress((i / num_images), desc=f"{i+1}/{num_images} ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘...")
205
+ result_img, status, final_prompt = process_images_with_prompt(image1, prompt, i, max_retries)
206
 
207
+ if result_img is not None:
208
+ results.append(result_img)
209
+ statuses.append(f"์ด๋ฏธ์ง€ #{i+1}: {status}")
210
+ prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
211
+ else:
212
+ results.append(None)
213
+ statuses.append(f"์ด๋ฏธ์ง€ #{i+1} ์ƒ์„ฑ ์‹คํŒจ: {status}")
214
+ prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
215
 
216
+ time.sleep(1)
217
+
218
+ progress(1.0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ!")
219
+
220
+ while len(results) < 4:
221
+ results.append(None)
222
+
223
+ combined_status = "\n".join(statuses)
224
+ combined_prompts = "\n".join(prompts)
225
+
226
+ return results[0], results[1], results[2], results[3], combined_status, combined_prompts
227
+
228
+ # UI ๊ตฌ์„ฑ
229
+ with gr.Blocks() as demo:
230
+ with gr.Column():
231
+ with gr.Group():
232
+ with gr.Column():
233
+ with gr.Row():
234
+ image1_input = gr.Image(type="pil", label="์ด๋ฏธ์ง€ ์—…๋กœ๋“œ", image_mode="RGB")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
 
236
+ prompt_input = gr.Textbox(
 
 
 
237
  lines=3,
238
+ placeholder="ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”. '#1'์œผ๋กœ ์—…๋กœ๋“œํ•œ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
239
+ label="ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ"
240
  )
241
 
242
+ with gr.Row():
243
+ submit_single_btn = gr.Button('์ด๋ฏธ์ง€ ์ƒ์„ฑ (1์žฅ)')
244
+ submit_btn = gr.Button('์ด๋ฏธ์ง€ ์ƒ์„ฑ (4์žฅ)')
245
 
246
+ with gr.Column():
247
+ with gr.Row():
248
+ with gr.Column():
249
+ output_image1 = gr.Image(label="์ด๋ฏธ์ง€ #1", type="filepath")
250
+ output_image3 = gr.Image(label="์ด๋ฏธ์ง€ #3", type="filepath")
251
+ with gr.Column():
252
+ output_image2 = gr.Image(label="์ด๋ฏธ์ง€ #2", type="filepath")
253
+ output_image4 = gr.Image(label="์ด๋ฏธ์ง€ #4", type="filepath")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
+ output_text = gr.Textbox(label="๊ฒฐ๊ณผ ์ •๋ณด", lines=2)
256
+ prompt_display = gr.Textbox(label="์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ (์˜์–ด)", lines=2)
257
+
258
+ # ๋‹จ์ผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
259
+ submit_single_btn.click(
260
+ fn=lambda image1, prompt: process_images_with_prompt(image1, prompt, 0),
261
+ inputs=[image1_input, prompt_input],
262
+ outputs=[output_image1, output_text, prompt_display],
263
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
+ # 4์žฅ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
266
+ submit_btn.click(
267
+ fn=generate_multiple_images,
268
+ inputs=[image1_input, prompt_input],
269
+ outputs=[output_image1, output_image2, output_image3, output_image4, output_text, prompt_display],
270
+ )
271
 
272
+ demo.queue()
273
+ demo.launch()