Spaces:
Runtime error
Runtime error
Download src/data/aptos_dataset.py from mshaiel2004/dr-severity-grading: direct link, hf CLI and curl.
- Browser
- Download file 4.34 kB
-
https://huggingface.co/spaces/mshaiel2004/dr-severity-grading/resolve/main/src/data/aptos_dataset.py
- Command line
-
hf download hf://spaces/mshaiel2004/dr-severity-grading/src/data/aptos_dataset.py
-
curl -L -o aptos_dataset.py https://huggingface.co/spaces/mshaiel2004/dr-severity-grading/resolve/main/src/data/aptos_dataset.py
4.34 kB
| """PyTorch Dataset class for the APTOS 2019 Blindness Detection challenge.""" | |
| import os | |
| from typing import Callable, Dict, Optional, Tuple, Union | |
| import cv2 | |
| import numpy as np | |
| import pandas as pd | |
| import torch | |
| from torch.utils.data import Dataset | |
| from src.data.preprocessing import ben_graham_pipeline | |
| class APTOSDataset(Dataset): | |
| """APTOS 2019 Diabetic Retinopathy dataset.""" | |
| def __init__( | |
| self, | |
| df_or_csv: Union[pd.DataFrame, str], | |
| images_dir: str, | |
| transform: Optional[Callable] = None, | |
| image_size: int = 380, | |
| apply_ben_graham: bool = True, | |
| is_preprocessed: bool = False, | |
| ) -> None: | |
| """Initialize APTOS dataset. | |
| Args: | |
| df_or_csv: DataFrame or path to CSV with columns ['id_code', 'diagnosis']. | |
| images_dir: Path to image directory containing PNG images. | |
| transform: Optional Albumentations transform pipeline. | |
| image_size: Target image dimension for resizing / preprocessing. | |
| apply_ben_graham: Whether to apply Ben Graham preprocessing on the fly. | |
| is_preprocessed: If True, images in images_dir are already preprocessed. | |
| """ | |
| if isinstance(df_or_csv, str): | |
| self.df = pd.read_csv(df_or_csv) | |
| else: | |
| self.df = df_or_csv.reset_index(drop=True) | |
| self.images_dir = images_dir | |
| self.transform = transform | |
| self.image_size = image_size | |
| self.apply_ben_graham = apply_ben_graham and not is_preprocessed | |
| self.is_preprocessed = is_preprocessed | |
| # Standardize column names | |
| if "id_code" in self.df.columns: | |
| self.image_ids = self.df["id_code"].values | |
| elif "image_id" in self.df.columns: | |
| self.image_ids = self.df["image_id"].values | |
| else: | |
| self.image_ids = self.df.iloc[:, 0].values | |
| if "diagnosis" in self.df.columns: | |
| self.labels = self.df["diagnosis"].values.astype(np.int64) | |
| else: | |
| self.labels = self.df.iloc[:, 1].values.astype(np.int64) | |
| def __len__(self) -> int: | |
| return len(self.df) | |
| def __getitem__(self, idx: int) -> Tuple[torch.Tensor, int, str]: | |
| """Fetch image tensor, integer label, and image path. | |
| Returns: | |
| Tuple of (image_tensor, label, image_path). | |
| """ | |
| img_id = str(self.image_ids[idx]) | |
| # Support both raw id and id with extension | |
| if not img_id.endswith((".png", ".jpg", ".jpeg")): | |
| img_filename = f"{img_id}.png" | |
| else: | |
| img_filename = img_id | |
| img_path = os.path.join(self.images_dir, img_filename) | |
| # Fallback to jpg if png not found | |
| if not os.path.exists(img_path): | |
| alt_path = os.path.join(self.images_dir, f"{img_id}.jpg") | |
| if os.path.exists(alt_path): | |
| img_path = alt_path | |
| image = cv2.imread(img_path) | |
| if image is None: | |
| raise FileNotFoundError(f"Failed to load image at {img_path}") | |
| image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) | |
| if self.apply_ben_graham: | |
| image = ben_graham_pipeline(image, image_size=self.image_size) | |
| if self.transform is not None: | |
| augmented = self.transform(image=image) | |
| image_tensor = augmented["image"] | |
| else: | |
| # Fallback simple tensor conversion if no transform provided | |
| image_tensor = torch.from_numpy(image.transpose(2, 0, 1)).float() / 255.0 | |
| label = int(self.labels[idx]) | |
| return image_tensor, label, img_path | |
| def get_class_counts(self) -> Dict[int, int]: | |
| """Compute frequency per severity grade.""" | |
| unique, counts = np.unique(self.labels, return_counts=True) | |
| return dict(zip(unique.tolist(), counts.tolist())) | |
| def get_class_weights(self) -> torch.Tensor: | |
| """Compute balanced inverse-frequency class weights for loss functions. | |
| weight_c = total_samples / (num_classes * count_c) | |
| """ | |
| counts = self.get_class_counts() | |
| num_classes = 5 | |
| total = len(self.labels) | |
| weights = [] | |
| for c in range(num_classes): | |
| c_count = counts.get(c, 1) | |
| weights.append(total / (num_classes * c_count)) | |
| return torch.tensor(weights, dtype=torch.float32) | |