aayanb09 commited on
Commit
def2892
Β·
verified Β·
1 Parent(s): 0045512

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +85 -59
app.py CHANGED
@@ -1,26 +1,21 @@
1
- import torch
2
  import gradio as gr
 
 
 
3
  from PIL import Image
4
  import numpy as np
5
- from torchvision import transforms, models
6
- import torch.nn as nn
7
- from sklearn.preprocessing import MultiLabelBinarizer # Add this import
8
 
9
- DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
 
 
10
 
11
- # -------------------------
12
- # MODEL DEFINITION
13
- # -------------------------
14
  class FoodIngredientClassifier(nn.Module):
15
  def __init__(self, num_classes):
16
  super().__init__()
17
-
18
- self.backbone = models.vit_b_16(
19
- weights=models.ViT_B_16_Weights.DEFAULT
20
- )
21
-
22
  num_features = self.backbone.heads.head.in_features
23
-
24
  self.backbone.heads = nn.Sequential(
25
  nn.Dropout(0.5),
26
  nn.Linear(num_features, 1024),
@@ -38,72 +33,103 @@ class FoodIngredientClassifier(nn.Module):
38
  return self.backbone(x)
39
 
40
 
41
- # -------------------------
42
- # LOAD CHECKPOINT - FIXED VERSION
43
- # -------------------------
44
- # Add the sklearn class to safe globals before loading
45
- torch.serialization.add_safe_globals([MultiLabelBinarizer])
46
 
47
- checkpoint = torch.load("model.pth", map_location=DEVICE)
 
48
 
49
- mlb = checkpoint["mlb"]
50
- num_classes = len(mlb.classes_)
 
 
 
 
 
 
 
51
 
52
- model = FoodIngredientClassifier(num_classes)
53
- model.load_state_dict(checkpoint["model_state_dict"])
54
- model.to(DEVICE)
55
- model.eval()
56
 
57
- threshold = checkpoint.get("optimal_threshold", 0.5)
 
 
58
 
59
- # -------------------------
60
- # IMAGE TRANSFORM
61
- # -------------------------
62
  transform = transforms.Compose([
63
  transforms.Resize((224, 224)),
64
  transforms.ToTensor(),
65
- transforms.Normalize(
66
- [0.485, 0.456, 0.406],
67
- [0.229, 0.224, 0.225]
68
- )
69
  ])
70
 
71
- # -------------------------
72
- # PREDICTION FUNCTION
73
- # -------------------------
74
- def predict(image):
75
- image = image.convert("RGB")
76
- img_tensor = transform(image).unsqueeze(0).to(DEVICE)
 
 
 
 
77
 
78
  with torch.no_grad():
79
  output = model(img_tensor)
80
  probs = torch.sigmoid(output).cpu().numpy()[0]
81
 
82
  pred_indices = np.where(probs > threshold)[0]
83
- ingredients = mlb.classes_[pred_indices]
84
- confidences = probs[pred_indices]
 
85
 
86
  results = sorted(
87
- [(ing, float(conf)) for ing, conf in zip(ingredients, confidences)],
88
  key=lambda x: x[1],
89
  reverse=True
90
  )
91
 
92
- if not results:
93
- return {"No ingredient detected": 1.0}
94
-
95
- return {k: v for k, v in results}
96
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- # -------------------------
99
- # GRADIO INTERFACE
100
- # -------------------------
101
- iface = gr.Interface(
102
- fn=predict,
103
- inputs=gr.Image(type="pil"),
104
- outputs=gr.Label(num_top_classes=10),
105
- title="Food Ingredient Classifier",
106
- description="Upload a food image to detect ingredients."
107
- )
108
 
109
- iface.launch()
 
 
 
1
  import gradio as gr
2
+ import torch
3
+ import torch.nn as nn
4
+ from torchvision import transforms, models
5
  from PIL import Image
6
  import numpy as np
7
+ import pickle
8
+ import os
 
9
 
10
+ # ============================================================================
11
+ # MODEL DEFINITION (must match training code exactly)
12
+ # ============================================================================
13
 
 
 
 
14
  class FoodIngredientClassifier(nn.Module):
15
  def __init__(self, num_classes):
16
  super().__init__()
17
+ self.backbone = models.vit_b_16(weights=None)
 
 
 
 
18
  num_features = self.backbone.heads.head.in_features
 
19
  self.backbone.heads = nn.Sequential(
20
  nn.Dropout(0.5),
21
  nn.Linear(num_features, 1024),
 
33
  return self.backbone(x)
34
 
35
 
36
+ # ============================================================================
37
+ # LOAD MODEL
38
+ # ============================================================================
 
 
39
 
40
+ DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
41
+ THRESHOLD = 0.5
42
 
43
+ def load_model():
44
+ checkpoint = torch.load("model.pth", map_location=DEVICE)
45
+ mlb = checkpoint["mlb"]
46
+ num_classes = len(mlb.classes_)
47
+ model = FoodIngredientClassifier(num_classes)
48
+ model.load_state_dict(checkpoint["model_state_dict"])
49
+ model.to(DEVICE)
50
+ model.eval()
51
+ return model, mlb
52
 
53
+ model, mlb = load_model()
 
 
 
54
 
55
+ # ============================================================================
56
+ # TRANSFORM
57
+ # ============================================================================
58
 
 
 
 
59
  transform = transforms.Compose([
60
  transforms.Resize((224, 224)),
61
  transforms.ToTensor(),
62
+ transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
 
 
 
63
  ])
64
 
65
+ # ============================================================================
66
+ # INFERENCE FUNCTION
67
+ # ============================================================================
68
+
69
+ def predict(image, threshold):
70
+ if image is None:
71
+ return "Please upload an image."
72
+
73
+ img = image.convert("RGB")
74
+ img_tensor = transform(img).unsqueeze(0).to(DEVICE)
75
 
76
  with torch.no_grad():
77
  output = model(img_tensor)
78
  probs = torch.sigmoid(output).cpu().numpy()[0]
79
 
80
  pred_indices = np.where(probs > threshold)[0]
81
+
82
+ if len(pred_indices) == 0:
83
+ return "No ingredients detected above the threshold. Try lowering it."
84
 
85
  results = sorted(
86
+ zip(mlb.classes_[pred_indices], probs[pred_indices]),
87
  key=lambda x: x[1],
88
  reverse=True
89
  )
90
 
91
+ output_lines = ["### 🍽️ Detected Ingredients\n"]
92
+ for ingredient, confidence in results:
93
+ bar = "β–ˆ" * int(confidence * 20)
94
+ output_lines.append(f"**{ingredient}** β€” {confidence:.1%} `{bar}`")
95
+
96
+ return "\n\n".join(output_lines)
97
+
98
+
99
+ # ============================================================================
100
+ # GRADIO UI
101
+ # ============================================================================
102
+
103
+ with gr.Blocks(title="Food Ingredient Detector") as demo:
104
+ gr.Markdown("""
105
+ # πŸ₯— Food Ingredient Detector
106
+ Upload a photo of food and the model will identify its ingredients.
107
+ Built with a **ViT-B/16** backbone fine-tuned for multi-label ingredient classification.
108
+ """)
109
+
110
+ with gr.Row():
111
+ with gr.Column():
112
+ image_input = gr.Image(type="pil", label="Upload Food Image")
113
+ threshold_slider = gr.Slider(
114
+ minimum=0.1, maximum=0.9, value=0.5, step=0.05,
115
+ label="Detection Threshold",
116
+ info="Lower = more ingredients detected, higher = only confident predictions"
117
+ )
118
+ predict_btn = gr.Button("πŸ” Detect Ingredients", variant="primary")
119
+
120
+ with gr.Column():
121
+ output = gr.Markdown(label="Results")
122
+
123
+ predict_btn.click(
124
+ fn=predict,
125
+ inputs=[image_input, threshold_slider],
126
+ outputs=output
127
+ )
128
 
129
+ gr.Examples(
130
+ examples=[], # Add example image paths here if you include sample images
131
+ inputs=image_input
132
+ )
 
 
 
 
 
 
133
 
134
+ if __name__ == "__main__":
135
+ demo.launch()