torch_em.data.datasets.medical.orvs

The ORVS dataset contains annotations for retinal vessel segmentation in fundus images.

It comprises 49 high-resolution fundus images (42 for training and 7 for testing) collected at a clinic in Calgary, Canada, with vessel masks manually traced by a trained expert.

NOTE: The original repository (AbdullahSarhan/ICPRVessels) referenced by the publication has been removed from GitHub. This module downloads the dataset from a public fork of it instead: https://github.com/hitszsyl/ICPRVessels (no explicit license is stated in the repository). This dataset is from the publication https://doi.org/10.48550/arXiv.2012.09250. Please cite it if you use this dataset in your research.

NOTE: The labels are stored as JPEG images, so the (originally binary) vessel masks have lossy compression artifacts near the mask boundaries. This module binarizes them with a fixed intensity threshold when caching the labels to disk.

  1"""The ORVS dataset contains annotations for retinal vessel segmentation in fundus images.
  2
  3It comprises 49 high-resolution fundus images (42 for training and 7 for testing) collected
  4at a clinic in Calgary, Canada, with vessel masks manually traced by a trained expert.
  5
  6NOTE: The original repository (AbdullahSarhan/ICPRVessels) referenced by the publication has
  7been removed from GitHub. This module downloads the dataset from a public fork of it instead:
  8https://github.com/hitszsyl/ICPRVessels (no explicit license is stated in the repository).
  9This dataset is from the publication https://doi.org/10.48550/arXiv.2012.09250.
 10Please cite it if you use this dataset in your research.
 11
 12NOTE: The labels are stored as JPEG images, so the (originally binary) vessel masks have lossy
 13compression artifacts near the mask boundaries. This module binarizes them with a fixed
 14intensity threshold when caching the labels to disk.
 15"""
 16
 17import os
 18from glob import glob
 19from tqdm import tqdm
 20from natsort import natsorted
 21from typing import Union, Tuple, Literal, List
 22
 23import numpy as np
 24import imageio.v3 as imageio
 25
 26from torch.utils.data import Dataset, DataLoader
 27
 28import torch_em
 29
 30from .. import util
 31
 32
 33URL = "https://github.com/hitszsyl/ICPRVessels/archive/421886be66a400d88ef6fe9c9e047d586486eee0.zip"
 34CHECKSUM = "45f8c7fc637c6e302d2cf6c71251aeb53d0a08f4411d5e60bd7ae6753acdb24b"
 35
 36SPLIT_DIRS = {"train": "Train", "test": "Test"}
 37
 38
 39def get_orvs_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 40    """Download the ORVS dataset.
 41
 42    Args:
 43        path: Filepath to a folder where the data is downloaded for further processing.
 44        download: Whether to download the data if it is not present.
 45
 46    Returns:
 47        Filepath to the folder with the downloaded ORVS images and vessel annotations.
 48    """
 49    data_dir = os.path.join(
 50        path, "ICPRVessels-421886be66a400d88ef6fe9c9e047d586486eee0", "Vessels-Datasets", "ORVS"
 51    )
 52    if os.path.exists(data_dir):
 53        return data_dir
 54
 55    os.makedirs(path, exist_ok=True)
 56
 57    zip_path = os.path.join(path, "ICPRVessels.zip")
 58    util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM)
 59    util.unzip(zip_path=zip_path, dst=path)
 60
 61    return data_dir
 62
 63
 64def get_orvs_paths(
 65    path: Union[os.PathLike, str], split: Literal["train", "test"] = "train", download: bool = False
 66) -> Tuple[List[str], List[str]]:
 67    """Get paths to the ORVS data.
 68
 69    Args:
 70        path: Filepath to a folder where the data is downloaded for further processing.
 71        split: The choice of data split.
 72        download: Whether to download the data if it is not present.
 73
 74    Returns:
 75        List of filepaths for the image data.
 76        List of filepaths for the label data.
 77    """
 78    data_dir = get_orvs_data(path=path, download=download)
 79
 80    if split not in SPLIT_DIRS:
 81        raise ValueError(f"'{split}' is not a valid split.")
 82
 83    split_dir = os.path.join(data_dir, SPLIT_DIRS[split], "Original")
 84    image_paths = natsorted(glob(os.path.join(split_dir, "Images", "*.jpg")))
 85
 86    label_dir = os.path.join(split_dir, "Labels_binary")
 87    os.makedirs(label_dir, exist_ok=True)
 88
 89    gt_paths = []
 90    for image_path in tqdm(image_paths, desc="Preprocessing ORVS labels"):
 91        fname = os.path.splitext(os.path.basename(image_path))[0]
 92        gt_path = os.path.join(label_dir, f"{fname}.tif")
 93        gt_paths.append(gt_path)
 94        if os.path.exists(gt_path):
 95            continue
 96
 97        label_path = os.path.join(split_dir, "Labels", f"{fname}.jpg")
 98        label = imageio.imread(label_path)
 99        label = (np.asarray(label) > 127).astype("uint8")
