DEV Community

wellallyTech
wellallyTech

Posted on

From Pixels to Proteins: Building a Precise Food Calorie Estimator with GPT-4o and Segment Anything (SAM) ๐ŸŽ

Weโ€™ve all been there: staring at a delicious plate of Pasta Carbonara, trying to figure out if itโ€™s 600 calories or a "let's-hit-the-gym-for-three-hours" 1,200 calories. Manual logging is tedious, and generic vision APIs often fail to distinguish between a small side of rice and a mountain of it.

In this tutorial, we are pushing the boundaries of Multimodal AI and Computer Vision by building a high-precision calorie estimation pipeline. We aren't just identifying objects; we are performing image segmentation with Metaโ€™s Segment Anything Model (SAM) and leveraging GPT-4o integration for advanced semantic reasoning. By the end of this post, you'll have a robust backend capable of turning a simple photo into a detailed nutritional breakdown.

For more production-ready AI patterns and advanced vision engineering guides, check out the deep dives at WellAlly Blog. ๐Ÿš€


The Architecture ๐Ÿ—๏ธ

The challenge with calorie estimation is depth and scale. A 2D image lacks a third dimension. To solve this, we use SAM to isolate food items precisely, calculate their relative pixel area, and then pass these high-fidelity masks to GPT-4o to infer volume and density based on culinary context.

graph TD
    A[User Uploads Photo] --> B[OpenCV Pre-processing]
    B --> C[Segment Anything Model - SAM]
    C --> D[Mask Generation & Area Calculation]
    D --> E[Cropped Food Segments]
    E --> F[GPT-4o Multimodal Analysis]
    F --> G[Nutritional Inference Engine]
    G --> H[FastAPI JSON Response]
    H --> I[User: Calories, Macros, Density]
Enter fullscreen mode Exit fullscreen mode

Prerequisites ๐Ÿ› ๏ธ

Before we dive into the code, ensure you have the following in your environment:

  • Python 3.10+
  • OpenAI API Key (with GPT-4o access)
  • Segment Anything Model Weights (sam_vit_h_4b8939.pth)
  • FastAPI & Uvicorn

Step 1: Segmenting the Plate with SAM ๐ŸงŠ

The Segment Anything Model (SAM) allows us to extract "masks" for every item on the plate without needing a pre-trained "food" model. This is crucial for distinguishing between the plate, the table, and the actual lasagna.

import numpy as np
import torch
import cv2
from segment_anything import sam_model_registry, SamPredictor

# Load SAM model
sam_checkpoint = "weights/sam_vit_h_4b8939.pth"
model_type = "vit_h"
device = "cuda" if torch.cuda.is_available() else "cpu"

sam = sam_model_registry[model_type](checkpoint=sam_checkpoint)
sam.to(device=device)
predictor = SamPredictor(sam)

def get_food_masks(image_path):
    image = cv2.imread(image_path)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    predictor.set_image(image)

    # In a production app, you might use a point-grid or 
    # a simple bounding box from a faster model like YOLOv8
    # For this demo, we generate masks for the entire image
    masks, scores, logits = predictor.predict(
        point_coords=None,
        point_labels=None,
        multimask_output=True,
    )
    return masks, image
Enter fullscreen mode Exit fullscreen mode

Step 2: GPT-4o Visual Reasoning ๐Ÿง 

Once we have the segments, we send the original image and the segmented metadata to GPT-4o. Why GPT-4o? Because it understands that a 500-pixel "white blob" on a plate is likely mashed potatoes (high density) rather than whipped cream (low density) based on the context of the steak next to it.

import base64
import requests

def analyze_nutrition_with_gpt4o(image_path, total_pixels):
    with open(image_path, "rb") as image_file:
        base64_image = base64.b64encode(image_file.read()).decode('utf-8')

    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {OPENAI_API_KEY}"
    }

    payload = {
        "model": "gpt-4o",
        "messages": [
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": f"Analyze this food image. I have calculated the pixel area. Based on the plate size, estimate the weight (grams) and nutritional macros for each item. Return a JSON object with: 'item_name', 'estimated_weight_g', 'calories', 'protein_g', 'carbs_g', 'fats_g'."
                    },
                    {
                        "type": "image_url",
                        "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}
                    }
                ]
            }
        ],
        "response_format": { "type": "json_object" }
    }

    response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload)
    return response.json()
Enter fullscreen mode Exit fullscreen mode

Step 3: Wrapping it in FastAPI โšก

Now, let's expose this as a high-performance API endpoint. We'll use FastAPI for its speed and native support for asynchronous tasks.

from fastapi import FastAPI, UploadFile, File
import shutil

app = FastAPI(title="NutriVision AI")

@app.post("/estimate-calories")
async def estimate_calories(file: UploadFile = File(...)):
    # 1. Save uploaded file
    temp_path = f"temp_{file.filename}"
    with open(temp_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)

    # 2. Perform SAM Segmentation (logic from Step 1)
    # 3. GPT-4o Nutritional Inference (logic from Step 2)

    # Mocking result for brevity
    result = {
        "status": "success",
        "data": {
            "items": [
                {"name": "Grilled Salmon", "weight": "150g", "calories": 310},
                {"name": "Asparagus", "weight": "100g", "calories": 20},
                {"name": "Quinoa", "weight": "120g", "calories": 140}
            ],
            "total_calories": 470
        }
    }

    return result
Enter fullscreen mode Exit fullscreen mode

Advanced Tip: Scaling and Production ๐Ÿ’ก

When building this in a real-world environment, you will encounter challenges like varying lighting and overlapping food items. Using SAM to generate masks helps GPT-4o "focus" on specific regions by passing cropped sub-images of each mask.

Pro-Tip: For advanced techniques on optimizing SAM performance on CPU or implementing low-latency inference pipelines for vision models, I highly recommend checking out the technical guides at WellAlly.tech/blog. They have some incredible resources on multi-agent systems and productionizing LLM vision pipelines.


Conclusion ๐Ÿฅ‘

By combining the spatial precision of the Segment Anything Model with the cognitive power of GPT-4o, we've turned a simple photo into a data-rich nutritional report. This "Multimodal Vision Engineering" approach is far more resilient than traditional classification models because it understands context, scale, and density.

What's next?

  1. Try implementing Depth Estimation (using models like MiDaS) to get actual 3D volume.
  2. Integrate a barcode scanner for packaged foods.
  3. Deploy this using Docker and FastAPI!

Happy coding! If you enjoyed this build, drop a comment below and let me know what multimodal project I should tackle next! ๐Ÿš€๐Ÿ’ป

Top comments (0)