Spaces:
Runtime error
Runtime error
| """ | |
| 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() | |
| def root(): | |
| """Simple health‑check.""" | |
| return {"message": "Food‑Recognition API is up"} | |
| # ------------------------------------------------------------ | |
| # 6️⃣ Main inference endpoint | |
| # ------------------------------------------------------------ | |
| 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), | |
| } | |