Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -15,6 +15,7 @@ def generate_edit(prompt, pil_image, api_key, model="gemini-2.0-flash-exp"):
|
|
| 15 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_img:
|
| 16 |
image_path = tmp_img.name
|
| 17 |
pil_image.save(image_path)
|
|
|
|
| 18 |
files = [client.files.upload(file=image_path)]
|
| 19 |
contents = [
|
| 20 |
types.Content(
|
|
@@ -33,8 +34,10 @@ def generate_edit(prompt, pil_image, api_key, model="gemini-2.0-flash-exp"):
|
|
| 33 |
response_modalities=["image", "text"],
|
| 34 |
response_mime_type="text/plain",
|
| 35 |
)
|
|
|
|
| 36 |
text_response = ""
|
| 37 |
image_out_path = None
|
|
|
|
| 38 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_out:
|
| 39 |
out_path = tmp_out.name
|
| 40 |
for chunk in client.models.generate_content_stream(
|
|
@@ -54,29 +57,24 @@ def generate_edit(prompt, pil_image, api_key, model="gemini-2.0-flash-exp"):
|
|
| 54 |
del files
|
| 55 |
return image_out_path, text_response
|
| 56 |
|
| 57 |
-
def process_image_and_prompt(pil_image, prompt, api_key
|
| 58 |
try:
|
| 59 |
-
if progress_callback:
|
| 60 |
-
progress_callback("Generating…")
|
| 61 |
image_path, text_out = generate_edit(prompt, pil_image, api_key)
|
| 62 |
if image_path:
|
| 63 |
img = Image.open(image_path)
|
| 64 |
if img.mode == "RGBA":
|
| 65 |
img = img.convert("RGB")
|
| 66 |
-
|
| 67 |
-
progress_callback("Done ✓")
|
| 68 |
-
return img, "Image generated successfully!", None
|
| 69 |
else:
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
return None, f"⚠️ {text_out.strip()}", None
|
| 73 |
except Exception as e:
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
return None, f"❌ Generation failed: {str(e)}", None
|
| 77 |
|
| 78 |
def reset_inputs(api_key_value=None):
|
| 79 |
-
|
|
|
|
| 80 |
|
| 81 |
# Styles
|
| 82 |
css_style = """
|
|
@@ -106,10 +104,6 @@ body, .app-container { background: var(--bg); color: var(--text); }
|
|
| 106 |
@media (max-width: 1100px) {
|
| 107 |
.layout-two-col { grid-template-columns: 1fr; }
|
| 108 |
}
|
| 109 |
-
.input-area, .output-area { background: #202533; border-radius: 12px; padding: 12px; }
|
| 110 |
-
#status-container { display: flex; flex-direction: column; gap: 6px; padding: 6px; background: #1b1f2b; border-radius: 8px; }
|
| 111 |
-
#status-label { font-weight: 700; color: #a6d1ff; }
|
| 112 |
-
#status-text { height: 6em; max-height: 8em; overflow: auto; white-space: pre-wrap; word-break: break-word; padding: 6px 8px; border: none; background: transparent; color: #cbd5e1; font-weight: 600; }
|
| 113 |
|
| 114 |
#output-viewport { display: flex; justify-content: center; align-items: center; min-height: 260px; }
|
| 115 |
#output-image { display: flex; justify-content: center; align-items: center; }
|
|
@@ -122,10 +116,11 @@ with gr.Blocks(css=css_style) as app:
|
|
| 122 |
<div class='header-block'>
|
| 123 |
<div class='header-gradient'>
|
| 124 |
<h1 class='header-title'>🖼️ Image Editor <span style="font-size:1.1em;">(Powered by Gemini)</span> 🔮</h1>
|
| 125 |
-
<div class='header-subtitle'>Step-by-step prompts
|
| 126 |
</div>
|
| 127 |
</div>
|
| 128 |
""")
|
|
|
|
| 129 |
with gr.Row():
|
| 130 |
with gr.Column(scale=3, elem_classes="sidebar"):
|
| 131 |
gr.Markdown(
|
|
@@ -158,28 +153,25 @@ with gr.Blocks(css=css_style) as app:
|
|
| 158 |
with gr.Row():
|
| 159 |
submit_btn = gr.Button("Generate Edit", elem_classes="gradient-button")
|
| 160 |
reset_btn = gr.Button("Reset Inputs")
|
|
|
|
|
|
|
| 161 |
|
| 162 |
-
|
| 163 |
-
with gr.Column():
|
| 164 |
-
gr.HTML("<div id='status-container'><div id='status-label'>Status</div></div>")
|
| 165 |
-
status_bar = gr.Textbox(label="", interactive=False, lines=6, elem_id="status-text")
|
| 166 |
-
|
| 167 |
-
def on_submit(pil_img, prompt, key, progress=None):
|
| 168 |
if not key or key.strip() == "":
|
| 169 |
raise gr.Error("Gemini API Key is required!")
|
| 170 |
-
|
| 171 |
-
return
|
| 172 |
|
| 173 |
submit_btn.click(
|
| 174 |
fn=on_submit,
|
| 175 |
inputs=[image_input, prompt_input, api_key_input],
|
| 176 |
-
outputs=[output_image
|
| 177 |
)
|
| 178 |
|
| 179 |
reset_btn.click(
|
| 180 |
fn=reset_inputs,
|
| 181 |
inputs=[api_key_input],
|
| 182 |
-
outputs=[image_input, prompt_input, api_key_input
|
| 183 |
)
|
| 184 |
|
| 185 |
app.launch()
|
|
|
|
| 15 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_img:
|
| 16 |
image_path = tmp_img.name
|
| 17 |
pil_image.save(image_path)
|
| 18 |
+
|
| 19 |
files = [client.files.upload(file=image_path)]
|
| 20 |
contents = [
|
| 21 |
types.Content(
|
|
|
|
| 34 |
response_modalities=["image", "text"],
|
| 35 |
response_mime_type="text/plain",
|
| 36 |
)
|
| 37 |
+
|
| 38 |
text_response = ""
|
| 39 |
image_out_path = None
|
| 40 |
+
|
| 41 |
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp_out:
|
| 42 |
out_path = tmp_out.name
|
| 43 |
for chunk in client.models.generate_content_stream(
|
|
|
|
| 57 |
del files
|
| 58 |
return image_out_path, text_response
|
| 59 |
|
| 60 |
+
def process_image_and_prompt(pil_image, prompt, api_key):
|
| 61 |
try:
|
|
|
|
|
|
|
| 62 |
image_path, text_out = generate_edit(prompt, pil_image, api_key)
|
| 63 |
if image_path:
|
| 64 |
img = Image.open(image_path)
|
| 65 |
if img.mode == "RGBA":
|
| 66 |
img = img.convert("RGB")
|
| 67 |
+
return img # Return only the image on success
|
|
|
|
|
|
|
| 68 |
else:
|
| 69 |
+
# If no image generated, raise an error for Gradio popup
|
| 70 |
+
raise gr.Error(f"⚠️ Image generation failed: {text_out.strip() if text_out.strip() else 'No specific error message.'}")
|
|
|
|
| 71 |
except Exception as e:
|
| 72 |
+
# Catch any other exceptions and re-raise as Gradio error
|
| 73 |
+
raise gr.Error(f"❌ Generation failed: {str(e)}")
|
|
|
|
| 74 |
|
| 75 |
def reset_inputs(api_key_value=None):
|
| 76 |
+
# Reset all inputs, keeping API key unchanged
|
| 77 |
+
return None, "", api_key_value or ""
|
| 78 |
|
| 79 |
# Styles
|
| 80 |
css_style = """
|
|
|
|
| 104 |
@media (max-width: 1100px) {
|
| 105 |
.layout-two-col { grid-template-columns: 1fr; }
|
| 106 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
|
| 108 |
#output-viewport { display: flex; justify-content: center; align-items: center; min-height: 260px; }
|
| 109 |
#output-image { display: flex; justify-content: center; align-items: center; }
|
|
|
|
| 116 |
<div class='header-block'>
|
| 117 |
<div class='header-gradient'>
|
| 118 |
<h1 class='header-title'>🖼️ Image Editor <span style="font-size:1.1em;">(Powered by Gemini)</span> 🔮</h1>
|
| 119 |
+
<div class='header-subtitle'>Step-by-step prompts for image editing</div>
|
| 120 |
</div>
|
| 121 |
</div>
|
| 122 |
""")
|
| 123 |
+
|
| 124 |
with gr.Row():
|
| 125 |
with gr.Column(scale=3, elem_classes="sidebar"):
|
| 126 |
gr.Markdown(
|
|
|
|
| 153 |
with gr.Row():
|
| 154 |
submit_btn = gr.Button("Generate Edit", elem_classes="gradient-button")
|
| 155 |
reset_btn = gr.Button("Reset Inputs")
|
| 156 |
+
|
| 157 |
+
# Note: Status bar elements removed as requested. Errors will now show as Gradio popups.
|
| 158 |
|
| 159 |
+
def on_submit(pil_img, prompt, key):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
if not key or key.strip() == "":
|
| 161 |
raise gr.Error("Gemini API Key is required!")
|
| 162 |
+
# process_image_and_prompt now raises gr.Error directly for failures
|
| 163 |
+
return process_image_and_prompt(pil_img, prompt, key)
|
| 164 |
|
| 165 |
submit_btn.click(
|
| 166 |
fn=on_submit,
|
| 167 |
inputs=[image_input, prompt_input, api_key_input],
|
| 168 |
+
outputs=[output_image] # Only output the image
|
| 169 |
)
|
| 170 |
|
| 171 |
reset_btn.click(
|
| 172 |
fn=reset_inputs,
|
| 173 |
inputs=[api_key_input],
|
| 174 |
+
outputs=[image_input, prompt_input, api_key_input] # Remove status_bar from outputs
|
| 175 |
)
|
| 176 |
|
| 177 |
app.launch()
|