har1zarD commited on
Commit
2936e62
·
1 Parent(s): 4ea8ae0

Remove app_ultimate.py and requirements_ultimate.txt files

Browse files
Files changed (2) hide show
  1. app.py +243 -386
  2. requirements.txt +11 -20
app.py CHANGED
@@ -6,6 +6,7 @@ from typing import Optional, Dict, Any, List
6
  import base64
7
  import re
8
  import requests
 
9
 
10
  import uvicorn
11
  from fastapi import FastAPI, File, UploadFile, HTTPException, Query
@@ -13,55 +14,93 @@ from fastapi.responses import JSONResponse
13
  from fastapi.middleware.cors import CORSMiddleware
14
  from PIL import Image
15
  import torch
16
- from transformers import AutoProcessor, LlavaNextForConditionalGeneration
 
17
 
18
  # --- Configuration ---
19
- # LLaVA-NeXT: NAJBOLJI stabilni open-source model za food recognition
20
- # Superiorna preciznost, brza inferenca, 100% stabilan
21
- MODEL_NAME = "llava-hf/llava-v1.6-mistral-7b-hf" # 🏆 NAJBOLJI MODEL - Perfektna preciznost
22
- # Alternative opcije (sve izvrsne):
23
- # - "llava-hf/llava-v1.6-vicuna-7b-hf" - Također odličan
24
- # - "llava-hf/llava-v1.6-vicuna-13b-hf" - Za maksimalnu preciznost (sporiji)
25
 
26
  # --- Helper Functions ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  def load_model():
28
  """
29
- Učitava LLaVA-NeXT vision-language model iz Hugging Face.
30
-
31
- LLaVA-NeXT je trenutno NAJBOLJI open-source multimodal model sa:
32
- - Superiornom vizuelnom razumijevanju
33
- - Odličnim performansama na food recognition taskovima
34
- - 100% stabilnim API-jem
35
- - Brzom inferencom
36
  """
37
  try:
38
- print(f"Loading ULTIMATE model: {MODEL_NAME}...")
39
-
40
- # Koristi GPU ako je dostupan, inače CPU
41
- device = "cuda" if torch.cuda.is_available() else "cpu"
42
- print(f"Using device: {device}")
43
-
44
- # Učitaj processor (FIXOVANO: bez trust_remote_code za stabilnost)
45
- processor = AutoProcessor.from_pretrained(
46
- MODEL_NAME
47
- )
48
-
49
- # Učitaj model sa optimizacijama (FIXOVANO: bez trust_remote_code)
50
- model = LlavaNextForConditionalGeneration.from_pretrained(
51
- MODEL_NAME,
52
- torch_dtype=torch.float16 if device == "cuda" else torch.float32,
53
- device_map="auto" if device == "cuda" else None
54
- )
55
-
56
- if device == "cpu":
57
- model.to(device)
58
-
59
  model.eval()
60
-
61
- print(" ULTIMATIVNI MODEL učitan uspješno!")
62
- return processor, model, device
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  except Exception as e:
64
- print(f"❌ Greška pri učitavanju modela: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  raise
66
 
67
  def is_image_file(file: UploadFile):
@@ -229,231 +268,128 @@ def get_estimated_nutrition(food_name: str) -> Dict[str, Any]:
229
  "note": "Nutritivne vrijednosti su procijenjene na osnovu kategorije hrane"
230
  }
231
 
