SebastianAndreu commited on
Commit
2abf59b
ยท
verified ยท
1 Parent(s): 0b2e1fe

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +175 -0
app.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ================================================================
2
+ # HW3: Driving a Stop Sign Image Classifier with Gradio
3
+ #
4
+ # Author: Your Name
5
+ # Course: 24679 - Designing and Deploying AI/ML Systems
6
+ # Dataset: Binary stop sign dataset (class_1 = stop sign, class_0 = not stop sign)
7
+ # Task: Image classification deployed via Hugging Face Space
8
+ #
9
+ # Acknowledgments:
10
+ # - Model trained by a classmate in Homework 2
11
+ # - Deployment scaffold and documentation supported with AI assistance (ChatGPT, OpenAI)
12
+ # - Reference: Class-provided notebook "image gradio.ipynb"
13
+ # ================================================================
14
+
15
+ import os # For reading environment variables
16
+ import shutil # For directory cleanup
17
+ import zipfile # For extracting model archives
18
+ import pathlib # For path manipulations
19
+ import tempfile # For creating temporary files/directories
20
+
21
+ import gradio # For interactive UI
22
+ import pandas # For tabular data handling
23
+ import PIL.Image # For image I/O
24
+ import huggingface_hub # For downloading model assets
25
+ import autogluon.multimodal # For loading AutoGluon image classifier
26
+
27
+
28
+ # -------------------------
29
+ # Hugging Face model setup
30
+ # -------------------------
31
+ MODEL_REPO_ID = "cassieli226/sign-identification-automl" # <- update with teammateโ€™s repo
32
+ ZIP_FILENAME = "autogluon_predictor_dir.zip"
33
+ HF_TOKEN = os.getenv("HF_TOKEN", None)
34
+
35
+ CACHE_DIR = pathlib.Path("hf_assets")
36
+ EXTRACT_DIR = CACHE_DIR / "predictor_native"
37
+
38
+
39
+ def _prepare_predictor_dir() -> str:
40
+ """Download and extract the AutoGluon predictor directory from Hugging Face."""
41
+ CACHE_DIR.mkdir(parents=True, exist_ok=True)
42
+ local_zip = huggingface_hub.hf_hub_download(
43
+ repo_id=MODEL_REPO_ID,
44
+ filename=ZIP_FILENAME,
45
+ repo_type="model",
46
+ token=HF_TOKEN,
47
+ local_dir=str(CACHE_DIR),
48
+ local_dir_use_symlinks=False,
49
+ )
50
+ if EXTRACT_DIR.exists():
51
+ shutil.rmtree(EXTRACT_DIR)
52
+ EXTRACT_DIR.mkdir(parents=True, exist_ok=True)
53
+ with zipfile.ZipFile(local_zip, "r") as zf:
54
+ zf.extractall(str(EXTRACT_DIR))
55
+ contents = list(EXTRACT_DIR.iterdir())
56
+ predictor_root = contents[0] if (len(contents) == 1 and contents[0].is_dir()) else EXTRACT_DIR
57
+ return str(predictor_root)
58
+
59
+
60
+ PREDICTOR_DIR = _prepare_predictor_dir()
61
+ PREDICTOR = autogluon.multimodal.MultiModalPredictor.load(PREDICTOR_DIR)
62
+
63
+
64
+ # -------------------------
65
+ # Class labels
66
+ # -------------------------
67
+ CLASS_LABELS = {
68
+ 0: "๐Ÿšฆ Not a Stop Sign",
69
+ 1: "๐Ÿ›‘ Stop Sign"
70
+ }
71
+
72
+
73
+ def _human_label(c):
74
+ try:
75
+ ci = int(c)
76
+ return CLASS_LABELS.get(ci, str(c))
77
+ except Exception:
78
+ return CLASS_LABELS.get(c, str(c))
79
+
80
+
81
+ # -------------------------
82
+ # Prediction + preprocessing
83
+ # -------------------------
84
+ def do_predict(pil_img: PIL.Image.Image):
85
+ """Run prediction on an uploaded image. Returns original, preprocessed, and probabilities."""
86
+
87
+ if pil_img is None:
88
+ return None, None, "No image provided.", {}
89
+
90
+ try:
91
+ # --- Save to temp path for AutoGluon ---
92
+ tmpdir = pathlib.Path(tempfile.mkdtemp())
93
+ img_path = tmpdir / "input.png"
94
+ pil_img.save(img_path)
95
+
96
+ # --- Preprocess (resize to 224x224 for visualization only) ---
97
+ preprocessed_img = pil_img.copy()
98
+ preprocessed_img = preprocessed_img.resize((224, 224))
99
+
100
+ # --- Build input dataframe for AutoGluon ---
101
+ df = pandas.DataFrame({"image": [str(img_path)]})
102
+
103
+ # --- Predict probabilities ---
104
+ proba_df = PREDICTOR.predict_proba(df)
105
+
106
+ # Rename for clarity
107
+ proba_df = proba_df.rename(columns={
108
+ 0: "๐Ÿšฆ Not a Stop Sign (0)",
109
+ 1: "๐Ÿ›‘ Stop Sign (1)"
110
+ })
111
+ row = proba_df.iloc[0]
112
+
113
+ pretty_dict = {
114
+ "๐Ÿšฆ Not a Stop Sign": float(row.get("๐Ÿšฆ Not a Stop Sign (0)", 0.0)),
115
+ "๐Ÿ›‘ Stop Sign": float(row.get("๐Ÿ›‘ Stop Sign (1)", 0.0)),
116
+ }
117
+
118
+ return pil_img, preprocessed_img, "Prediction complete", pretty_dict
119
+
120
+ except Exception as e:
121
+ return None, None, f"Error: {str(e)}", {}
122
+
123
+
124
+ # -------------------------
125
+ # Example images
126
+ # -------------------------
127
+ EXAMPLES = [
128
+ ["https://upload.wikimedia.org/wikipedia/commons/thumb/f/f9/STOP_sign.jpg/640px-STOP_sign.jpg"],
129
+ ["https://upload.wikimedia.org/wikipedia/commons/1/19/Swiss_Frutiger_Traffic_Sign.jpg"]
130
+ ]
131
+
132
+
133
+ # -------------------------
134
+ # Gradio interface
135
+ # -------------------------
136
+ with gradio.Blocks() as demo:
137
+
138
+ gradio.Markdown("# ๐Ÿ›‘ Stop Sign Detector")
139
+ gradio.Markdown("""
140
+ Upload a road scene or traffic sign image, and this app will classify whether
141
+ a stop sign is present. The interface shows both the original image and the
142
+ preprocessed image (224x224) that the model actually sees.
143
+ """)
144
+
145
+ with gradio.Row():
146
+ image_in = gradio.Image(
147
+ type="pil",
148
+ label="Upload or capture an image",
149
+ sources=["upload", "webcam"],
150
+ image_mode="RGB"
151
+ )
152
+
153
+ with gradio.Row():
154
+ orig_out = gradio.Image(type="pil", label="Original Image")
155
+ preproc_out = gradio.Image(type="pil", label="Preprocessed Image (224x224)")
156
+
157
+ status_out = gradio.Textbox(label="Status")
158
+ proba_pretty = gradio.Label(num_top_classes=2, label="Class probabilities")
159
+
160
+ image_in.change(
161
+ fn=do_predict,
162
+ inputs=[image_in],
163
+ outputs=[orig_out, preproc_out, status_out, proba_pretty]
164
+ )
165
+
166
+ gradio.Examples(
167
+ examples=EXAMPLES,
168
+ inputs=[image_in],
169
+ label="Representative examples",
170
+ examples_per_page=3,
171
+ cache_examples=False,
172
+ )
173
+
174
+ if __name__ == "__main__":
175
+ demo.launch()