Image_Interface / app.py
kevinkyi's picture
Upload app.py with huggingface_hub
4a909ca verified
import os, pathlib, zipfile, tempfile
import pandas as pd
import gradio as gr
from PIL import Image
from huggingface_hub import hf_hub_download, snapshot_download
import autogluon.multimodal as ag
MODEL_REPO_ID = "samder03/2025-24679-image-autogluon-predictor"
CLASS_LABELS = {
0: "No Stop Sign",
1: "Stop Sign",
"class_0": "No Stop Sign",
"class_1": "Stop Sign",
}
def _human_label(c):
try:
ci = int(c)
return CLASS_LABELS.get(ci, str(c))
except Exception:
return CLASS_LABELS.get(str(c), str(c))
def _locate_predictor_dir_from_repo_folder(repo_dir: str) -> str:
rd = pathlib.Path(repo_dir)
for p in rd.rglob("predictor.pkl"):
return str(p.parent)
return ""
def _prepare_predictor_dir() -> str:
repo_dir = snapshot_download(repo_id=MODEL_REPO_ID, repo_type="model")
pred_dir = _locate_predictor_dir_from_repo_folder(repo_dir)
if pred_dir:
return pred_dir
# Fallback: try to find a zip and extract
zips = list(pathlib.Path(repo_dir).rglob("*.zip"))
if not zips:
raise FileNotFoundError("Could not find a predictor directory or .zip in the model repo.")
zip_path = str(zips[0])
workdir = tempfile.mkdtemp(prefix="ag_img_predictor_")
with zipfile.ZipFile(zip_path, "r") as zf:
zf.extractall(workdir)
entries = list(pathlib.Path(workdir).iterdir())
if len(entries) == 1 and entries[0].is_dir():
return str(entries[0])
return workdir
PREDICTOR_DIR = _prepare_predictor_dir()
PREDICTOR = ag.MultiModalPredictor.load(PREDICTOR_DIR)
def _ensure_rgb(img: Image.Image) -> Image.Image:
return img.convert("RGB") if img.mode != "RGB" else img
def _resize_shorter(img: Image.Image, shorter: int) -> Image.Image:
w, h = img.size
if min(w, h) == shorter:
return img
if w < h:
new_w = shorter
new_h = int(h * (shorter / w))
else:
new_h = shorter
new_w = int(w * (shorter / h))
return img.resize((new_w, new_h), Image.BICUBIC)
def _center_crop(img: Image.Image, size: int) -> Image.Image:
w, h = img.size
side = min(w, h, size)
left = (w - side) // 2
top = (h - side) // 2
return img.crop((left, top, left + side, top + side)).resize((size, size), Image.BICUBIC)
def _validate_image(pil_img: Image.Image, max_pixels: int = 8_000_000):
if pil_img is None:
return False, "No image provided."
if pil_img.width * pil_img.height > max_pixels:
return False, f"Image too large (>{max_pixels:,} pixels). Please upload a smaller image."
return True, ""
def preprocess(pil_img: Image.Image, resize_shorter: int, do_center_crop: bool, crop_size: int) -> Image.Image:
img = _ensure_rgb(pil_img)
img = _resize_shorter(img, resize_shorter)
if do_center_crop:
img = _center_crop(img, crop_size)
return img
def do_predict(pil_img: Image.Image, resize_shorter: int, do_center_crop: bool, crop_size: int, top_k: int):
ok, msg = _validate_image(pil_img)
if not ok:
return None, None, {"Error": 1.0}
pre_img = preprocess(pil_img, resize_shorter, do_center_crop, crop_size)
tmpdir = pathlib.Path(tempfile.mkdtemp(prefix="ag_img_run_"))
orig_path = tmpdir / "original.png"
pre_path = tmpdir / "preprocessed.png"
pil_img.save(orig_path)
pre_img.save(pre_path)
df = pd.DataFrame({"image": [str(pre_path)]})
proba_df = PREDICTOR.predict_proba(df)
proba_df = proba_df.rename(columns={c: _human_label(c) for c in proba_df.columns})
row = proba_df.iloc[0]
items = sorted(row.items(), key=lambda kv: float(kv[1]), reverse=True)[:max(1, int(top_k))]
pretty = {k: float(v) for k, v in items}
return Image.open(orig_path), Image.open(pre_path), pretty
with gr.Blocks(title="Stop Sign Classifier") as demo:
gr.Markdown("# Stop Sign? — Image Classifier (Classmate Model)")
gr.Markdown(
"Upload a PNG/JPG (or use webcam). You’ll see the **original** and the **preprocessed** image, "
"plus ranked class probabilities."
)
with gr.Row():
image_in = gr.Image(type="pil", label="Input image (PNG/JPG)", sources=["upload", "webcam"])
with gr.Column():
with gr.Accordion("Inference Parameters", open=False):
resize_shorter = gr.Slider(64, 1024, value=384, step=16, label="Resize (shorter side)")
do_center_crop = gr.Checkbox(value=True, label="Center-crop to square")
crop_size = gr.Slider(64, 1024, value=384, step=16, label="Crop size (if center-crop)")
top_k = gr.Slider(1, 2, value=2, step=1, label="Top-K classes to display")
with gr.Row():
img_orig = gr.Image(label="Original")
img_proc = gr.Image(label="Preprocessed")
proba_pretty = gr.Label(num_top_classes=2, label="Class probabilities (Top-K)")
image_in.change(
fn=do_predict,
inputs=[image_in, resize_shorter, do_center_crop, crop_size, top_k],
outputs=[img_orig, img_proc, proba_pretty]
)
gr.Examples(
examples=[["examples/example_0.png"], ["examples/example_11.png"], ["examples/example_23.png"]],
inputs=[image_in],
label="Example images",
examples_per_page=6,
cache_examples=False,
)
if __name__ == "__main__":
demo.launch()