232
-
233
- def analyze_image_with_llava(
234
- image: Image.Image,
235
- processor,
236
- model,
237
- device
238
- ) -> Dict[str, Any]:
239
- """
240
- Analizira sliku koristeći LLaVA-NeXT za sveobuhvatnu food recognition analizu.
241
-
242
- LLaVA-NeXT mogućnosti:
243
- - Ultra-detaljna detekcija hrane
244
- - Identifikacija sastojaka
245
- - Procjena porcija
246
- - Nutritivni kontekst
247
- - Detekcija više objekata
248
- - Visual question answering
249
- - OCR i razumijevanje teksta
250
- - Kontekstualno razumijevanje
251
-
252
- Args:
253
- image: PIL Image objekat
254
- processor: LLaVA procesor
255
- model: LLaVA model
256
- device: Device za izvršavanje (cuda/cpu)
257
-
258
- Returns:
259
- Dictionary sa sveobuhvatnim rezultatima analize
260
- """
261
- results = {}
262
-
263
- # Task 1: Sveobuhvatna Food Analiza
264
- try:
265
- prompt = """[INST] <image>
266
- Analiziraj ovu sliku detaljno. Opiši koju hranu ili objekte vidiš, njihove približne porcije,
267
- sastojke koje možeš identificirati, i bilo koji vidljivi tekst. Budi veoma specifičan i detaljan. [/INST]"""
268
-
269
- inputs = processor(prompt, image, return_tensors="pt").to(device)
270
-
271
- with torch.no_grad():
272
- output = model.generate(
273
- **inputs,
274
- max_new_tokens=512,
275
- temperature=0.1,
276
- top_p=0.9,
277
- do_sample=False
278
- )
279
-
280
- response = processor.decode(output[0], skip_special_tokens=True)
281
- # Izvuci samo odgovor (skip prompt)
282
- response = response.split("[/INST]")[-1].strip()
283
-
284
- results["detailed_analysis"] = response
285
- except Exception as e:
286
- print(f"Greška u detaljnoj analizi: {e}")
287
- results["detailed_analysis"] = ""
288
-
289
- # Task 2: Specifična Identifikacija Hrane
290
- try:
291
- prompt = """[INST] <image>
292
- Nabroj sve namirnice koje možeš identificirati na ovoj slici. Za svaku stavku navedite:
293
- 1) Naziv
294
- 2) Procijenjena porcija/količina
295
- 3) Glavni sastojci ako su vidljivi
296
-
297
- Formatiraj kao numerisanu listu. [/INST]"""
298
-
299
- inputs = processor(prompt, image, return_tensors="pt").to(device)
300
-
301
- with torch.no_grad():
302
- output = model.generate(
303
- **inputs,
304
- max_new_tokens=512,
305
- temperature=0.1,
306
- top_p=0.9,
307
- do_sample=False
308
- )
309
-
310
- response = processor.decode(output[0], skip_special_tokens=True)
311
- response = response.split("[/INST]")[-1].strip()
312
-
313
- results["food_items"] = response
314
- except Exception as e:
315
- print(f"Greška u identifikaciji hrane: {e}")
316
- results["food_items"] = ""
317
-
318
- # Task 3: Nutritivni Kontekst
319
- try:
320
- prompt = """[INST] <image>
321
- Na osnovu onoga što vidiš, daj kratak nutritivni pregled: Da li je ovaj obrok bogat proteinima,
322
- ugljenim hidratima ili mastima? Da li je to zdrav izbor? Bilo kakve dijetetske napomene? [/INST]"""
323
-
324
- inputs = processor(prompt, image, return_tensors="pt").to(device)
325
-
326
- with torch.no_grad():
327
- output = model.generate(
328
- **inputs,
329
- max_new_tokens=256,
330
- temperature=0.1,
331
- top_p=0.9,
332
- do_sample=False
333
- )
334
-
335
- response = processor.decode(output[0], skip_special_tokens=True)
336
- response = response.split("[/INST]")[-1].strip()
337
-
338
- results["nutritional_context"] = response
339
- except Exception as e:
340
- print(f"Greška u nutritivnoj analizi: {e}")
341
- results["nutritional_context"] = ""
342
-
343
- # Task 4: OCR - Izvuci vidljivi tekst
344
- try:
345
- prompt = """[INST] <image>
346
- Izvuci bilo koji vidljivi tekst na ovoj slici (etikete, nutritivne informacije, menije, znakove, itd.).
347
- Ako nema teksta, reci 'Tekst nije detektovan'. [/INST]"""
348
-
349
- inputs = processor(prompt, image, return_tensors="pt").to(device)
350
-
351
- with torch.no_grad():
352
- output = model.generate(
353
- **inputs,
354
- max_new_tokens=256,
355
- temperature=0.1,
356
- top_p=0.9,
357
- do_sample=False
358
- )
359
-
360
- response = processor.decode(output[0], skip_special_tokens=True)
361
- response = response.split("[/INST]")[-1].strip()
362
-
363
- results["ocr_text"] = response
364
- except Exception as e:
365
- print(f"Greška u OCR-u: {e}")
366
- results["ocr_text"] = ""
367
-
368
- return results
369
-
370
- def extract_food_info(analysis_results: Dict[str, Any]) -> Dict[str, Any]:
371
- """
372
- Izvlači strukturirane food informacije iz LLaVA rezultata analize.
373
-
374
- Args:
375
- analysis_results: Sirovi rezultati iz LLaVA analize
376
-
377
- Returns:
378
- Formatirane food informacije
379
- """
380
- detailed_analysis = analysis_results.get("detailed_analysis", "").lower()
381
- food_items = analysis_results.get("food_items", "")
382
-
383
- # Provjeri da li je prisutna hrana
384
- food_keywords = [
385
- "food", "meal", "dish", "plate", "bowl", "fruit", "vegetable", "hrana", "jelo",
386
- "meat", "chicken", "beef", "fish", "pasta", "rice", "bread", "meso", "piletina",
387
- "salad", "sandwich", "pizza", "burger", "dessert", "cake", "salata", "sendvič",
388
- "cookie", "snack", "breakfast", "lunch", "dinner", "drink", "doručak", "ručak",
389
- "beverage", "coffee", "tea", "juice", "kafa", "čaj", "sok"
390
  ]
391
-
392
- has_food = any(keyword in detailed_analysis for keyword in food_keywords)
393
-
394
- # Izvuci primarni label iz food items
395
- primary_label = "unknown"
396
- alternative_labels = []
397
-
398
- if food_items and len(food_items) > 10:
399
- # Pokušaj izvući nazive stavki
400
- lines = food_items.split('\n')
401
- for line in lines:
402
- if line.strip() and (line.strip()[0].isdigit() or line.strip().startswith('-')):
403
- # Izvuci naziv hrane iz numerisane ili bullet liste
404
- parts = line.split('.', 1) if '.' in line else line.split(')', 1)
405
- if len(parts) > 1:
406
- food_name = parts[1].split(':')[0].split('-')[0].strip()
407
- if food_name:
408
- if primary_label == "unknown":
409
- primary_label = food_name
410
- else:
411
- alternative_labels.append(food_name)
412
-
413
- if primary_label == "unknown" and detailed_analysis:
414
- # Pokušaj izvući iz detaljne analize
415
- for keyword in food_keywords:
416
- if keyword in detailed_analysis:
417
- primary_label = keyword
418
- break
419
-
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  return {
421
  "primary_label": primary_label.title(),
422
- "alternative_labels": alternative_labels[:5], # Do 5 alternativa
423
- "detailed_analysis": analysis_results.get("detailed_analysis", ""),
424
- "food_items": food_items,
425
- "nutritional_context": analysis_results.get("nutritional_context", ""),
426
- "ocr_text": analysis_results.get("ocr_text", ""),
427
- "has_food": has_food,
428
- "confidence": 0.9 if has_food and primary_label != "unknown" else 0.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
  }
430
 
431
  # --- Učitaj Model pri Pokretanju Aplikacije ---
432
- print("🚀 Pokrećem ULTIMATIVNI Food Scanner API sa LLaVA-NeXT...")
433
- processor, model, device = load_model()
 
 
 
 
434
 
435
  # --- FastAPI Aplikacija ---
