Spaces:
Running
Running
har1zarD
commited on
Commit
·
a986fa9
1
Parent(s):
2936e62
Update app.py with latest changes
Browse files
app.py
CHANGED
|
@@ -16,12 +16,17 @@ from PIL import Image
|
|
| 16 |
import torch
|
| 17 |
import torch.nn.functional as F
|
| 18 |
from transformers import CLIPProcessor, CLIPModel
|
|
|
|
| 19 |
|
| 20 |
# --- Configuration ---
|
| 21 |
# LITE varijanta: CLIP zero-shot klasifikacija nad Food-101 labelama (CPU-friendly)
|
| 22 |
# Zadano koristi ViT-L/14 model; može se promijeniti preko env varijable MODEL_NAME
|
| 23 |
MODEL_NAME = os.environ.get("MODEL_NAME", "openai/clip-vit-large-patch14")
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
# --- Helper Functions ---
|
| 26 |
def select_device() -> str:
|
| 27 |
"""Odabire najbolji dostupni uređaj: CUDA > MPS (Apple) > CPU."""
|
|
@@ -103,6 +108,26 @@ def load_model():
|
|
| 103 |
print(f"❌ Greška pri učitavanju CLIP modela: {e}")
|
| 104 |
raise
|
| 105 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
def is_image_file(file: UploadFile):
|
| 107 |
"""Provjerava da li je fajl podržani format slike."""
|
| 108 |
return file.content_type in ["image/jpeg", "image/png", "image/jpg", "image/webp"]
|
|
@@ -342,6 +367,27 @@ def classify_image_with_clip(image: Image.Image, processor: CLIPProcessor, model
|
|
| 342 |
"top5": list(zip(top_labels, top_probs))
|
| 343 |
}
|
| 344 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 345 |
def extract_clip_food_info(classification: Dict[str, Any]) -> Dict[str, Any]:
|
| 346 |
"""Formatira rezultat CLIP klasifikacije u zajedničku strukturu."""
|
| 347 |
primary = classification["primary_label"]
|
|
@@ -363,16 +409,17 @@ def extract_clip_food_info(classification: Dict[str, Any]) -> Dict[str, Any]:
|
|
| 363 |
}
|
| 364 |
|
| 365 |
# --- Učitaj Model pri Pokretanju Aplikacije ---
|
| 366 |
-
print("🚀 Pokrećem LITE Food Scanner API (CLIP)...")
|
| 367 |
processor, model, device, dtype = load_model()
|
| 368 |
CURRENT_DTYPE = dtype
|
| 369 |
TEXT_LABELS = get_food101_labels()
|
| 370 |
TEXT_FEATURES = build_text_cache(TEXT_LABELS, processor, model, device, dtype)
|
| 371 |
warmup_model(processor, model, device, dtype)
|
|
|
|
| 372 |
|
| 373 |
# --- FastAPI Aplikacija ---
|
| 374 |
app = FastAPI(
|
| 375 |
-
title="🍎 LITE Food Scanner API - Nutrition Edition (CLIP)",
|
| 376 |
description="""
|
| 377 |
**🏆 Lako i brzo prepoznavanje hrane + Nutrition Lookup (CPU-friendly)**
|
| 378 |
|
|
@@ -412,7 +459,7 @@ app = FastAPI(
|
|
| 412 |
- 🤖 Inteligentna procjena za nepoznatu hranu
|
| 413 |
- ✅ Production-ready i stabilan
|
| 414 |
""",
|
| 415 |
-
version="9.
|
| 416 |
)
|
| 417 |
|
| 418 |
# Dodaj CORS middleware za web aplikacije
|
|
@@ -426,7 +473,7 @@ app.add_middleware(
|
|
| 426 |
|
| 427 |
@app.post("/analyze",
|
| 428 |
summary="Analiziraj Food Sliku",
|
| 429 |
-
description="Upload-uj sliku da dobiješ food label + nutritivne podatke (CLIP LITE)",
|
| 430 |
response_description="Rezultati food recognition i nutritivnih podataka"
|
| 431 |
)
|
| 432 |
async def analyze(file: UploadFile = File(...)):
|
|
@@ -464,10 +511,26 @@ async def analyze(file: UploadFile = File(...)):
|
|
| 464 |
raise HTTPException(status_code=500, detail=f"Greška pri čitanju slike: {e}")
|
| 465 |
|
| 466 |
try:
|
| 467 |
-
#
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 471 |
except Exception as e:
|
| 472 |
print(f"Greška tokom analize: {e}")
|
| 473 |
raise HTTPException(status_code=500, detail=f"Greška tokom analize: {e}")
|
|
@@ -515,11 +578,15 @@ async def analyze(file: UploadFile = File(...)):
|
|
| 515 |
},
|
| 516 |
|
| 517 |
"model_info": {
|
| 518 |
-
"vision_model": MODEL_NAME,
|
| 519 |
"nutrition_source": nutrition_data["source"],
|
| 520 |
-
"type":
|
|
|
|
|
|
|
|
|
|
|
|
|
| 521 |
"capabilities": [
|
| 522 |
-
"Food Recognition (Food-101)",
|
| 523 |
"Nutrition Data Lookup"
|
| 524 |
]
|
| 525 |
}
|
|
@@ -591,20 +658,20 @@ async def search_nutrition(food_name: str):
|
|
| 591 |
def root():
|
| 592 |
"""Root endpoint sa API informacijama."""
|
| 593 |
return {
|
| 594 |
-
"message": "🍎 LITE Food Scanner API v9.
|
| 595 |
"status": "🟢 Online",
|
| 596 |
"tagline": "🏆 Najbolji Self-Hosted Food Recognition + Nutrition API",
|
| 597 |
"model": {
|
| 598 |
-
"vision_model": MODEL_NAME,
|
| 599 |
"nutrition_source": "Open Food Facts + AI Estimation",
|
| 600 |
-
"type": "CLIP Zero-shot Classifier + Nutrition Database",
|
| 601 |
-
"provider": "OpenAI CLIP + Open Food Facts",
|
| 602 |
-
"generation": "CLIP (ViT-L/14)",
|
| 603 |
"device": device.upper(),
|
| 604 |
"rank": "🥇 LITE rješenje za Food Recognition"
|
| 605 |
},
|
| 606 |
"capabilities": {
|
| 607 |
-
"food_recognition": "✅ Food-101 Zero-shot (CLIP)",
|
| 608 |
"nutrition_data": "✅ Realne Nutritivne Vrijednosti",
|
| 609 |
"nutrition_lookup": "✅ Manual Search po Imenu",
|
| 610 |
"ingredient_analysis": "❌ (LITE)",
|
|
@@ -643,7 +710,7 @@ def root():
|
|
| 643 |
)
|
| 644 |
def health_check():
|
| 645 |
"""Health check endpoint za monitoring i load balancere."""
|
| 646 |
-
model_status = model is not None and processor is not None
|
| 647 |
|
| 648 |
# Test nutrition API
|
| 649 |
nutrition_api_status = "unknown"
|
|
@@ -656,12 +723,12 @@ def health_check():
|
|
| 656 |
return {
|
| 657 |
"status": "healthy" if model_status else "unhealthy",
|
| 658 |
"model_loaded": model_status,
|
| 659 |
-
"vision_model": MODEL_NAME,
|
| 660 |
"nutrition_api": nutrition_api_status,
|
| 661 |
-
"model_type": "CLIP Zero-shot Classifier + Nutrition Database",
|
| 662 |
"device": device,
|
| 663 |
"device_available": torch.cuda.is_available() if device == "cuda" else True,
|
| 664 |
-
"version": "9.
|
| 665 |
"timestamp": "2025-10-08",
|
| 666 |
"ranking": "🥇 LITE Food Recognition + Nutrition Rješenje"
|
| 667 |
}
|
|
@@ -675,7 +742,7 @@ def get_capabilities():
|
|
| 675 |
return {
|
| 676 |
"vision_model": MODEL_NAME,
|
| 677 |
"nutrition_source": "Open Food Facts",
|
| 678 |
-
|
| 679 |
"release": "2024 (Stable)",
|
| 680 |
"vision_tasks": {
|
| 681 |
"food_recognition": {
|
|
@@ -725,8 +792,8 @@ def get_capabilities():
|
|
| 725 |
"🔬 700,000+ proizvoda u bazi"
|
| 726 |
],
|
| 727 |
"technical_specs": {
|
| 728 |
-
"parameters": "~427M",
|
| 729 |
-
"architecture": "CLIP (ViT-L/14)",
|
| 730 |
"training_data": "WIT + zero-shot na Food-101",
|
| 731 |
"supported_formats": ["JPEG", "PNG", "WebP"],
|
| 732 |
"max_resolution": "Podrška za visoke rezolucije",
|
|
@@ -741,12 +808,12 @@ def get_capabilities():
|
|
| 741 |
# --- Pokreni API ---
|
| 742 |
if __name__ == "__main__":
|
| 743 |
print("=" * 80)
|
| 744 |
-
print("🍎 LITE FOOD SCANNER API v9.
|
| 745 |
print("=" * 80)
|
| 746 |
-
print(f"🤖 Vision Model: {MODEL_NAME}")
|
| 747 |
print(f"📊 Nutrition Source: Open Food Facts + AI Estimation")
|
| 748 |
-
print(f"🏢 Provider: OpenAI CLIP + Open Food Facts")
|
| 749 |
-
print(f"🔧 Type: CLIP Zero-shot Classifier + Nutrition Database")
|
| 750 |
print(f"💻 Device: {device.upper()}")
|
| 751 |
print(f"🎯 Rank: LITE Food Recognition + Nutrition Rješenje")
|
| 752 |
print(f"✨ Status: Production Ready - NUTRITION EDITION")
|
|
|
|
| 16 |
import torch
|
| 17 |
import torch.nn.functional as F
|
| 18 |
from transformers import CLIPProcessor, CLIPModel
|
| 19 |
+
from transformers import pipeline as hf_pipeline
|
| 20 |
|
| 21 |
# --- Configuration ---
|
| 22 |
# LITE varijanta: CLIP zero-shot klasifikacija nad Food-101 labelama (CPU-friendly)
|
| 23 |
# Zadano koristi ViT-L/14 model; može se promijeniti preko env varijable MODEL_NAME
|
| 24 |
MODEL_NAME = os.environ.get("MODEL_NAME", "openai/clip-vit-large-patch14")
|
| 25 |
|
| 26 |
+
# Opcioni HF klasifikator (supervizirani model za Food-101). Preporučeni default: nateraw/food
|
| 27 |
+
USE_HF_CLASSIFIER = os.environ.get("USE_HF_CLASSIFIER", "1") == "1"
|
| 28 |
+
HF_FOOD_MODEL_NAME = os.environ.get("FOOD_CLASSIFIER_MODEL", "nateraw/food")
|
| 29 |
+
|
| 30 |
# --- Helper Functions ---
|
| 31 |
def select_device() -> str:
|
| 32 |
"""Odabire najbolji dostupni uređaj: CUDA > MPS (Apple) > CPU."""
|
|
|
|
| 108 |
print(f"❌ Greška pri učitavanju CLIP modela: {e}")
|
| 109 |
raise
|
| 110 |
|
| 111 |
+
def load_hf_food_classifier(device: str):
|
| 112 |
+
"""Pokušava učitati HF image-classification pipeline (npr. nateraw/food)."""
|
| 113 |
+
if not USE_HF_CLASSIFIER:
|
| 114 |
+
return None
|
| 115 |
+
try:
|
| 116 |
+
print(f"Loading HF Food Classifier: {HF_FOOD_MODEL_NAME} ...")
|
| 117 |
+
# Map device string -> pipeline device index
|
| 118 |
+
device_index = 0 if device in ("cuda", "mps") else -1
|
| 119 |
+
clf = hf_pipeline(
|
| 120 |
+
task="image-classification",
|
| 121 |
+
model=HF_FOOD_MODEL_NAME,
|
| 122 |
+
device=device_index,
|
| 123 |
+
top_k=5,
|
| 124 |
+
)
|
| 125 |
+
print("✅ HF Food Classifier učitan uspješno!")
|
| 126 |
+
return clf
|
| 127 |
+
except Exception as e:
|
| 128 |
+
print(f"⚠️ HF Food Classifier nije moguće učitati ('{HF_FOOD_MODEL_NAME}'): {e}. Nastavljam sa CLIP LITE.")
|
| 129 |
+
return None
|
| 130 |
+
|
| 131 |
def is_image_file(file: UploadFile):
|
| 132 |
"""Provjerava da li je fajl podržani format slike."""
|
| 133 |
return file.content_type in ["image/jpeg", "image/png", "image/jpg", "image/webp"]
|
|
|
|
| 367 |
"top5": list(zip(top_labels, top_probs))
|
| 368 |
}
|
| 369 |
|
| 370 |
+
def classify_image_with_hf(image: Image.Image, clf) -> Dict[str, Any]:
|
| 371 |
+
"""Klasifikacija slike preko HF pipeline image-classification (top-5)."""
|
| 372 |
+
preds = clf(image)
|
| 373 |
+
# preds je lista dict-ova: {label, score}
|
| 374 |
+
if not preds:
|
| 375 |
+
return {
|
| 376 |
+
"primary_label": "Unknown",
|
| 377 |
+
"alternatives": [],
|
| 378 |
+
"confidence": 0.0,
|
| 379 |
+
"top5": []
|
| 380 |
+
}
|
| 381 |
+
top_labels = [p.get("label", "Unknown") for p in preds]
|
| 382 |
+
top_probs = [float(p.get("score", 0.0)) for p in preds]
|
| 383 |
+
primary_label = top_labels[0]
|
| 384 |
+
return {
|
| 385 |
+
"primary_label": primary_label,
|
| 386 |
+
"alternatives": top_labels[1:],
|
| 387 |
+
"confidence": top_probs[0] if top_probs else 0.0,
|
| 388 |
+
"top5": list(zip(top_labels, top_probs))
|
| 389 |
+
}
|
| 390 |
+
|
| 391 |
def extract_clip_food_info(classification: Dict[str, Any]) -> Dict[str, Any]:
|
| 392 |
"""Formatira rezultat CLIP klasifikacije u zajedničku strukturu."""
|
| 393 |
primary = classification["primary_label"]
|
|
|
|
| 409 |
}
|
| 410 |
|
| 411 |
# --- Učitaj Model pri Pokretanju Aplikacije ---
|
| 412 |
+
print("🚀 Pokrećem LITE Food Scanner API (CLIP + opcioni HF classifier)...")
|
| 413 |
processor, model, device, dtype = load_model()
|
| 414 |
CURRENT_DTYPE = dtype
|
| 415 |
TEXT_LABELS = get_food101_labels()
|
| 416 |
TEXT_FEATURES = build_text_cache(TEXT_LABELS, processor, model, device, dtype)
|
| 417 |
warmup_model(processor, model, device, dtype)
|
| 418 |
+
HF_CLASSIFIER = load_hf_food_classifier(device)
|
| 419 |
|
| 420 |
# --- FastAPI Aplikacija ---
|
| 421 |
app = FastAPI(
|
| 422 |
+
title="🍎 LITE Food Scanner API - Nutrition Edition (HF+CLIP)",
|
| 423 |
description="""
|
| 424 |
**🏆 Lako i brzo prepoznavanje hrane + Nutrition Lookup (CPU-friendly)**
|
| 425 |
|
|
|
|
| 459 |
- 🤖 Inteligentna procjena za nepoznatu hranu
|
| 460 |
- ✅ Production-ready i stabilan
|
| 461 |
""",
|
| 462 |
+
version="9.1.0 - LITE (HF+CLIP)"
|
| 463 |
)
|
| 464 |
|
| 465 |
# Dodaj CORS middleware za web aplikacije
|
|
|
|
| 473 |
|
| 474 |
@app.post("/analyze",
|
| 475 |
summary="Analiziraj Food Sliku",
|
| 476 |
+
description="Upload-uj sliku da dobiješ food label + nutritivne podatke (HF classifier ako je dostupan, inače CLIP LITE)",
|
| 477 |
response_description="Rezultati food recognition i nutritivnih podataka"
|
| 478 |
)
|
| 479 |
async def analyze(file: UploadFile = File(...)):
|
|
|
|
| 511 |
raise HTTPException(status_code=500, detail=f"Greška pri čitanju slike: {e}")
|
| 512 |
|
| 513 |
try:
|
| 514 |
+
# Ako postoji nadzirani HF classifier, koristi njega; inače CLIP zero-shot
|
| 515 |
+
if HF_CLASSIFIER is not None:
|
| 516 |
+
print("🔍 Analiziram sliku sa HF image-classification modelom ...")
|
| 517 |
+
classification = classify_image_with_hf(image, HF_CLASSIFIER)
|
| 518 |
+
# Prebaci u zajednički format
|
| 519 |
+
detailed = "Top-5: " + ", ".join([f"{l} ({p:.2f})" for l, p in classification["top5"]])
|
| 520 |
+
food_info = {
|
| 521 |
+
"primary_label": classification["primary_label"],
|
| 522 |
+
"alternative_labels": classification["alternatives"],
|
| 523 |
+
"detailed_analysis": detailed,
|
| 524 |
+
"food_items": f"1) {classification['primary_label']}",
|
| 525 |
+
"nutritional_context": "",
|
| 526 |
+
"ocr_text": "",
|
| 527 |
+
"has_food": True,
|
| 528 |
+
"confidence": classification["confidence"],
|
| 529 |
+
}
|
| 530 |
+
else:
|
| 531 |
+
print("🔍 Analiziram sliku sa CLIP (zero-shot Food-101)...")
|
| 532 |
+
classification = classify_image_with_clip(image, processor, model, device)
|
| 533 |
+
food_info = extract_clip_food_info(classification)
|
| 534 |
except Exception as e:
|
| 535 |
print(f"Greška tokom analize: {e}")
|
| 536 |
raise HTTPException(status_code=500, detail=f"Greška tokom analize: {e}")
|
|
|
|
| 578 |
},
|
| 579 |
|
| 580 |
"model_info": {
|
| 581 |
+
"vision_model": HF_FOOD_MODEL_NAME if HF_CLASSIFIER is not None else MODEL_NAME,
|
| 582 |
"nutrition_source": nutrition_data["source"],
|
| 583 |
+
"type": (
|
| 584 |
+
"HF Image Classification + Nutrition Database"
|
| 585 |
+
if HF_CLASSIFIER is not None else
|
| 586 |
+
"CLIP Zero-shot Classifier + Nutrition Database"
|
| 587 |
+
),
|
| 588 |
"capabilities": [
|
| 589 |
+
"Food Recognition (supervised Food-101)" if HF_CLASSIFIER is not None else "Food Recognition (Food-101)",
|
| 590 |
"Nutrition Data Lookup"
|
| 591 |
]
|
| 592 |
}
|
|
|
|
| 658 |
def root():
|
| 659 |
"""Root endpoint sa API informacijama."""
|
| 660 |
return {
|
| 661 |
+
"message": "🍎 LITE Food Scanner API v9.1 - HF+CLIP + Nutrition Edition",
|
| 662 |
"status": "🟢 Online",
|
| 663 |
"tagline": "🏆 Najbolji Self-Hosted Food Recognition + Nutrition API",
|
| 664 |
"model": {
|
| 665 |
+
"vision_model": HF_FOOD_MODEL_NAME if HF_CLASSIFIER is not None else MODEL_NAME,
|
| 666 |
"nutrition_source": "Open Food Facts + AI Estimation",
|
| 667 |
+
"type": "HF Image Classification + Nutrition Database" if HF_CLASSIFIER is not None else "CLIP Zero-shot Classifier + Nutrition Database",
|
| 668 |
+
"provider": "Hugging Face + Open Food Facts" if HF_CLASSIFIER is not None else "OpenAI CLIP + Open Food Facts",
|
| 669 |
+
"generation": "ViT/ConvNeXt (supervised Food-101)" if HF_CLASSIFIER is not None else "CLIP (ViT-L/14)",
|
| 670 |
"device": device.upper(),
|
| 671 |
"rank": "🥇 LITE rješenje za Food Recognition"
|
| 672 |
},
|
| 673 |
"capabilities": {
|
| 674 |
+
"food_recognition": "✅ Food-101 (HF supervised)" if HF_CLASSIFIER is not None else "✅ Food-101 Zero-shot (CLIP)",
|
| 675 |
"nutrition_data": "✅ Realne Nutritivne Vrijednosti",
|
| 676 |
"nutrition_lookup": "✅ Manual Search po Imenu",
|
| 677 |
"ingredient_analysis": "❌ (LITE)",
|
|
|
|
| 710 |
)
|
| 711 |
def health_check():
|
| 712 |
"""Health check endpoint za monitoring i load balancere."""
|
| 713 |
+
model_status = (model is not None and processor is not None) or (HF_CLASSIFIER is not None)
|
| 714 |
|
| 715 |
# Test nutrition API
|
| 716 |
nutrition_api_status = "unknown"
|
|
|
|
| 723 |
return {
|
| 724 |
"status": "healthy" if model_status else "unhealthy",
|
| 725 |
"model_loaded": model_status,
|
| 726 |
+
"vision_model": HF_FOOD_MODEL_NAME if HF_CLASSIFIER is not None else MODEL_NAME,
|
| 727 |
"nutrition_api": nutrition_api_status,
|
| 728 |
+
"model_type": "HF Image Classification + Nutrition Database" if HF_CLASSIFIER is not None else "CLIP Zero-shot Classifier + Nutrition Database",
|
| 729 |
"device": device,
|
| 730 |
"device_available": torch.cuda.is_available() if device == "cuda" else True,
|
| 731 |
+
"version": "9.1.0 - LITE (HF+CLIP)",
|
| 732 |
"timestamp": "2025-10-08",
|
| 733 |
"ranking": "🥇 LITE Food Recognition + Nutrition Rješenje"
|
| 734 |
}
|
|
|
|
| 742 |
return {
|
| 743 |
"vision_model": MODEL_NAME,
|
| 744 |
"nutrition_source": "Open Food Facts",
|
| 745 |
+
"generation": ("HF (supervised Food-101) + Nutrition Database" if HF_CLASSIFIER is not None else "CLIP (ViT-L/14) + Nutrition Database"),
|
| 746 |
"release": "2024 (Stable)",
|
| 747 |
"vision_tasks": {
|
| 748 |
"food_recognition": {
|
|
|
|
| 792 |
"🔬 700,000+ proizvoda u bazi"
|
| 793 |
],
|
| 794 |
"technical_specs": {
|
| 795 |
+
"parameters": "~86M-200M (ovisno o HF modelu)" if HF_CLASSIFIER is not None else "~427M",
|
| 796 |
+
"architecture": "HF Vision Classifier (npr. ViT-B/16)" if HF_CLASSIFIER is not None else "CLIP (ViT-L/14)",
|
| 797 |
"training_data": "WIT + zero-shot na Food-101",
|
| 798 |
"supported_formats": ["JPEG", "PNG", "WebP"],
|
| 799 |
"max_resolution": "Podrška za visoke rezolucije",
|
|
|
|
| 808 |
# --- Pokreni API ---
|
| 809 |
if __name__ == "__main__":
|
| 810 |
print("=" * 80)
|
| 811 |
+
print("🍎 LITE FOOD SCANNER API v9.1 - NUTRITION EDITION (HF+CLIP)")
|
| 812 |
print("=" * 80)
|
| 813 |
+
print(f"🤖 Vision Model: {HF_FOOD_MODEL_NAME if HF_CLASSIFIER is not None else MODEL_NAME}")
|
| 814 |
print(f"📊 Nutrition Source: Open Food Facts + AI Estimation")
|
| 815 |
+
print(f"🏢 Provider: {'Hugging Face + Open Food Facts' if HF_CLASSIFIER is not None else 'OpenAI CLIP + Open Food Facts'}")
|
| 816 |
+
print(f"🔧 Type: {'HF Image Classification + Nutrition Database' if HF_CLASSIFIER is not None else 'CLIP Zero-shot Classifier + Nutrition Database'}")
|
| 817 |
print(f"💻 Device: {device.upper()}")
|
| 818 |
print(f"🎯 Rank: LITE Food Recognition + Nutrition Rješenje")
|
| 819 |
print(f"✨ Status: Production Ready - NUTRITION EDITION")
|