File size: 4,717 Bytes
e6d4a9f
 
 
 
 
 
 
 
 
9d88c31
 
 
 
 
 
 
 
 
 
 
 
 
 
3800217
9d88c31
 
 
 
 
 
 
3800217
 
9d88c31
3800217
9d88c31
3800217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9d88c31
e6d4a9f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9d88c31
 
 
 
e6d4a9f
 
 
 
fbea1a6
8a27e32
fbea1a6
e6d4a9f
 
 
 
 
 
 
 
 
 
 
9d88c31
e6d4a9f
 
 
 
 
0f0a138
e6d4a9f
 
 
d455dab
0f0a138
d455dab
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
126
127
128
129
130
131
132
133
134
### 1. Imports and class names setup ###
import gradio as gr
import os
import torch

from model import create_effnetb2_model
from timeit import default_timer as timer
from typing import Tuple, Dict

#calorie count
import requests

def get_calorie_count(food_name):
    # Use USDA FoodData Central Search API
    api_key = "8Kw59j70SiLY9BWuBBOYOQkLzwcsXVFEJO4pVvpf"
    search_url = f"https://api.nal.usda.gov/fdc/v1/foods/search"
    params = {
        "api_key": api_key,
        "query": food_name,
        "pageSize": 1,
    }
    try:
        response = requests.get(search_url, params=params)
        response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
        data = response.json()
        if "foods" in data and data["foods"]:
            food = data["foods"][0]
            for nutrient in food.get("foodNutrients", []):
                if nutrient.get("nutrientName", "").lower() == "energy" and nutrient.get("unitName", "").lower() == "kcal":
                    return f'{nutrient.get("value")} kcal per 100g'
        return "Calorie info not found"
    except requests.exceptions.RequestException as e:
        return f"API Error: {e}" # More specific error handling
    except Exception as e:
        return f"General Error: {e}"

# Pre-fetch calorie values for all food classes
class_calories = {}

def preload_calories():
    with open('class_names.txt', 'r') as f:
        for food_name in f:
            food_name = food_name.strip() # Remove leading/trailing whitespace
            if food_name: # Ensure the line is not empty
                # Replace underscores with spaces
                formatted_food_name = food_name.replace('_', ' ')
                #print(f"Searching for: {formatted_food_name}") # Added for debugging
                class_calories[food_name] = get_calorie_count(formatted_food_name)

preload_calories()

def get_calorie_count(food_name):
    return class_calories.get(food_name, "Calorie info not found")

# Setup class names
with open("class_names.txt", "r") as f: # reading them in from class_names.txt
    class_names = [food_name.strip() for food_name in  f.readlines()]

### 2. Model and transforms preparation ###

# Create model
effnetb2, effnetb2_transforms = create_effnetb2_model(
    num_classes=101, # could also use len(class_names)
)

# Load saved weights
effnetb2.load_state_dict(
    torch.load(
        f="pretrained_effnetb2_feature_extractor_food101_20_percent.pth",
        map_location=torch.device("cpu"),  # load to CPU
    )
)

### 3. Predict function ###

# Create predict function
def predict(img) -> Tuple[Dict, float]:
    """Transforms and performs a prediction on img and returns prediction and time taken.
    """
    # Start the timer
    start_time = timer()

    # Transform the target image and add a batch dimension
    img = effnetb2_transforms(img).unsqueeze(0)

    # Put model into evaluation mode and turn on inference mode
    effnetb2.eval()
    with torch.inference_mode():
        # Pass the transformed image through the model and turn the prediction logits into prediction probabilities
        pred_probs = torch.softmax(effnetb2(img), dim=1)

    # Create a prediction label and prediction probability dictionary for each prediction class (this is the required format for Gradio's output parameter)
    pred_labels_and_probs = {class_names[i]: float(pred_probs[0][i]) for i in range(len(class_names))}

    # Calculate the prediction time
    pred_time = round(timer() - start_time, 5)

    # Return the prediction dictionary and prediction time
    # return pred_labels_and_probs, pred_time
    top_food = max(pred_labels_and_probs, key=pred_labels_and_probs.get)
    calorie_count = get_calorie_count(top_food)
    return pred_labels_and_probs, pred_time, calorie_count

### 4. Gradio app ###

# Create title, description and article strings
title = "Food Vision ๐Ÿ”๐Ÿ‘"
description = "An EfficientNetB2 feature extractor computer vision model to classify images of food into 101 different classes"
article = "Created at : https://github.com/rageya/food-vision"

# Create examples list from "examples/" directory
example_list = [["examples/" + example] for example in os.listdir("examples")]

# Create Gradio interface
demo = gr.Interface(
    fn=predict,
    inputs=gr.Image(type="pil"),
    outputs=[
        gr.Label(num_top_classes=5, label="Predictions"),
        gr.Number(label="Prediction time (s)"),
        gr.Textbox(label="Estimated Calories (per 100g)"),
    ],
    examples=example_list,
    title=title,
    description=description,
    article=article,
    #api_name="predict"
)

# Launch the app!
# Launch the app with API explicitly enabled!
demo.launch(share=True)