Spaces:
Running
Running
Commit
·
cb8b92c
1
Parent(s):
b938416
deploy: 推送到 Hugging Face Spaces
Browse files- food_analyzer.py +12 -29
- services/meal_service.py +23 -25
food_analyzer.py
CHANGED
@@ -3,10 +3,10 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
3 |
from PIL import Image
|
4 |
import io
|
5 |
import base64
|
6 |
-
from transformers import pipeline
|
7 |
import requests
|
8 |
import json
|
9 |
-
from typing import Dict, Any
|
10 |
from pydantic import BaseModel
|
11 |
import uvicorn
|
12 |
|
@@ -245,33 +245,26 @@ async def analyze_food(file: UploadFile = File(...)):
|
|
245 |
# 檢查模型是否載入成功
|
246 |
if not food_classifier:
|
247 |
raise HTTPException(status_code=500, detail="AI模型尚未載入,請稍後再試")
|
248 |
-
|
249 |
# 檢查文件類型
|
250 |
-
if not file.content_type.startswith("image/"):
|
251 |
raise HTTPException(status_code=400, detail="請上傳圖片文件")
|
252 |
-
|
253 |
# 讀取圖片
|
254 |
image_data = await file.read()
|
255 |
image = Image.open(io.BytesIO(image_data))
|
256 |
-
|
257 |
# 確保圖片是RGB格式
|
258 |
if image.mode != "RGB":
|
259 |
image = image.convert("RGB")
|
260 |
-
|
261 |
# 使用AI模型進行食物辨識
|
262 |
results = food_classifier(image)
|
263 |
-
|
264 |
-
|
265 |
top_result = results[0]
|
266 |
-
food_name = top_result
|
267 |
-
confidence = top_result
|
268 |
-
|
269 |
# 獲取營養資訊
|
270 |
nutrition_info = get_nutrition_info(food_name)
|
271 |
-
|
272 |
# 生成AI建議
|
273 |
ai_suggestions = generate_ai_suggestions(food_name, nutrition_info)
|
274 |
-
|
275 |
return FoodAnalysisResponse(
|
276 |
success=True,
|
277 |
food_name=food_name,
|
@@ -280,7 +273,6 @@ async def analyze_food(file: UploadFile = File(...)):
|
|
280 |
ai_suggestions=ai_suggestions,
|
281 |
message="食物分析完成"
|
282 |
)
|
283 |
-
|
284 |
except Exception as e:
|
285 |
raise HTTPException(status_code=500, detail=f"分析失敗: {str(e)}")
|
286 |
|
@@ -291,38 +283,30 @@ async def analyze_food_base64(image_data: dict):
|
|
291 |
# 檢查模型是否載入成功
|
292 |
if not food_classifier:
|
293 |
raise HTTPException(status_code=500, detail="AI模型尚未載入,請稍後再試")
|
294 |
-
|
295 |
# 解碼base64圖片
|
296 |
base64_string = image_data.get("image", "")
|
297 |
if not base64_string:
|
298 |
raise HTTPException(status_code=400, detail="缺少圖片資料")
|
299 |
-
|
300 |
# 移除base64前綴(如果有的話)
|
301 |
if "," in base64_string:
|
302 |
-
base64_string = base64_string.split(",")[1]
|
303 |
-
|
304 |
# 解碼圖片
|
305 |
image_bytes = base64.b64decode(base64_string)
|
306 |
image = Image.open(io.BytesIO(image_bytes))
|
307 |
-
|
308 |
# 確保圖片是RGB格式
|
309 |
if image.mode != "RGB":
|
310 |
image = image.convert("RGB")
|
311 |
-
|
312 |
# 使用AI模型進行食物辨識
|
313 |
results = food_classifier(image)
|
314 |
-
|
315 |
-
|
316 |
top_result = results[0]
|
317 |
-
food_name = top_result
|
318 |
-
confidence = top_result
|
319 |
-
|
320 |
# 獲取營養資訊
|
321 |
nutrition_info = get_nutrition_info(food_name)
|
322 |
-
|
323 |
# 生成AI建議
|
324 |
ai_suggestions = generate_ai_suggestions(food_name, nutrition_info)
|
325 |
-
|
326 |
return FoodAnalysisResponse(
|
327 |
success=True,
|
328 |
food_name=food_name,
|
@@ -331,7 +315,6 @@ async def analyze_food_base64(image_data: dict):
|
|
331 |
ai_suggestions=ai_suggestions,
|
332 |
message="食物分析完成"
|
333 |
)
|
334 |
-
|
335 |
except Exception as e:
|
336 |
raise HTTPException(status_code=500, detail=f"分析失敗: {str(e)}")
|
337 |
|
|
|
3 |
from PIL import Image
|
4 |
import io
|
5 |
import base64
|
6 |
+
from transformers.pipelines import pipeline # 修正匯入
|
7 |
import requests
|
8 |
import json
|
9 |
+
from typing import Dict, Any, List, Optional
|
10 |
from pydantic import BaseModel
|
11 |
import uvicorn
|
12 |
|
|
|
245 |
# 檢查模型是否載入成功
|
246 |
if not food_classifier:
|
247 |
raise HTTPException(status_code=500, detail="AI模型尚未載入,請稍後再試")
|
|
|
248 |
# 檢查文件類型
|
249 |
+
if not file.content_type or not file.content_type.startswith("image/"):
|
250 |
raise HTTPException(status_code=400, detail="請上傳圖片文件")
|
|
|
251 |
# 讀取圖片
|
252 |
image_data = await file.read()
|
253 |
image = Image.open(io.BytesIO(image_data))
|
|
|
254 |
# 確保圖片是RGB格式
|
255 |
if image.mode != "RGB":
|
256 |
image = image.convert("RGB")
|
|
|
257 |
# 使用AI模型進行食物辨識
|
258 |
results = food_classifier(image)
|
259 |
+
if not isinstance(results, list) or not results:
|
260 |
+
raise HTTPException(status_code=500, detail="AI模型辨識失敗")
|
261 |
top_result = results[0]
|
262 |
+
food_name = str(top_result.get("label", "Unknown"))
|
263 |
+
confidence = float(top_result.get("score", 0.0))
|
|
|
264 |
# 獲取營養資訊
|
265 |
nutrition_info = get_nutrition_info(food_name)
|
|
|
266 |
# 生成AI建議
|
267 |
ai_suggestions = generate_ai_suggestions(food_name, nutrition_info)
|
|
|
268 |
return FoodAnalysisResponse(
|
269 |
success=True,
|
270 |
food_name=food_name,
|
|
|
273 |
ai_suggestions=ai_suggestions,
|
274 |
message="食物分析完成"
|
275 |
)
|
|
|
276 |
except Exception as e:
|
277 |
raise HTTPException(status_code=500, detail=f"分析失敗: {str(e)}")
|
278 |
|
|
|
283 |
# 檢查模型是否載入成功
|
284 |
if not food_classifier:
|
285 |
raise HTTPException(status_code=500, detail="AI模型尚未載入,請稍後再試")
|
|
|
286 |
# 解碼base64圖片
|
287 |
base64_string = image_data.get("image", "")
|
288 |
if not base64_string:
|
289 |
raise HTTPException(status_code=400, detail="缺少圖片資料")
|
|
|
290 |
# 移除base64前綴(如果有的話)
|
291 |
if "," in base64_string:
|
292 |
+
base64_string = base64_string.split(",", 1)[1]
|
|
|
293 |
# 解碼圖片
|
294 |
image_bytes = base64.b64decode(base64_string)
|
295 |
image = Image.open(io.BytesIO(image_bytes))
|
|
|
296 |
# 確保圖片是RGB格式
|
297 |
if image.mode != "RGB":
|
298 |
image = image.convert("RGB")
|
|
|
299 |
# 使用AI模型進行食物辨識
|
300 |
results = food_classifier(image)
|
301 |
+
if not isinstance(results, list) or not results:
|
302 |
+
raise HTTPException(status_code=500, detail="AI模型辨識失敗")
|
303 |
top_result = results[0]
|
304 |
+
food_name = str(top_result.get("label", "Unknown"))
|
305 |
+
confidence = float(top_result.get("score", 0.0))
|
|
|
306 |
# 獲取營養資訊
|
307 |
nutrition_info = get_nutrition_info(food_name)
|
|
|
308 |
# 生成AI建議
|
309 |
ai_suggestions = generate_ai_suggestions(food_name, nutrition_info)
|
|
|
310 |
return FoodAnalysisResponse(
|
311 |
success=True,
|
312 |
food_name=food_name,
|
|
|
315 |
ai_suggestions=ai_suggestions,
|
316 |
message="食物分析完成"
|
317 |
)
|
|
|
318 |
except Exception as e:
|
319 |
raise HTTPException(status_code=500, detail=f"分析失敗: {str(e)}")
|
320 |
|
services/meal_service.py
CHANGED
@@ -22,17 +22,16 @@ class MealService:
|
|
22 |
food_name=food_name,
|
23 |
meal_type=meal_type,
|
24 |
portion_size=portion_size,
|
25 |
-
calories=nutrition.get('calories', 0),
|
26 |
-
protein=nutrition.get('protein', 0),
|
27 |
-
carbs=nutrition.get('carbs', 0),
|
28 |
-
fat=nutrition.get('fat', 0),
|
29 |
-
fiber=nutrition.get('fiber', 0),
|
30 |
meal_date=meal_date,
|
31 |
image_url=image_url,
|
32 |
ai_analysis=ai_analysis,
|
33 |
created_at=datetime.utcnow()
|
34 |
)
|
35 |
-
|
36 |
self.db.add(meal_log)
|
37 |
self.db.commit()
|
38 |
self.db.refresh(meal_log)
|
@@ -46,14 +45,12 @@ class MealService:
|
|
46 |
) -> List[MealLog]:
|
47 |
"""獲取用餐記錄"""
|
48 |
query = self.db.query(MealLog)
|
49 |
-
|
50 |
if start_date:
|
51 |
query = query.filter(MealLog.meal_date >= start_date)
|
52 |
if end_date:
|
53 |
query = query.filter(MealLog.meal_date <= end_date)
|
54 |
if meal_type:
|
55 |
query = query.filter(MealLog.meal_type == meal_type)
|
56 |
-
|
57 |
return query.order_by(MealLog.meal_date.desc()).all()
|
58 |
|
59 |
def get_nutrition_summary(
|
@@ -63,27 +60,28 @@ class MealService:
|
|
63 |
) -> Dict[str, float]:
|
64 |
"""獲取指定時間範圍內的營養攝入總結"""
|
65 |
meals = self.get_meal_logs(start_date, end_date)
|
66 |
-
|
67 |
-
|
68 |
-
'
|
69 |
-
'
|
70 |
-
'
|
71 |
-
'
|
72 |
-
'total_fiber': 0
|
73 |
}
|
74 |
-
|
75 |
for meal in meals:
|
76 |
-
|
|
|
|
|
|
|
|
|
|
|
77 |
multiplier = {
|
78 |
'small': 0.7,
|
79 |
'medium': 1.0,
|
80 |
'large': 1.3
|
81 |
-
}.get(
|
82 |
-
|
83 |
-
summary['
|
84 |
-
summary['
|
85 |
-
summary['
|
86 |
-
summary['
|
87 |
-
summary['total_fiber'] += meal.fiber * multiplier
|
88 |
-
|
89 |
return summary
|
|
|
22 |
food_name=food_name,
|
23 |
meal_type=meal_type,
|
24 |
portion_size=portion_size,
|
25 |
+
calories=float(nutrition.get('calories', 0)),
|
26 |
+
protein=float(nutrition.get('protein', 0)),
|
27 |
+
carbs=float(nutrition.get('carbs', 0)),
|
28 |
+
fat=float(nutrition.get('fat', 0)),
|
29 |
+
fiber=float(nutrition.get('fiber', 0)),
|
30 |
meal_date=meal_date,
|
31 |
image_url=image_url,
|
32 |
ai_analysis=ai_analysis,
|
33 |
created_at=datetime.utcnow()
|
34 |
)
|
|
|
35 |
self.db.add(meal_log)
|
36 |
self.db.commit()
|
37 |
self.db.refresh(meal_log)
|
|
|
45 |
) -> List[MealLog]:
|
46 |
"""獲取用餐記錄"""
|
47 |
query = self.db.query(MealLog)
|
|
|
48 |
if start_date:
|
49 |
query = query.filter(MealLog.meal_date >= start_date)
|
50 |
if end_date:
|
51 |
query = query.filter(MealLog.meal_date <= end_date)
|
52 |
if meal_type:
|
53 |
query = query.filter(MealLog.meal_type == meal_type)
|
|
|
54 |
return query.order_by(MealLog.meal_date.desc()).all()
|
55 |
|
56 |
def get_nutrition_summary(
|
|
|
60 |
) -> Dict[str, float]:
|
61 |
"""獲取指定時間範圍內的營養攝入總結"""
|
62 |
meals = self.get_meal_logs(start_date, end_date)
|
63 |
+
summary: Dict[str, float] = {
|
64 |
+
'total_calories': 0.0,
|
65 |
+
'total_protein': 0.0,
|
66 |
+
'total_carbs': 0.0,
|
67 |
+
'total_fat': 0.0,
|
68 |
+
'total_fiber': 0.0
|
|
|
69 |
}
|
|
|
70 |
for meal in meals:
|
71 |
+
portion_size = getattr(meal, 'portion_size', 'medium')
|
72 |
+
calories = getattr(meal, 'calories', 0.0)
|
73 |
+
protein = getattr(meal, 'protein', 0.0)
|
74 |
+
carbs = getattr(meal, 'carbs', 0.0)
|
75 |
+
fat = getattr(meal, 'fat', 0.0)
|
76 |
+
fiber = getattr(meal, 'fiber', 0.0)
|
77 |
multiplier = {
|
78 |
'small': 0.7,
|
79 |
'medium': 1.0,
|
80 |
'large': 1.3
|
81 |
+
}.get(portion_size, 1.0)
|
82 |
+
summary['total_calories'] += float(calories) * multiplier
|
83 |
+
summary['total_protein'] += float(protein) * multiplier
|
84 |
+
summary['total_carbs'] += float(carbs) * multiplier
|
85 |
+
summary['total_fat'] += float(fat) * multiplier
|
86 |
+
summary['total_fiber'] += float(fiber) * multiplier
|
|
|
|
|
87 |
return summary
|