|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import os |
|
|
import shutil |
|
|
import zipfile |
|
|
import pathlib |
|
|
import tempfile |
|
|
|
|
|
import gradio |
|
|
import pandas |
|
|
import PIL.Image |
|
|
import huggingface_hub |
|
|
import autogluon.multimodal |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MODEL_REPO_ID = "cassieli226/sign-identification-automl" |
|
|
ZIP_FILENAME = "autogluon_predictor_dir.zip" |
|
|
HF_TOKEN = os.getenv("HF_TOKEN", None) |
|
|
|
|
|
CACHE_DIR = pathlib.Path("hf_assets") |
|
|
EXTRACT_DIR = CACHE_DIR / "predictor_native" |
|
|
|
|
|
|
|
|
def _prepare_predictor_dir() -> str: |
|
|
"""Download and extract the AutoGluon predictor directory from Hugging Face.""" |
|
|
CACHE_DIR.mkdir(parents=True, exist_ok=True) |
|
|
local_zip = huggingface_hub.hf_hub_download( |
|
|
repo_id=MODEL_REPO_ID, |
|
|
filename=ZIP_FILENAME, |
|
|
repo_type="model", |
|
|
token=HF_TOKEN, |
|
|
local_dir=str(CACHE_DIR), |
|
|
local_dir_use_symlinks=False, |
|
|
) |
|
|
if EXTRACT_DIR.exists(): |
|
|
shutil.rmtree(EXTRACT_DIR) |
|
|
EXTRACT_DIR.mkdir(parents=True, exist_ok=True) |
|
|
with zipfile.ZipFile(local_zip, "r") as zf: |
|
|
zf.extractall(str(EXTRACT_DIR)) |
|
|
contents = list(EXTRACT_DIR.iterdir()) |
|
|
predictor_root = contents[0] if (len(contents) == 1 and contents[0].is_dir()) else EXTRACT_DIR |
|
|
return str(predictor_root) |
|
|
|
|
|
|
|
|
PREDICTOR_DIR = _prepare_predictor_dir() |
|
|
PREDICTOR = autogluon.multimodal.MultiModalPredictor.load(PREDICTOR_DIR) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CLASS_LABELS = { |
|
|
0: "π¦ Not a Stop Sign", |
|
|
1: "π Stop Sign" |
|
|
} |
|
|
|
|
|
|
|
|
def _human_label(c): |
|
|
try: |
|
|
ci = int(c) |
|
|
return CLASS_LABELS.get(ci, str(c)) |
|
|
except Exception: |
|
|
return CLASS_LABELS.get(c, str(c)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def do_predict(pil_img: PIL.Image.Image): |
|
|
"""Run prediction on an uploaded image. Returns original, preprocessed, and probabilities.""" |
|
|
|
|
|
if pil_img is None: |
|
|
return None, None, "No image provided.", {} |
|
|
|
|
|
try: |
|
|
|
|
|
tmpdir = pathlib.Path(tempfile.mkdtemp()) |
|
|
img_path = tmpdir / "input.png" |
|
|
pil_img.save(img_path) |
|
|
|
|
|
|
|
|
preprocessed_img = pil_img.copy() |
|
|
preprocessed_img = preprocessed_img.resize((224, 224)) |
|
|
|
|
|
|
|
|
df = pandas.DataFrame({"image": [str(img_path)]}) |
|
|
|
|
|
|
|
|
proba_df = PREDICTOR.predict_proba(df) |
|
|
|
|
|
|
|
|
proba_df = proba_df.rename(columns={ |
|
|
0: "π¦ Not a Stop Sign (0)", |
|
|
1: "π Stop Sign (1)" |
|
|
}) |
|
|
row = proba_df.iloc[0] |
|
|
|
|
|
pretty_dict = { |
|
|
"π¦ Not a Stop Sign": float(row.get("π¦ Not a Stop Sign (0)", 0.0)), |
|
|
"π Stop Sign": float(row.get("π Stop Sign (1)", 0.0)), |
|
|
} |
|
|
|
|
|
return pil_img, preprocessed_img, "Prediction complete", pretty_dict |
|
|
|
|
|
except Exception as e: |
|
|
return None, None, f"Error: {str(e)}", {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EXAMPLES = [ |
|
|
["https://upload.wikimedia.org/wikipedia/commons/thumb/f/f9/STOP_sign.jpg/640px-STOP_sign.jpg"], |
|
|
["https://upload.wikimedia.org/wikipedia/commons/1/19/Swiss_Frutiger_Traffic_Sign.jpg"] |
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
with gradio.Blocks() as demo: |
|
|
|
|
|
gradio.Markdown("# π Stop Sign Detector") |
|
|
gradio.Markdown(""" |
|
|
Upload a road scene or traffic sign image, and this app will classify whether |
|
|
a stop sign is present. The interface shows both the original image and the |
|
|
preprocessed image (224x224) that the model actually sees. |
|
|
""") |
|
|
|
|
|
with gradio.Row(): |
|
|
image_in = gradio.Image( |
|
|
type="pil", |
|
|
label="Upload or capture an image", |
|
|
sources=["upload", "webcam"], |
|
|
image_mode="RGB" |
|
|
) |
|
|
|
|
|
with gradio.Row(): |
|
|
orig_out = gradio.Image(type="pil", label="Original Image") |
|
|
preproc_out = gradio.Image(type="pil", label="Preprocessed Image (224x224)") |
|
|
|
|
|
status_out = gradio.Textbox(label="Status") |
|
|
proba_pretty = gradio.Label(num_top_classes=2, label="Class probabilities") |
|
|
|
|
|
image_in.change( |
|
|
fn=do_predict, |
|
|
inputs=[image_in], |
|
|
outputs=[orig_out, preproc_out, status_out, proba_pretty] |
|
|
) |
|
|
|
|
|
gradio.Examples( |
|
|
examples=EXAMPLES, |
|
|
inputs=[image_in], |
|
|
label="Representative examples", |
|
|
examples_per_page=3, |
|
|
cache_examples=False, |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch() |