Spaces:
Runtime error
Runtime error
| from __future__ import annotations | |
| from pathlib import Path | |
| import cv2 | |
| import numpy as np | |
| import torch | |
| import torch.nn as nn | |
| from torchvision import models, transforms | |
| class DiseaseClassifier: | |
| def __init__(self, checkpoint_path: Path, device: str = "auto", image_size: int = 320) -> None: | |
| self.device = self._resolve_device(device) | |
| self.checkpoint_path = Path(checkpoint_path) | |
| checkpoint = torch.load( | |
| self.checkpoint_path, | |
| map_location=self.device, | |
| weights_only=False, | |
| ) | |
| self.model_name = checkpoint.get("model_name", "efficientnet_v2_s") | |
| self.class_names = list(checkpoint["class_names"]) | |
| self.image_size = int(checkpoint.get("img_size", image_size)) | |
| self.model = self._build_model(self.model_name, len(self.class_names)) | |
| self.model.load_state_dict(checkpoint["model_state"]) | |
| self.model.to(self.device) | |
| self.model.eval() | |
| self.transform = transforms.Compose( | |
| [ | |
| transforms.ToPILImage(), | |
| transforms.Resize(int(self.image_size * 1.12)), | |
| transforms.CenterCrop(self.image_size), | |
| transforms.ToTensor(), | |
| transforms.Normalize( | |
| mean=[0.485, 0.456, 0.406], | |
| std=[0.229, 0.224, 0.225], | |
| ), | |
| ] | |
| ) | |
| def _resolve_device(device: str) -> str: | |
| if device == "auto": | |
| return "cuda" if torch.cuda.is_available() else "cpu" | |
| return device | |
| def _build_model(model_name: str, num_classes: int) -> nn.Module: | |
| if model_name == "efficientnet_v2_s": | |
| model = models.efficientnet_v2_s(weights=None) | |
| in_features = model.classifier[1].in_features | |
| model.classifier[1] = nn.Linear(in_features, num_classes) | |
| return model | |
| if model_name == "convnext_tiny": | |
| model = models.convnext_tiny(weights=None) | |
| in_features = model.classifier[2].in_features | |
| model.classifier[2] = nn.Linear(in_features, num_classes) | |
| return model | |
| raise ValueError(f"Unsupported classifier model: {model_name}") | |
| def predict(self, crop_bgr: np.ndarray) -> dict: | |
| if crop_bgr.size == 0: | |
| raise ValueError("Empty crop passed to disease classifier") | |
| crop_rgb = cv2.cvtColor(crop_bgr, cv2.COLOR_BGR2RGB) | |
| tensor = self.transform(crop_rgb).unsqueeze(0).to(self.device) | |
| logits = self.model(tensor) | |
| probs = torch.softmax(logits, dim=1)[0].detach().cpu().numpy() | |
| idx = int(np.argmax(probs)) | |
| return { | |
| "label": self.class_names[idx], | |
| "confidence": float(probs[idx]), | |
| "probabilities": { | |
| class_name: float(probs[i]) | |
| for i, class_name in enumerate(self.class_names) | |
| }, | |
| } | |