100        if label.ndim == 3:
101            label = label.max(axis=-1)
102        imageio.imwrite(gt_path, label, compression="zlib")
103
104    return image_paths, gt_paths
105
106
107def get_orvs_dataset(
108    path: Union[os.PathLike, str],
109    patch_shape: Tuple[int, int],
110    split: Literal["train", "test"] = "train",
111    resize_inputs: bool = False,
112    download: bool = False,
113    **kwargs
114) -> Dataset:
115    """Get the ORVS dataset for retinal vessel segmentation in fundus images.
116
117    Args:
118        path: Filepath to a folder where the data is downloaded for further processing.
119        patch_shape: The patch shape to use for training.
120        split: The choice of data split.
121        resize_inputs: Whether to resize the inputs to the patch shape.
122        download: Whether to download the data if it is not present.
123        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
124
125    Returns:
126        The segmentation dataset.
127    """
128    image_paths, gt_paths = get_orvs_paths(path, split, download)
129
130    if resize_inputs:
131        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": True}
132        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
133            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
134        )
135
136    return torch_em.default_segmentation_dataset(
137        raw_paths=image_paths,
138        raw_key=None,
139        label_paths=gt_paths,
140        label_key=None,
141        patch_shape=patch_shape,
142        is_seg_dataset=False,
143        **kwargs
144    )
145
146
147def get_orvs_loader(
148    path: Union[os.PathLike, str],
149    batch_size: int,
150    patch_shape: Tuple[int, int],
151    split: Literal["train", "test"] = "train",
152    resize_inputs: bool = False,
153    download: bool = False,
154    **kwargs
155) -> DataLoader:
156    """Get the ORVS dataloader for retinal vessel segmentation in fundus images.
157
158    Args:
159        path: Filepath to a folder where the data is downloaded for further processing.
160        batch_size: The batch size for training.
161        patch_shape: The patch shape to use for training.
162        split: The choice of data split.
163        resize_inputs: Whether to resize the inputs to the patch shape.
164        download: Whether to download the data if it is not present.
165        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
166
167    Returns:
168        The DataLoader.
169    """
170    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
171    dataset = get_orvs_dataset(path, patch_shape, split, resize_inputs, download, **ds_kwargs)
172    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URL = 'https://github.com/hitszsyl/ICPRVessels/archive/421886be66a400d88ef6fe9c9e047d586486eee0.zip'
CHECKSUM = '45f8c7fc637c6e302d2cf6c71251aeb53d0a08f4411d5e60bd7ae6753acdb24b'
SPLIT_DIRS = {'train': 'Train', 'test': 'Test'}
def get_orvs_data(path: Union[os.PathLike, str], download: bool = False) -> str:
40def get_orvs_data(path: Union[os.PathLike, str], download: bool = False) -> str:
41    """Download the ORVS dataset.
42
43    Args:
44        path: Filepath to a folder where the data is downloaded for further processing.
45        download: Whether to download the data if it is not present.
46
47    Returns:
48        Filepath to the folder with the downloaded ORVS images and vessel annotations.
49    """
50    data_dir = os.path.join(
51        path, "ICPRVessels-421886be66a400d88ef6fe9c9e047d586486eee0", "Vessels-Datasets", "ORVS"
52    )
53    if os.path.exists(data_dir):
54        return data_dir
55
56    os.makedirs(path, exist_ok=True)
57
58    zip_path = os.path.join(path, "ICPRVessels.zip")
59    util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM)
60    util.unzip(zip_path=zip_path, dst=path)
61
62    return data_dir

Download the ORVS dataset.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • download: Whether to download the data if it is not present.
Returns:

Filepath to the folder with the downloaded ORVS images and vessel annotations.