436
  app = FastAPI(
437
- title="🍎 ULTIMATIVNI Food Scanner API - Nutrition Edition",
438
  description="""
439
- **🏆 KOMPLETNO Production-Grade Food Recognition + Nutrition Analysis API**
440
 
441
- Kombinuje LLaVA-NeXT vision model sa Open Food Facts nutrition bazom podataka.
442
 
443
  ### 🌟 Glavne Mogućnosti:
444
- - 🍕 **AI Food Recognition** - LLaVA-NeXT prepoznaje hranu iz slike sa visokom preciznošću
445
  - 📊 **REALNI Nutritivni Podaci** - Automatski vraća kalorije, makroe, mikronutrijente
446
  - 🔍 **Open Food Facts Integracija** - 700,000+ proizvoda u bazi
447
  - 🤖 **AI Fallback Estimation** - Inteligentna procjena za nepoznatu hranu
448
  - 🔎 **Manual Nutrition Lookup** - Pretraži nutrition po imenu hrane
449
  - 📝 **Analiza Sastojaka** - Identificira vidljive sastojke i komponente
450
- - 📄 **OCR Podrška** - Čita nutritivne etikete, menije, recepte
451
- - 🎯 **Visual Question Answering** - Postavi bilo koje pitanje o slici
452
- - 🌍 **Višejezična Podrška** - Radi sa tekstom na više jezika
453
 
454
  ### 🎯 Kako Radi:
455
  1. **Upload** - Pošalji sliku hrane na `/analyze` endpoint
456
- 2. **AI Detection** - LLaVA-NeXT identificira koja je hrana na slici
457
  3. **Nutrition Lookup** - Automatski pretraži Open Food Facts bazu
458
  4. **Response** - Primiš naziv hrane + kompletan nutrition breakdown
459
 
@@ -476,7 +412,7 @@ app = FastAPI(
476
  - 🤖 Inteligentna procjena za nepoznatu hranu
477
  - ✅ Production-ready i stabilan
478
  """,
479
- version="8.0.0 - NUTRITION EDITION"
480
  )
481
 
482
  # Dodaj CORS middleware za web aplikacije
