Update app.py
Browse files
app.py
CHANGED
|
@@ -4,7 +4,30 @@ import numpy as np
|
|
| 4 |
from PIL import Image
|
| 5 |
import json
|
| 6 |
import os
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
# Page configuration
|
| 10 |
st.set_page_config(
|
|
@@ -71,66 +94,72 @@ st.markdown('<div class="pakistan-flag">π΅π° Pakistan π΅π°</div>', unsaf
|
|
| 71 |
MODEL_PATH = "dish_classifier_final.keras"
|
| 72 |
CLASSES_PATH = "class_names.json"
|
| 73 |
|
| 74 |
-
# Try to download model from Hugging Face cache if not exists
|
| 75 |
@st.cache_resource
|
| 76 |
def load_model():
|
| 77 |
-
"""Load the trained model
|
| 78 |
|
| 79 |
-
#
|
| 80 |
-
if os.path.exists(MODEL_PATH):
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
-
# If not found locally, try to find in Hugging Face cache
|
| 89 |
try:
|
| 90 |
-
#
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
)
|
| 96 |
-
model = tf.keras.models.load_model(model_path)
|
| 97 |
-
st.success("β
Model loaded from Hugging Face cache!")
|
| 98 |
-
return model
|
| 99 |
-
except:
|
| 100 |
-
st.error("""
|
| 101 |
-
β οΈ Model file not found!
|
| 102 |
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
return None
|
| 110 |
|
| 111 |
@st.cache_data
|
| 112 |
def load_class_names():
|
| 113 |
"""Load class names from JSON file"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
try:
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
return class_names
|
| 120 |
-
else:
|
| 121 |
-
st.error("class_names.json not found! Please upload the file.")
|
| 122 |
-
return None
|
| 123 |
except Exception as e:
|
| 124 |
st.error(f"Error loading class names: {e}")
|
| 125 |
return None
|
| 126 |
|
| 127 |
def preprocess_image(image):
|
| 128 |
"""Preprocess image for model prediction"""
|
| 129 |
-
# Convert to RGB
|
| 130 |
if image.mode != 'RGB':
|
| 131 |
image = image.convert('RGB')
|
| 132 |
|
| 133 |
-
# Resize to 224x224
|
| 134 |
image = image.resize((224, 224))
|
| 135 |
|
| 136 |
# Convert to array
|
|
@@ -207,7 +236,7 @@ def main():
|
|
| 207 |
st.markdown("---")
|
| 208 |
st.markdown("### π Model Performance")
|
| 209 |
|
| 210 |
-
# Add
|
| 211 |
st.markdown("**Exact Match Accuracy**")
|
| 212 |
st.progress(0.56)
|
| 213 |
st.caption("56.25%")
|
|
@@ -297,14 +326,20 @@ def main():
|
|
| 297 |
|
| 298 |
# Add confidence visualization
|
| 299 |
st.markdown("### π Confidence Level")
|
| 300 |
-
|
| 301 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 302 |
|
| 303 |
-
|
| 304 |
-
if top_5_probs[0] < 50:
|
| 305 |
-
st.info("π‘ Tip: Try uploading a clearer image of the dish alone for better accuracy")
|
| 306 |
|
| 307 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
if not uploaded_file:
|
| 309 |
st.markdown("---")
|
| 310 |
st.markdown("### π About This Model")
|
|
|
|
| 4 |
from PIL import Image
|
| 5 |
import json
|
| 6 |
import os
|
| 7 |
+
|
| 8 |
+
# Define custom metrics (MUST be defined before loading model)
|
| 9 |
+
def top_3_accuracy(y_true, y_pred):
|
| 10 |
+
return tf.keras.metrics.top_k_categorical_accuracy(y_true, y_pred, k=3)
|
| 11 |
+
|
| 12 |
+
def top_5_accuracy(y_true, y_pred):
|
| 13 |
+
return tf.keras.metrics.top_k_categorical_accuracy(y_true, y_pred, k=5)
|
| 14 |
+
|
| 15 |
+
# Register custom functions with Keras
|
| 16 |
+
@tf.keras.utils.register_keras_serializable()
|
| 17 |
+
def top_3_accuracy_serializable(y_true, y_pred):
|
| 18 |
+
return tf.keras.metrics.top_k_categorical_accuracy(y_true, y_pred, k=3)
|
| 19 |
+
|
| 20 |
+
@tf.keras.utils.register_keras_serializable()
|
| 21 |
+
def top_5_accuracy_serializable(y_true, y_pred):
|
| 22 |
+
return tf.keras.metrics.top_k_categorical_accuracy(y_true, y_pred, k=5)
|
| 23 |
+
|
| 24 |
+
# Custom objects dictionary
|
| 25 |
+
custom_objects = {
|
| 26 |
+
'top_3_accuracy': top_3_accuracy,
|
| 27 |
+
'top_5_accuracy': top_5_accuracy,
|
| 28 |
+
'top_3_accuracy_serializable': top_3_accuracy_serializable,
|
| 29 |
+
'top_5_accuracy_serializable': top_5_accuracy_serializable
|
| 30 |
+
}
|
| 31 |
|
| 32 |
# Page configuration
|
| 33 |
st.set_page_config(
|
|
|
|
| 94 |
MODEL_PATH = "dish_classifier_final.keras"
|
| 95 |
CLASSES_PATH = "class_names.json"
|
| 96 |
|
|
|
|
| 97 |
@st.cache_resource
|
| 98 |
def load_model():
|
| 99 |
+
"""Load the trained model with custom objects"""
|
| 100 |
|
| 101 |
+
# Check if model exists
|
| 102 |
+
if not os.path.exists(MODEL_PATH):
|
| 103 |
+
st.error(f"""
|
| 104 |
+
β Model file not found: `{MODEL_PATH}`
|
| 105 |
+
|
| 106 |
+
**Please upload the model file:**
|
| 107 |
+
1. Click on **"Files"** tab above
|
| 108 |
+
2. Click **"Add file"** β **"Upload file"**
|
| 109 |
+
3. Upload `dish_classifier_final.keras`
|
| 110 |
+
4. Upload `class_names.json`
|
| 111 |
+
5. Refresh this page
|
| 112 |
+
|
| 113 |
+
**Note:** The model file should be around 80-100 MB.
|
| 114 |
+
""")
|
| 115 |
+
return None
|
| 116 |
|
|
|
|
| 117 |
try:
|
| 118 |
+
# Load model with custom objects
|
| 119 |
+
model = tf.keras.models.load_model(
|
| 120 |
+
MODEL_PATH,
|
| 121 |
+
custom_objects=custom_objects,
|
| 122 |
+
compile=False
|
| 123 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
+
# Recompile the model with the same metrics
|
| 126 |
+
model.compile(
|
| 127 |
+
optimizer='adam',
|
| 128 |
+
loss='categorical_crossentropy',
|
| 129 |
+
metrics=['accuracy', top_3_accuracy, top_5_accuracy]
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
st.success("β
Model loaded successfully!")
|
| 133 |
+
return model
|
| 134 |
+
except Exception as e:
|
| 135 |
+
st.error(f"Error loading model: {str(e)}")
|
| 136 |
+
st.info("Make sure the model file is not corrupted and was trained with TensorFlow 2.x")
|
| 137 |
return None
|
| 138 |
|
| 139 |
@st.cache_data
|
| 140 |
def load_class_names():
|
| 141 |
"""Load class names from JSON file"""
|
| 142 |
+
if not os.path.exists(CLASSES_PATH):
|
| 143 |
+
st.error(f"β Class names file not found: `{CLASSES_PATH}`")
|
| 144 |
+
st.info("Please upload `class_names.json` file")
|
| 145 |
+
return None
|
| 146 |
+
|
| 147 |
try:
|
| 148 |
+
with open(CLASSES_PATH, 'r') as f:
|
| 149 |
+
class_names = json.load(f)
|
| 150 |
+
st.success(f"β
Loaded {len(class_names)} Pakistani food classes!")
|
| 151 |
+
return class_names
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
except Exception as e:
|
| 153 |
st.error(f"Error loading class names: {e}")
|
| 154 |
return None
|
| 155 |
|
| 156 |
def preprocess_image(image):
|
| 157 |
"""Preprocess image for model prediction"""
|
| 158 |
+
# Convert to RGB if needed
|
| 159 |
if image.mode != 'RGB':
|
| 160 |
image = image.convert('RGB')
|
| 161 |
|
| 162 |
+
# Resize to 224x224
|
| 163 |
image = image.resize((224, 224))
|
| 164 |
|
| 165 |
# Convert to array
|
|
|
|
| 236 |
st.markdown("---")
|
| 237 |
st.markdown("### π Model Performance")
|
| 238 |
|
| 239 |
+
# Add simple gauges for accuracy
|
| 240 |
st.markdown("**Exact Match Accuracy**")
|
| 241 |
st.progress(0.56)
|
| 242 |
st.caption("56.25%")
|
|
|
|
| 326 |
|
| 327 |
# Add confidence visualization
|
| 328 |
st.markdown("### π Confidence Level")
|
| 329 |
+
if top_5_probs[0] > 70:
|
| 330 |
+
confidence_color = "π’ High Confidence"
|
| 331 |
+
elif top_5_probs[0] > 50:
|
| 332 |
+
confidence_color = "π‘ Medium Confidence"
|
| 333 |
+
else:
|
| 334 |
+
confidence_color = "π Low Confidence"
|
| 335 |
|
| 336 |
+
st.markdown(f"{confidence_color} - {top_5_probs[0]:.1f}% confident this is **{top_5_labels[0]}**")
|
|
|
|
|
|
|
| 337 |
|
| 338 |
+
# Add suggestion for low confidence
|
| 339 |
+
if top_5_probs[0] < 50:
|
| 340 |
+
st.info("π‘ **Tip:** Try uploading a clearer image of just the dish for better accuracy")
|
| 341 |
+
|
| 342 |
+
# Add information when no image is uploaded
|
| 343 |
if not uploaded_file:
|
| 344 |
st.markdown("---")
|
| 345 |
st.markdown("### π About This Model")
|