def get_orvs_paths( path: Union[os.PathLike, str], split: Literal['train', 'test'] = 'train', download: bool = False) -> Tuple[List[str], List[str]]:
 65def get_orvs_paths(
 66    path: Union[os.PathLike, str], split: Literal["train", "test"] = "train", download: bool = False
 67) -> Tuple[List[str], List[str]]:
 68    """Get paths to the ORVS data.
 69
 70    Args:
 71        path: Filepath to a folder where the data is downloaded for further processing.
 72        split: The choice of data split.
 73        download: Whether to download the data if it is not present.
 74
 75    Returns:
 76        List of filepaths for the image data.
 77        List of filepaths for the label data.
 78    """
 79    data_dir = get_orvs_data(path=path, download=download)
 80
 81    if split not in SPLIT_DIRS:
 82        raise ValueError(f"'{split}' is not a valid split.")
 83
 84    split_dir = os.path.join(data_dir, SPLIT_DIRS[split], "Original")
 85    image_paths = natsorted(glob(os.path.join(split_dir, "Images", "*.jpg")))
 86
 87    label_dir = os.path.join(split_dir, "Labels_binary")
 88    os.makedirs(label_dir, exist_ok=True)
 89
 90    gt_paths = []
 91    for image_path in tqdm(image_paths, desc="Preprocessing ORVS labels"):
 92        fname = os.path.splitext(os.path.basename(image_path))[0]
 93        gt_path = os.path.join(label_dir, f"{fname}.tif")
 94        gt_paths.append(gt_path)
 95        if os.path.exists(gt_path):
 96            continue
 97
 98        label_path = os.path.join(split_dir, "Labels", f"{fname}.jpg")
 99        label = imageio.imread(label_path)
100        label = (np.asarray(label) > 127).astype("uint8")
101        if label.ndim == 3:
102            label = label.max(axis=-1)
103        imageio.imwrite(gt_path, label, compression="zlib")
104
105    return image_paths, gt_paths

Get paths to the ORVS data.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • split: The choice of data split.
  • download: Whether to download the data if it is not present.
Returns:

List of filepaths for the image data. List of filepaths for the label data.

def get_orvs_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int], split: Literal['train', 'test'] = 'train', resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
108def get_orvs_dataset(
109    path: Union[os.PathLike, str],
110    patch_shape: Tuple[int, int],
111    split: Literal["train", "test"] = "train",
112    resize_inputs: bool = False,
113    download: bool = False,
114    **kwargs
115) -> Dataset:
116    """Get the ORVS dataset for retinal vessel segmentation in fundus images.
117
118    Args:
119        path: Filepath to a folder where the data is downloaded for further processing.
120        patch_shape: The patch shape to use for training.
121        split: The choice of data split.
122        resize_inputs: Whether to resize the inputs to the patch shape.
123        download: Whether to download the data if it is not present.
124        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
125
126    Returns:
127        The segmentation dataset.
128    """
129    image_paths, gt_paths = get_orvs_paths(path, split, download)
130
131    if resize_inputs:
132        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": True}
133        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
134            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
135        )
136
137    return torch_em.default_segmentation_dataset(
138        raw_paths=image_paths,
139        raw_key=None,
140        label_paths=gt_paths,
141        label_key=None,
142        patch_shape=patch_shape,
143        is_seg_dataset=False,
144        **kwargs
145    )

Get the ORVS dataset for retinal vessel segmentation in fundus images.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • patch_shape: The patch shape to use for training.
  • split: The choice of data split.
  • resize_inputs: Whether to resize the inputs to the patch shape.
  • download: Whether to download the data if it is not present.
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset.
Returns:

The segmentation dataset.

def get_orvs_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, int], split: Literal['train', 'test'] = 'train', resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
148def get_orvs_loader(
149    path: Union[os.PathLike, str],
150    batch_size: int,
151    patch_shape: Tuple[int, int],
152    split: Literal["train", "test"] = "train",
153    resize_inputs: bool = False,
154    download: bool = False,
155    **kwargs
156) -> DataLoader:
157    """Get the ORVS dataloader for retinal vessel segmentation in fundus images.
158
159    Args:
160        path: Filepath to a folder where the data is downloaded for further processing.
161        batch_size: The batch size for training.
162        patch_shape: The patch shape to use for training.
163        split: The choice of data split.
164        resize_inputs: Whether to resize the inputs to the patch shape.
165        download: Whether to download the data if it is not present.
166        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
167
168    Returns:
169        The DataLoader.
170    """
171    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
172    dataset = get_orvs_dataset(path, patch_shape, split, resize_inputs, download, **ds_kwargs)
173    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the ORVS dataloader for retinal vessel segmentation in fundus images.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • batch_size: The batch size for training.
  • patch_shape: The patch shape to use for training.
  • split: The choice of data split.
  • resize_inputs: Whether to resize the inputs to the patch shape.
  • download: Whether to download the data if it is not present.
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset or for the PyTorch DataLoader.
Returns:

The DataLoader.