从像素到卡路里:使用 GPT-4o 构建多模态餐食分析引擎
Source: Dev.to
Source: …
🍝 从像素到热量 – 多模态 AI 与自动热量追踪
我们都有过这样的经历:盯着一盘美味的意面,想弄清它是 400 卡路里 还是偷偷的 800。手动记录是健康习惯的终极杀手。要是你的手机能 看见 食材并瞬间估算营养成分,那该多好?
在本教程中,我们将深入探讨 多模态 AI 与 自动热量追踪。我们将使用 GPT‑4o API 构建一个基于视觉的营养引擎,利用其高级推理能力解决计算机视觉中的经典“体积估算”问题。通过将视觉‑语言模型与结构化数据解析相结合,一张简单的照片即可转化为详细的营养分析。
注意: 若想了解面向生产环境的 AI 模式和高级计算机视觉架构,请查看 WellAlly Tech Blog 上的深度文章——它们为这里使用的结构化输出逻辑提供了灵感。
📊 高层流程
graph TD
A[User Uploads Photo] --> B[OpenCV: Resize & Encode]
B --> C[GPT‑4o Multimodal Vision]
C --> D{Structured Output}
D --> E[Pydantic Validation]
E --> F[Streamlit Dashboard]
F --> G[Nutritional Insights & Charts]
🛠️ 你需要准备的东西
- GPT‑4o API Key – 用于视觉和推理的重活。
- Streamlit – 用于快速前端展示。
- Pydantic – 确保我们的 LLM 返回有效的 JSON。
- OpenCV – 用于快速图像缩放(降低 token 消耗)。
使用 LLM 时最大的挑战是 幻觉 与格式不一致。我们将借助 Pydantic 明确定义引擎应返回的结构:对盘中每一样食物的结构化拆解。
📐 使用 Pydantic 定义结构化输出
from pydantic import BaseModel, Field
from typing import List
class FoodItem(BaseModel):
name: str = Field(description="Name of the food item")
estimated_weight_g: float = Field(description="Estimated weight in grams")
calories: int = Field(description="Calories for this portion")
protein_g: float = Field(description="Protein content in grams")
carbs_g: float = Field(description="Carbohydrate content in grams")
fats_g: float = Field(description="Fat content in grams")
class MealAnalysis(BaseModel):
total_calories: int
items: List[FoodItem]
health_score: int = Field(description="A score from 1‑10 based on nutritional balance")
advice: str = Field(description="Short dietary advice based on the meal")
📸 图像预处理
import base64
import cv2
import openai
def process_image(image_path: str) -> str:
"""
Resize the image to 800 × 800 px and return a base64‑encoded JPEG.
Args:
image_path: Path to the input image file.
Returns:
Base64‑encoded string of the JPEG image.
"""
# Load the image from disk
img = cv2.imread(image_path)
# Resize for cheaper token usage
img = cv2.resize(img, (800, 800))
# Encode as JPEG
_, buffer = cv2.imencode(".jpg", img)
# Convert the binary buffer to a base64 string
return base64.b64encode(buffer).decode("utf-8")
🤖 使用结构化解析调用 GPT‑4o
def analyze_meal(base64_image: str) -> MealAnalysis:
client = openai.OpenAI()
response = client.beta.chat.completions.parse(
model="gpt-4o-2024-08-06",
messages=[
{
"role": "system",
"content": (
"You are an expert nutritionist. Analyze the meal in the image. "
"Estimate portion sizes and calculate nutritional values."
),
},
{
"role": "user",
"content": [
{
"type": "text",
"text": "Identify all food items and provide a nutritional breakdown.",
},
{
"type": "image_url",
import streamlit as st
st.set_page_config(page_title="AI Nutritionist", page_icon="🥑")
st.title("🥑 From Pixels to Calories")
st.write("Upload a photo of your meal and let GPT‑4o do the math!")
uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])
if uploaded_file:
st.image(uploaded_file, caption="Your delicious meal.", use_column_width=True)
with st.spinner("Analyzing nutrients... 🧬"):
# Save temporary file for OpenCV processing
temp_path = "temp_img.jpg"
with open(temp_path, "wb") as f:
f.write(uploaded_file.getbuffer())
encoded_img = process_image(temp_path)
analysis = analyze_meal(encoded_img)
# ----- Display Results -----
st.header(f"Total Calories: {analysis.total_calories} kcal")
col1, col2 = st.columns(2)
with col1:
st.metric("Health Score", f"{analysis.health_score}/10")
with col2:
st.write(f"**Pro Tip:** {analysis.advice}")
st.table([item.dict() for item in analysis.items])
📱 构建一个简易 Streamlit 界面
🚀 超越原型的扩展
虽然这在个人使用时效果很好,但要打造生产级的基于视觉的营养引擎仍需额外考虑:
- 参考对象 – 在画面中加入硬币、手或其他已知尺寸的物体,以获得更好的尺度估计。
- 微调 – 为特定菜系或饮食限制训练自定义视觉适配器。
- 提示链 – 在计算卡路里之前先验证识别出的食材,以减少幻觉。
欲了解更深入的实现模式、部署指南以及低延迟 AI 技巧,请访问 WellAlly Tech Blog 上的技术资源。
底线: 我们已将混乱的像素阵列转化为结构化且有意义的营养报告。通过结合 GPT‑4o 的多模态能力和 Pydantic 的模式约束,我们省去了数月的传统计算机视觉训练,能够在几秒钟内获得可靠的卡路里估算。
祝编码愉快,尽情享用(精准追踪的)餐点!
医疗的未来是多模态的!
你正在使用视觉 API 构建项目吗?在下方留言或分享你的成果吧!
祝编码愉快!