Update app.py
Browse files
app.py
CHANGED
@@ -8,6 +8,7 @@ import gradio as gr
|
|
8 |
import base64
|
9 |
import mimetypes
|
10 |
import logging
|
|
|
11 |
|
12 |
from google import genai
|
13 |
from google.genai import types
|
@@ -29,9 +30,9 @@ def save_binary_file(file_name, data):
|
|
29 |
logger.debug(f"ํ์ผ ์ ์ฅ ์๋ฃ: {file_name}")
|
30 |
|
31 |
|
32 |
-
def generate(text,
|
33 |
-
logger.debug(f"generate ํจ์ ์์ - ํ
์คํธ: '{text}', ํ์ผ๋ช
: '{
|
34 |
-
|
35 |
try:
|
36 |
# API ํค๋ ํ๊ฒฝ๋ณ์์์ ๋ถ๋ฌ์ด
|
37 |
effective_api_key = os.environ.get("GEMINI_API_KEY")
|
@@ -44,68 +45,43 @@ def generate(text, file_name, background_file=None, style_file=None, model="gemi
|
|
44 |
client = genai.Client(api_key=effective_api_key)
|
45 |
logger.debug("Gemini ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ์๋ฃ.")
|
46 |
|
47 |
-
#
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
#
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
)
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
# ์คํ์ผ ์ด๋ฏธ์ง ํํธ (์กด์ฌ ์)
|
79 |
-
if style_file is not None:
|
80 |
-
# ๋ฐฐ๊ฒฝ์ด ์๋ ๊ฒฝ์ฐ uploaded_files[1]๊ฐ ์คํ์ผ ์ด๋ฏธ์ง๊ฐ ๋จ์ ์ฃผ์
|
81 |
-
style_index = 2 if background_file is not None else 1
|
82 |
-
parts.append(
|
83 |
-
types.Part.from_uri(
|
84 |
-
file_uri=uploaded_files[style_index].uri,
|
85 |
-
mime_type=uploaded_files[style_index].mime_type,
|
86 |
-
)
|
87 |
-
)
|
88 |
-
# ๋ง์ง๋ง์ผ๋ก ํ
์คํธ ํํธ ์ถ๊ฐ
|
89 |
-
parts.append(types.Part.from_text(text=text))
|
90 |
-
|
91 |
-
contents = [
|
92 |
-
types.Content(
|
93 |
-
role="user",
|
94 |
-
parts=parts,
|
95 |
-
),
|
96 |
-
]
|
97 |
-
logger.debug(f"์ปจํ
์ธ ๊ฐ์ฒด ์์ฑ ์๋ฃ: {contents}")
|
98 |
-
|
99 |
generate_content_config = types.GenerateContentConfig(
|
100 |
temperature=1,
|
101 |
top_p=0.95,
|
102 |
top_k=40,
|
103 |
max_output_tokens=8192,
|
104 |
-
response_modalities=[
|
105 |
-
"image",
|
106 |
-
"text",
|
107 |
-
],
|
108 |
-
response_mime_type="text/plain",
|
109 |
)
|
110 |
logger.debug(f"์์ฑ ์ค์ : {generate_content_config}")
|
111 |
|
@@ -113,30 +89,31 @@ def generate(text, file_name, background_file=None, style_file=None, model="gemi
|
|
113 |
temp_path = tmp.name
|
114 |
logger.debug(f"์์ ํ์ผ ์์ฑ๋จ: {temp_path}")
|
115 |
|
116 |
-
|
|
|
117 |
model=model,
|
118 |
contents=contents,
|
119 |
config=generate_content_config,
|
120 |
)
|
121 |
-
|
122 |
-
logger.debug("์๋ต
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
logger.
|
137 |
-
|
138 |
-
|
139 |
-
logger.debug("
|
140 |
return temp_path
|
141 |
|
142 |
except Exception as e:
|
@@ -169,20 +146,41 @@ def process_image_and_prompt(original_pil, prompt, background_pil=None, style_pi
|
|
169 |
style_pil.save(style_path)
|
170 |
logger.debug(f"์คํ์ผ ์ด๋ฏธ์ง ์ ์ฅ ์๋ฃ: {style_path}")
|
171 |
|
172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
173 |
model = "gemini-2.0-flash-exp-image-generation"
|
174 |
|
175 |
-
gemma_edited_image_path = generate(
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
|
|
|
|
180 |
|
181 |
if gemma_edited_image_path:
|
182 |
logger.debug(f"์ด๋ฏธ์ง ์์ฑ ์๋ฃ. ๊ฒฝ๋ก: {gemma_edited_image_path}")
|
183 |
result_img = Image.open(gemma_edited_image_path)
|
184 |
if result_img.mode == "RGBA":
|
185 |
result_img = result_img.convert("RGB")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
186 |
return [result_img]
|
187 |
else:
|
188 |
logger.error("generate ํจ์์์ None ๋ฐํ๋จ.")
|
@@ -212,9 +210,9 @@ with gr.Blocks() as demo:
|
|
212 |
|
213 |
with gr.Row():
|
214 |
with gr.Column():
|
215 |
-
original_input = gr.Image(type="pil", label="์๋ณธ ์ด๋ฏธ์ง", image_mode="
|
216 |
-
background_input = gr.Image(type="pil", label="๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง", image_mode="
|
217 |
-
style_input = gr.Image(type="pil", label="์คํ์ผ ์ด๋ฏธ์ง", image_mode="
|
218 |
prompt_input = gr.Textbox(
|
219 |
lines=2,
|
220 |
placeholder="ํธ์งํ ๋ด์ฉ์ ์
๋ ฅํ์ธ์...",
|
@@ -223,6 +221,7 @@ with gr.Blocks() as demo:
|
|
223 |
submit_btn = gr.Button("์ด๋ฏธ์ง ํธ์ง ์คํ")
|
224 |
with gr.Column():
|
225 |
output_gallery = gr.Gallery(label="ํธ์ง ๊ฒฐ๊ณผ")
|
|
|
226 |
|
227 |
submit_btn.click(
|
228 |
fn=process_image_and_prompt,
|
@@ -230,19 +229,17 @@ with gr.Blocks() as demo:
|
|
230 |
outputs=output_gallery,
|
231 |
)
|
232 |
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
demo.launch(share=True)
|
|
|
8 |
import base64
|
9 |
import mimetypes
|
10 |
import logging
|
11 |
+
from io import BytesIO
|
12 |
|
13 |
from google import genai
|
14 |
from google.genai import types
|
|
|
30 |
logger.debug(f"ํ์ผ ์ ์ฅ ์๋ฃ: {file_name}")
|
31 |
|
32 |
|
33 |
+
def generate(text, original_image_path, background_image_path=None, style_image_path=None, model="gemini-2.0-flash-exp-image-generation"):
|
34 |
+
logger.debug(f"generate ํจ์ ์์ - ํ
์คํธ: '{text}', ์๋ณธ ํ์ผ๋ช
: '{original_image_path}', ๋ชจ๋ธ: '{model}'")
|
35 |
+
|
36 |
try:
|
37 |
# API ํค๋ ํ๊ฒฝ๋ณ์์์ ๋ถ๋ฌ์ด
|
38 |
effective_api_key = os.environ.get("GEMINI_API_KEY")
|
|
|
45 |
client = genai.Client(api_key=effective_api_key)
|
46 |
logger.debug("Gemini ํด๋ผ์ด์ธํธ ์ด๊ธฐํ ์๋ฃ.")
|
47 |
|
48 |
+
# PIL ์ด๋ฏธ์ง ๊ฐ์ฒด๋ก ๋ณํ
|
49 |
+
original_img = Image.open(original_image_path)
|
50 |
+
|
51 |
+
# ์ปจํ
์ธ ๋ฆฌ์คํธ ์์ฑ (๊ณต์ ๋ฌธ์ ๋ฐฉ์๋๋ก)
|
52 |
+
contents = []
|
53 |
+
|
54 |
+
# ํ
์คํธ ํ๋กฌํํธ๋ฅผ ๋ช
ํํ๊ฒ ์์ฑ
|
55 |
+
prompt = text
|
56 |
+
if background_image_path and "๋ฐฐ๊ฒฝ" not in text.lower():
|
57 |
+
prompt += " ์๋ณธ ์ด๋ฏธ์ง์ ๋ฐฐ๊ฒฝ์ ๋ ๋ฒ์งธ ์
๋ก๋๋ ์ด๋ฏธ์ง๋ก ์์ ํ ๊ต์ฒดํด ์ฃผ์ธ์. ์ด๋ฏธ์ง๋ฅผ ์
๋ฐ์ดํธํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ์ธ์."
|
58 |
+
if style_image_path and "์คํ์ผ" not in text.lower():
|
59 |
+
prompt += " ์ธ ๋ฒ์งธ ์ด๋ฏธ์ง์ ์คํ์ผ์ ์ ์ฒด์ ์ผ๋ก ์ ์ฉํด ์ฃผ์ธ์."
|
60 |
+
|
61 |
+
contents.append(prompt)
|
62 |
+
contents.append(original_img)
|
63 |
+
|
64 |
+
# ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์ถ๊ฐ (์๋ ๊ฒฝ์ฐ)
|
65 |
+
if background_image_path:
|
66 |
+
background_img = Image.open(background_image_path)
|
67 |
+
contents.append(background_img)
|
68 |
+
logger.debug("๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์ถ๊ฐ๋จ")
|
69 |
+
|
70 |
+
# ์คํ์ผ ์ด๋ฏธ์ง ์ถ๊ฐ (์๋ ๊ฒฝ์ฐ)
|
71 |
+
if style_image_path:
|
72 |
+
style_img = Image.open(style_image_path)
|
73 |
+
contents.append(style_img)
|
74 |
+
logger.debug("์คํ์ผ ์ด๋ฏธ์ง ์ถ๊ฐ๋จ")
|
75 |
+
|
76 |
+
logger.debug(f"์ปจํ
์ธ ๊ฐ์ฒด ์์ฑ ์๋ฃ: {len(contents)} ์์ดํ
")
|
77 |
+
|
78 |
+
# ์์ฑ ์ค์
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
generate_content_config = types.GenerateContentConfig(
|
80 |
temperature=1,
|
81 |
top_p=0.95,
|
82 |
top_k=40,
|
83 |
max_output_tokens=8192,
|
84 |
+
response_modalities=["text", "image"], # ๋ฌธ์์ ๋ง๊ฒ ๋์๋ฌธ์ ์์
|
|
|
|
|
|
|
|
|
85 |
)
|
86 |
logger.debug(f"์์ฑ ์ค์ : {generate_content_config}")
|
87 |
|
|
|
89 |
temp_path = tmp.name
|
90 |
logger.debug(f"์์ ํ์ผ ์์ฑ๋จ: {temp_path}")
|
91 |
|
92 |
+
# ์คํธ๋ฆฌ๋ฐ ๋์ ๋จ์ผ ์์ฒญ์ผ๋ก ๋ณ๊ฒฝ (์ด๋ฏธ์ง ์์ฑ์ ๋ ์ ํฉ)
|
93 |
+
response = client.models.generate_content(
|
94 |
model=model,
|
95 |
contents=contents,
|
96 |
config=generate_content_config,
|
97 |
)
|
98 |
+
|
99 |
+
logger.debug("์๋ต ์ฒ๋ฆฌ ์์...")
|
100 |
+
|
101 |
+
# ์๋ต์์ ์ด๋ฏธ์ง ์ถ์ถ
|
102 |
+
image_saved = False
|
103 |
+
for part in response.candidates[0].content.parts:
|
104 |
+
if hasattr(part, 'text') and part.text:
|
105 |
+
logger.info(f"์์ ๋ ํ
์คํธ: {part.text}")
|
106 |
+
print(part.text)
|
107 |
+
elif hasattr(part, 'inline_data') and part.inline_data:
|
108 |
+
save_binary_file(temp_path, part.inline_data.data)
|
109 |
+
logger.info(f"MIME ํ์
{part.inline_data.mime_type}์ ํ์ผ์ด ์ ์ฅ๋จ: {temp_path}")
|
110 |
+
image_saved = True
|
111 |
+
|
112 |
+
if not image_saved:
|
113 |
+
logger.warning("์ด๋ฏธ์ง๊ฐ ์์ฑ๋์ง ์์์ต๋๋ค.")
|
114 |
+
return None
|
115 |
+
|
116 |
+
logger.debug("์ด๋ฏธ์ง ์์ฑ ์๋ฃ.")
|
117 |
return temp_path
|
118 |
|
119 |
except Exception as e:
|
|
|
146 |
style_pil.save(style_path)
|
147 |
logger.debug(f"์คํ์ผ ์ด๋ฏธ์ง ์ ์ฅ ์๋ฃ: {style_path}")
|
148 |
|
149 |
+
# ํ๋กฌํํธ ๋ณด๊ฐ (์์ด์ ํ๊ตญ์ด ๋ ์ธ์ด๋ก ์ ๊ณต)
|
150 |
+
if prompt and not prompt.strip():
|
151 |
+
if background_path and style_path:
|
152 |
+
prompt = "์๋ณธ ์ด๋ฏธ์ง์ ์ธ๋ฌผ์ ์ ์งํ๋ฉด์ ๋ฐฐ๊ฒฝ์ ๋ ๋ฒ์งธ ์ด๋ฏธ์ง๋ก ๊ต์ฒดํ๊ณ ์ธ ๋ฒ์งธ ์ด๋ฏธ์ง์ ์คํ์ผ์ ์ ์ฉํด ์ฃผ์ธ์. Please replace the background with the second image while keeping the person, and apply the style of the third image."
|
153 |
+
elif background_path:
|
154 |
+
prompt = "์๋ณธ ์ด๋ฏธ์ง์ ์ธ๋ฌผ์ ์ ์งํ๋ฉด์ ๋ฐฐ๊ฒฝ์ ๋ ๋ฒ์งธ ์ด๋ฏธ์ง๋ก ๊ต์ฒดํด ์ฃผ์ธ์. Please replace the background with the second image while keeping the person."
|
155 |
+
elif style_path:
|
156 |
+
prompt = "์๋ณธ ์ด๋ฏธ์ง์ ๋ ๋ฒ์งธ ์ด๋ฏธ์ง์ ์คํ์ผ์ ์ ์ฉํด ์ฃผ์ธ์. Please apply the style of the second image to the original image."
|
157 |
+
|
158 |
model = "gemini-2.0-flash-exp-image-generation"
|
159 |
|
160 |
+
gemma_edited_image_path = generate(
|
161 |
+
text=prompt,
|
162 |
+
original_image_path=original_path,
|
163 |
+
background_image_path=background_path,
|
164 |
+
style_image_path=style_path,
|
165 |
+
model=model
|
166 |
+
)
|
167 |
|
168 |
if gemma_edited_image_path:
|
169 |
logger.debug(f"์ด๋ฏธ์ง ์์ฑ ์๋ฃ. ๊ฒฝ๋ก: {gemma_edited_image_path}")
|
170 |
result_img = Image.open(gemma_edited_image_path)
|
171 |
if result_img.mode == "RGBA":
|
172 |
result_img = result_img.convert("RGB")
|
173 |
+
|
174 |
+
# ์์ ํ์ผ ์ ๋ฆฌ
|
175 |
+
try:
|
176 |
+
os.unlink(original_path)
|
177 |
+
if background_path:
|
178 |
+
os.unlink(background_path)
|
179 |
+
if style_path:
|
180 |
+
os.unlink(style_path)
|
181 |
+
except Exception as e:
|
182 |
+
logger.warning(f"์์ ํ์ผ ์ญ์ ์ค ์ค๋ฅ: {str(e)}")
|
183 |
+
|
184 |
return [result_img]
|
185 |
else:
|
186 |
logger.error("generate ํจ์์์ None ๋ฐํ๋จ.")
|
|
|
210 |
|
211 |
with gr.Row():
|
212 |
with gr.Column():
|
213 |
+
original_input = gr.Image(type="pil", label="์๋ณธ ์ด๋ฏธ์ง", image_mode="RGB")
|
214 |
+
background_input = gr.Image(type="pil", label="๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง", image_mode="RGB")
|
215 |
+
style_input = gr.Image(type="pil", label="์คํ์ผ ์ด๋ฏธ์ง", image_mode="RGB")
|
216 |
prompt_input = gr.Textbox(
|
217 |
lines=2,
|
218 |
placeholder="ํธ์งํ ๋ด์ฉ์ ์
๋ ฅํ์ธ์...",
|
|
|
221 |
submit_btn = gr.Button("์ด๋ฏธ์ง ํธ์ง ์คํ")
|
222 |
with gr.Column():
|
223 |
output_gallery = gr.Gallery(label="ํธ์ง ๊ฒฐ๊ณผ")
|
224 |
+
output_text = gr.Textbox(label="API ์๋ต ํ
์คํธ", visible=True)
|
225 |
|
226 |
submit_btn.click(
|
227 |
fn=process_image_and_prompt,
|
|
|
229 |
outputs=output_gallery,
|
230 |
)
|
231 |
|
232 |
+
gr.HTML("""
|
233 |
+
<div style="margin-top: 20px; padding: 10px; background-color: #f8f9fa; border-radius: 8px;">
|
234 |
+
<h3>์ฌ์ฉ ํ:</h3>
|
235 |
+
<ul>
|
236 |
+
<li><strong>ํ๋กฌํํธ ์์ฑ ์์:</strong> "์๋ณธ ์ธ๋ฌผ์ ์ ์งํ๋ฉด์ ๋ฐฐ๊ฒฝ์ ๋ ๋ฒ์งธ ์ด๋ฏธ์ง๋ก ๊ต์ฒดํด ์ฃผ์ธ์."</li>
|
237 |
+
<li><strong>๋ฐฐ๊ฒฝ ๊ต์ฒด ๋ช
ํํ:</strong> "๋ฐฐ๊ฒฝ๋ง ์์ ํ ๊ต์ฒดํ๊ณ ์๋ณธ ์ธ๋ฌผ์ ๊ทธ๋๋ก ์ ์งํด ์ฃผ์ธ์."</li>
|
238 |
+
<li><strong>์คํ์ผ ์ ์ฉ:</strong> "์ธ ๋ฒ์งธ ์ด๋ฏธ์ง์ ์์ ์คํ์ผ์ ์ ์ฒด ์ด๋ฏธ์ง์ ์ ์ฉํด ์ฃผ์ธ์."</li>
|
239 |
+
<li><strong>์์ด ํ๋กฌํํธ:</strong> ๋ ๋์ ๊ฒฐ๊ณผ๋ฅผ ์ํด ์์ด์ ํ๊ตญ์ด๋ฅผ ํจ๊ป ์ฌ์ฉํด ๋ณด์ธ์.</li>
|
240 |
+
</ul>
|
241 |
+
</div>
|
242 |
+
""")
|
243 |
+
|
244 |
+
# --- ์คํ ---
|
245 |
+
demo.launch(share=True)
|
|
|
|