# 檔案路徑: app/routers/ai_router.py from fastapi import APIRouter, File, UploadFile, HTTPException from pydantic import BaseModel from typing import Dict, Any, List, Optional import logging # 導入新的整合服務 from ..services.integrated_food_analysis_service import analyze_food_image_integrated # 設置日誌 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) router = APIRouter( prefix="/ai", tags=["AI"], ) # 新增 Pydantic 模型定義 class IntegratedAnalysisResponse(BaseModel): success: bool analysis_time: float food_analysis: Dict[str, Any] reference_analysis: Dict[str, Any] weight_analysis: Dict[str, Any] nutrition_analysis: Dict[str, Any] summary: Dict[str, Any] architecture: Dict[str, str] class WeightEstimationResponse(BaseModel): food_type: str estimated_weight: float weight_confidence: float weight_error_range: List[float] nutrition: Dict[str, Any] reference_object: Optional[str] = None note: str class FoodAnalysisResponse(BaseModel): food_name: str nutrition_info: Dict[str, Any] @router.post("/analyze-food-image/") async def analyze_food_image_endpoint(file: UploadFile = File(...)): """ 這個端點接收使用者上傳的食物圖片,使用 AI 模型進行辨識, 並返回辨識出的食物名稱。 """ # 檢查上傳的檔案是否為圖片格式 if not file.content_type or not file.content_type.startswith("image/"): raise HTTPException(status_code=400, detail="上傳的檔案不是圖片格式。") try: # 讀取圖片數據 image_bytes = await file.read() # 使用新的整合服務進行分析 result = analyze_food_image_integrated(image_bytes, debug=False) if not result.get("success", False): raise HTTPException(status_code=500, detail=result.get("error_message", "分析失敗")) # 返回簡化的結果 return { "food_name": result["food_analysis"]["food_name"], "nutrition_info": result["nutrition_analysis"]["adjusted_nutrition"] } except Exception as e: logger.error(f"食物分析失敗: {str(e)}") raise HTTPException(status_code=500, detail=f"分析失敗: {str(e)}") @router.post("/analyze-food-image-with-weight/", response_model=WeightEstimationResponse) async def analyze_food_image_with_weight_endpoint(file: UploadFile = File(...)): """ 整合食物辨識、重量估算與營養分析的端點。 使用新的架構:FOOD101 → YOLO(參考物) → SAM+DPT → USDA API """ # 檢查上傳的檔案是否為圖片格式 if not file.content_type or not file.content_type.startswith("image/"): raise HTTPException(status_code=400, detail="上傳的檔案不是圖片格式。") try: # 讀取圖片數據 image_bytes = await file.read() # 使用新的整合服務進行分析 result = analyze_food_image_integrated(image_bytes, debug=False) if not result.get("success", False): raise HTTPException(status_code=500, detail=result.get("error_message", "分析失敗")) # 轉換為舊格式以保持向後兼容 weight_analysis = result["weight_analysis"] nutrition_analysis = result["nutrition_analysis"] return WeightEstimationResponse( food_type=result["food_analysis"]["food_name"], estimated_weight=weight_analysis["estimated_weight"], weight_confidence=weight_analysis["weight_confidence"], weight_error_range=weight_analysis["weight_error_range"], nutrition=nutrition_analysis["adjusted_nutrition"], reference_object=weight_analysis["reference_object"], note=f"使用新架構分析,耗時 {result['analysis_time']} 秒" ) except Exception as e: logger.error(f"重量估算分析失敗: {str(e)}") raise HTTPException(status_code=500, detail=f"分析失敗: {str(e)}") @router.post("/analyze-food-image-integrated/") async def analyze_food_image_integrated_endpoint(file: UploadFile = File(...)): """ 新的整合分析端點,返回完整的分析結果 架構:FOOD101 → YOLO(參考物) → SAM+DPT → USDA API """ # 檢查上傳的檔案是否為圖片格式 if not file.content_type or not file.content_type.startswith("image/"): raise HTTPException(status_code=400, detail="上傳的檔案不是圖片格式。") try: # 讀取圖片數據 image_bytes = await file.read() # 使用新的整合服務進行分析 result = analyze_food_image_integrated(image_bytes, debug=False) return result except Exception as e: logger.error(f"整合分析失敗: {str(e)}") raise HTTPException(status_code=500, detail=f"分析失敗: {str(e)}") @router.get("/health") async def health_check(): """ 健康檢查端點,確認 AI 服務是否正常運作 """ return { "status": "healthy", "services": { "food_classification": "available (FOOD101)", "reference_detection": "available (YOLO)", "weight_estimation": "available (SAM+DPT)", "nutrition_api": "available (USDA)", "integrated_analysis": "available" }, "architecture": { "layer_1": "FOOD101 (食物識別)", "layer_2": "YOLO (參考物偵測)", "layer_3": "SAM+DPT (重量計算)", "layer_4": "USDA API (營養查詢)", "layer_5": "重量調整 (營養計算)" } }