Aminatron / app.py
sdhaos's picture
Upload app.py with huggingface_hub
a123653 verified
Raw
History Blame Contribute Delete
7.86 kB
from __future__ import annotations
import base64
import json
import os
import smtplib
from datetime import datetime, timezone
from email.message import EmailMessage
from io import BytesIO
import gradio as gr
import requests
from PIL import Image
from aminatron_model import detect_image, format_detections, render_result_image, summarize_detections
SPACE_MODEL = os.getenv("AMINATRON_MODEL") or None
EMAIL_TO = os.getenv("EMAIL_TO", "mammadov.amin2000@gmail.com")
EMAIL_FROM = os.getenv("EMAIL_FROM", "Aminatron <onboarding@resend.dev>")
RESEND_API_KEY = os.getenv("RESEND_API_KEY")
SMTP_HOST = os.getenv("SMTP_HOST", "smtp.gmail.com")
SMTP_PORT = int(os.getenv("SMTP_PORT", "587"))
SMTP_USER = os.getenv("SMTP_USER")
SMTP_PASSWORD = os.getenv("SMTP_PASSWORD") or os.getenv("GMAIL_APP_PASSWORD")
def image_to_png_bytes(image: Image.Image) -> bytes:
"""Convert a PIL image to PNG bytes for email attachment."""
buffer = BytesIO()
image.convert("RGB").save(buffer, format="PNG")
return buffer.getvalue()
def build_email_payload(
original: Image.Image,
annotated: Image.Image,
detections: list[dict[str, object]],
summary: dict[str, int],
) -> tuple[str, str, bytes, bytes]:
"""Build reusable email subject/body/attachments."""
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
subject = f"Aminatron prediction review - {timestamp}"
body = (
"A new Aminatron Space prediction was processed.\n\n"
f"Time: {timestamp}\n"
f"Summary: {json.dumps(summary, ensure_ascii=False)}\n\n"
f"Detections:\n{json.dumps(detections, ensure_ascii=False, indent=2)}\n"
)
return subject, body, image_to_png_bytes(original), image_to_png_bytes(annotated)
def send_review_email_resend(
original: Image.Image,
annotated: Image.Image,
detections: list[dict[str, object]],
summary: dict[str, int],
) -> str:
"""Send review email through Resend HTTPS API."""
if not RESEND_API_KEY:
return "Resend is not configured. Add RESEND_API_KEY to Space secrets."
subject, body, original_png, result_png = build_email_payload(
original,
annotated,
detections,
summary,
)
response = requests.post(
"https://api.resend.com/emails",
headers={
"Authorization": f"Bearer {RESEND_API_KEY}",
"Content-Type": "application/json",
},
json={
"from": EMAIL_FROM,
"to": [EMAIL_TO],
"subject": subject,
"text": body,
"attachments": [
{
"filename": "original.png",
"content": base64.b64encode(original_png).decode("ascii"),
},
{
"filename": "aminatron_result.png",
"content": base64.b64encode(result_png).decode("ascii"),
},
],
},
timeout=30,
)
if response.status_code >= 400:
return f"Resend email failed: {response.status_code} {response.text}"
return f"Email sent to {EMAIL_TO} through Resend."
def send_review_email(
original: Image.Image,
annotated: Image.Image,
detections: list[dict[str, object]],
summary: dict[str, int],
) -> str:
"""Send original and processed images to email."""
if RESEND_API_KEY:
try:
return send_review_email_resend(original, annotated, detections, summary)
except Exception as error:
error_message = f"Resend email failed: {error}"
print(error_message)
return error_message
if not SMTP_USER or not SMTP_PASSWORD:
return (
"Email is not configured. Add RESEND_API_KEY, or SMTP_USER and "
"SMTP_PASSWORD/GMAIL_APP_PASSWORD."
)
subject, body, original_png, result_png = build_email_payload(
original,
annotated,
detections,
summary,
)
message = EmailMessage()
message["Subject"] = subject
message["From"] = SMTP_USER
message["To"] = EMAIL_TO
message.set_content(body)
message.add_attachment(
original_png,
maintype="image",
subtype="png",
filename="original.png",
)
message.add_attachment(
result_png,
maintype="image",
subtype="png",
filename="aminatron_result.png",
)
try:
smtp_class = smtplib.SMTP_SSL if SMTP_PORT == 465 else smtplib.SMTP
with smtp_class(SMTP_HOST, SMTP_PORT, timeout=20) as smtp:
if SMTP_PORT != 465:
smtp.starttls()
smtp.login(SMTP_USER, SMTP_PASSWORD)
smtp.send_message(message)
return f"Email sent to {EMAIL_TO}."
except Exception as error:
error_message = f"Email notification failed: {error}"
print(error_message)
return error_message
def predict(image: Image.Image, confidence: float, iou: float, image_size: int, max_detections: int):
"""Detect objects for the Gradio interface."""
if image is None:
return None, {}, [], None
result = detect_image(
image,
model_path=SPACE_MODEL,
confidence=confidence,
iou=iou,
image_size=image_size,
max_detections=max_detections,
)
detections = format_detections(result)
summary = summarize_detections(detections)
annotated_image = render_result_image(result)
rows = [
[item["class"], f"{item['confidence'] * 100:.2f}%", item["box"]]
for item in detections
]
email_status = send_review_email(image, annotated_image, detections, summary)
print(email_status)
share_state = {
"original": image,
"annotated": annotated_image,
"detections": detections,
"summary": summary,
}
return annotated_image, summary, rows, share_state
def share_result(share_state):
"""Send the latest prediction result to email manually from the Share button."""
if not share_state:
print("No result to share yet. Run Detect first.")
return
email_status = send_review_email(
share_state["original"],
share_state["annotated"],
share_state["detections"],
share_state["summary"],
)
print(email_status)
with gr.Blocks(title="Aminatron") as demo:
gr.Markdown(
"""
# Aminatron
Aminatron is a multi-object detection model. Upload one image and it can find
several COCO objects at once: people, cats, dogs, birds, cows, cars, chairs and more.
"""
)
with gr.Row():
image_input = gr.Image(type="pil", label="Upload image")
image_output = gr.Image(type="pil", label="Detected objects")
with gr.Row():
confidence = gr.Slider(0.05, 0.9, value=0.25, step=0.05, label="Confidence")
iou = gr.Slider(0.1, 0.9, value=0.45, step=0.05, label="IoU")
image_size = gr.Dropdown([416, 512, 640, 768], value=640, label="Image size")
max_detections = gr.Slider(1, 100, value=50, step=1, label="Max detections")
share_state = gr.State(None)
with gr.Row():
detect_button = gr.Button("Detect")
share_button = gr.Button("Share result")
summary_output = gr.JSON(label="Summary")
detections_output = gr.Dataframe(
headers=["Class", "Confidence", "Box [x1, y1, x2, y2]"],
label="Detections",
interactive=False,
)
detect_button.click(
fn=predict,
inputs=[image_input, confidence, iou, image_size, max_detections],
outputs=[image_output, summary_output, detections_output, share_state],
)
share_button.click(
fn=share_result,
inputs=[share_state],
outputs=[],
)
if __name__ == "__main__":
demo.launch()