File size: 3,490 Bytes
da50974
def2892
 
 
da50974
 
def2892
 
da50974
5eab86a
da50974
9dc3e41
 
 
def2892
9dc3e41
 
 
 
 
 
 
 
 
 
 
 
 
0045512
9dc3e41
 
 
0045512
5eab86a
21518d9
def2892
 
9dc3e41
def2892
0401c3e
def2892
 
 
 
 
 
 
9dc3e41
def2892
da50974
5eab86a
9dc3e41
da50974
 
 
def2892
da50974
 
5eab86a
def2892
 
 
 
 
 
 
0045512
da50974
 
 
0045512
da50974
def2892
 
 
0045512
da50974
def2892
da50974
 
 
0045512
def2892
 
 
 
 
 
 
 
5eab86a
def2892
 
 
5eab86a
def2892
 
 
 
 
 
 
 
 
 
 
 
5eab86a
def2892
 
 
 
 
 
 
 
 
0045512
def2892
 
 
 
da50974
def2892
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import gradio as gr
import torch
import torch.nn as nn
from torchvision import transforms, models
from PIL import Image
import numpy as np
import pickle
import os

# Model definition

class FoodIngredientClassifier(nn.Module):
    def __init__(self, num_classes):
        super().__init__()
        self.backbone = models.vit_b_16(weights=None)
        num_features = self.backbone.heads.head.in_features
        self.backbone.heads = nn.Sequential(
            nn.Dropout(0.5),
            nn.Linear(num_features, 1024),
            nn.BatchNorm1d(1024),
            nn.ReLU(),
            nn.Dropout(0.4),
            nn.Linear(1024, 512),
            nn.BatchNorm1d(512),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(512, num_classes)
        )

    def forward(self, x):
        return self.backbone(x)


# Load model

DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
THRESHOLD = 0.5

def load_model():
    checkpoint = torch.load("model.pth", map_location=DEVICE, weights_only=False)
    mlb = checkpoint["mlb"]
    num_classes = len(mlb.classes_)
    model = FoodIngredientClassifier(num_classes)
    model.load_state_dict(checkpoint["model_state_dict"])
    model.to(DEVICE)
    model.eval()
    return model, mlb

model, mlb = load_model()

# Transform

transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

# Inference Function

def predict(image, threshold):
    if image is None:
        return "Please upload an image."

    img = image.convert("RGB")
    img_tensor = transform(img).unsqueeze(0).to(DEVICE)

    with torch.no_grad():
        output = model(img_tensor)
        probs = torch.sigmoid(output).cpu().numpy()[0]

    pred_indices = np.where(probs > threshold)[0]

    if len(pred_indices) == 0:
        return "No ingredients detected above the threshold. Try lowering it."

    results = sorted(
        zip(mlb.classes_[pred_indices], probs[pred_indices]),
        key=lambda x: x[1],
        reverse=True
    )

    output_lines = ["### 🍽️ Detected Ingredients\n"]
    for ingredient, confidence in results:
        bar = "█" * int(confidence * 20)
        output_lines.append(f"**{ingredient}** — {confidence:.1%}  `{bar}`")

    return "\n\n".join(output_lines)


# UI

with gr.Blocks(title="Food Ingredient Detector") as demo:
    gr.Markdown("""
    # Food Ingredient Detector
    Upload a photo of food and the model will identify its ingredients.
    Built with a **ViT-B/16** backbone fine-tuned for multi-label ingredient classification.
    """)

    with gr.Row():
        with gr.Column():
            image_input = gr.Image(type="pil", label="Upload Food Image")
            threshold_slider = gr.Slider(
                minimum=0.1, maximum=0.9, value=0.5, step=0.05,
                label="Detection Threshold",
                info="Lower = more ingredients detected, higher = only confident predictions"
            )
            predict_btn = gr.Button("Detect Ingredients", variant="primary")

        with gr.Column():
            output = gr.Markdown(label="Results")

    predict_btn.click(
        fn=predict,
        inputs=[image_input, threshold_slider],
        outputs=output
    )

    gr.Examples(
        examples=[],  # Add example image paths here if you include sample images
        inputs=image_input
    )

if __name__ == "__main__":
    demo.launch()