@@ -490,8 +426,8 @@ app.add_middleware(
490
 
491
  @app.post("/analyze",
492
  summary="Analiziraj Food Sliku",
493
- description="Upload-uj sliku da dobiješ sveobuhvatnu food analizu sa LLaVA-NeXT",
494
- response_description="Detaljni rezultati food recognition i analize"
495
  )
496
  async def analyze(file: UploadFile = File(...)):
497
  """
@@ -528,13 +464,10 @@ async def analyze(file: UploadFile = File(...)):
528
  raise HTTPException(status_code=500, detail=f"Greška pri čitanju slike: {e}")
529
 
530
  try:
531
- # Izvrši sveobuhvatnu analizu sa LLaVA-NeXT
532
- print("🔍 Analiziram sliku sa LLaVA-NeXT...")
533
- analysis_results = analyze_image_with_llava(image, processor, model, device)
534
-
535
- # Izvuci strukturirane food informacije
536
- food_info = extract_food_info(analysis_results)
537
-
538
  except Exception as e:
539
  print(f"Greška tokom analize: {e}")
540
  raise HTTPException(status_code=500, detail=f"Greška tokom analize: {e}")
@@ -567,12 +500,12 @@ async def analyze(file: UploadFile = File(...)):
567
  # Alternative
568
  "alternatives": food_info["alternative_labels"],
569
 
570
- # Dodatne informacije iz AI analize
571
- "ai_analysis": {
572
  "detailed_description": food_info["detailed_analysis"],
573
  "food_items": food_info["food_items"],
574
- "nutritional_context": food_info["nutritional_context"],
575
- "ocr_text": food_info["ocr_text"]
576
  },
577
 
578
  "image_info": {
@@ -584,14 +517,10 @@ async def analyze(file: UploadFile = File(...)):
584
  "model_info": {
585
  "vision_model": MODEL_NAME,
586
  "nutrition_source": nutrition_data["source"],
587
- "type": "Vision-Language Model (VLM) + Nutrition Database",
588
  "capabilities": [
589
- "Food Recognition",
590
- "Nutrition Data Lookup",
591
- "Ingredient Analysis",
592
- "Portion Estimation",
593
- "Multi-Object Detection",
594
- "OCR & Text Understanding"
595
  ]
596
  }
597
  }
@@ -599,66 +528,14 @@ async def analyze(file: UploadFile = File(...)):
599
  return JSONResponse(content=final_response)
600
 
601
  @app.post("/ask",
602
- summary="Postavi Pitanje o Slici",
603
- description="Upload-uj sliku i postavi specifično pitanje o njoj"
604
  )
605
  async def ask_about_image(
606
  file: UploadFile = File(...),
607
  question: str = Query(..., description="Tvoje pitanje o slici")
608
  ):
609
- """
610
- **Visual Question Answering Endpoint**
611
-
612
- Upload-uj sliku i postavi BILO KOJE pitanje o njoj:
613
- - "Koje sastojke vidiš?"
614
- - "Da li je ovo zdrav obrok?"
615
- - "Koliko približno kalorija?"
616
- - "Koja je ovo kuhinja?"
617
- - "Može li vegetarijanac ovo jesti?"
618
- """
619
- if not file:
620
- raise HTTPException(status_code=400, detail="Slika nije poslata.")
621
-
622
- if not is_image_file(file):
623
- raise HTTPException(
624
- status_code=400,
625
- detail="Nepodržan format slike. Koristi JPEG, PNG ili WebP."
626
- )
627
-
628
- try:
629
- contents = await file.read()
630
- image = Image.open(BytesIO(contents))
631
-
632
- if image.mode != "RGB":
633
- image = image.convert("RGB")
634
-
635
- # Pripremi VQA prompt
636
- prompt = f"[INST] <image>\n{question} [/INST]"
637
-
638
- inputs = processor(prompt, image, return_tensors="pt").to(device)
639
-
640
- with torch.no_grad():
641
- output = model.generate(
642
- **inputs,
643
- max_new_tokens=512,
644
- temperature=0.2,
645
- top_p=0.9,
646
- do_sample=True
647
- )
648
-
649
- response = processor.decode(output[0], skip_special_tokens=True)
650
- answer = response.split("[/INST]")[-1].strip()
651
-
652
- return JSONResponse(content={
653
- "success": True,
654
- "question": question,
655
- "answer": answer,
656
- "model": MODEL_NAME
657
- })
658
-
659
- except Exception as e:
660
- print(f"Greška tokom VQA: {e}")
661
- raise HTTPException(status_code=500, detail=f"Greška tokom analize: {e}")
662
 
663
  @app.get("/search-nutrition/{food_name}",
664
  summary="Pretraži Nutritivne Podatke",
@@ -714,33 +591,33 @@ async def search_nutrition(food_name: str):
714
  def root():
715
  """Root endpoint sa API informacijama."""
716
  return {
717
- "message": "🍎 Ultimativni Food Scanner API v8.0 - LLaVA-NeXT + Nutrition Edition",
718
  "status": "🟢 Online",
719
  "tagline": "🏆 Najbolji Self-Hosted Food Recognition + Nutrition API",
720
  "model": {
721
  "vision_model": MODEL_NAME,
722
  "nutrition_source": "Open Food Facts + AI Estimation",
723
- "type": "Vision-Language Model (VLM) + Nutrition Database",
724
- "provider": "LLaVA Team / Haotian Liu + Open Food Facts",
725
- "generation": "LLaVA-NeXT (v1.6)",
726
  "device": device.upper(),
727
- "rank": "🥇 #1 Kompletno Food Recognition Rješenje"
728
  },
729
  "capabilities": {
730
- "food_recognition": "✅ Ultra-Detaljno (AI Vision)",
731
  "nutrition_data": "✅ Realne Nutritivne Vrijednosti",
732
  "nutrition_lookup": "✅ Manual Search po Imenu",
733
- "ingredient_analysis": " Napredno",
734
- "portion_estimation": " Precizno",
735
- "multi_object_detection": " Neograničeno",
736
- "ocr": " Višejezično",
737
- "visual_qa": " Konverzaciono",
738
- "offline_mode": "✅ Puna Podrška (za vision)",
739
  "database": "✅ Open Food Facts (700K+ proizvoda)"
740
  },
741
  "endpoints": {
742
  "POST /analyze": "🍕 Upload food sliku - AI prepozna + vrati nutritivne podatke",
743
- "POST /ask": " Upload sliku i postavi bilo koje pitanje o njoj",
744
  "GET /search-nutrition/{food_name}": "🔍 Pretraži nutritivne podatke po imenu hrane",
745
  "GET /health": "💚 Provjeri API i model health status",
746
  "GET /capabilities": "📋 Lista svih mogućnosti modela",
@@ -750,11 +627,11 @@ def root():
750
  "advantages": {
751
  "cost": "💰 100% Besplatno - Nikad nema API troškova",
752
  "privacy": "🔒 Self-hosted - Tvoji podaci ostaju privatni",
753
- "performance": "⚡ State-of-the-art preciznost",
754
  "nutrition_accuracy": "📊 Realni podaci iz Open Food Facts baze",
755
  "fallback": "🤖 AI procjena ako hrana nije u bazi",
756
- "offline": "📡 Vision model radi bez interneta",
757
- "stability": "✅ 100% stabilno i production-ready",
758
  "updates": "🔄 Open-source - Uvijek se poboljšava"
759
  },
760
  "documentation": "Posjeti /docs za interaktivno API testiranje"
@@ -781,12 +658,12 @@ def health_check():
781
  "model_loaded": model_status,
782
  "vision_model": MODEL_NAME,
783
  "nutrition_api": nutrition_api_status,
784
- "model_type": "LLaVA-NeXT Vision-Language Model + Nutrition Database",
785
  "device": device,
786
  "device_available": torch.cuda.is_available() if device == "cuda" else True,
787
- "version": "8.0.0 - NUTRITION EDITION",
788
  "timestamp": "2025-10-08",
789
- "ranking": "🥇 #1 Kompletno Food Recognition + Nutrition Rješenje"
790
  }
791
 
792
  @app.get("/capabilities",
@@ -798,13 +675,13 @@ def get_capabilities():
798
  return {
799
  "vision_model": MODEL_NAME,
800
  "nutrition_source": "Open Food Facts",
801
- "generation": "LLaVA-NeXT (v1.6) + Nutrition Database",
802
- "release": "2024 (Latest Stable)",
803
  "vision_tasks": {
804
  "food_recognition": {
805
- "description": "Identificira specifična jela, kuhinje i stilove kuvanja",
806
- "accuracy": "State-of-the-art",
807
- "features": ["Multi-food detection", "Ingredient identification", "Cuisine classification"]
808
  },
809
  "nutrition_data": {
810
  "description": "Vraća REALNE nutritivne vrijednosti iz baze podataka",
@@ -813,30 +690,10 @@ def get_capabilities():
813
  "data_includes": ["Kalorije", "Proteini", "Ugljeni hidrati", "Masti", "Vlakna", "Šećeri", "Natrijum"],
814
  "per_serving": "100g (standardno)"
815
  },
816
- "nutritional_analysis": {
817
- "description": "AI analiza nutritivnog konteksta iz slike",
818
- "capabilities": ["Macro estimation", "Portion analysis", "Dietary recommendations"]
819
- },
820
- "visual_understanding": {
821
- "description": "Sveobuhvatno razumijevanje i opis slike",
822
- "output": "Detaljni opisi na prirodnom jeziku",
823
- "depth": "Ultra-detaljno sa kontekstom"
824
- },
825
- "ocr": {
826
- "description": "Izvlači i razumije tekst sa slika",
827
- "languages": "Višejezično (100+ jezika)",
828
- "applications": ["Nutrition labels", "Menus", "Recipes", "Signs"]
829
- },
830
- "visual_qa": {
831
- "description": "Odgovara na bilo koje pitanje o slici",
832
- "interaction": "Konverzacijski",
833
- "examples": [
834
- "Koje sastojke vidiš?",
835
- "Da li je ovo zdrav obrok?",
836
- "Koliko približno kalorija?",
837
- "Koja je ovo kuhinja?"
838
- ]
839
- }
840
  },
841
  "use_cases": [
842
  "Profesionalno nutrition tracking sa realnim podacima",
@@ -846,32 +703,31 @@ def get_capabilities():
846
  "Sistemi za dijetetske preporuke",
847
  "Food delivery aplikacije sa nutrition labels",
848
  "Health i fitness platforme",
849
- "Analiza recepata sa nutritivnim vrijednostima",
850
- "Prepoznavanje i analiza sastojaka",
851
- "Kontrola porcija i kalorija",
852
  "Edukativne food i nutrition aplikacije",
853
  "Medical i healthcare nutrition tracking"
854
  ],
855
  "advantages": [
856
- "🏆 Najbolji stabilni open-source vision model",
857
  "📊 REALNI nutritivni podaci iz Open Food Facts",
858
- "💯 State-of-the-art preciznost u food recognition",
859
  "🆓 Potpuno besplatno za korištenje",
860
  "🔒 Self-hostable za privatnost",
861
  "⚡ Brza inferenca",
862
  "🤖 AI fallback estimation za nepoznatu hranu",
863
  "📡 Vision model radi offline",
864
  "🌍 Višejezična podrška",
865
- "🎯 Specijalizovan za hranu + nutrition",
866
  "💪 Robustan i pouzdan",
867
  "🔄 Aktivno održavan",
868
- "✅ 100% stabilan i production-ready",
869
  "🔬 700,000+ proizvoda u bazi"
870
  ],
871
  "technical_specs": {
872
- "parameters": "7 Billion",
873
- "architecture": "Vision-Language Transformer (LLaVA-NeXT)",
874
- "training_data": "Masivni multimodalni dataset",
875
  "supported_formats": ["JPEG", "PNG", "WebP"],
876
  "max_resolution": "Podrška za visoke rezolucije",
877
  "batch_processing": "Podržano",
@@ -885,28 +741,29 @@ def get_capabilities():
885
  # --- Pokreni API ---
886
  if __name__ == "__main__":
887
  print("=" * 80)
888
- print("🍎 ULTIMATIVNI FOOD SCANNER API v8.0 - NUTRITION EDITION")
889
  print("=" * 80)
890
  print(f"🤖 Vision Model: {MODEL_NAME}")
891
  print(f"📊 Nutrition Source: Open Food Facts + AI Estimation")
892
- print(f"🏢 Provider: LLaVA Team / Haotian Liu")
893
- print(f"🔧 Type: Vision-Language Model (VLM) + Nutrition Database")
894
  print(f"💻 Device: {device.upper()}")
895
- print(f"🎯 Rank: #1 Kompletno Food Recognition + Nutrition Rješenje")
896
  print(f"✨ Status: Production Ready - NUTRITION EDITION")
897
  print(f"💰 Cost: $0 - 100% Besplatno Self-Hosted")
898
  print("=" * 80)
899
  print("🌟 NOVE MOGUĆNOSTI:")
900
- print(" ✅ AI prepoznavanje hrane iz slike")
901
  print(" ✅ Automatsko vraćanje nutritivnih vrijednosti")
902
  print(" ✅ 700,000+ proizvoda u Open Food Facts bazi")
903
  print(" ✅ AI procjena za nepoznatu hranu")
904
  print(" ✅ Manual nutrition lookup po imenu")
905
  print("=" * 80)
906
- print("🌍 Pokrećem server na http://0.0.0.0:8000")
907
- print("📚 API Docs: http://0.0.0.0:8000/docs")
908
- print("🔥 Spreman za food recognition + nutrition analysis!")
 
909
  print("=" * 80)
910
- uvicorn.run(app, host="0.0.0.0", port=8000)
911
 
912
 
 
6
  import base64
7
  import re
8
  import requests
9
+ import contextlib
10
 
11
  import uvicorn
12
  from fastapi import FastAPI, File, UploadFile, HTTPException, Query
 
14
  from fastapi.middleware.cors import CORSMiddleware
15
  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."""
28
+ if torch.cuda.is_available():
29
+ return "cuda"
30
+ # MPS (Apple Silicon)
31
+ try:
32
+ if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
33
+ return "mps"
34
+ except Exception:
35
+ pass
36
+ return "cpu"
37
+
38
+ def select_dtype(device: str):
39
+ """Odabire optimalni dtype za dati uređaj (za manju memoriju i bržu inferencu)."""
40
+ if device == "cuda":
41
+ return torch.float16
42
+ # MPS je najstabilniji sa float16 za CLIP u praksi
43
+ if device == "mps":
44
+ return torch.float16
45
+ return torch.float32
46
+
47
+ def autocast_context(device: str, dtype):
48
+ """Vraća odgovarajući autocast kontekst za dati uređaj i dtype (ili no-op)."""
49
+ if device in ("cuda", "cpu", "mps"):
50
+ try:
51
+ return torch.autocast(device_type=device, dtype=dtype)
52
+ except Exception:
53
+ return contextlib.nullcontext()
54
+ return contextlib.nullcontext()
55
+
56
  def load_model():
57
  """
58
+ Učitava lagani CLIP model i processor za zero-shot klasifikaciju.
 
 
 
 
 
 
59
  """
60
  try:
61
+ print(f"Loading LITE model: {MODEL_NAME}...")
62
+ device = select_device()
63
+ dtype = select_dtype(device)
64
+ print(f"Using device: {device} | dtype: {dtype}")
65
+ processor = CLIPProcessor.from_pretrained(MODEL_NAME)
66
+ # Preferiraj sigurnije safetensors težine + učitaj direktno u niži dtype
67
+ model = CLIPModel.from_pretrained(MODEL_NAME, use_safetensors=True, torch_dtype=dtype)
68
+ model.to(device)
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  model.eval()
70
+ # Opcionalni compile za dodatni throughput na CUDA
71
+ if device == "cuda" and os.environ.get("CLIP_COMPILE", "1") == "1" and hasattr(torch, "compile"):
72
+ try:
73
+ model = torch.compile(model, mode="reduce-overhead", fullgraph=False)
74
+ print("⚡ torch.compile omogućen (reduce-overhead)")
75
+ except Exception as _e:
76
+ print(f"ℹ️ torch.compile nije omogućen: {_e}")
77
+ print("✅ LITE CLIP model učitan uspješno!")
78
+ return processor, model, device, dtype
79
+ except ValueError as e:
80
+ # Jasnija poruka za CVE i torch>=2.6 zahtjev
81
+ if "upgrade torch to at least v2.6" in str(e) or "torch.load" in str(e):
82
+ msg = (
83
+ "Zbog CVE-2025-32434 potrebno je koristiti torch>=2.6. "
84
+ "Ažuriraj okruženje: pip install --upgrade 'torch>=2.6' 'safetensors>=0.4.3'\n"
85
+ "Ako radiš lokalno: pip install -r requirements.txt"
86
+ )
87
+ print(f"❌ {msg}")
88
+ raise
89
  except Exception as e:
90
+ # Pokušaj fallback bez safetensors (ako je dostupno i okruženje je sigurno)
91
+ print(f"⚠️ Primarni load sa safetensors nije uspio: {e}. Pokušavam fallback...")
92
+ try:
93
+ device = select_device()
94
+ dtype = select_dtype(device)
95
+ processor = CLIPProcessor.from_pretrained(MODEL_NAME)
96
+ model = CLIPModel.from_pretrained(MODEL_NAME, use_safetensors=False, torch_dtype=dtype)
97
+ model.to(device)
98
+ model.eval()
99
+ print("✅ LITE CLIP model učitan uspješno (fallback način)!")
100
+ return processor, model, device, dtype
101
+ except Exception as e2:
102
+ print(f"❌ Greška pri učitavanju CLIP modela (fallback): {e2}")
103
+ print(f"❌ Greška pri učitavanju CLIP modela: {e}")
104
  raise
105
 
106
  def is_image_file(file: UploadFile):
 
268
  "note": "Nutritivne vrijednosti su procijenjene na osnovu kategorije hrane"
269
  }
