Awashati commited on
Commit
1307a08
·
verified ·
1 Parent(s): 2810986

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -29
app.py CHANGED
@@ -1,40 +1,147 @@
 
 
 
1
  import gradio as gr
2
- import joblib
3
- import json
4
- import os
5
- from PIL import Image
6
  import numpy as np
7
- from sklearn.neighbors import KNeighborsClassifier
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- # Load trained model
10
- MODEL_PATH = "my_model_k7.pkl"
11
- LABELS_PATH = "labels.json"
 
 
 
 
 
12
 
13
- clf = joblib.load(MODEL_PATH)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- with open(LABELS_PATH, "r") as f:
16
- labels = json.load(f)
 
 
 
 
 
 
 
17
 
18
- # Feature extractor (resize + flatten)
19
- def preprocess(img):
20
- img = img.convert("RGB").resize((224, 224))
21
- return np.array(img).flatten().reshape(1, -1)
22
 
23
- # Prediction function
24
- def predict(image):
25
- features = preprocess(image)
26
- pred = clf.predict(features)[0]
27
- return f"🌿 Predicted Plant: {labels[int(pred)]}"
28
 
29
- # Gradio UI
30
- demo = gr.Interface(
31
- fn=predict,
32
- inputs=gr.Image(type="pil", label="Upload a Plant Image"),
33
- outputs="text",
34
- title="🌍 UAE Flora Explorer",
35
- description="Upload an image of a UAE plant and the model will identify it."
36
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  if __name__ == "__main__":
39
  demo.launch()
40
-
 
1
+ import os, json, traceback
2
+ from typing import List, Tuple, Optional
3
+
4
  import gradio as gr
 
 
 
 
5
  import numpy as np
6
+ from PIL import Image
7
+ import joblib
8
+
9
+ # -------------------------------
10
+ # Config (change if you used other names)
11
+ # -------------------------------
12
+ MODEL_FILE = os.getenv("MODEL_FILE", "my_model_k7.pkl") # e.g. "my_model.pkl" or "my_model_k5.pkl"
13
+ LABELS_FILE = os.getenv("LABELS_FILE", "labels.json") # the labels you saved
14
+
15
+ IMG_SIZE = 224 # MobileNetV2 default input size
16
+
17
+ # -------------------------------
18
+ # Feature extractor (MobileNetV2)
19
+ # -------------------------------
20
+ # We use TF/Keras MobileNetV2 (without the top) + global average pooling ("avg") for embeddings.
21
+ # Ref: Keras Applications docs and MobileNetV2 API.
22
+ try:
23
+ import tensorflow as tf
24
+ from tensorflow.keras.applications.mobilenet_v2 import (
25
+ MobileNetV2,
26
+ preprocess_input,
27
+ )
28
+
29
+ _feature_model = MobileNetV2(
30
+ weights="imagenet", include_top=False, pooling="avg",
31
+ input_shape=(IMG_SIZE, IMG_SIZE, 3)
32
+ )
33
+
34
+ def embed_image(img: Image.Image) -> np.ndarray:
35
+ img = img.convert("RGB").resize((IMG_SIZE, IMG_SIZE))
36
+ x = np.array(img, dtype=np.float32)
37
+ x = np.expand_dims(x, axis=0)
38
+ x = preprocess_input(x)
39
+ feat = _feature_model.predict(x, verbose=0)[0]
40
+ return feat
41
 
42
+ FE_MSG = "TF MobileNetV2 embeddings (pooling=avg)"
43
+ except Exception as e:
44
+ # Very robust fallback (no TF): simple resize + flatten
45
+ _feature_model = None
46
+ FE_MSG = "Fallback embeddings (resize+flatten) — consider enabling TensorFlow for better accuracy."
47
+ def embed_image(img: Image.Image) -> np.ndarray:
48
+ img = img.convert("RGB").resize((IMG_SIZE, IMG_SIZE))
49
+ return (np.array(img, dtype=np.float32).ravel() / 255.0)
50
 
51
+ # -------------------------------
52
+ # Load model & labels (if present)
53
+ # -------------------------------
54
+ def _load_labels(path: str) -> Optional[List[str]]:
55
+ if not os.path.exists(path):
56
+ return None
57
+ with open(path, "r", encoding="utf-8") as f:
58
+ data = json.load(f)
59
+ # Accept list OR dict ({"0": "classA", ...})
60
+ if isinstance(data, list):
61
+ return [str(x) for x in data]
62
+ if isinstance(data, dict):
63
+ try:
64
+ # keys are indices as strings
65
+ return [data[str(i)] for i in sorted(map(int, data.keys()))]
66
+ except Exception:
67
+ # fallback: values in insertion order
68
+ return list(map(str, data.values()))
69
+ return None
70
 
71
+ def _load_model_and_labels() -> Tuple[Optional[object], Optional[List[str]], str]:
72
+ model, labels, note = None, None, ""
73
+ if os.path.exists(MODEL_FILE):
74
+ try:
75
+ model = joblib.load(MODEL_FILE)
76
+ except Exception as e:
77
+ note += f"⚠️ Could not load model '{MODEL_FILE}': {e}\n"
78
+ else:
79
+ note += f"❗ Model file not found: `{MODEL_FILE}`. Upload it in the **Files** tab.\n"
80
 
81
+ labels = _load_labels(LABELS_FILE)
82
+ if labels is None:
83
+ note += f"❗ Labels file not found or unreadable: `{LABELS_FILE}`. Upload a JSON labels file (list or index→name dict).\n"
 
84
 
85
+ if model and labels:
86
+ note = f"✅ Model & labels loaded. Feature extractor: **{FE_MSG}**"
87
+ elif not note:
88
+ note = "❗ Model and/or labels not loaded yet."
89
+ return model, labels, note
90
 
91
+ clf, LABELS, STATUS = _load_model_and_labels()
92
+
93
+ # -------------------------------
94
+ # Prediction
95
+ # -------------------------------
96
+ def predict(img: Image.Image):
97
+ if img is None:
98
+ return "Please upload an image.", None
99
+
100
+ if clf is None or LABELS is None:
101
+ return (
102
+ "Model/labels missing. Upload your `*.pkl` and `labels.json` in the Space **Files** tab, "
103
+ "then click **Restart**.", None
104
+ )
105
+
106
+ try:
107
+ feat = embed_image(img).reshape(1, -1)
108
+ # Predicted class
109
+ pred_idx = int(clf.predict(feat)[0])
110
+ pred_label = LABELS[pred_idx] if 0 <= pred_idx < len(LABELS) else f"Class #{pred_idx}"
111
+
112
+ # Top-3 probabilities (if available)
113
+ top3 = None
114
+ if hasattr(clf, "predict_proba"):
115
+ probs = clf.predict_proba(feat)[0]
116
+ order = np.argsort(probs)[::-1][:min(3, len(probs))]
117
+ top3 = [
118
+ {"Class": LABELS[i] if i < len(LABELS) else f"#{i}",
119
+ "Probability": float(probs[i])}
120
+ for i in order
121
+ ]
122
+
123
+ return pred_label, top3
124
+ except Exception as e:
125
+ tb = traceback.format_exc(limit=2)
126
+ return f"Error during prediction: {e}\n{tb}", None
127
+
128
+ # -------------------------------
129
+ # UI
130
+ # -------------------------------
131
+ with gr.Blocks(title="UAE Flora Classifier") as demo:
132
+ gr.Markdown(
133
+ "# 🌿 UAE Flora Classifier\n"
134
+ "Upload a plant photo and I’ll predict the species using a KNN classifier over MobileNetV2 embeddings."
135
+ )
136
+ gr.Markdown(f"**Status:** {STATUS}\n\n**Model file:** `{MODEL_FILE}` • **Labels file:** `{LABELS_FILE}`")
137
+
138
+ with gr.Row():
139
+ in_img = gr.Image(type="pil", label="Upload plant image")
140
+ out_label = gr.Label(label="Predicted class")
141
+ out_table = gr.Dataframe(headers=["Class", "Probability"], label="Top-3 (if available)", interactive=False)
142
+
143
+ btn = gr.Button("Predict")
144
+ btn.click(fn=predict, inputs=[in_img], outputs=[out_label, out_table])
145
 
146
  if __name__ == "__main__":
147
  demo.launch()