Update app.py
Browse files
app.py
CHANGED
@@ -10,11 +10,11 @@ from io import BytesIO
|
|
10 |
from google import genai
|
11 |
from google.genai import types
|
12 |
|
13 |
-
#
|
14 |
from dotenv import load_dotenv
|
15 |
load_dotenv()
|
16 |
|
17 |
-
#
|
18 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
19 |
logger = logging.getLogger(__name__)
|
20 |
|
@@ -24,83 +24,83 @@ def save_binary_file(file_name, data):
|
|
24 |
|
25 |
def preprocess_prompt(prompt, image1, image2, image3):
|
26 |
"""
|
27 |
-
|
28 |
"""
|
29 |
-
#
|
30 |
has_img1 = image1 is not None
|
31 |
has_img2 = image2 is not None
|
32 |
has_img3 = image3 is not None
|
33 |
|
34 |
-
# #1, #2, #3
|
35 |
if "#1" in prompt:
|
36 |
-
prompt = prompt.replace("#1", "
|
37 |
if "#2" in prompt:
|
38 |
-
prompt = prompt.replace("#2", "
|
39 |
if "#3" in prompt:
|
40 |
-
prompt = prompt.replace("#3", "
|
41 |
|
42 |
-
#
|
43 |
-
if "1.
|
44 |
-
desc_match = re.search(r'#1
|
45 |
if desc_match:
|
46 |
description = desc_match.group(1)
|
47 |
-
prompt = f"
|
48 |
else:
|
49 |
-
prompt = "
|
50 |
|
51 |
-
elif "2.
|
52 |
-
text_match = re.search(r'
|
53 |
if text_match:
|
54 |
text_to_remove = text_match.group(1)
|
55 |
-
prompt = f"
|
56 |
else:
|
57 |
-
prompt = "
|
58 |
|
59 |
-
elif "3.
|
60 |
-
prompt = "
|
61 |
|
62 |
-
elif "4.
|
63 |
-
if "#3" in prompt or "
|
64 |
-
prompt = "
|
65 |
else:
|
66 |
-
prompt = "
|
67 |
|
68 |
-
elif "5.
|
69 |
-
prompt = "
|
70 |
|
71 |
-
elif "6.
|
72 |
-
if "#3" in prompt or "
|
73 |
-
prompt = "
|
74 |
else:
|
75 |
-
prompt = "
|
76 |
|
77 |
-
elif "7.
|
78 |
-
prompt = "
|
79 |
|
80 |
-
elif "
|
81 |
-
prompt = "
|
82 |
|
83 |
-
prompt += "
|
84 |
return prompt
|
85 |
|
86 |
def generate_with_images(prompt, images):
|
87 |
"""
|
88 |
-
|
89 |
"""
|
90 |
try:
|
91 |
api_key = os.environ.get("GEMINI_API_KEY")
|
92 |
if not api_key:
|
93 |
-
return None, "API
|
94 |
|
95 |
client = genai.Client(api_key=api_key)
|
96 |
-
logger.info(f"Gemini API
|
97 |
|
98 |
contents = []
|
99 |
contents.append(prompt)
|
100 |
for idx, img in enumerate(images, 1):
|
101 |
if img is not None:
|
102 |
contents.append(img)
|
103 |
-
logger.info(f"
|
104 |
|
105 |
response = client.models.generate_content(
|
106 |
model="gemini-2.0-flash-exp-image-generation",
|
@@ -123,145 +123,134 @@ def generate_with_images(prompt, images):
|
|
123 |
for part in response.candidates[0].content.parts:
|
124 |
if hasattr(part, 'text') and part.text:
|
125 |
result_text += part.text
|
126 |
-
logger.info(f"
|
127 |
elif hasattr(part, 'inline_data') and part.inline_data:
|
128 |
save_binary_file(temp_path, part.inline_data.data)
|
129 |
image_found = True
|
130 |
-
logger.info("
|
131 |
|
132 |
if not image_found:
|
133 |
-
return None, f"API
|
134 |
|
135 |
result_img = Image.open(temp_path)
|
136 |
if result_img.mode == "RGBA":
|
137 |
result_img = result_img.convert("RGB")
|
138 |
|
139 |
-
return result_img, f"
|
140 |
|
141 |
except Exception as e:
|
142 |
-
logger.exception("
|
143 |
-
return None, f"
|
144 |
|
145 |
def process_images_with_prompt(image1, image2, image3, prompt):
|
146 |
"""
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
"""
|
151 |
try:
|
152 |
images = [image1, image2, image3]
|
153 |
-
valid_images = [img for img in images if img is not None]
|
154 |
|
155 |
-
# 프롬프트 자동 생성 (업로드된 이미지 수에 따라)
|
156 |
if not prompt or not prompt.strip():
|
157 |
if len(valid_images) == 0:
|
158 |
-
prompt = "
|
159 |
-
logger.info("
|
160 |
elif len(valid_images) == 1:
|
161 |
-
prompt = "
|
162 |
-
logger.info("
|
163 |
elif len(valid_images) == 2:
|
164 |
-
prompt = "
|
165 |
-
logger.info("
|
166 |
else:
|
167 |
-
prompt = "
|
168 |
-
logger.info("
|
169 |
else:
|
170 |
prompt = preprocess_prompt(prompt, image1, image2, image3)
|
171 |
|
172 |
-
# 최대 3회까지 재시도
|
173 |
max_retries = 3
|
174 |
for attempt in range(max_retries):
|
175 |
result_img, status = generate_with_images(prompt, valid_images)
|
176 |
if result_img is not None:
|
177 |
return result_img, status
|
178 |
else:
|
179 |
-
logger.info(f"
|
180 |
-
return None, f"
|
181 |
|
182 |
except Exception as e:
|
183 |
-
logger.exception("
|
184 |
-
return None, f"
|
185 |
|
186 |
def update_prompt_from_function(function_choice):
|
187 |
"""
|
188 |
-
|
189 |
-
(
|
190 |
"""
|
191 |
function_templates = {
|
192 |
-
"1.
|
193 |
-
"2.
|
194 |
-
"3.
|
195 |
-
"4.
|
196 |
-
"5.
|
197 |
-
"6.
|
198 |
-
"7.
|
199 |
}
|
200 |
|
201 |
return function_templates.get(function_choice, "")
|
202 |
|
203 |
-
# Gradio
|
204 |
with gr.Blocks() as demo:
|
205 |
gr.HTML(
|
206 |
"""
|
207 |
<div style="text-align: center; margin-bottom: 1rem;">
|
208 |
-
<h1
|
209 |
-
<p
|
210 |
</div>
|
211 |
"""
|
212 |
)
|
213 |
|
214 |
with gr.Row():
|
215 |
with gr.Column():
|
216 |
-
# 3개의 이미지 입력 (업로드하지 않아도 됨)
|
217 |
with gr.Row():
|
218 |
image1_input = gr.Image(type="pil", label="#1", image_mode="RGB")
|
219 |
image2_input = gr.Image(type="pil", label="#2", image_mode="RGB")
|
220 |
image3_input = gr.Image(type="pil", label="#3", image_mode="RGB")
|
221 |
|
222 |
-
# 기능 선택 드롭다운 (커스텀 텍스트 입력 제거)
|
223 |
function_dropdown = gr.Dropdown(
|
224 |
choices=[
|
225 |
-
"1.
|
226 |
-
"2.
|
227 |
-
"3.
|
228 |
-
"4.
|
229 |
-
"5.
|
230 |
-
"6.
|
231 |
-
"7.
|
232 |
],
|
233 |
-
label="
|
234 |
value=None
|
235 |
)
|
236 |
|
237 |
-
# 프롬프트 입력 (선택 사항)
|
238 |
prompt_input = gr.Textbox(
|
239 |
lines=3,
|
240 |
-
placeholder="
|
241 |
-
label="
|
242 |
)
|
243 |
|
244 |
-
|
245 |
-
submit_btn = gr.Button("이미지 생성", variant="primary")
|
246 |
|
247 |
with gr.Column():
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
# 사용된 프롬프트 표시
|
253 |
-
prompt_display = gr.Textbox(label="사용된 프롬프트", visible=True)
|
254 |
|
255 |
-
#
|
256 |
function_dropdown.change(
|
257 |
fn=update_prompt_from_function,
|
258 |
inputs=[function_dropdown],
|
259 |
outputs=[prompt_input]
|
260 |
)
|
261 |
|
262 |
-
# 이미지 생성 버튼 클릭 이벤트
|
263 |
def process_and_show_prompt(image1, image2, image3, prompt):
|
264 |
-
# 이미지 개수 확인
|
265 |
images = [image1, image2, image3]
|
266 |
valid_images = [img for img in images if img is not None]
|
267 |
|
@@ -269,13 +258,13 @@ with gr.Blocks() as demo:
|
|
269 |
auto_prompt = prompt
|
270 |
if not prompt or not prompt.strip():
|
271 |
if len(valid_images) == 0:
|
272 |
-
auto_prompt = "
|
273 |
elif len(valid_images) == 1:
|
274 |
-
auto_prompt = "
|
275 |
elif len(valid_images) == 2:
|
276 |
-
auto_prompt = "
|
277 |
else:
|
278 |
-
auto_prompt = "
|
279 |
else:
|
280 |
auto_prompt = preprocess_prompt(prompt, image1, image2, image3)
|
281 |
|
@@ -283,8 +272,8 @@ with gr.Blocks() as demo:
|
|
283 |
|
284 |
return result_img, status, auto_prompt
|
285 |
except Exception as e:
|
286 |
-
logger.exception("
|
287 |
-
return None, f"
|
288 |
|
289 |
submit_btn.click(
|
290 |
fn=process_and_show_prompt,
|
@@ -294,15 +283,14 @@ with gr.Blocks() as demo:
|
|
294 |
|
295 |
gr.Markdown(
|
296 |
"""
|
297 |
-
###
|
298 |
|
299 |
-
1.
|
300 |
-
2.
|
301 |
-
3.
|
302 |
-
4.
|
303 |
"""
|
304 |
)
|
305 |
|
306 |
-
# 애플리케이션 실행
|
307 |
if __name__ == "__main__":
|
308 |
demo.launch(share=True)
|
|
|
10 |
from google import genai
|
11 |
from google.genai import types
|
12 |
|
13 |
+
# Load environment variables
|
14 |
from dotenv import load_dotenv
|
15 |
load_dotenv()
|
16 |
|
17 |
+
# Logging configuration
|
18 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
19 |
logger = logging.getLogger(__name__)
|
20 |
|
|
|
24 |
|
25 |
def preprocess_prompt(prompt, image1, image2, image3):
|
26 |
"""
|
27 |
+
Process the prompt and interpret function commands.
|
28 |
"""
|
29 |
+
# Check for image existence
|
30 |
has_img1 = image1 is not None
|
31 |
has_img2 = image2 is not None
|
32 |
has_img3 = image3 is not None
|
33 |
|
34 |
+
# Replace #1, #2, #3 with description (if image is missing, note it)
|
35 |
if "#1" in prompt:
|
36 |
+
prompt = prompt.replace("#1", "first image" if has_img1 else "first image (none)")
|
37 |
if "#2" in prompt:
|
38 |
+
prompt = prompt.replace("#2", "second image" if has_img2 else "second image (none)")
|
39 |
if "#3" in prompt:
|
40 |
+
prompt = prompt.replace("#3", "third image" if has_img3 else "third image (none)")
|
41 |
|
42 |
+
# Interpret function commands
|
43 |
+
if "1. Change Image" in prompt:
|
44 |
+
desc_match = re.search(r'#1 to "(.*?)"', prompt)
|
45 |
if desc_match:
|
46 |
description = desc_match.group(1)
|
47 |
+
prompt = f"Please change the first image to {description}. Reinterpret it with a new style and mood while preserving its main content."
|
48 |
else:
|
49 |
+
prompt = "Please creatively transform the first image into a more vivid and artistic version."
|
50 |
|
51 |
+
elif "2. Remove Text" in prompt:
|
52 |
+
text_match = re.search(r'remove "(.*?)" from #1', prompt)
|
53 |
if text_match:
|
54 |
text_to_remove = text_match.group(1)
|
55 |
+
prompt = f"Please find and naturally remove the text '{text_to_remove}' from the first image, filling the area with a harmonious background."
|
56 |
else:
|
57 |
+
prompt = "Please naturally remove all text from the first image to create a clean look."
|
58 |
|
59 |
+
elif "3. Replace Face" in prompt:
|
60 |
+
prompt = "Please seamlessly replace the face in the first image with the face from the second image. Follow the expression and features of the second image while keeping the rest of the first image intact."
|
61 |
|
62 |
+
elif "4. Change Outfit" in prompt:
|
63 |
+
if "#3" in prompt or "or #3" in prompt:
|
64 |
+
prompt = "Please seamlessly change the outfit in the first image to the outfit from the second or third image. Follow the style and color of the referenced image while maintaining the body proportions and pose of the first image."
|
65 |
else:
|
66 |
+
prompt = "Please seamlessly change the outfit in the first image to the outfit from the second image. Follow the style and color of the second image while maintaining the body proportions and pose of the first image."
|
67 |
|
68 |
+
elif "5. Change Background" in prompt:
|
69 |
+
prompt = "Please seamlessly replace the background of the first image with the background from the second image, keeping the main subject intact and harmoniously merging with the new background."
|
70 |
|
71 |
+
elif "6. Blend Images (with product)" in prompt:
|
72 |
+
if "#3" in prompt or "or #3" in prompt:
|
73 |
+
prompt = "Please seamlessly blend the first, second, and third images into one image, ensuring that all key elements are included, especially the product."
|
74 |
else:
|
75 |
+
prompt = "Please seamlessly blend the first and second images into one image, ensuring that all key elements are included, especially the product."
|
76 |
|
77 |
+
elif "7. Apply Style" in prompt:
|
78 |
+
prompt = "Please transform the content of the first image into the style of the second image, preserving its main subject and composition while applying the artistic style, colors, and texture of the second image."
|
79 |
|
80 |
+
elif "change to red" in prompt:
|
81 |
+
prompt = "Please change the first image to a red tone. Adjust the overall colors to red hues while maintaining a natural look."
|
82 |
|
83 |
+
prompt += " Please generate the image."
|
84 |
return prompt
|
85 |
|
86 |
def generate_with_images(prompt, images):
|
87 |
"""
|
88 |
+
Generate an image via the API call.
|
89 |
"""
|
90 |
try:
|
91 |
api_key = os.environ.get("GEMINI_API_KEY")
|
92 |
if not api_key:
|
93 |
+
return None, "API key is not set. Please check your environment variables."
|
94 |
|
95 |
client = genai.Client(api_key=api_key)
|
96 |
+
logger.info(f"Starting Gemini API request - Prompt: {prompt}")
|
97 |
|
98 |
contents = []
|
99 |
contents.append(prompt)
|
100 |
for idx, img in enumerate(images, 1):
|
101 |
if img is not None:
|
102 |
contents.append(img)
|
103 |
+
logger.info(f"Added image #{idx}")
|
104 |
|
105 |
response = client.models.generate_content(
|
106 |
model="gemini-2.0-flash-exp-image-generation",
|
|
|
123 |
for part in response.candidates[0].content.parts:
|
124 |
if hasattr(part, 'text') and part.text:
|
125 |
result_text += part.text
|
126 |
+
logger.info(f"Response text: {part.text}")
|
127 |
elif hasattr(part, 'inline_data') and part.inline_data:
|
128 |
save_binary_file(temp_path, part.inline_data.data)
|
129 |
image_found = True
|
130 |
+
logger.info("Successfully extracted image from response")
|
131 |
|
132 |
if not image_found:
|
133 |
+
return None, f"API did not generate an image. Response text: {result_text}"
|
134 |
|
135 |
result_img = Image.open(temp_path)
|
136 |
if result_img.mode == "RGBA":
|
137 |
result_img = result_img.convert("RGB")
|
138 |
|
139 |
+
return result_img, f"Image successfully generated. {result_text}"
|
140 |
|
141 |
except Exception as e:
|
142 |
+
logger.exception("Error occurred during image generation:")
|
143 |
+
return None, f"Error occurred: {str(e)}"
|
144 |
|
145 |
def process_images_with_prompt(image1, image2, image3, prompt):
|
146 |
"""
|
147 |
+
Process three images and a prompt.
|
148 |
+
Generates an image based on the prompt even if no images are uploaded,
|
149 |
+
and retries up to 3 times if generation fails.
|
150 |
"""
|
151 |
try:
|
152 |
images = [image1, image2, image3]
|
153 |
+
valid_images = [img for img in images if img is not None]
|
154 |
|
|
|
155 |
if not prompt or not prompt.strip():
|
156 |
if len(valid_images) == 0:
|
157 |
+
prompt = "Please creatively generate an image based on this prompt."
|
158 |
+
logger.info("Automatically generated text-only prompt")
|
159 |
elif len(valid_images) == 1:
|
160 |
+
prompt = "Please creatively transform this image into a more vivid and artistic version."
|
161 |
+
logger.info("Automatically generated single image prompt")
|
162 |
elif len(valid_images) == 2:
|
163 |
+
prompt = "Please seamlessly blend these two images into one, integrating the elements harmoniously."
|
164 |
+
logger.info("Automatically generated two-image blending prompt")
|
165 |
else:
|
166 |
+
prompt = "Please creatively blend these three images into one cohesive scene, including all main elements."
|
167 |
+
logger.info("Automatically generated three-image blending prompt")
|
168 |
else:
|
169 |
prompt = preprocess_prompt(prompt, image1, image2, image3)
|
170 |
|
|
|
171 |
max_retries = 3
|
172 |
for attempt in range(max_retries):
|
173 |
result_img, status = generate_with_images(prompt, valid_images)
|
174 |
if result_img is not None:
|
175 |
return result_img, status
|
176 |
else:
|
177 |
+
logger.info(f"Image generation failed, retrying ({attempt+1}/{max_retries})")
|
178 |
+
return None, f"Failed to generate image. Last status: {status}"
|
179 |
|
180 |
except Exception as e:
|
181 |
+
logger.exception("Error occurred during image processing:")
|
182 |
+
return None, f"Error occurred: {str(e)}"
|
183 |
|
184 |
def update_prompt_from_function(function_choice):
|
185 |
"""
|
186 |
+
Returns a prompt template based on the selected function.
|
187 |
+
(Custom text input has been removed.)
|
188 |
"""
|
189 |
function_templates = {
|
190 |
+
"1. Change Image": '#1 to "desired description"',
|
191 |
+
"2. Remove Text": 'remove "text to remove" from #1',
|
192 |
+
"3. Replace Face": "replace the face in #1 with the face from #2",
|
193 |
+
"4. Change Outfit": "change the outfit in #1 to that from #2 or #3",
|
194 |
+
"5. Change Background": "change the background of #1 to the background from #2",
|
195 |
+
"6. Blend Images (with product)": "blend #1 with #2 or #3",
|
196 |
+
"7. Apply Style": "apply the style of #2 to #1"
|
197 |
}
|
198 |
|
199 |
return function_templates.get(function_choice, "")
|
200 |
|
201 |
+
# Gradio Interface
|
202 |
with gr.Blocks() as demo:
|
203 |
gr.HTML(
|
204 |
"""
|
205 |
<div style="text-align: center; margin-bottom: 1rem;">
|
206 |
+
<h1>Simple Image Generator</h1>
|
207 |
+
<p>You can generate an image by entering only a prompt. Alternatively, you can select a function below.</p>
|
208 |
</div>
|
209 |
"""
|
210 |
)
|
211 |
|
212 |
with gr.Row():
|
213 |
with gr.Column():
|
|
|
214 |
with gr.Row():
|
215 |
image1_input = gr.Image(type="pil", label="#1", image_mode="RGB")
|
216 |
image2_input = gr.Image(type="pil", label="#2", image_mode="RGB")
|
217 |
image3_input = gr.Image(type="pil", label="#3", image_mode="RGB")
|
218 |
|
|
|
219 |
function_dropdown = gr.Dropdown(
|
220 |
choices=[
|
221 |
+
"1. Change Image",
|
222 |
+
"2. Remove Text",
|
223 |
+
"3. Replace Face",
|
224 |
+
"4. Change Outfit",
|
225 |
+
"5. Change Background",
|
226 |
+
"6. Blend Images (with product)",
|
227 |
+
"7. Apply Style"
|
228 |
],
|
229 |
+
label="Select Function",
|
230 |
value=None
|
231 |
)
|
232 |
|
|
|
233 |
prompt_input = gr.Textbox(
|
234 |
lines=3,
|
235 |
+
placeholder="Enter a prompt or leave blank for automatic generation.",
|
236 |
+
label="Prompt (optional)"
|
237 |
)
|
238 |
|
239 |
+
submit_btn = gr.Button("Generate Image", variant="primary")
|
|
|
240 |
|
241 |
with gr.Column():
|
242 |
+
output_image = gr.Image(label="Generated Image")
|
243 |
+
output_text = gr.Textbox(label="Status Message")
|
244 |
+
prompt_display = gr.Textbox(label="Used Prompt", visible=True)
|
|
|
|
|
|
|
245 |
|
246 |
+
# Update prompt textbox when function is selected
|
247 |
function_dropdown.change(
|
248 |
fn=update_prompt_from_function,
|
249 |
inputs=[function_dropdown],
|
250 |
outputs=[prompt_input]
|
251 |
)
|
252 |
|
|
|
253 |
def process_and_show_prompt(image1, image2, image3, prompt):
|
|
|
254 |
images = [image1, image2, image3]
|
255 |
valid_images = [img for img in images if img is not None]
|
256 |
|
|
|
258 |
auto_prompt = prompt
|
259 |
if not prompt or not prompt.strip():
|
260 |
if len(valid_images) == 0:
|
261 |
+
auto_prompt = "Please creatively generate an image based on this prompt."
|
262 |
elif len(valid_images) == 1:
|
263 |
+
auto_prompt = "Please creatively transform this image into a more vivid and artistic version."
|
264 |
elif len(valid_images) == 2:
|
265 |
+
auto_prompt = "Please seamlessly blend these two images into one, integrating the elements harmoniously."
|
266 |
else:
|
267 |
+
auto_prompt = "Please creatively blend these three images into one cohesive scene, including all main elements."
|
268 |
else:
|
269 |
auto_prompt = preprocess_prompt(prompt, image1, image2, image3)
|
270 |
|
|
|
272 |
|
273 |
return result_img, status, auto_prompt
|
274 |
except Exception as e:
|
275 |
+
logger.exception("Error occurred during processing:")
|
276 |
+
return None, f"Error occurred: {str(e)}", prompt
|
277 |
|
278 |
submit_btn.click(
|
279 |
fn=process_and_show_prompt,
|
|
|
283 |
|
284 |
gr.Markdown(
|
285 |
"""
|
286 |
+
### Usage:
|
287 |
|
288 |
+
1. **Text-Only Generation:** Generate an image without uploading any image by simply entering a prompt.
|
289 |
+
2. **Select Function:** Choose a function from the dropdown and the corresponding template will automatically apply to the prompt.
|
290 |
+
3. **Image Upload:** Upload one or more images to apply the function accordingly.
|
291 |
+
4. **Automatic Retry:** If image generation fails, it will automatically retry up to 2 additional times.
|
292 |
"""
|
293 |
)
|
294 |
|
|
|
295 |
if __name__ == "__main__":
|
296 |
demo.launch(share=True)
|