import torch import torch.nn as nn import timm from torchvision import transforms from PIL import Image from fastapi import FastAPI, UploadFile, File import io import numpy as np app = FastAPI() class SingleSwinGAPGMP(nn.Module): def __init__(self, model_name="swin_tiny_patch4_window7_224", num_classes=3, pretrained=False, drop_path_rate=0.0): super(SingleSwinGAPGMP, self).__init__() self.backbone = timm.create_model( model_name, pretrained=pretrained, num_classes=0, drop_path_rate=drop_path_rate ) self.num_features = self.backbone.num_features self.head = nn.Linear(self.num_features * 2, num_classes) def extract_features(self, x): x = self.backbone.forward_features(x) if hasattr(self.backbone, 'norm'): x = self.backbone.norm(x) if x.dim() == 4: gap = x.mean(dim=[1, 2]) gmp = x.amax(dim=[1, 2]) elif x.dim() == 3: gap = x.mean(dim=1) gmp = x.amax(dim=1) else: gap = x.mean(dim=[-2, -1]) gmp = x.amax(dim=[-2, -1]) return torch.cat([gap, gmp], dim=1) def forward(self, x): features = self.extract_features(x) logits = self.head(features) return logits device = torch.device("cuda" if torch.cuda.is_available() else "cpu") MODEL_NAME = "swin_tiny_patch4_window7_224" num_classes = 3 labels = ["Highly Fresh", "Fresh", "Not Fresh"] model = SingleSwinGAPGMP(model_name=MODEL_NAME, num_classes=num_classes, pretrained=False) model.load_state_dict(torch.load("fish_model.pth", map_location=device)) model.to(device) model.eval() preprocess = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) @app.post("/predict") async def predict(file: UploadFile = File(...)): img_bytes = await file.read() image = Image.open(io.BytesIO(img_bytes)).convert('RGB') input_tensor = preprocess(image).unsqueeze(0).to(device) with torch.no_grad(): outputs_orig = model(input_tensor) outputs_hflip = model(torch.flip(input_tensor, dims=[3])) outputs_vflip = model(torch.flip(input_tensor, dims=[2])) outputs_hvflip = model(torch.flip(input_tensor, dims=[2, 3])) outputs_avg = (outputs_orig + outputs_hflip + outputs_vflip + outputs_hvflip) / 4.0 probabilities = torch.nn.functional.softmax(outputs_avg[0], dim=0) conf, idx = torch.max(probabilities, 0) return { "label": labels[idx.item()], "confidence": float(conf.item()), "all_probs": {labels[i]: float(probabilities[i]) for i in range(len(labels))} } @app.get("/") def health_check(): return {"status": "ready", "model": MODEL_NAME}