Spaces:
Running
Running
# 檔案路徑: 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] | |
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)}") | |
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)}") | |
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)}") | |
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": "重量調整 (營養計算)" | |
} | |
} |