rishab1090 commited on
Commit
662cc40
Β·
verified Β·
1 Parent(s): 521a49d

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +201 -0
app.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import json
4
+ import shutil
5
+ from difflib import get_close_matches
6
+ from functools import lru_cache
7
+
8
+ from inference_sdk import InferenceHTTPClient
9
+
10
+ # =====================
11
+ # LOAD ENV
12
+ # =====================
13
+ ROBOFLOW_API_KEY = os.getenv("ROBOFLOW_API_KEY")
14
+
15
+ # =====================
16
+ # LOAD NUTRITION DB
17
+ # =====================
18
+ with open("nutrition_db.json", "r") as f:
19
+ NUTRITION_DB = json.load(f)
20
+
21
+ print("βœ… Loaded DB:", len(NUTRITION_DB))
22
+
23
+ # =====================
24
+ # ROBOFLOW CLIENT
25
+ # =====================
26
+ rf_client = InferenceHTTPClient(
27
+ api_url="https://serverless.roboflow.com",
28
+ api_key=ROBOFLOW_API_KEY
29
+ )
30
+
31
+ # =====================
32
+ # NORMALIZATION
33
+ # =====================
34
+ def normalize_food_name(name):
35
+ name = name.lower()
36
+
37
+ mapping = {
38
+ "chapati": "wheat",
39
+ "roti": "wheat",
40
+ "naan": "wheat",
41
+ "paratha": "wheat",
42
+ "omelette": "egg",
43
+ "omellete": "egg",
44
+ "fried rice": "rice",
45
+ "plain rice": "rice"
46
+ }
47
+
48
+ return mapping.get(name, name)
49
+
50
+ # =====================
51
+ # FIND MATCH
52
+ # =====================
53
+ def find_food(name):
54
+ if name in NUTRITION_DB:
55
+ return name
56
+
57
+ matches = get_close_matches(name, NUTRITION_DB.keys(), n=1, cutoff=0.6)
58
+ return matches[0] if matches else None
59
+
60
+ # =====================
61
+ # FALLBACK
62
+ # =====================
63
+ def estimate_unknown_food(grams):
64
+ return {
65
+ "calories": round(1.5 * grams, 2),
66
+ "protein": round(0.05 * grams, 2),
67
+ "carbs": round(0.2 * grams, 2),
68
+ "fat": round(0.05 * grams, 2),
69
+ }
70
+
71
+ # =====================
72
+ # NUTRITION CACHE
73
+ # =====================
74
+ @lru_cache(maxsize=1000)
75
+ def compute_nutrition_cached(food_key, grams):
76
+ base = NUTRITION_DB[food_key]
77
+ factor = grams / 100
78
+
79
+ return {
80
+ "calories": round(base["calories"] * factor, 2),
81
+ "protein": round(base["protein"] * factor, 2),
82
+ "carbs": round(base["carbs"] * factor, 2),
83
+ "fat": round(base["fat"] * factor, 2),
84
+ }
85
+
86
+ # =====================
87
+ # GET NUTRITION
88
+ # =====================
89
+ def get_nutrition(dish, grams):
90
+ dish = normalize_food_name(dish)
91
+ food_key = find_food(dish)
92
+
93
+ if not food_key:
94
+ return estimate_unknown_food(grams)
95
+
96
+ return compute_nutrition_cached(food_key, grams)
97
+
98
+ # =====================
99
+ # SIMPLE QUANTITY ESTIMATION
100
+ # =====================
101
+ def estimate_quantity(pred):
102
+ width = pred.get("width", 0)
103
+ height = pred.get("height", 0)
104
+
105
+ area = width * height
106
+ ratio = area / (640 * 640)
107
+
108
+ grams = 150 + (ratio * 300)
109
+ return round(grams, 1)
110
+
111
+ # =====================
112
+ # DETECTION
113
+ # =====================
114
+ def detect(image_path):
115
+ try:
116
+ result = rf_client.run_workflow(
117
+ workspace_name="rishab-5ghrt",
118
+ workflow_id="detect-count-and-visualize-4",
119
+ images={"image": image_path}
120
+ )
121
+
122
+ r = result[0]
123
+
124
+ if "predictions" in r:
125
+ preds = r["predictions"]
126
+
127
+ if isinstance(preds, dict):
128
+ return preds.get("predictions", [])
129
+
130
+ return preds
131
+
132
+ return []
133
+
134
+ except Exception as e:
135
+ return f"❌ Roboflow Error: {e}"
136
+
137
+ # =====================
138
+ # MAIN FUNCTION (GRADIO)
139
+ # =====================
140
+ def analyze_image(image):
141
+ if image is None:
142
+ return "Please upload an image"
143
+
144
+ path = "temp.jpg"
145
+ shutil.copy(image, path)
146
+
147
+ preds = detect(path)
148
+
149
+ if isinstance(preds, str):
150
+ return preds
151
+
152
+ if len(preds) == 0:
153
+ os.remove(path)
154
+ return "❌ No food detected"
155
+
156
+ output = ""
157
+ total = {"calories": 0, "protein": 0, "carbs": 0, "fat": 0}
158
+
159
+ for pred in preds:
160
+ dish = pred.get("class", "unknown")
161
+
162
+ grams = estimate_quantity(pred)
163
+ nutrition = get_nutrition(dish, grams)
164
+
165
+ output += f"🍽️ {dish}\n"
166
+ output += f"πŸ“ {grams} g\n"
167
+ output += f"πŸ”₯ {nutrition['calories']} kcal\n"
168
+ output += f"πŸ’ͺ Protein: {nutrition['protein']} g\n"
169
+ output += f"🍞 Carbs: {nutrition['carbs']} g\n"
170
+ output += f"🧈 Fat: {nutrition['fat']} g\n"
171
+ output += "-"*30 + "\n"
172
+
173
+ for k in total:
174
+ total[k] += nutrition[k]
175
+
176
+ os.remove(path)
177
+
178
+ output += "\n🧾 TOTAL:\n"
179
+ output += f"πŸ”₯ Calories: {round(total['calories'],2)}\n"
180
+ output += f"πŸ’ͺ Protein: {round(total['protein'],2)} g\n"
181
+ output += f"🍞 Carbs: {round(total['carbs'],2)} g\n"
182
+ output += f"🧈 Fat: {round(total['fat'],2)} g\n"
183
+
184
+ return output
185
+
186
+ # =====================
187
+ # GRADIO UI
188
+ # =====================
189
+ demo = gr.Interface(
190
+ fn=analyze_image,
191
+ inputs=gr.Image(type="filepath"),
192
+ outputs="text",
193
+ title="🍽️ AI Nutritionist",
194
+ description="Upload a food image to get calories & macros"
195
+ )
196
+
197
+ # =====================
198
+ # RUN
199
+ # =====================
200
+ if __name__ == "__main__":
201
+ demo.launch(server_name="0.0.0.0", server_port=7860)