270
 
271
+ def get_food101_labels() -> List[str]:
272
+ """Vraća listu Food-101 klasa (formatirano sa razmacima)."""
273
+ raw_labels = [
274
+ "apple_pie", "baby_back_ribs", "baklava", "beef_carpaccio", "beef_tartare",
275
+ "beet_salad", "beignets", "bibimbap", "bread_pudding", "breakfast_burrito",
276
+ "bruschetta", "caesar_salad", "cannoli", "caprese_salad", "carrot_cake",
277
+ "ceviche", "cheesecake", "cheese_plate", "chicken_curry", "chicken_quesadilla",
278
+ "chicken_wings", "chocolate_cake", "chocolate_mousse", "churros", "clam_chowder",
279
+ "club_sandwich", "crab_cakes", "creme_brulee", "croque_madame", "cup_cakes",
280
+ "deviled_eggs", "donuts", "dumplings", "edamame", "eggs_benedict",
281
+ "escargots", "falafel", "filet_mignon", "fish_and_chips", "foie_gras",
282
+ "french_fries", "french_onion_soup", "french_toast", "fried_calamari", "fried_rice",
283
+ "frozen_yogurt", "garlic_bread", "gnocchi", "greek_salad", "grilled_cheese_sandwich",
284
+ "grilled_salmon", "guacamole", "gyoza", "hamburger", "hot_and_sour_soup",
285
+ "hot_dog", "huevos_rancheros", "hummus", "ice_cream", "lasagna",
286
+ "lobster_bisque", "lobster_roll_sandwich", "macaroni_and_cheese", "macarons", "miso_soup",
287
+ "mussels", "nachos", "omelette", "onion_rings", "oysters",
288
+ "pad_thai", "paella", "pancakes", "panna_cotta", "peking_duck",
289
+ "pho", "pizza", "pork_chop", "poutine", "prime_rib",
290
+ "pulled_pork_sandwich", "ramen", "ravioli", "red_velvet_cake", "risotto",
291
+ "samosa", "sashimi", "scallops", "seaweed_salad", "shrimp_and_grits",
292
+ "spaghetti_bolognese", "spaghetti_carbonara", "spring_rolls", "steak", "strawberry_shortcake",
293
+ "sushi", "tacos", "takoyaki", "tiramisu", "tuna_tartare",
294
+ "waffles"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  ]
296
+ return [label.replace("_", " ") for label in raw_labels]
297
+
298
+ def build_text_cache(labels: List[str], processor: CLIPProcessor, model: CLIPModel, device: str, dtype) -> torch.Tensor:
299
+ """Prekompajlira i kešira CLIP tekstualne embeddinge za Food-101 labele (L2-normalizovane)."""
300
+ with torch.no_grad():
301
+ text_inputs = processor(text=labels, return_tensors="pt", padding=True, truncation=True)
302
+ text_inputs = {k: v.to(device) for k, v in text_inputs.items()}
303
+ with autocast_context(device, dtype):
304
+ text_features = model.get_text_features(**text_inputs)
305
+ text_features = text_features / text_features.norm(dim=-1, keepdim=True)
306
+ return text_features
307
+
308
+ def warmup_model(processor: CLIPProcessor, model: CLIPModel, device: str, dtype):
309
+ """Kratki warmup da se popune keševi i stabilizuje latency (posebno uz torch.compile)."""
310
+ try:
311
+ img = Image.new("RGB", (224, 224), color=(127, 127, 127))
312
+ img_inputs = processor(images=img, return_tensors="pt")
313
+ img_inputs = {k: v.to(device) for k, v in img_inputs.items()}
314
+ with torch.no_grad(), autocast_context(device, dtype):
315
+ _ = model.get_image_features(**img_inputs)
316
+ if device == "cuda":
317
+ torch.cuda.synchronize()
318
+ print("🔥 Warmup završen")
319
+ except Exception as _e:
320
+ print(f"ℹ️ Warmup preskočen: {_e}")
321
+
322
+ def classify_image_with_clip(image: Image.Image, processor: CLIPProcessor, model: CLIPModel, device: str) -> Dict[str, Any]:
323
+ """Zero-shot klasifikacija slike nad Food-101 labelama koristeći CLIP sa keširanim tekst embedding-ima."""
324
+ global TEXT_FEATURES, TEXT_LABELS, CURRENT_DTYPE
325
+ labels = TEXT_LABELS
326
+ img_inputs = processor(images=image, return_tensors="pt")
327
+ img_inputs = {k: v.to(device) for k, v in img_inputs.items()}
328
+ with torch.no_grad(), autocast_context(device, CURRENT_DTYPE):
329
+ image_features = model.get_image_features(**img_inputs)
330
+ image_features = image_features / image_features.norm(dim=-1, keepdim=True)
331
+ logits = (image_features @ TEXT_FEATURES.t()) * 100.0
332
+ probs = F.softmax(logits, dim=1).cpu().numpy()[0]
333
+ # Top-5
334
+ top_indices = probs.argsort()[-5:][::-1]
335
+ top_labels = [labels[i] for i in top_indices]
336
+ top_probs = [float(probs[i]) for i in top_indices]
337
+ primary_label = top_labels[0]
338
  return {
339
  "primary_label": primary_label.title(),
340
+ "alternatives": [l.title() for l in top_labels[1:]],
341
+ "confidence": top_probs[0],
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"]
348
+ alts = classification["alternatives"]
349
+ conf = classification["confidence"]
350
+ # Jednostavan tekstualni rezime umjesto LLaVA eseja
351
+ detailed = f"Detektovano: {primary} (povjerenje {conf:.2f}). Top-5: " + \
352
+ ", ".join([f"{l.title()} ({p:.2f})" for l, p in classification["top5"]])
353
+ items = f"1) {primary}"
354
+ return {
355
+ "primary_label": primary,
356
+ "alternative_labels": alts,
357
+ "detailed_analysis": detailed,
358
+ "food_items": items,
359
+ "nutritional_context": "",
360
+ "ocr_text": "",
361
+ "has_food": True,
362
+ "confidence": conf
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
 
379
+ Koristi CLIP zero-shot klasifikaciju nad Food-101 klasama i Open Food Facts bazu.
380
 
381
  ### 🌟 Glavne Mogućnosti:
382
+ - 🍕 **AI Food Recognition** - CLIP zero-shot prepoznaje hranu iz slike
383
  - 📊 **REALNI Nutritivni Podaci** - Automatski vraća kalorije, makroe, mikronutrijente
384
  - 🔍 **Open Food Facts Integracija** - 700,000+ proizvoda u bazi
385
  - 🤖 **AI Fallback Estimation** - Inteligentna procjena za nepoznatu hranu
386
  - 🔎 **Manual Nutrition Lookup** - Pretraži nutrition po imenu hrane
387
  - 📝 **Analiza Sastojaka** - Identificira vidljive sastojke i komponente
388
+ - 📄 **OCR / VQA** - Onemogućeno u LITE modu radi uštede memorije
 
 
389
 
390
  ### 🎯 Kako Radi:
391
  1. **Upload** - Pošalji sliku hrane na `/analyze` endpoint
392
+ 2. **AI Detection** - CLIP model identificira koja je hrana na slici
393
  3. **Nutrition Lookup** - Automatski pretraži Open Food Facts bazu
394
  4. **Response** - Primiš naziv hrane + kompletan nutrition breakdown
395
 
 
412
  - 🤖 Inteligentna procjena za nepoznatu hranu
413
  - ✅ Production-ready i stabilan
414
  """,
415
+ version="9.0.0 - LITE (CLIP)"
416
  )
417
 
418
  # Dodaj CORS middleware za web aplikacije
 
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(...)):
433
  """
 
464
  raise HTTPException(status_code=500, detail=f"Greška pri čitanju slike: {e}")
465
 
466
  try:
467
+ # Zero-shot klasifikacija sa CLIP-om
468
+ print("🔍 Analiziram sliku sa CLIP (zero-shot Food-101)...")
469
+ classification = classify_image_with_clip(image, processor, model, device)
470
+ food_info = extract_clip_food_info(classification)
 
 
 
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}")
 
500
  # Alternative
501
  "alternatives": food_info["alternative_labels"],
502
 
503
+ # Dodatne informacije (LITE)
504
+ "ai_analysis": {
505
  "detailed_description": food_info["detailed_analysis"],
506
  "food_items": food_info["food_items"],
507
+ "nutritional_context": "",
508
+ "ocr_text": ""
509
  },
510
 
511
  "image_info": {
 
517
  "model_info": {
518
  "vision_model": MODEL_NAME,
519
  "nutrition_source": nutrition_data["source"],
520
+ "type": "CLIP Zero-shot Classifier + Nutrition Database",
521
  "capabilities": [
522
+ "Food Recognition (Food-101)",
523
+ "Nutrition Data Lookup"
 
 
 
 
524
  ]
525
  }
526
  }
 
528
  return JSONResponse(content=final_response)
529
 
530
  @app.post("/ask",
531
+ summary="Postavi Pitanje o Slici (LITE onemogućeno)",
532
+ description="U LITE modu VQA je onemogućeno radi uštede memorije"
533
  )
534
  async def ask_about_image(
535
  file: UploadFile = File(...),
536
  question: str = Query(..., description="Tvoje pitanje o slici")
537
  ):
538
+ raise HTTPException(status_code=501, detail="VQA je onemogućeno u LITE modu. Koristi /analyze za prepoznavanje hrane.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
 
540
  @app.get("/search-nutrition/{food_name}",
541
  summary="Pretraži Nutritivne Podatke",
 
591
  def root():
592
  """Root endpoint sa API informacijama."""
593
  return {
594
+ "message": "🍎 LITE Food Scanner API v9.0 - CLIP + Nutrition Edition",
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)",
611
+ "portion_estimation": " (LITE)",
612
+ "multi_object_detection": " (LITE)",
613
+ "ocr": " (LITE)",
614
+ "visual_qa": " (LITE)",
615
+ "offline_mode": "✅",
616
  "database": "✅ Open Food Facts (700K+ proizvoda)"
617
  },
618
  "endpoints": {
619
  "POST /analyze": "🍕 Upload food sliku - AI prepozna + vrati nutritivne podatke",
620
+ "POST /ask": " Onemogućeno u LITE modu",
621
  "GET /search-nutrition/{food_name}": "🔍 Pretraži nutritivne podatke po imenu hrane",
622
  "GET /health": "💚 Provjeri API i model health status",
623
  "GET /capabilities": "📋 Lista svih mogućnosti modela",
 
627
  "advantages": {
628
  "cost": "💰 100% Besplatno - Nikad nema API troškova",
629
  "privacy": "🔒 Self-hosted - Tvoji podaci ostaju privatni",
630
+ "performance": "⚡ Brza inferenca (CPU-friendly)",
631
  "nutrition_accuracy": "📊 Realni podaci iz Open Food Facts baze",
632
  "fallback": "🤖 AI procjena ako hrana nije u bazi",
633
+ "offline": "📡 Radi offline (model)",
634
+ "stability": "✅ Stabilno i production-ready",
635
  "updates": "🔄 Open-source - Uvijek se poboljšava"
636
  },
637
  "documentation": "Posjeti /docs za interaktivno API testiranje"
 
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.0.0 - LITE (CLIP)",
665
  "timestamp": "2025-10-08",
666
+ "ranking": "🥇 LITE Food Recognition + Nutrition Rješenje"
667
  }
668
 
669
  @app.get("/capabilities",
 
675
  return {
676
  "vision_model": MODEL_NAME,
677
  "nutrition_source": "Open Food Facts",
678
+ "generation": "CLIP (ViT-L/14) + Nutrition Database",
679
+ "release": "2024 (Stable)",
680
  "vision_tasks": {
681
  "food_recognition": {
682
+ "description": "Zero-shot klasifikacija nad Food-101 listom klasa",
683
+ "accuracy": "Visoka (zavisno od scene)",
684
+ "features": ["Top-5 predlozi", "CPU-friendly"]
685
  },
686
  "nutrition_data": {
687
  "description": "Vraća REALNE nutritivne vrijednosti iz baze podataka",
 
690
  "data_includes": ["Kalorije", "Proteini", "Ugljeni hidrati", "Masti", "Vlakna", "Šećeri", "Natrijum"],
691
  "per_serving": "100g (standardno)"
692
  },
693
+ "nutritional_analysis": {"description": "(LITE) Onemogućeno"},
694
+ "visual_understanding": {"description": "(LITE) Onemogućeno"},
695
+ "ocr": {"description": "(LITE) Onemogućeno"},
696
+ "visual_qa": {"description": "(LITE) Onemogućeno"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
  },
698
  "use_cases": [
699
  "Profesionalno nutrition tracking sa realnim podacima",
 
703
  "Sistemi za dijetetske preporuke",
704
  "Food delivery aplikacije sa nutrition labels",
705
  "Health i fitness platforme",
706
+ "Analiza recepata (preko naziva)",
707
+ "Kontrola porcija (ručno)",
 
708
  "Edukativne food i nutrition aplikacije",
709
  "Medical i healthcare nutrition tracking"
710
  ],
711
  "advantages": [
712
+ "🏆 Lagano i brzo rješenje",
713
  "📊 REALNI nutritivni podaci iz Open Food Facts",
714
+ "💯 Dobra preciznost u food recognition (Food-101)",
715
  "🆓 Potpuno besplatno za korištenje",
716
  "🔒 Self-hostable za privatnost",
717
  "⚡ Brza inferenca",
718
  "🤖 AI fallback estimation za nepoznatu hranu",
719
  "📡 Vision model radi offline",
720
  "🌍 Višejezična podrška",
721
+ "🎯 Fokus na hranu + nutrition",
722
  "💪 Robustan i pouzdan",
723
  "🔄 Aktivno održavan",
724
+ "✅ Stabilan i production-ready",
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",
733
  "batch_processing": "Podržano",
 
741
  # --- Pokreni API ---
742
  if __name__ == "__main__":
743
  print("=" * 80)
744
+ print("🍎 LITE FOOD SCANNER API v9.0 - NUTRITION EDITION (CLIP)")
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")
753
  print(f"💰 Cost: $0 - 100% Besplatno Self-Hosted")
754
  print("=" * 80)
755
  print("🌟 NOVE MOGUĆNOSTI:")
756
+ print(" ✅ Zero-shot prepoznavanje hrane (Food-101)")
757
  print(" ✅ Automatsko vraćanje nutritivnih vrijednosti")
758
  print(" ✅ 700,000+ proizvoda u Open Food Facts bazi")
759
  print(" ✅ AI procjena za nepoznatu hranu")
760
  print(" ✅ Manual nutrition lookup po imenu")
761
  print("=" * 80)
762
+ run_port = int(os.environ.get("PORT", "8000"))
763
+ print(f"🌍 Pokrećem server na http://0.0.0.0:{run_port}")
764
+ print(f"📚 API Docs: http://0.0.0.0:{run_port}/docs")
765
+ print("🔥 Spreman za food recognition + nutrition analysis (LITE)!")
766
  print("=" * 80)
767
+ uvicorn.run(app, host="0.0.0.0", port=run_port)
768
 
769
 
requirements.txt CHANGED
@@ -1,5 +1,5 @@
1
- # ULTIMATIVNI Food Scanner API - LLaVA-NeXT Edition
2
- # Requirements za NAJBOLJI stabilni food recognition model
3
 
4
  # Core API Framework
5
  fastapi==0.115.0
@@ -9,26 +9,17 @@ python-multipart==0.0.12
9
  # Image Processing
10
  pillow==11.0.0
11
 
12
- # Deep Learning Framework
13
- torch>=2.0.0
14
- torchvision>=0.15.0
 
15
 
16
- # Transformers & Model (FIXOVANO: Najnovije verzije za potpunu LLaVA-NeXT podršku)
17
- transformers>=4.41.0
18
- accelerate>=0.31.0
19
 
20
- # Vision Processing
21
- timm>=0.9.0
22
- einops>=0.7.0
23
-
24
- # Utilities
25
- numpy>=1.24.0
26
- sentencepiece>=0.2.0
27
- protobuf>=4.25.0
28
  requests>=2.32.0
29
- httpx>=0.27.0
30
 
31
- # NOTE: Ovaj model je 100% stabilan i radi na svim verzijama!
32
- # LLaVA-NeXT ne zahtijeva dodatne biblioteke kao Qwen2-VL
33
- # Sve dependencies su standardne i provjerene za production
34
 
 
1
+ # LITE Food Scanner API - CLIP Edition
2
+ # Minimalni requirements za CPU-friendly food recognition
3
 
4
  # Core API Framework
5
  fastapi==0.115.0
 
9
  # Image Processing
10
  pillow==11.0.0
11
 
12
+ # Deep Learning / Transformers
13
+ # NOTE: Due to CVE-2025-32434, torch must be >=2.6 to allow torch.load() via transformers
14
+ torch>=2.6.0
15
+ safetensors>=0.4.3
16
 
17
+ # Transformers (CLIP)
18
+ transformers>=4.44.2
 
19
 
20
+ # HTTP util
 
 
 
 
 
 
 
21
  requests>=2.32.0
 
22
 
23
+ # Napomena: LITE varijanta ne zahtijeva torchvision/timm/accelerate/einops
24
+ # CLIP radi preko transformers + torch
 
25