Spaces:
Sleeping
Sleeping
| from pathlib import Path | |
| import io | |
| from typing import Any, Dict, List | |
| from PIL import Image | |
| import torch | |
| from torchvision import models, transforms | |
| # Define path configurations | |
| MODEL_PATH = Path(__file__).resolve().parent / "model_resnet.pth" | |
| # List of all food class names supported by the model | |
| CLASS_NAMES = [ | |
| "apple_pie", "baby_back_ribs", "baklava", "beef_carpaccio", "beef_tartare", | |
| "beet_salad", "beignets", "bibimbap", "bread_pudding", "breakfast_burrito", | |
| "bruschetta", "caesar_salad", "cannoli", "caprese_salad", "carrot_cake", | |
| "ceviche", "cheesecake", "cheese_plate", "chicken_curry", "chicken_quesadilla", | |
| "chicken_wings", "chocolate_cake", "chocolate_mousse", "churros", "clam_chowder", | |
| "club_sandwich", "crab_cakes", "creme_brulee", "croque_madame", "cup_cakes" | |
| ] | |
| # Mapping classes to human-readable labels and emojis | |
| CLASS_DISPLAY_NAMES = { | |
| "apple_pie": {"label": "Apple Pie", "icon": "๐ฅง"}, | |
| "baby_back_ribs": {"label": "Baby Back Ribs", "icon": "๐"}, | |
| "baklava": {"label": "Baklava", "icon": "๐ฏ"}, | |
| "beef_carpaccio": {"label": "Beef Carpaccio", "icon": "๐ฅฉ"}, | |
| "beef_tartare": {"label": "Beef Tartare", "icon": "๐ฅฉ"}, | |
| "beet_salad": {"label": "Beet Salad", "icon": "๐ฅ"}, | |
| "beignets": {"label": "Beignets", "icon": "๐ฉ"}, | |
| "bibimbap": {"label": "Bibimbap", "icon": "๐"}, | |
| "bread_pudding": {"label": "Bread Pudding", "icon": "๐ฎ"}, | |
| "breakfast_burrito": {"label": "Breakfast Burrito", "icon": "๐ฏ"}, | |
| "bruschetta": {"label": "Bruschetta", "icon": "๐ "}, | |
| "caesar_salad": {"label": "Caesar Salad", "icon": "๐ฅ"}, | |
| "cannoli": {"label": "Cannoli", "icon": "๐ฅ"}, | |
| "caprese_salad": {"label": "Caprese Salad", "icon": "๐ "}, | |
| "carrot_cake": {"label": "Carrot Cake", "icon": "๐"}, | |
| "ceviche": {"label": "Ceviche", "icon": "๐ค"}, | |
| "cheesecake": {"label": "Cheesecake", "icon": "๐ฐ"}, | |
| "cheese_plate": {"label": "Cheese Plate", "icon": "๐ง"}, | |
| "chicken_curry": {"label": "Chicken Curry", "icon": "๐"}, | |
| "chicken_quesadilla": {"label": "Chicken Quesadilla", "icon": "๐ฏ"}, | |
| "chicken_wings": {"label": "Chicken Wings", "icon": "๐"}, | |
| "chocolate_cake": {"label": "Chocolate Cake", "icon": "๐ซ"}, | |
| "chocolate_mousse": {"label": "Chocolate Mousse", "icon": "๐ฎ"}, | |
| "churros": {"label": "Churros", "icon": "๐ง"}, | |
| "clam_chowder": {"label": "Clam Chowder", "icon": "๐ฅฃ"}, | |
| "club_sandwich": {"label": "Club Sandwich", "icon": "๐ฅช"}, | |
| "crab_cakes": {"label": "Crab Cakes", "icon": "๐ฆ"}, | |
| "creme_brulee": {"label": "Crรจme Brรปlรฉe", "icon": "๐ฎ"}, | |
| "croque_madame": {"label": "Croque Madame", "icon": "๐ฅช"}, | |
| "cup_cakes": {"label": "Cupcakes", "icon": "๐ง"}, | |
| } | |
| NUM_CLASSES = len(CLASS_NAMES) | |
| # Determine hardware device (GPU or CPU) | |
| def get_device() -> torch.device: | |
| return torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| # Define standard transformations for input images | |
| def get_transform() -> transforms.Compose: | |
| return transforms.Compose([ | |
| transforms.Resize((512, 512)), | |
| transforms.ToTensor(), | |
| transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) | |
| ]) | |
| # Convert uploaded image bytes to PIL format | |
| def load_image(image_bytes: bytes) -> Image.Image: | |
| return Image.open(io.BytesIO(image_bytes)).convert("RGB") | |
| # Load the model architecture and pretrained weights | |
| def build_model(device: torch.device) -> torch.nn.Module: | |
| if not MODEL_PATH.exists(): | |
| raise FileNotFoundError(f"Model file not found: {MODEL_PATH}") | |
| model = models.resnet50(weights=None) | |
| in_features = model.fc.in_features | |
| # Customize the final fully connected layer | |
| model.fc = torch.nn.Sequential( | |
| torch.nn.Dropout(p=0.3), | |
| torch.nn.Linear(in_features=in_features, out_features=NUM_CLASSES) | |
| ) | |
| state_dict = torch.load(MODEL_PATH, map_location=device) | |
| model.load_state_dict(state_dict) | |
| model.to(device) | |
| model.eval() | |
| return model | |
| # Perform inference on the provided image | |
| def predict(image: Image.Image, model: torch.nn.Module, device: torch.device, top_k: int = 5) -> List[Dict[str, Any]]: | |
| transform = get_transform() | |
| tensor = transform(image).unsqueeze(0).to(device) | |
| # Disable gradient calculation for inference | |
| with torch.no_grad(): | |
| outputs = model(tensor) | |
| probabilities = torch.softmax(outputs[0], dim=0) | |
| # Get the top K highest probability classes | |
| top_probs, top_idxs = torch.topk(probabilities, k=min(top_k, NUM_CLASSES)) | |
| predictions = [] | |
| for score, idx in zip(top_probs.tolist(), top_idxs.tolist()): | |
| class_name = CLASS_NAMES[idx] | |
| display_info = CLASS_DISPLAY_NAMES.get(class_name, {"label": class_name.replace("_", " ").title(), "icon": "๐ฝ๏ธ"}) | |
| predictions.append({ | |
| "label": display_info["label"], | |
| "icon": display_info["icon"], | |
| "confidence": float(score * 100), | |
| "index": int(idx) | |
| }) | |
| return predictions |