import gradio as gr import os import json import requests from PIL import Image from difflib import get_close_matches from functools import lru_cache # ===================== # ENV # ===================== ROBOFLOW_API_KEY = os.getenv("ROBOFLOW_API_KEY") # ===================== # LOAD DB # ===================== with open("nutrition_db.json", "r") as f: NUTRITION_DB = json.load(f) print("✅ Loaded DB:", len(NUTRITION_DB)) # ===================== # NORMALIZATION # ===================== def normalize_food_name(name): name = name.lower() mapping = { "chapati": "wheat", "roti": "wheat", "naan": "wheat", "paratha": "wheat", "omelette": "egg", "omellete": "egg", "fried rice": "rice", "plain rice": "rice" } return mapping.get(name, name) # ===================== # FIND MATCH # ===================== def find_food(name): if name in NUTRITION_DB: return name matches = get_close_matches(name, NUTRITION_DB.keys(), n=1, cutoff=0.6) return matches[0] if matches else None # ===================== # FALLBACK # ===================== def estimate_unknown_food(grams): return { "calories": round(1.5 * grams, 2), "protein": round(0.05 * grams, 2), "carbs": round(0.2 * grams, 2), "fat": round(0.05 * grams, 2), } # ===================== # CACHE # ===================== @lru_cache(maxsize=1000) def compute_nutrition_cached(food_key, grams): base = NUTRITION_DB[food_key] factor = grams / 100 return { "calories": round(base["calories"] * factor, 2), "protein": round(base["protein"] * factor, 2), "carbs": round(base["carbs"] * factor, 2), "fat": round(base["fat"] * factor, 2), } # ===================== # GET NUTRITION # ===================== def get_nutrition(dish, grams): dish = normalize_food_name(dish) food_key = find_food(dish) if not food_key: return estimate_unknown_food(grams) return compute_nutrition_cached(food_key, grams) # ===================== # QUANTITY # ===================== def estimate_quantity(pred): width = pred.get("width", 0) height = pred.get("height", 0) area = width * height ratio = area / (640 * 640) grams = 150 + (ratio * 300) return round(grams, 1) # ===================== # ROBOFLOW (HTTP) # ===================== import requests import os def detect(image_path): api_key = os.getenv("ROBOFLOW_API_KEY") if not api_key: return "❌ API KEY NOT FOUND" # model_id = "almost-final/1" url = "https://detect.roboflow.com/almost-final/1" try: with open(image_path, "rb") as f: response = requests.post( url, files={"file": f}, # ✅ correct format params={"api_key": api_key}, timeout=15 ) print("STATUS:", response.status_code) print("RESPONSE:", response.text) if response.status_code != 200: return f"❌ Roboflow Error: {response.text}" data = response.json() return data.get("predictions", []) except Exception as e: return f"❌ Request Failed: {str(e)}" # ===================== # MAIN FUNCTION # ===================== def analyze_image(image): if image is None: return "Please upload an image" path = "temp.jpg" # PIL image save image.save(path) preds = detect(path) if isinstance(preds, str): return preds if len(preds) == 0: return "❌ No food detected" output = "" total = {"calories": 0, "protein": 0, "carbs": 0, "fat": 0} for pred in preds: dish = pred.get("class", "unknown") grams = estimate_quantity(pred) nutrition = get_nutrition(dish, grams) output += f"🍽️ {dish}\n" output += f"📏 {grams} g\n" output += f"🔥 {nutrition['calories']} kcal\n" output += f"💪 Protein: {nutrition['protein']} g\n" output += f"🍞 Carbs: {nutrition['carbs']} g\n" output += f"🧈 Fat: {nutrition['fat']} g\n" output += "-"*30 + "\n" for k in total: total[k] += nutrition[k] output += "\n🧾 TOTAL:\n" output += f"🔥 Calories: {round(total['calories'],2)}\n" output += f"💪 Protein: {round(total['protein'],2)} g\n" output += f"🍞 Carbs: {round(total['carbs'],2)} g\n" output += f"🧈 Fat: {round(total['fat'],2)} g\n" return output # ===================== # UI # ===================== demo = gr.Interface( fn=analyze_image, inputs=gr.Image(type="pil"), # ✅ FIXED outputs="text", title="🍽️ AI Nutritionist", description="Upload food image to get calories & macros" ) # ===================== # RUN # ===================== if __name__ == "__main__": demo.launch( server_name="0.0.0.0", server_port=7860, ssr_mode=False # ✅ CRITICAL F )