""" FastAPI service exposing BinhQuocNguyen/food‑recognition‑model. """ # ------------------------------------------------------------ # 1️⃣ Imports # ------------------------------------------------------------ from fastapi import FastAPI, HTTPException from pydantic import BaseModel import base64 import io from PIL import Image from transformers import pipeline # ------------------------------------------------------------ # 2️⃣ Load the model once at startup (fast, no re‑load per request) # ------------------------------------------------------------ classifier = pipeline( "image-classification", model="BinhQuocNguyen/food-recognition-model" ) # ------------------------------------------------------------ # 3️⃣ Simple in‑memory “nutritional DB” # (calories per 100 g, average portion weight in grams) # ------------------------------------------------------------ nutrient_db = { "Apple": {"calories_per_100g": 52, "portion_g": 182}, "Banana": {"calories_per_100g": 89, "portion_g": 118}, "Orange": {"calories_per_100g": 43, "portion_g": 131}, "Pizza": {"calories_per_100g": 266, "portion_g": 200}, "Bread": {"calories_per_100g": 265, "portion_g": 30}, # ← add the rest of your favourite foods here } # ------------------------------------------------------------ # 4️⃣ Pydantic model for the incoming JSON payload # ------------------------------------------------------------ class ImageRequest(BaseModel): """Base64‑encoded image sent by the client.""" image: str # ------------------------------------------------------------ # 5️⃣ FastAPI app & health‑check endpoint # ------------------------------------------------------------ app = FastAPI() @app.get("/") def root(): """Simple health‑check.""" return {"message": "Food‑Recognition API is up"} # ------------------------------------------------------------ # 6️⃣ Main inference endpoint # ------------------------------------------------------------ @app.post("/analyze") def analyze(request: ImageRequest): """ 1️⃣ Decode the base64 image. 2️⃣ Run the classifier. 3️⃣ Look up (or fall back to) nutritional information. 4️⃣ Return a JSON response. """ # ---- 1️⃣ decode the base64 image --------------------------------- try: raw_bytes = base64.b64decode(request.image) pil_img = Image.open(io.BytesIO(raw_bytes)).convert("RGB") except Exception as e: # pragma: no cover raise HTTPException(status_code=400, detail="Invalid base64 image") from e # ---- 2️⃣ run the classifier -------------------------------------- results = classifier(pil_img) if not results: raise HTTPException(status_code=500, detail="Model returned no results") top = results[0] label = top.get("label") confidence = top.get("score") # ---- 3️⃣ look up nutrition data ---------------------------------- # Fallback values if the label isn’t in the DB nutrition = nutrient_db.get(label, {"calories_per_100g": 0, "portion_g": 100}) calories_per_100g = nutrition["calories_per_100g"] portion_g = nutrition["portion_g"] # ---- 4️⃣ estimated calories for the default portion size ---------- est_calories = calories_per_100g * (portion_g / 100.0) # ---- 5️⃣ build the JSON response -------------------------------- return { "label": label, "confidence": confidence, "estimated_portion_g": portion_g, "calories_per_100g": calories_per_100g, "estimated_calories": round(est_calories, 2), }