ssboost commited on
Commit
73d4b03
ยท
verified ยท
1 Parent(s): a91da0a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1 -1034
app.py CHANGED
@@ -1,1035 +1,2 @@
1
  import os
2
- import tempfile
3
- from PIL import Image, ImageEnhance, ImageFilter
4
- import gradio as gr
5
- import logging
6
- import re
7
- import time
8
- import cv2
9
- import numpy as np
10
- from io import BytesIO
11
- from datetime import datetime, timedelta
12
-
13
- from google import genai
14
- from google.genai import types
15
- from dotenv import load_dotenv
16
-
17
- load_dotenv()
18
-
19
- # ๋กœ๊น… ์„ค์ •
20
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
21
- logger = logging.getLogger(__name__)
22
-
23
- # ------------------- API ํ‚ค ์ˆœํ™˜ ์‹œ์Šคํ…œ -------------------
24
- API_KEYS = [] # API ํ‚ค ๋ชฉ๋ก
25
- current_key_index = 0 # ํ˜„์žฌ ์‚ฌ์šฉ ์ค‘์ธ ํ‚ค ์ธ๋ฑ์Šค
26
-
27
- def initialize_api_keys():
28
- """API ํ‚ค ๋ชฉ๋ก์„ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ํ•จ์ˆ˜"""
29
- global API_KEYS
30
- # ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ API ํ‚ค ๊ฐ€์ ธ์˜ค๊ธฐ
31
- key1 = os.environ.get("GEMINI_API_KEY_1", "")
32
- key2 = os.environ.get("GEMINI_API_KEY_2", "")
33
- key3 = os.environ.get("GEMINI_API_KEY_3", "")
34
- key4 = os.environ.get("GEMINI_API_KEY_4", "")
35
- key5 = os.environ.get("GEMINI_API_KEY_5", "")
36
-
37
- # ๋นˆ ๋ฌธ์ž์—ด์ด ์•„๋‹Œ ํ‚ค๋งŒ ์ถ”๊ฐ€
38
- if key1:
39
- API_KEYS.append(key1)
40
- if key2:
41
- API_KEYS.append(key2)
42
- if key3:
43
- API_KEYS.append(key3)
44
- if key4:
45
- API_KEYS.append(key4)
46
- if key5:
47
- API_KEYS.append(key5)
48
-
49
- # ๊ธฐ์กด GEMINI_API_KEY๊ฐ€ ์žˆ์œผ๋ฉด ์ถ”๊ฐ€
50
- default_key = os.environ.get("GEMINI_API_KEY", "")
51
- if default_key and default_key not in API_KEYS:
52
- API_KEYS.append(default_key)
53
-
54
- logger.info(f"API ํ‚ค {len(API_KEYS)}๊ฐœ๊ฐ€ ๋กœ๋“œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
55
-
56
- def get_next_api_key():
57
- """๋‹ค์Œ API ํ‚ค๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜"""
58
- global current_key_index
59
-
60
- if not API_KEYS:
61
- return None
62
-
63
- # ํ˜„์žฌ ํ‚ค ๊ฐ€์ ธ์˜ค๊ธฐ
64
- api_key = API_KEYS[current_key_index]
65
-
66
- # ๋‹ค์Œ ํ‚ค ์ธ๋ฑ์Šค๋กœ ์—…๋ฐ์ดํŠธ
67
- current_key_index = (current_key_index + 1) % len(API_KEYS)
68
-
69
- return api_key
70
-
71
- # ========== ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ ๊ด€๋ จ ํ•จ์ˆ˜ ==========
72
- def save_binary_file(file_name, data):
73
- with open(file_name, "wb") as f:
74
- f.write(data)
75
-
76
- def translate_prompt_to_english(prompt):
77
- if not re.search("[๊ฐ€-ํžฃ]", prompt):
78
- return prompt
79
-
80
- prompt = prompt.replace("#1", "IMAGE_TAG_ONE")
81
- prompt = prompt.replace("#2", "IMAGE_TAG_TWO")
82
- prompt = prompt.replace("#3", "IMAGE_TAG_THREE")
83
-
84
- try:
85
- api_key = get_next_api_key()
86
- if not api_key:
87
- logger.error("Gemini API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
88
- prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
89
- prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
90
- prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
91
- return prompt
92
-
93
- client = genai.Client(api_key=api_key)
94
- translation_prompt = f"""
95
- Translate the following Korean text to English:
96
-
97
- {prompt}
98
-
99
- IMPORTANT: The tokens IMAGE_TAG_ONE, IMAGE_TAG_TWO, and IMAGE_TAG_THREE are special tags
100
- and must be preserved exactly as is in your translation. Do not translate these tokens.
101
- """
102
-
103
- logger.info(f"Translation prompt: {translation_prompt}")
104
- response = client.models.generate_content(
105
- model="gemini-2.0-flash",
106
- contents=[translation_prompt],
107
- config=types.GenerateContentConfig(
108
- response_modalities=['Text'],
109
- temperature=0.2,
110
- top_p=0.95,
111
- top_k=40,
112
- max_output_tokens=512
113
- )
114
- )
115
-
116
- translated_text = ""
117
- for part in response.candidates[0].content.parts:
118
- if hasattr(part, 'text') and part.text:
119
- translated_text += part.text
120
-
121
- if translated_text.strip():
122
- translated_text = translated_text.replace("IMAGE_TAG_ONE", "#1")
123
- translated_text = translated_text.replace("IMAGE_TAG_TWO", "#2")
124
- translated_text = translated_text.replace("IMAGE_TAG_THREE", "#3")
125
- logger.info(f"Translated text: {translated_text.strip()}")
126
- return translated_text.strip()
127
- else:
128
- logger.warning("๋ฒˆ์—ญ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ")
129
- prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
130
- prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
131
- prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
132
- return prompt
133
- except Exception as e:
134
- logger.exception("๋ฒˆ์—ญ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
135
- prompt = prompt.replace("IMAGE_TAG_ONE", "#1")
136
- prompt = prompt.replace("IMAGE_TAG_TWO", "#2")
137
- prompt = prompt.replace("IMAGE_TAG_THREE", "#3")
138
- return prompt
139
-
140
- def preprocess_prompt(prompt, image1, image2, image3):
141
- # ๊ธฐ์กด ํ•จ์ˆ˜ ์œ ์ง€
142
- has_img1 = image1 is not None
143
- has_img2 = image2 is not None
144
- has_img3 = image3 is not None
145
-
146
- if "#1" in prompt and not has_img1:
147
- prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
148
- else:
149
- prompt = prompt.replace("#1", "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
150
-
151
- if "#2" in prompt and not has_img2:
152
- prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
153
- else:
154
- prompt = prompt.replace("#2", "๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
155
-
156
- if "#3" in prompt and not has_img3:
157
- prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(์—†์Œ)")
158
- else:
159
- prompt = prompt.replace("#3", "์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€")
160
-
161
- if "1. ์ด๋ฏธ์ง€ ๋ณ€๊ฒฝ" in prompt:
162
- desc_match = re.search(r'#1์„ "(.*?)"์œผ๋กœ ๋ฐ”๊ฟ”๋ผ', prompt)
163
- if desc_match:
164
- description = desc_match.group(1)
165
- prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ {description}์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์›๋ณธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ๋‚ด์šฉ์€ ์œ ์ง€ํ•˜๋˜ ์ƒˆ๋กœ์šด ์Šคํƒ€์ผ๊ณผ ๋ถ„์œ„๊ธฐ๋กœ ์žฌํ•ด์„ํ•ด์ฃผ์„ธ์š”."
166
- else:
167
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์ฐฝ์˜์ ์œผ๋กœ ๋ณ€ํ˜•ํ•ด์ฃผ์„ธ์š”. ๋” ์ƒ์ƒํ•˜๊ณ  ์˜ˆ์ˆ ์ ์ธ ๋ฒ„์ „์œผ๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
168
-
169
- elif "2. ๊ธ€์ž์ง€์šฐ๊ธฐ" in prompt:
170
- text_match = re.search(r'#1์—์„œ "(.*?)"๋ฅผ ์ง€์›Œ๋ผ', prompt)
171
- if text_match:
172
- text_to_remove = text_match.group(1)
173
- prompt = f"์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ '{text_to_remove}' ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ํ…์ŠคํŠธ๊ฐ€ ์žˆ๋˜ ๋ถ€๋ถ„์„ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ์ฑ„์›Œ์ฃผ์„ธ์š”."
174
- else:
175
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์—์„œ ๋ชจ๋“  ํ…์ŠคํŠธ๋ฅผ ์ฐพ์•„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ œ๊ฑฐํ•ด์ฃผ์„ธ์š”. ๊น”๋”ํ•œ ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”."
176
-
177
- elif "4. ์˜ท๋ฐ”๊พธ๊ธฐ" in prompt:
178
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ธ๋ฌผ ์˜์ƒ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์˜์ƒ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์˜์ƒ์˜ ์Šคํƒ€์ผ๊ณผ ์ƒ‰์ƒ์€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋ฅด๋˜, ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€๋ฅผ ์œ ์ง€ํ•ด์ฃผ์„ธ์š”."
179
-
180
- elif "5. ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ" in prompt:
181
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์„ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ์œผ๋กœ ๋ณ€๊ฒฝํ•ด์ฃผ์„ธ์š”. ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ํ”ผ์‚ฌ์ฒด๋Š” ์œ ์ง€ํ•˜๊ณ , ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์˜ ๋ฐฐ๊ฒฝ๊ณผ ์กฐํ™”๋กญ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”."
182
-
183
- elif "6. ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ(์ƒํ’ˆํฌํ•จ)" in prompt:
184
- prompt = "์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€์™€ ๋‘ ๋ฒˆ์งธ ์ด๋ฏธ์ง€(๋˜๋Š” ์„ธ ๋ฒˆ์งธ ์ด๋ฏธ์ง€)๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ•ฉ์„ฑํ•ด์ฃผ์„ธ์š”. ๋ชจ๋“  ์ด๋ฏธ์ง€์˜ ์ฃผ์š” ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๊ณ , ํŠนํžˆ ์ƒํ’ˆ์ด ๋‹๋ณด์ด๋„๋ก ์กฐํ™”๋กญ๊ฒŒ ํ†ตํ•ฉํ•ด์ฃผ์„ธ์š”."
185
-
186
- prompt += " ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”. ์ด๋ฏธ์ง€์— ํ…์ŠคํŠธ๋‚˜ ๊ธ€์ž๋ฅผ ํฌํ•จํ•˜์ง€ ๋งˆ์„ธ์š”."
187
- return prompt
188
-
189
- def generate_with_images(prompt, images, variation_index=0):
190
- try:
191
- api_key = get_next_api_key()
192
- if not api_key:
193
- return None, "API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ™˜๊ฒฝ ๋ณ€์ˆ˜์— GEMINI_API_KEY_1, GEMINI_API_KEY_2, GEMINI_API_KEY_3, GEMINI_API_KEY_4, GEMINI_API_KEY_5 ์ค‘ ํ•˜๋‚˜ ์ด์ƒ์„ ์„ค์ •ํ•ด์ฃผ์„ธ์š”."
194
-
195
- client = genai.Client(api_key=api_key)
196
- logger.info(f"Gemini API ์š”์ฒญ ์‹œ์ž‘ - ํ”„๋กฌํ”„ํŠธ: {prompt}, ๋ณ€ํ˜• ์ธ๋ฑ์Šค: {variation_index}")
197
-
198
- variation_suffixes = [
199
- " Create this as the first variation. Do not add any text, watermarks, or labels to the image.",
200
- " Create this as the second variation with more vivid colors. Do not add any text, watermarks, or labels to the image.",
201
- " Create this as the third variation with a more creative style. Do not add any text, watermarks, or labels to the image.",
202
- " Create this as the fourth variation with enhanced details. Do not add any text, watermarks, or labels to the image."
203
- ]
204
-
205
- if variation_index < len(variation_suffixes):
206
- prompt = prompt + variation_suffixes[variation_index]
207
- else:
208
- prompt = prompt + " Do not add any text, watermarks, or labels to the image."
209
-
210
- contents = [prompt]
211
- for idx, img in enumerate(images, 1):
212
- if img is not None:
213
- contents.append(img)
214
- logger.info(f"์ด๋ฏธ์ง€ #{idx} ์ถ”๊ฐ€๋จ")
215
-
216
- response = client.models.generate_content(
217
- model="gemini-2.0-flash-exp-image-generation",
218
- contents=contents,
219
- config=types.GenerateContentConfig(
220
- response_modalities=['Text', 'Image'],
221
- temperature=1,
222
- top_p=0.95,
223
- top_k=40,
224
- max_output_tokens=8192
225
- )
226
- )
227
-
228
- with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
229
- temp_path = tmp.name
230
- result_text = ""
231
- image_found = False
232
- for part in response.candidates[0].content.parts:
233
- if hasattr(part, 'text') and part.text:
234
- result_text += part.text
235
- logger.info(f"์‘๋‹ต ํ…์ŠคํŠธ: {part.text}")
236
- elif hasattr(part, 'inline_data') and part.inline_data:
237
- save_binary_file(temp_path, part.inline_data.data)
238
- image_found = True
239
- logger.info("์‘๋‹ต์—์„œ ์ด๋ฏธ์ง€ ์ถ”์ถœ ์„ฑ๊ณต")
240
- if not image_found:
241
- return None, f"API์—์„œ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ํ…์ŠคํŠธ: {result_text}"
242
- result_img = Image.open(temp_path)
243
- if result_img.mode == "RGBA":
244
- result_img = result_img.convert("RGB")
245
- return result_img, f"์ด๋ฏธ์ง€๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. {result_text}"
246
- except Exception as e:
247
- logger.exception("์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:")
248
- return None, f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
249
-
250
- def process_images_with_prompt(image1, image2, image3, prompt, variation_index=0, max_retries=3):
251
- retry_count = 0
252
- last_error = None
253
-
254
- while retry_count < max_retries:
255
- try:
256
- images = [image1, image2, image3]
257
- valid_images = [img for img in images if img is not None]
258
- if not valid_images:
259
- return None, "์ ์–ด๋„ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”.", ""
260
-
261
- if prompt and prompt.strip():
262
- processed_prompt = preprocess_prompt(prompt, image1, image2, image3)
263
- if re.search("[๊ฐ€-ํžฃ]", processed_prompt):
264
- final_prompt = translate_prompt_to_english(processed_prompt)
265
- else:
266
- final_prompt = processed_prompt
267
- else:
268
- if len(valid_images) == 1:
269
- 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."
270
- logger.info("Default prompt generated for single image")
271
- elif len(valid_images) == 2:
272
- 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."
273
- logger.info("Default prompt generated for two images")
274
- else:
275
- 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."
276
- logger.info("Default prompt generated for three images")
277
-
278
- result_img, status = generate_with_images(final_prompt, valid_images, variation_index)
279
- if result_img is not None:
280
- return result_img, status, final_prompt
281
- else:
282
- last_error = status
283
- retry_count += 1
284
- logger.warning(f"์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‹คํŒจ, ์žฌ์‹œ๋„ {retry_count}/{max_retries}: {status}")
285
- time.sleep(1)
286
- except Exception as e:
287
- last_error = str(e)
288
- retry_count += 1
289
- logger.exception(f"์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ, ์žฌ์‹œ๋„ {retry_count}/{max_retries}:")
290
- time.sleep(1)
291
-
292
- return None, f"์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜({max_retries}ํšŒ) ์ดˆ๊ณผ ํ›„ ์‹คํŒจ: {last_error}", prompt
293
-
294
- def generate_multiple_images(image1, image2, image3, prompt, progress=gr.Progress()):
295
- results = []
296
- statuses = []
297
- prompts = []
298
-
299
- num_images = 4
300
- max_retries = 3
301
-
302
- progress(0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค€๋น„ ์ค‘...")
303
-
304
- for i in range(num_images):
305
- progress((i / num_images), desc=f"{i+1}/{num_images} ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์ค‘...")
306
- result_img, status, final_prompt = process_images_with_prompt(image1, image2, image3, prompt, i, max_retries)
307
-
308
- if result_img is not None:
309
- results.append(result_img)
310
- statuses.append(f"์ด๋ฏธ์ง€ #{i+1}: {status}")
311
- prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
312
- else:
313
- results.append(None)
314
- statuses.append(f"์ด๋ฏธ์ง€ #{i+1} ์ƒ์„ฑ ์‹คํŒจ: {status}")
315
- prompts.append(f"์ด๋ฏธ์ง€ #{i+1}: {final_prompt}")
316
-
317
- time.sleep(1)
318
-
319
- progress(1.0, desc="์ด๋ฏธ์ง€ ์ƒ์„ฑ ์™„๋ฃŒ!")
320
-
321
- while len(results) < 4:
322
- results.append(None)
323
-
324
- combined_status = "\n".join(statuses)
325
- combined_prompts = "\n".join(prompts)
326
-
327
- return results[0], results[1], results[2], results[3], combined_status, combined_prompts
328
-
329
- # ========== ์ด๋ฏธ์ง€ ํŽธ์ง‘๊ธฐ ๊ด€๋ จ ํ•จ์ˆ˜ ==========
330
- def adjust_brightness(image, value):
331
- """์ด๋ฏธ์ง€ ๋ฐ๊ธฐ ์กฐ์ ˆ"""
332
- value = float(value - 1) * 100 # 0-2 ๋ฒ”์œ„๋ฅผ -100์—์„œ +100์œผ๋กœ ๋ณ€ํ™˜
333
- hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
334
- h, s, v = cv2.split(hsv)
335
- v = cv2.add(v, value)
336
- v = np.clip(v, 0, 255)
337
- final_hsv = cv2.merge((h, s, v))
338
- return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
339
-
340
- def adjust_contrast(image, value):
341
- """์ด๋ฏธ์ง€ ๋Œ€๋น„ ์กฐ์ ˆ"""
342
- value = float(value)
343
- return np.clip(image * value, 0, 255).astype(np.uint8)
344
-
345
- def adjust_saturation(image, value):
346
- """์ด๋ฏธ์ง€ ์ฑ„๋„ ์กฐ์ ˆ"""
347
- value = float(value - 1) * 100 # 0-2 ๋ฒ”์œ„๋ฅผ -100์—์„œ +100์œผ๋กœ ๋ณ€ํ™˜
348
- hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
349
- h, s, v = cv2.split(hsv)
350
- s = cv2.add(s, value)
351
- s = np.clip(s, 0, 255)
352
- final_hsv = cv2.merge((h, s, v))
353
- return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
354
-
355
- def adjust_temperature(image, value):
356
- """์ด๋ฏธ์ง€ ์ƒ‰์˜จ๋„ ์กฐ์ ˆ (์ƒ‰์ƒ ๋ฐธ๋Ÿฐ์Šค)"""
357
- value = float(value) * 30 # ํšจ๊ณผ ์Šค์ผ€์ผ ์กฐ์ ˆ
358
- b, g, r = cv2.split(image)
359
- if value > 0: # ๋”ฐ๋œปํ•˜๊ฒŒ
360
- r = cv2.add(r, value)
361
- b = cv2.subtract(b, value)
362
- else: # ์ฐจ๊ฐ‘๊ฒŒ
363
- r = cv2.add(r, value)
364
- b = cv2.subtract(b, value)
365
-
366
- r = np.clip(r, 0, 255)
367
- b = np.clip(b, 0, 255)
368
- return cv2.merge([b, g, r])
369
-
370
- def adjust_tint(image, value):
371
- """์ด๋ฏธ์ง€ ์ƒ‰์กฐ ์กฐ์ ˆ"""
372
- hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
373
- h, s, v = cv2.split(hsv_image)
374
- h = cv2.add(h, int(value))
375
- h = np.clip(h, 0, 179) # Hue ๊ฐ’์€ 0-179 ๋ฒ”์œ„
376
- final_hsv = cv2.merge((h, s, v))
377
- return cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
378
-
379
- def adjust_exposure(image, value):
380
- """์ด๋ฏธ์ง€ ๋…ธ์ถœ ์กฐ์ ˆ"""
381
- enhancer = ImageEnhance.Brightness(Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)))
382
- img_enhanced = enhancer.enhance(1 + float(value) / 5.0)
383
- return cv2.cvtColor(np.array(img_enhanced), cv2.COLOR_RGB2BGR)
384
-
385
- def adjust_vibrance(image, value):
386
- """์ด๋ฏธ์ง€ ํ™œ๊ธฐ ์กฐ์ ˆ"""
387
- img = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
388
- converter = ImageEnhance.Color(img)
389
- factor = 1 + (float(value) / 100.0)
390
- img = converter.enhance(factor)
391
- return cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
392
-
393
- def adjust_color_mixer_blues(image, value):
394
- """์ด๋ฏธ์ง€ ์ปฌ๋Ÿฌ ๋ฏน์„œ (๋ธ”๋ฃจ) ์กฐ์ ˆ"""
395
- b, g, r = cv2.split(image)
396
- b = cv2.add(b, float(value))
397
- b = np.clip(b, 0, 255)
398
- return cv2.merge([b, g, r])
399
-
400
- def adjust_shadows(image, value):
401
- """์ด๋ฏธ์ง€ ๊ทธ๋ฆผ์ž ์กฐ์ ˆ"""
402
- pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
403
- enhancer = ImageEnhance.Brightness(pil_image)
404
- factor = 1 + (float(value) / 100.0)
405
- pil_image = enhancer.enhance(factor)
406
- return cv2.cvtColor(np.array(pil_image), cv2.COLOR_BGR2RGB)
407
-
408
- def process_image(image, brightness, contrast, saturation, temperature, tint, exposure, vibrance, color_mixer_blues, shadows):
409
- """๋ชจ๋“  ์กฐ์ • ์‚ฌํ•ญ์„ ์ด๋ฏธ์ง€์— ์ ์šฉ"""
410
- if image is None:
411
- return None
412
-
413
- # PIL ์ด๋ฏธ์ง€๋ฅผ OpenCV ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜
414
- image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
415
-
416
- # ์กฐ์ • ์‚ฌํ•ญ ์ˆœ์ฐจ ์ ์šฉ
417
- image = adjust_brightness(image, brightness)
418
- image = adjust_contrast(image, contrast)
419
- image = adjust_saturation(image, saturation)
420
- image = adjust_temperature(image, temperature)
421
- image = adjust_tint(image, tint)
422
- image = adjust_exposure(image, exposure)
423
- image = adjust_vibrance(image, vibrance)
424
- image = adjust_color_mixer_blues(image, color_mixer_blues)
425
- image = adjust_shadows(image, shadows)
426
-
427
- # PIL ์ด๋ฏธ์ง€๋กœ ๋‹ค์‹œ ๋ณ€ํ™˜
428
- return Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
429
-
430
- def download_edited_image(image, input_image_name):
431
- """์ด๋ฏธ์ง€๋ฅผ JPG ํ˜•์‹์œผ๋กœ ์ €์žฅํ•˜๊ณ  ๊ฒฝ๋กœ ๋ฐ˜ํ™˜"""
432
- if image is None:
433
- return None
434
-
435
- # ํ•œ๊ตญ ์‹œ๊ฐ„ ํƒ€์ž„์Šคํƒฌํ”„ ์ƒ์„ฑ
436
- def get_korean_timestamp():
437
- korea_time = datetime.utcnow() + timedelta(hours=9)
438
- return korea_time.strftime('%Y%m%d_%H%M%S')
439
-
440
- timestamp = get_korean_timestamp()
441
- if input_image_name and hasattr(input_image_name, 'name'):
442
- base_name = input_image_name.name.split('.')[0] # ํŒŒ์ผ ๊ฐ์ฒด์—์„œ ์ด๋ฆ„ ์ถ”์ถœ
443
- else:
444
- base_name = "์ด๋ฏธ์ง€"
445
-
446
- file_name = f"[๋์žฅAI]๋์žฅํ•„ํ„ฐ_{base_name}_{timestamp}.jpg"
447
-
448
- # ํŒŒ์ผ ์ €์žฅ
449
- temp_file_path = tempfile.gettempdir() + "/" + file_name
450
- image.save(temp_file_path, format="JPEG")
451
- return temp_file_path
452
-
453
- # ์ปค์Šคํ…€ CSS ์Šคํƒ€์ผ - ์ƒˆ๋กœ์šด ์Šคํƒ€์ผ ์ ์šฉ
454
- custom_css = """
455
- :root {
456
- --primary-color: #FB7F0D;
457
- --secondary-color: #ff9a8b;
458
- --accent-color: #FF6B6B;
459
- --background-color: #FFF3E9;
460
- --card-bg: #ffffff;
461
- --text-color: #334155;
462
- --border-radius: 18px;
463
- --shadow: 0 8px 30px rgba(251, 127, 13, 0.08);
464
- }
465
-
466
- body {
467
- font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
468
- background-color: var(--background-color);
469
- color: var(--text-color);
470
- line-height: 1.6;
471
- }
472
-
473
- /* Gradio ์ปจํ…Œ์ด๋„ˆ ์˜ค๋ฒ„๋ผ์ด๋“œ */
474
- .gradio-container {
475
- max-width: 100% !important; /* 200%์—์„œ 100%๋กœ ๋ณ€๊ฒฝ */
476
- width: 100% !important; /* ์ถ”๊ฐ€: ๋„ˆ๋น„ 100% ์ง€์ • */
477
- margin: 0 auto !important;
478
- padding: 0 !important;
479
- background-color: var(--background-color) !important;
480
- box-sizing: border-box !important; /* ์ถ”๊ฐ€: ํŒจ๋”ฉ์ด ๋„ˆ๋น„์— ํฌํ•จ๋˜๋„๋ก ์„ค์ • */
481
- }
482
-
483
- /* ์ถ”๊ฐ€: ๋‚ด๋ถ€ ์ปจํ…Œ์ด๋„ˆ๋„ 100% ๋„ˆ๋น„๋กœ ์„ค์ • */
484
- .contain {
485
- max-width: 100% !important;
486
- width: 100% !important;
487
- }
488
-
489
- /* ์ถ”๊ฐ€: ๊ฐ ํ–‰(Row)๋„ 100% ๋„ˆ๋น„๋กœ ์„ค์ • */
490
- .gr-padded {
491
- padding: 0 !important;
492
- width: 100% !important;
493
- max-width: 100% !important;
494
- }
495
-
496
- /* ํŒจ๋„ ์Šคํƒ€์ผ๋ง */
497
- .gr-group {
498
- background-color: var(--card-bg);
499
- border-radius: var(--border-radius) !important;
500
- box-shadow: var(--shadow) !important;
501
- padding: 1.5rem !important;
502
- margin-bottom: 1.5rem !important;
503
- border: 1px solid rgba(0, 0, 0, 0.04) !important;
504
- transition: transform 0.3s ease;
505
- }
506
-
507
- .gr-group:hover {
508
- transform: translateY(-5px);
509
- }
510
-
511
- /* ์„น์…˜ ์ œ๋ชฉ */
512
- .section-title {
513
- font-size: 22px !important;
514
- font-weight: 700 !important;
515
- color: #333333 !important;
516
- margin-bottom: 1rem !important;
517
- padding-bottom: 0.5rem !important;
518
- border-bottom: 2px solid var(--primary-color) !important;
519
- display: flex;
520
- align-items: center;
521
- }
522
-
523
- .section-title span {
524
- color: var(--primary-color);
525
- }
526
-
527
- /* ๋ฒ„ํŠผ ์Šคํƒ€์ผ๋ง */
528
- .custom-button {
529
- background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important;
530
- color: white !important;
531
- font-weight: 600 !important;
532
- border: none !important;
533
- border-radius: 30px !important;
534
- padding: 12px 24px !important;
535
- box-shadow: 0 4px 8px rgba(251, 127, 13, 0.25) !important;
536
- transition: all 0.3s ease !important;
537
- text-transform: none !important;
538
- display: flex !important;
539
- align-items: center !important;
540
- justify-content: center !important;
541
- }
542
-
543
- .custom-button:hover {
544
- transform: translateY(-2px) !important;
545
- box-shadow: 0 6px 12px rgba(251, 127, 13, 0.3) !important;
546
- }
547
-
548
- .custom-button.primary {
549
- background: linear-gradient(135deg, var(--accent-color), #ff9a8b) !important;
550
- }
551
-
552
- /* ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ */
553
- .image-container {
554
- border-radius: var(--border-radius);
555
- overflow: hidden;
556
- border: 1px solid rgba(0, 0, 0, 0.08);
557
- transition: all 0.3s ease;
558
- background-color: white;
559
- }
560
-
561
- .image-container:hover {
562
- box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
563
- }
564
-
565
- /* ํƒญ ์Šคํƒ€์ผ ๊ฐœ์„  */
566
- .tabs {
567
- border-bottom: none !important;
568
- }
569
-
570
- .tab-nav {
571
- background-color: transparent !important;
572
- border-bottom: 1px solid #eeeeee !important;
573
- padding: 0 !important;
574
- }
575
-
576
- .tab-nav button {
577
- border-radius: var(--border-radius) var(--border-radius) 0 0 !important;
578
- margin-right: 5px !important;
579
- padding: 12px 20px !important;
580
- font-size: 18px !important;
581
- font-weight: 600 !important;
582
- border: 1px solid #eeeeee !important;
583
- border-bottom: none !important;
584
- background-color: rgba(255, 255, 255, 0.7) !important;
585
- color: var(--text-color) !important;
586
- transition: all 0.3s ease !important;
587
- min-width: 150px !important;
588
- text-align: center !important;
589
- }
590
-
591
- .tab-nav button.selected {
592
- background-color: var(--primary-color) !important;
593
- color: white !important;
594
- border-color: var(--primary-color) !important;
595
- box-shadow: 0 -2px 6px rgba(251, 127, 13, 0.2) !important;
596
- }
597
-
598
- .tab-nav button:hover:not(.selected) {
599
- background-color: var(--background-color) !important;
600
- border-bottom: none !important;
601
- }
602
-
603
- /* ์ž…๋ ฅ ํ•„๋“œ ์Šคํƒ€์ผ */
604
- .gr-input, .gr-text-input, .gr-sample-inputs {
605
- border-radius: var(--border-radius) !important;
606
- border: 1px solid #dddddd !important;
607
- padding: 12px !important;
608
- box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05) !important;
609
- transition: all 0.3s ease !important;
610
- }
611
-
612
- .gr-input:focus, .gr-text-input:focus {
613
- border-color: var(--primary-color) !important;
614
- outline: none !important;
615
- box-shadow: 0 0 0 2px rgba(251, 127, 13, 0.2) !important;
616
- }
617
-
618
- /* ๋ฒ„ํŠผ ๊ทธ๋ฃน */
619
- .button-grid {
620
- display: grid;
621
- grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
622
- gap: 0.8rem;
623
- margin-bottom: 1.2rem;
624
- }
625
-
626
- /* ๋ฉ”์ธ ์ปจํ…์ธ  ์Šคํฌ๋กค๋ฐ” */
627
- ::-webkit-scrollbar {
628
- width: 8px;
629
- height: 8px;
630
- }
631
-
632
- ::-webkit-scrollbar-track {
633
- background: rgba(0, 0, 0, 0.05);
634
- border-radius: 10px;
635
- }
636
-
637
- ::-webkit-scrollbar-thumb {
638
- background: var(--primary-color);
639
- border-radius: 10px;
640
- }
641
-
642
- /* ํŽธ์ง‘๊ธฐ ํŠน๋ณ„ ์Šคํƒ€์ผ */
643
- .editor-section {
644
- background-color: var(--card-bg);
645
- border-radius: var(--border-radius);
646
- box-shadow: var(--shadow);
647
- padding: 1.5rem;
648
- margin-bottom: 1.5rem;
649
- }
650
-
651
- .editor-title {
652
- font-size: 1.5rem;
653
- font-weight: 700;
654
- color: var(--primary-color);
655
- margin-bottom: 1rem;
656
- display: flex;
657
- align-items: center;
658
- }
659
-
660
- .editor-title i {
661
- margin-right: 0.5rem;
662
- }
663
-
664
- .download-button {
665
- background-color: var(--accent-color) !important;
666
- color: white !important;
667
- border: none !important;
668
- padding: 10px !important;
669
- font-size: 12px !important;
670
- border-radius: var(--border-radius) !important;
671
- font-weight: 600 !important;
672
- transition: all 0.3s ease !important;
673
- }
674
-
675
- .download-button:hover {
676
- transform: translateY(-2px) !important;
677
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15) !important;
678
- }
679
-
680
- .download-container {
681
- display: flex;
682
- flex-direction: column;
683
- align-items: center;
684
- width: 100%;
685
- }
686
-
687
- .download-output {
688
- width: 100%;
689
- margin-top: 1rem;
690
- }
691
-
692
- /* ์• ๋‹ˆ๋ฉ”์ด์…˜ ์Šคํƒ€์ผ */
693
- @keyframes fadeIn {
694
- from { opacity: 0; transform: translateY(10px); }
695
- to { opacity: 1; transform: translateY(0); }
696
- }
697
-
698
- .fade-in {
699
- animation: fadeIn 0.5s ease-out;
700
- }
701
-
702
- /* Examples ์„น์…˜ ์Šคํƒ€์ผ */
703
- .examples-section {
704
- display: grid;
705
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
706
- gap: 1.5rem;
707
- margin-top: 1rem;
708
- }
709
-
710
- .example-item {
711
- background-color: white;
712
- border-radius: var(--border-radius);
713
- overflow: hidden;
714
- box-shadow: var(--shadow);
715
- transition: transform 0.3s ease;
716
- }
717
-
718
- .example-item:hover {
719
- transform: translateY(-5px);
720
- }
721
-
722
- /* ๋ฐ˜์‘ํ˜• */
723
- @media (max-width: 768px) {
724
- .button-grid {
725
- grid-template-columns: repeat(2, 1fr);
726
- }
727
-
728
- .examples-section {
729
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
730
- }
731
- }
732
- """ # CSS ๋ฌธ์ž์—ด ์ข…๋ฃŒ
733
-
734
- # FontAwesome ์•„์ด์ฝ˜ ํฌํ•จ
735
- fontawesome_link = """
736
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer" />
737
- """
738
-
739
- # ์ œ๋ชฉ๊ณผ ์‚ฌ์šฉ ๊ฐ€์ด๋“œ ์ œ๊ฑฐ
740
- header_html = ""
741
- image_generator_guide_html = ""
742
- image_editor_guide_html = ""
743
-
744
- # UI ๊ตฌ์„ฑ
745
- with gr.Blocks(css=custom_css, theme=gr.themes.Default(
746
- primary_hue="orange",
747
- secondary_hue="orange",
748
- font=[gr.themes.GoogleFont("Noto Sans KR"), "ui-sans-serif", "system-ui"]
749
- )) as demo:
750
- gr.HTML(fontawesome_link)
751
- # ์ œ๋ชฉ ์ œ๊ฑฐ
752
- # gr.HTML(header_html)
753
-
754
- with gr.Tabs(elem_classes="tabs") as tabs:
755
- # ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ ํƒญ
756
- with gr.Tab("โœจ ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ", elem_classes="tab-content"):
757
- # ์‚ฌ์šฉ ๊ฐ€์ด๋“œ ์„น์…˜ ์ œ๊ฑฐ
758
- # gr.HTML(image_generator_guide_html)
759
- with gr.Row(equal_height=True):
760
- with gr.Column(scale=1):
761
- # API ํ‚ค ์ž…๋ ฅ ์„น์…˜ ์ œ๊ฑฐ
762
-
763
- # ======== ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๋ฐ ์„ค์ • ์„น์…˜ ========
764
- with gr.Group():
765
- gr.HTML('<div class="section-title"><i class="fas fa-upload"></i> <span>์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ๋ฐ ์„ค์ •</span></div>')
766
- with gr.Row():
767
- image1_input = gr.Image(type="pil", label="#1", image_mode="RGB", elem_classes="image-container", height=400)
768
- image2_input = gr.Image(type="pil", label="#2", image_mode="RGB", elem_classes="image-container", height=400)
769
- image3_input = gr.Image(type="pil", label="#3", image_mode="RGB", elem_classes="image-container", height=400)
770
-
771
- # ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ ํ•„๋“œ ์ถ”๊ฐ€
772
- prompt_input = gr.Textbox(
773
- lines=3,
774
- placeholder="ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ ๋น„์›Œ๋‘๋ฉด ์ž๋™ ํ•ฉ์„ฑ๋ฉ๋‹ˆ๋‹ค. '#1', '#2', '#3'์œผ๋กœ ๊ฐ ์ด๋ฏธ์ง€๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
775
- label="ํ”„๋กฌํ”„ํŠธ (์„ ํƒ ์‚ฌํ•ญ)",
776
- elem_classes="gr-text-input"
777
- )
778
-
779
- # ======== ๋ณ€ํ™˜ ์˜ต์…˜ ์„น์…˜ ========
780
- with gr.Group():
781
- gr.HTML('<div class="section-title"><i class="fas fa-sliders-h"></i> <span>ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ</span></div>')
782
- with gr.Column(elem_classes="button-grid"):
783
- image_change_btn1 = gr.Button('๐Ÿ”„ ๋ถ€๋ถ„๋ณ€๊ฒฝ-1', elem_classes="custom-button")
784
- image_change_btn2 = gr.Button('๐Ÿ”„ ๋ถ€๋ถ„๋ณ€๊ฒฝ-2', elem_classes="custom-button")
785
- image_change_btn3= gr.Button('๐Ÿ”„ ๋ถ€๋ถ„๋ณ€๊ฒฝ-3', elem_classes="custom-button")
786
- text_remove_btn = gr.Button('๐Ÿงน ๊ธ€์ž์ง€์šฐ๊ธฐ', elem_classes="custom-button")
787
- text_change_btn = gr.Button('๐Ÿ”ค ๊ธ€์ž๋ณ€๊ฒฝ', elem_classes="custom-button")
788
- clothes_change_btn1 = gr.Button('๐Ÿ‘• ์ƒํ’ˆ์ฐฉ์šฉ-1', elem_classes="custom-button")
789
- clothes_change_btn2 = gr.Button('๐Ÿ‘“ ์ƒํ’ˆ์ฐฉ์šฉ-2', elem_classes="custom-button")
790
- holding_product_btn = gr.Button('๐Ÿท ์ƒํ’ˆ๋“ค๊ณ ', elem_classes="custom-button")
791
- background_change_btn = gr.Button('๐Ÿ–ผ๏ธ ๋ฐฐ๊ฒฝ๋ฐ”๊พธ๊ธฐ', elem_classes="custom-button")
792
- composite_product_btn = gr.Button('โœ‚๏ธ ๋ถ€๋ถ„์ง€์šฐ๊ธฐ', elem_classes="custom-button")
793
- outpainting_btn = gr.Button('๐Ÿ” ์ด๋ฏธ์ง€ํ™•์žฅ', elem_classes="custom-button")
794
- food_btn_1 = gr.Button('๐Ÿฝ๏ธ ํ”Œ๋ ˆ์ดํŒ…-1', elem_classes="custom-button")
795
- food_btn_2 = gr.Button('๐Ÿฝ๏ธ ํ”Œ๋ ˆ์ดํŒ…-2', elem_classes="custom-button")
796
- food_btn_3 = gr.Button('๐Ÿฝ๏ธ ํ”Œ๋ ˆ์ดํŒ…-3', elem_classes="custom-button")
797
-
798
- # ======== ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์„น์…˜ ========
799
- with gr.Group():
800
- gr.HTML('<div class="section-title"><i class="fas fa-image"></i> <span>์ด๋ฏธ์ง€ ์ƒ์„ฑ</span></div>')
801
- submit_single_btn = gr.Button('โœจ ์ด๋ฏธ์ง€ ์ƒ์„ฑ (1์žฅ)', elem_classes="custom-button primary")
802
- submit_btn = gr.Button('โœจ ์ด๋ฏธ์ง€ ์ƒ์„ฑ (4์žฅ)', elem_classes="custom-button primary")
803
-
804
- with gr.Column(scale=1):
805
- # ======== ์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€ ์„น์…˜ ========
806
- with gr.Group():
807
- gr.HTML('<div class="section-title"><i class="fas fa-images"></i> <span>์ƒ์„ฑ๋œ ์ด๋ฏธ์ง€</span></div>')
808
- with gr.Row():
809
- with gr.Column():
810
- output_image1 = gr.Image(label="์ด๋ฏธ์ง€ #1", elem_classes="image-container", height=400)
811
- output_image3 = gr.Image(label="์ด๋ฏธ์ง€ #3", elem_classes="image-container", height=400)
812
- with gr.Column():
813
- output_image2 = gr.Image(label="์ด๋ฏธ์ง€ #2", elem_classes="image-container", height=400)
814
- output_image4 = gr.Image(label="์ด๋ฏธ์ง€ #4", elem_classes="image-container", height=400)
815
-
816
- # ======== ๊ฒฐ๊ณผ ์ •๋ณด ์„น์…˜ ========
817
- with gr.Group():
818
- gr.HTML('<div class="section-title"><i class="fas fa-info-circle"></i> <span>๊ฒฐ๊ณผ ์ •๋ณด</span></div>')
819
- output_text = gr.Textbox(label="์ƒํƒœ ๋ฉ”์‹œ์ง€", lines=2, elem_classes="gr-text-input")
820
- prompt_display = gr.Textbox(label="์‚ฌ์šฉ๋œ ํ”„๋กฌํ”„ํŠธ (์˜์–ด)", visible=True, lines=2, elem_classes="gr-text-input")
821
-
822
- # ======== ์˜ˆ์ œ ์ด๋ฏธ์ง€ ์„น์…˜ ========
823
- gr.HTML('<div class="section-title"><i class="fas fa-lightbulb"></i> <span>์˜ˆ์ œ ์ด๋ฏธ์ง€</span></div>')
824
-
825
- # ๋ชจ๋“  ์˜ˆ์ œ ํ•œ ํŽ˜์ด์ง€์— ํ‘œ์‹œ
826
- examples = [
827
- ["down/๋ชจ๋ธ.jpg", None, None, "(#1์˜ ์—ฌ์„ฑ)์ด ์‚ด์ง ๋’ค๋กœ ๋Œ์•„๋ณด๋Š” ๋ชจ์Šต์œผ๋กœ ์ตœ๋Œ€ํ•œ ์ด์ „ seed๋ฅผ ์œ ์ง€ํ•œ์ฒด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ผ."],
828
- ["down/์ƒ์–ด๋ ˆ๊ณ ๋ชจํ˜•.png", None, None, "(#1 ๋ ˆ๋ชจ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋งŒ ๊ฒ€์€์ƒ‰ ๊ณ ๋ž˜๋ ˆ๊ณ ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ seed๋ฅผ ๋ณ€๊ฒฝ์„ ํ•˜์ง€๋งˆ๋ผ."],
829
- ["down/์–ผ์Œ๊ฐ€๋ฐฉ.png", None, None, "(#1 ์—ฌํ–‰์šฉ ์–ผ์Œ๋ฐ•์Šค)์•ž์— ์–ผ์Œ์ด ๋‹ด๊ธด 3์ž”์˜ ์ฝœ๋ผ๊ฐ€ ๋†“์—ฌ์žˆ๋Š” ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ."],
830
- ["down/์ค‘๊ตญ์–ด.png", None, None, "(#1 ์ด๋ฏธ์ง€)์— ์žˆ๋Š” ์ค‘๊ตญ์–ด๋ฅผ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜๋ผ."],
831
- ["down/ํ…์ŠคํŠธ.webp", None, None, '(#1์˜ ํ…์ŠคํŠธ)๋ฅผ ์Šคํƒ€์ผ์„ ์œ ์ง€ํ•œ์ฒด ํ…์ŠคํŠธ๋งŒ "Hello"๋กœ ๋ฐ”๊ฟ”๋ผ'],
832
- ["down/๋ชจ๋ธ2.png", "down/์„ ๊ธ€๋ผ์Šค.png", "down/์ฒญ๋ฐ”์ง€.png", "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์œ ์ง€ํ•œ ์ฒด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์™€ (#3์˜ ์ฒญ๋ฐ”์ง€)๋ฅผ ์ง์ ‘ ๋ชจ๋ธ์ด ์ฐฉ์šฉํ•œ๊ฒƒ ์ฒ˜๋Ÿผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ."],
833
- ["down/๋ชจ๋ธ2.png", "down/์„ ๊ธ€๋ผ์Šค.png", "down/์นดํŽ˜์ „๊ฒฝ.png", "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์„ ์ฐฉ์šฉํ•˜๊ณ  (#3์˜ ๋’ท๋ฐฐ๊ฒฝ์˜ ์นดํŽ˜์ „์ฒด๊ฐ€ ๋ณด์ด๋ฉฐ) ์˜์ž์— ์•‰์•„ ์žˆ๋Š” ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ."],
834
- ["down/๋ชจ๋ธ2.png", "down/์™€์ธ์ž”.png", None, "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด(#2์˜ ์™€์ธ์ž”)์„ ๋“ค๊ณ  ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ."],
835
- ["down/๋ชจ๋ธ2.png", "down/์นดํŽ˜์ „๊ฒฝ.png", None, "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2 ์นดํŽ˜)์—์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์žˆ๋Š” ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ."],
836
- ["down/์ƒ์–ด๋ ˆ๊ณ ๋ชจํ˜•.png", None, None, "(#1์˜ ๋ ˆ๊ณ ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋ฅผ ์ œ๊ฑฐํ•œ ํ›„, ๊ทธ ์ž๋ฆฌ๋ฅผ ์ฃผ๋ณ€ ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ฑ„์›Œ์ฃผ์„ธ์š”. ๋‹จ, ์ด๋ฏธ์ง€์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์˜ ์ฃผ์š” ์š”์†Œ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ํ•ด์•ผํ•œ๋‹ค."],
837
- ["down/์นดํŽ˜์ „๊ฒฝ.png", None, None, "(#1 ์ด๋ฏธ์ง€)๋ฅผ ์›๋ณธ๊ทธ๋Œ€๋กœ ์ค‘์•™์— ๋‘๊ณ  ๋น„์œจ๋กœ ์œ ์ง€ํ•œ ์ฒด ์œ„์•„๋ž˜ ๋ฐ ์ขŒ์šฐ๋กœ ํฌ๊ฒŒ ํ™•์žฅํ•˜๋ผ."],
838
- ["down/์ƒ๋Ÿฌ๋“œ.png", None, None, "(#1์…€๋Ÿฌ๋“œ)์— ๋‹ด์€ ์šฉ๊ธฐ๋Š” ๋ฒ„๋ฆฌ๊ณ  ๋„“๊ณ  ํฐ ์˜ˆ์œ ์ ‘์‹œ์— (#1์…€๋Ÿฌ๋“œ)์Œ์‹๋งŒ ๊ฐ€๋“ ์ฑ„์›Œ์„œ ์ƒ์—…์ ์ธ ๊ฐ๋„๋กœ ์–ด์šธ๋ฆฌ๋Š” ์†Œํ’ˆ๊ณผ ํ•จ๊ป˜ ํ”Œ๋ ˆ์ดํŒ… ํ•œ ๋ชจ์Šต์„ ์ด๋ฏธ์ง€๋กœ ์ƒ์„ฑํ•˜๋ผ. "],
839
- ["down/์ƒ๋Ÿฌ๋“œ.png", "down/ํ”Œ๋ ˆ์ดํŒ….png", None, "(#2 ํ”Œ๋ ˆ์ดํŒ…ํ•œ ์ด๋ฏธ์ง€)์— ๋‹ด๊ธด ์Œ์‹์„ (#1 ์ƒ๋Ÿฌ๋“œ)๋กœ ๋ฐ”๊พธ๊ณ  ๋‚˜๋จธ์ง€๋Š” ์‹œ๋“œ๋ฅผ ์œ ์ง€ํ•œ ์ฒด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ."],
840
- ["down/์ปต.png", None, None, "(#1์ปต)์— ๋”ธ๊ธฐ, ๋ฐ”๋‹๋ผ, ์ดˆ์ฝ” ์•„์ด์Šคํฌ๋ฆผ์„ ๋‹ด๊ณ  ๊ทธ ์œ„์— ์ดˆ์ฝ” ์‹œ๋Ÿฝ์ด ํ๋ฅด๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ."]
841
- ]
842
-
843
- # ๋ชจ๋“  ์˜ˆ์ œ๋ฅผ ํ•œ ํŽ˜์ด์ง€์— ํ‘œ์‹œํ•˜๋„๋ก ์ˆ˜์ •๋œ ๋ถ€๋ถ„
844
- gr.Examples(
845
- examples=examples,
846
- inputs=[image1_input, image2_input, image3_input, prompt_input],
847
- examples_per_page=len(examples) # ๋ชจ๋“  ์˜ˆ์ œ๋ฅผ ํ•œ ํŽ˜์ด์ง€์— ํ‘œ์‹œ
848
- )
849
-
850
- # ์ด๋ฏธ์ง€ ํŽธ์ง‘๊ธฐ ํƒญ ์ถ”๊ฐ€
851
- with gr.Tab("๐ŸŽจ ์ด๋ฏธ์ง€ ํŽธ์ง‘๊ธฐ", elem_classes="tab-content"):
852
- # ์‚ฌ์šฉ ๊ฐ€์ด๋“œ ์„น์…˜ ์ œ๊ฑฐ
853
- # gr.HTML(image_editor_guide_html)
854
-
855
- with gr.Row():
856
- # ์™ผ์ชฝ ์—ด: ๋น„์œจ 1
857
- with gr.Column(scale=1):
858
- # ======== ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์„น์…˜ ========
859
- with gr.Group():
860
- gr.HTML('<div class="section-title"><i class="fas fa-upload"></i> <span>์ด๋ฏธ์ง€ ์—…๋กœ๋“œ</span></div>')
861
- edit_input_image = gr.Image(type="pil", label="ํŽธ์ง‘ํ•  ์ด๋ฏธ์ง€", elem_classes="image-container")
862
-
863
- # ======== ์ด๋ฏธ์ง€ ์กฐ์ • ์„น์…˜ ========
864
- with gr.Group():
865
- gr.HTML('<div class="section-title"><i class="fas fa-sliders-h"></i> <span>์ด๋ฏธ์ง€ ์กฐ์ •</span></div>')
866
- brightness_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="๋ฐ๊ธฐ ์กฐ์ ˆ")
867
- contrast_slider = gr.Slider(0.5, 1.5, value=1.0, step=0.1, label="๋Œ€๋น„ ์กฐ์ ˆ")
868
- saturation_slider = gr.Slider(0.0, 2.0, value=1.0, step=0.1, label="์ฑ„๋„ ์กฐ์ ˆ")
869
- temperature_slider = gr.Slider(-1.0, 1.0, value=0.0, step=0.1, label="์ƒ‰์˜จ๋„ ์กฐ์ ˆ")
870
- tint_slider = gr.Slider(-100, 100, value=0, step=1, label="์ƒ‰์กฐ ์กฐ์ ˆ")
871
- exposure_slider = gr.Slider(-5.0, 5.0, value=0.0, step=0.1, label="๋…ธ์ถœ ์กฐ์ ˆ")
872
- vibrance_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="ํ™œ๊ธฐ ์กฐ์ ˆ")
873
- color_mixer_blues_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="์ปฌ๋Ÿฌ ๋ฏน์„œ (๋ธ”๋ฃจ)")
874
- shadows_slider = gr.Slider(-100.0, 100.0, value=0.0, step=1.0, label="๊ทธ๋ฆผ์ž ์กฐ์ ˆ")
875
-
876
- # ์˜ค๋ฅธ์ชฝ ์—ด: ๋น„์œจ 1
877
- with gr.Column(scale=1):
878
- # ======== ํŽธ์ง‘๋œ ์ด๋ฏธ์ง€ ์„น์…˜ ========
879
- with gr.Group():
880
- gr.HTML('<div class="section-title"><i class="fas fa-images"></i> <span>ํŽธ์ง‘๋œ ์ด๋ฏธ์ง€</span></div>')
881
- edit_output_image = gr.Image(type="pil", label="ํŽธ์ง‘๋œ ์ด๋ฏธ์ง€", elem_classes="image-container")
882
-
883
- # ======== ์ €์žฅ ์„น์…˜ ========
884
- with gr.Group():
885
- gr.HTML('<div class="section-title"><i class="fas fa-download"></i> <span>์ €์žฅ</span></div>')
886
- with gr.Row(elem_classes="download-container"):
887
- download_button = gr.Button("JPG๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ", elem_classes="download-button")
888
- with gr.Row(elem_classes="download-container"):
889
- download_output = gr.File(label="JPG ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ", elem_classes="download-output")
890
-
891
- # ========== ์ด๋ฏธ์ง€ ์ƒ์„ฑ๊ธฐ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ ==========
892
- # ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ
893
- image_change_btn1.click(
894
- fn=lambda: "(#1์˜ ์—ฌ์„ฑ)์ด ์‚ด์ง ๋’ค๋กœ ๋Œ์•„๋ณด๋Š” ๋ชจ์Šต์œผ๋กœ ์ตœ๋Œ€ํ•œ ์ด์ „ seed๋ฅผ ์œ ์ง€ํ•œํ…Œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ผ.",
895
- inputs=[],
896
- outputs=prompt_input
897
- )
898
- image_change_btn2.click(
899
- fn=lambda: "(#1 ๋ ˆ๋ชจ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋งŒ ๊ฒ€์€์ƒ‰ ๊ณ ๋ž˜๋ ˆ๊ณ ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์€ seed๋ฅผ ๋ณ€๊ฒฝ์„ ํ•˜์ง€๋งˆ๋ผ.",
900
- inputs=[],
901
- outputs=prompt_input
902
- )
903
- image_change_btn3.click(
904
- fn=lambda: "(#1 ์—ฌํ–‰์šฉ ์–ผ์Œ๋ฐ•์Šค)์•ž์— ์–ผ์Œ์ด ๋‹ด๊ธด 3์ž”์˜ ์ฝœ๋ผ๊ฐ€ ๋†“์—ฌ์žˆ๋Š” ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ.",
905
- inputs=[],
906
- outputs=prompt_input
907
- )
908
-
909
- text_remove_btn.click(
910
- fn=lambda: "(#1 ์ด๋ฏธ์ง€)์— ์žˆ๋Š” ์ค‘๊ตญ์–ด๋ฅผ ๋ชจ๋‘ ์ œ๊ฑฐํ•˜๋ผ.",
911
- inputs=[],
912
- outputs=prompt_input
913
- )
914
- text_change_btn.click(
915
- fn=lambda: '(#1์˜ ํ…์ŠคํŠธ)๋ฅผ ์Šคํƒ€์ผ์„ ์œ ์ง€ํ•œ์ฒด ํ…์ŠคํŠธ๋งŒ "Hello"๋กœ ๋ฐ”๊ฟ”๋ผ',
916
- inputs=[],
917
- outputs=prompt_input
918
- )
919
- clothes_change_btn1.click(
920
- fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด ์‹ ์ฒด ๋น„์œจ๊ณผ ํฌ์ฆˆ๋Š” ์œ ์ง€ํ•œ ์ฒด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์™€ (#3์˜ ์ฒญ๋ฐ”์ง€)๋ฅผ ์ง์ ‘ ๋ชจ๋ธ์ด ์ฐฉ์šฉํ•œ๊ฒƒ ์ฒ˜๋Ÿผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ.",
921
- inputs=[],
922
- outputs=prompt_input
923
- )
924
- clothes_change_btn2.click(
925
- fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2์˜ ์„ ๊ธ€๋ผ์Šค)์„ ์ฐฉ์šฉํ•˜๏ฟฝ๏ฟฝ๏ฟฝ (#3์˜ ๋’ท๋ฐฐ๊ฒฝ์˜ ์นดํŽ˜์ „์ฒด๊ฐ€ ๋ณด์ด๋ฉฐ) ์˜์ž์— ์•‰์•„ ์žˆ๋Š” ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ.",
926
- inputs=[],
927
- outputs=prompt_input
928
- )
929
- holding_product_btn.click(
930
- fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด(#2์˜ ์™€์ธ์ž”)์„ ๋“ค๊ณ  ์žˆ๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ.",
931
- inputs=[],
932
- outputs=prompt_input
933
- )
934
- background_change_btn.click(
935
- fn=lambda: "(#1์˜ ์—ฌ์„ฑ๋ชจ๋ธ)์ด (#2 ์นดํŽ˜)์—์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์žˆ๋Š” ๋ชจ์Šต์„ ์ƒ์„ฑํ•˜๋ผ.",
936
- inputs=[],
937
- outputs=prompt_input
938
- )
939
- composite_product_btn.click(
940
- fn=lambda: "(#1์˜ ๋ ˆ๊ณ ๋ชจํ˜•)์—์„œ ์ฒญ์ƒ‰์ƒ์–ด๋ ˆ๊ณ ๋ฅผ ์ œ๊ฑฐํ•œ ํ›„, ๊ทธ ์ž๋ฆฌ๋ฅผ ์ฃผ๋ณ€ ๋ฐฐ๊ฒฝ๊ณผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์–ด์šฐ๋Ÿฌ์ง€๋„๋ก ์ฑ„์›Œ์ฃผ์„ธ์š”. ๋‹จ, ์ด๋ฏธ์ง€์˜ ๋‹ค๋ฅธ ๋ถ€๋ถ„์˜ ์ฃผ์š” ์š”์†Œ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€ ํ•ด์•ผํ•œ๋‹ค.",
941
- inputs=[],
942
- outputs=prompt_input
943
- )
944
- outpainting_btn.click(
945
- fn=lambda: "(#1 ์ด๋ฏธ์ง€)๋ฅผ ์›๋ณธ๊ทธ๋Œ€๋กœ ์ค‘์•™์— ๋‘๊ณ  ๋น„์œจ๋กœ ์œ ์ง€ํ•œ ์ฒด ์œ„์•„๋ž˜ ๋ฐ ์ขŒ์šฐ๋กœ ํฌ๊ฒŒ ํ™•์žฅํ•˜๋ผ.",
946
- inputs=[],
947
- outputs=prompt_input
948
-
949
- )
950
- food_btn_1.click(
951
- fn=lambda: "(#1์…€๋Ÿฌ๋“œ)์— ๋‹ด์€ ์šฉ๊ธฐ๋Š” ๋ฒ„๋ฆฌ๊ณ  ๋„“๊ณ  ํฐ ์˜ˆ์œ ์ ‘์‹œ์— (#1์…€๋Ÿฌ๋“œ)์Œ์‹๋งŒ ๊ฐ€๋“ ์ฑ„์›Œ์„œ ์ƒ์—…์ ์ธ ๊ฐ๋„๋กœ ์–ด์šธ๋ฆฌ๋Š” ์†Œํ’ˆ๊ณผ ํ•จ๊ป˜ ํ”Œ๋ ˆ์ดํŒ… ํ•œ ๋ชจ์Šต์„ ์ด๋ฏธ์ง€๋กœ ์ƒ์„ฑํ•˜๋ผ. ",
952
- inputs=[],
953
- outputs=prompt_input
954
- )
955
- food_btn_2.click(
956
- fn=lambda: "(#2 ํ”Œ๋ ˆ์ดํŒ…ํ•œ ์ด๋ฏธ์ง€)์— ๋‹ด๊ธด ์Œ์‹์„ (#1 ์ƒ๋Ÿฌ๋“œ)๋กœ ๋ฐ”๊พธ๊ณ  ๋‚˜๋จธ์ง€๋Š” ์‹œ๋“œ๋ฅผ ์œ ์ง€ํ•œ ์ฒด ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ.",
957
- inputs=[],
958
- outputs=prompt_input
959
- )
960
-
961
- food_btn_3.click(
962
- fn=lambda: "(#1์ปต)์— ๋”ธ๊ธฐ, ๋ฐ”๋‹๋ผ, ์ดˆ์ฝ” ์•„์ด์Šคํฌ๋ฆผ์„ ๋‹ด๊ณ  ๊ทธ ์œ„์— ์ดˆ์ฝ” ์‹œ๋Ÿฝ์ด ํ๋ฅด๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋ผ.",
963
- inputs=[],
964
- outputs=prompt_input
965
- )
966
-
967
- # ๋‹จ์ผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - API ํ‚ค ์ž…๋ ฅ๊ฐ’ ์ œ๊ฑฐ
968
- def generate_single_image(image1, image2, image3, prompt):
969
- return process_images_with_prompt(image1, image2, image3, prompt, 0, 3)
970
-
971
- submit_single_btn.click(
972
- fn=generate_single_image,
973
- inputs=[image1_input, image2_input, image3_input, prompt_input],
974
- outputs=[output_image1, output_text, prompt_display],
975
- )
976
-
977
- # 4์žฅ ์ด๋ฏธ์ง€ ์ƒ์„ฑ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ - API ํ‚ค ์ž…๋ ฅ๊ฐ’ ์ œ๊ฑฐ
978
- submit_btn.click(
979
- fn=generate_multiple_images,
980
- inputs=[image1_input, image2_input, image3_input, prompt_input],
981
- outputs=[output_image1, output_image2, output_image3, output_image4, output_text, prompt_display],
982
- )
983
-
984
- # ========== ์ด๋ฏธ์ง€ ํŽธ์ง‘๊ธฐ ์ด๋ฒคํŠธ ์—ฐ๊ฒฐ ==========
985
- # ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜ ์—ฐ๊ฒฐ
986
- edit_inputs = [
987
- edit_input_image,
988
- brightness_slider,
989
- contrast_slider,
990
- saturation_slider,
991
- temperature_slider,
992
- tint_slider,
993
- exposure_slider,
994
- vibrance_slider,
995
- color_mixer_blues_slider,
996
- shadows_slider
997
- ]
998
-
999
- edit_input_components = [
1000
- brightness_slider,
1001
- contrast_slider,
1002
- saturation_slider,
1003
- temperature_slider,
1004
- tint_slider,
1005
- exposure_slider,
1006
- vibrance_slider,
1007
- color_mixer_blues_slider,
1008
- shadows_slider
1009
- ]
1010
-
1011
- for input_component in edit_input_components:
1012
- input_component.change(
1013
- fn=process_image,
1014
- inputs=edit_inputs,
1015
- outputs=edit_output_image
1016
- )
1017
-
1018
- # ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ ์‹œ ์ž๋™์œผ๋กœ ํŽธ์ง‘ ์—…๋ฐ์ดํŠธ
1019
- edit_input_image.change(
1020
- fn=process_image,
1021
- inputs=edit_inputs,
1022
- outputs=edit_output_image
1023
- )
1024
-
1025
- # ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ๊ธฐ๋Šฅ
1026
- download_button.click(
1027
- fn=download_edited_image,
1028
- inputs=[edit_output_image, edit_input_image],
1029
- outputs=download_output
1030
- )
1031
-
1032
- # API ํ‚ค ์ดˆ๊ธฐํ™” ๋ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰
1033
- initialize_api_keys() # API ํ‚ค ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜ ํ˜ธ์ถœ
1034
- demo.queue()
1035
- demo.launch(share=False, inbrowser=True, width="100%") # width ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€
 
1
  import os
2
+ exec(os.environ.get('APP'))