diff --git a/README.md b/README.md index 5a459cf..0c00a49 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ - 脂肪 - 膳食纤维 - 使用先进的视觉模型,识别准确 +- **份量调整功能**: + - AI 提供每 100g 的标准营养数据 + - AI 估算图片中食物的大致份量 + - 用户可手动调整实际重量 + - 营养数据按份量比例自动计算 +- 支持一键保存到健康档案 ### 3. 健康数据管理 - **饮食记录**:记录每日摄入的食物和营养数据 @@ -89,7 +95,10 @@ coze dev - 点击导航栏的 "图片识别" - 上传食物图片 - 点击 "开始分析" -- 查看详细的营养成分信息 +- AI 会返回每 100g 的标准营养数据 +- 调整实际份量(克),营养数据会自动按比例计算 +- AI 会提供图片中食物的大致份量估算作为参考 +- 可将分析结果保存到健康档案 #### 3. 健康档案 - 点击导航栏的 "健康档案" @@ -136,10 +145,17 @@ AI 对话接口,支持流式输出 "carbs": 20, "fat": 5, "fiber": 2, + "standardWeight": 100, + "weightHint": "图片中约200g", "description": "食物描述" } ``` +**说明:** +- `standardWeight`: 标准份量,固定为 100g +- `weightHint`: AI 估算的图片中食物大致份量 +- 其他营养数据均为每 100g 的标准值 + ## 数据存储 健康数据使用浏览器的 `localStorage` 进行本地存储: @@ -151,7 +167,9 @@ AI 对话接口,支持流式输出 1. **图片上传限制**:最大 5MB 2. **支持格式**:JPG、PNG、WebP 3. **图片质量**:建议使用清晰、光线充足的图片 -4. **数据安全**:所有数据存储在本地浏览器中,不会上传到服务器 +4. **份量估算**:AI 仅供参考,建议结合实际情况调整份量 +5. **营养数据**:基于标准营养数据库,实际含量可能因食材和烹饪方式不同 +6. **数据安全**:所有数据存储在本地浏览器中,不会上传到服务器 ## 开发 diff --git a/src/app/api/analyze-food/route.ts b/src/app/api/analyze-food/route.ts index 0a35a76..528c278 100644 --- a/src/app/api/analyze-food/route.ts +++ b/src/app/api/analyze-food/route.ts @@ -14,6 +14,8 @@ interface NutritionInfo { fat: number; fiber: number; description: string; + standardWeight: number; // 标准份量(单位:g) + weightHint: string; // 份量提示 } export async function POST(request: NextRequest) { @@ -31,17 +33,20 @@ export async function POST(request: NextRequest) { const systemPrompt = `你是一位专业的营养师。请分析图片中的食物,提供准确的营养信息。 请以 JSON 格式返回结果,包含以下字段: - foodName: 食物名称(中文) -- calories: 卡路里(数值,单位 kcal) -- protein: 蛋白质(数值,单位 g) -- carbs: 碳水化合物(数值,单位 g) -- fat: 脂肪(数值,单位 g) -- fiber: 膳食纤维(数值,单位 g) +- calories: 卡路里(数值,单位 kcal),基于标准份量(100g) +- protein: 蛋白质(数值,单位 g),基于标准份量(100g) +- carbs: 碳水化合物(数值,单位 g),基于标准份量(100g) +- fat: 脂肪(数值,单位 g),基于标准份量(100g) +- fiber: 膳食纤维(数值,单位 g),基于标准份量(100g) +- standardWeight: 标准份量(数值,固定为 100) +- weightHint: 份量提示(简短说明,告诉用户图片中的大致份量,例如"图片中约200g") - description: 食物描述(简短说明,50字以内) 注意: 1. 只返回 JSON 格式,不要包含其他文字 -2. 如果图片中有多种食物,估算总和 -3. 营养数值基于标准份量估算`; +2. 所有营养数值都基于100g标准份量计算 +3. 在 weightHint 中给出图片中食物的大致重量估算 +4. 如果图片中有多种食物,给出主要食物的营养数据并在描述中说明`; const messages = [ { @@ -104,6 +109,8 @@ export async function POST(request: NextRequest) { carbs: 0, fat: 0, fiber: 0, + standardWeight: 100, + weightHint: '无法估算份量', description: '无法识别图片中的食物,请重新上传更清晰的图片', } as NutritionInfo, { status: 200 } diff --git a/src/app/recognition/page.tsx b/src/app/recognition/page.tsx index 7e94cb1..e3baacb 100644 --- a/src/app/recognition/page.tsx +++ b/src/app/recognition/page.tsx @@ -1,8 +1,9 @@ 'use client'; -import { useState, useRef } from 'react'; -import { Camera, Upload, X, ArrowLeft, Loader2 } from 'lucide-react'; +import { useState, useRef, useMemo } from 'react'; +import { Camera, Upload, X, ArrowLeft, Loader2, Info } from 'lucide-react'; import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; import Link from 'next/link'; interface NutritionInfo { @@ -13,6 +14,8 @@ interface NutritionInfo { fat: number; fiber: number; description: string; + standardWeight: number; + weightHint: string; } export default function RecognitionPage() { @@ -20,8 +23,26 @@ export default function RecognitionPage() { const [isAnalyzing, setIsAnalyzing] = useState(false); const [nutritionInfo, setNutritionInfo] = useState(null); const [error, setError] = useState(null); + const [userWeight, setUserWeight] = useState('100'); const fileInputRef = useRef(null); + // 计算实际份量的营养数据 + const calculatedNutrition = useMemo(() => { + if (!nutritionInfo || !userWeight) return null; + + const weight = parseFloat(userWeight) || 0; + const ratio = weight / nutritionInfo.standardWeight; + + return { + weight, + calories: Math.round(nutritionInfo.calories * ratio), + protein: Math.round(nutritionInfo.protein * ratio * 10) / 10, + carbs: Math.round(nutritionInfo.carbs * ratio * 10) / 10, + fat: Math.round(nutritionInfo.fat * ratio * 10) / 10, + fiber: Math.round(nutritionInfo.fiber * ratio * 10) / 10, + }; + }, [nutritionInfo, userWeight]); + const handleFileSelect = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { @@ -35,6 +56,7 @@ export default function RecognitionPage() { setSelectedImage(e.target?.result as string); setNutritionInfo(null); setError(null); + setUserWeight('100'); }; reader.readAsDataURL(file); } @@ -63,6 +85,15 @@ export default function RecognitionPage() { const data = await response.json(); setNutritionInfo(data); + + // 如果有份量提示,尝试从提示中提取数字 + if (data.weightHint) { + const weightMatch = data.weightHint.match(/(\d+(?:\.\d+)?)\s*(?:g|克)/); + if (weightMatch && weightMatch[1]) { + const estimatedWeight = Math.round(parseFloat(weightMatch[1])); + setUserWeight(estimatedWeight.toString()); + } + } } catch (error) { console.error('Analysis error:', error); setError(error instanceof Error ? error.message : '分析失败,请稍后再试'); @@ -75,6 +106,7 @@ export default function RecognitionPage() { setSelectedImage(null); setNutritionInfo(null); setError(null); + setUserWeight('100'); if (fileInputRef.current) { fileInputRef.current.value = ''; } @@ -194,7 +226,8 @@ export default function RecognitionPage() {
  • • 确保图片清晰,光线充足
  • • 尽量拍摄食物的正面或侧面
  • • 避免背景过于复杂
  • -
  • • 支持单一食物或多食物组合
  • +
  • • 分析后可调整实际份量
  • +
  • • 营养数据会按份量比例自动计算
  • @@ -227,7 +260,7 @@ export default function RecognitionPage() { )} - {nutritionInfo && ( + {nutritionInfo && calculatedNutrition && (
    {/* 食物名称 */}
    @@ -239,59 +272,111 @@ export default function RecognitionPage() {

    + {/* 份量输入 */} +
    + +
    + setUserWeight(e.target.value)} + min="1" + step="1" + className="flex-1" + /> +
    + 每100g: + + {nutritionInfo.calories} kcal + +
    +
    + {nutritionInfo.weightHint && ( +
    + + AI 估算: {nutritionInfo.weightHint} +
    + )} +
    + {/* 卡路里 */}
    - - 卡路里 - - - {nutritionInfo.calories}{' '} - kcal - +
    + + 卡路里 + +
    + {calculatedNutrition.weight}g 份量 +
    +
    +
    +
    + {calculatedNutrition.calories}{' '} + kcal +
    +
    {/* 营养成分 */} -
    -
    -
    - 蛋白质 +
    +

    + 营养成分 +

    +
    +
    +
    + 蛋白质 +
    +
    + {calculatedNutrition.protein}g +
    -
    - {nutritionInfo.protein}g +
    +
    + 碳水化合物 +
    +
    + {calculatedNutrition.carbs}g +
    -
    -
    -
    - 碳水化合物 +
    +
    + 脂肪 +
    +
    + {calculatedNutrition.fat}g +
    -
    - {nutritionInfo.carbs}g -
    -
    -
    -
    - 脂肪 -
    -
    - {nutritionInfo.fat}g -
    -
    -
    -
    - 膳食纤维 -
    -
    - {nutritionInfo.fiber}g +
    +
    + 膳食纤维 +
    +
    + {calculatedNutrition.fiber}g +
    {/* 保存按钮 */} - +
    + + +
    )}