torch_em.data.datasets.medical.csc_fundus_segmentation

The CSC Fundus Segmentation dataset contains annotations for subretinal fluid (SRF) segmentation in fundus photographs of eyes with central serous chorioretinopathy (CSC).

This dataset is located at https://doi.org/10.17632/4k64fwnp4k.3, under the CC BY 4.0 license. The dataset is from the publication https://doi.org/10.1167/tvst.11.2.11. Please cite it if you use this dataset for your research.

The dataset comprises fundus photographs from 194 eyes with CSC, with the SRF area manually segmented by three ophthalmologists (two licensed ophthalmologists and one ophthalmology resident), provided here as grader1, grader2 and grader3. Each image is shipped as a paired image, with the raw fundus photograph on the left half and the corresponding segmentation rendered as a pink overlay on a white background on the right half. We split these paired images here into the raw image and a binarized foreground / background mask.

  1"""The CSC Fundus Segmentation dataset contains annotations for subretinal fluid (SRF)
  2segmentation in fundus photographs of eyes with central serous chorioretinopathy (CSC).
  3
  4This dataset is located at https://doi.org/10.17632/4k64fwnp4k.3, under the CC BY 4.0 license.
  5The dataset is from the publication https://doi.org/10.1167/tvst.11.2.11.
  6Please cite it if you use this dataset for your research.
  7
  8The dataset comprises fundus photographs from 194 eyes with CSC, with the SRF area manually
  9segmented by three ophthalmologists (two licensed ophthalmologists and one ophthalmology
 10resident), provided here as `grader1`, `grader2` and `grader3`. Each image is shipped as a
 11paired image, with the raw fundus photograph on the left half and the corresponding
 12segmentation rendered as a pink overlay on a white background on the right half. We split
 13these paired images here into the raw image and a binarized foreground / background mask.
 14"""
 15
 16import os
 17from glob import glob
 18from pathlib import Path
 19from natsort import natsorted
 20from typing import Union, Tuple, Literal, List
 21
 22import numpy as np
 23import imageio.v3 as imageio
 24
 25from torch.utils.data import Dataset, DataLoader
 26
 27import torch_em
 28
 29from .. import util
 30
 31
 32URL = "https://data.mendeley.com/public-files/datasets/4k64fwnp4k/files/{}/file_downloaded"
 33FNAMES_TO_IDS = {
 34    "Grader_1.zip": "e53c7210-9e09-4e0e-8f36-87103128fe38",
 35    "Grader_2.zip": "2b551de5-1855-47cf-88cf-6f55eb26d001",
 36    "Grader_3.zip": "fd6214bf-a33a-413c-948c-7430beebe286",
 37}
 38CHECKSUMS = {
 39    "Grader_1.zip": "e9820a9cd2b13490f972e2c58b316b0ab62dccef00564d1c1cc768e55658b435",
 40    "Grader_2.zip": "78ca3f61b58cf85ef2619b031c6c4a4a95f9af7f8bcbc567e9d295f6ead1120e",
 41    "Grader_3.zip": "a2c9699125c056cb8f46d073a72f93eec1e3a57b77370eaa15ed1a3475dba07a",
 42}
 43
 44
 45def get_csc_fundus_segmentation_data(
 46    path: Union[os.PathLike, str], grader: Literal["grader1", "grader2", "grader3"], download: bool = False
 47) -> str:
 48    """Download the CSC Fundus Segmentation dataset.
 49
 50    Args:
 51        path: Filepath to a folder where the data is downloaded for further processing.
 52        grader: The choice of annotator whose segmentations are used.
 53        download: Whether to download the data if it is not present.
 54
 55    Returns:
 56        Filepath where the data is downloaded.
 57    """
 58    assert grader in ["grader1", "grader2", "grader3"], f"'{grader}' is not a valid grader choice."
 59
 60    fname = f"Grader_{grader[-1]}.zip"
 61    data_dir = os.path.join(path, Path(fname).stem)
 62    if os.path.exists(data_dir):
 63        return data_dir
 64
 65    os.makedirs(path, exist_ok=True)
 66
 67    zip_path = os.path.join(path, fname)
 68    util.download_source(
 69        path=zip_path, url=URL.format(FNAMES_TO_IDS[fname]), download=download, checksum=CHECKSUMS[fname]
 70    )
 71    util.unzip(zip_path=zip_path, dst=data_dir)
 72
 73    return data_dir
 74
 75
 76def _split_paired_image(paired_path, image_dir, gt_dir):
 77    fname = Path(paired_path).stem
 78    image_path = os.path.join(image_dir, f"{fname}.png")
 79    gt_path = os.path.join(gt_dir, f"{fname}.png")
 80
 81    if os.path.exists(image_path) and os.path.exists(gt_path):
 82        return image_path, gt_path
 83
 84    os.makedirs(image_dir, exist_ok=True)
 85    os.makedirs(gt_dir, exist_ok=True)
 86
 87    paired_image = imageio.imread(paired_path)
 88    width = paired_image.shape[1]
 89    half = width // 2
 90
 91    raw = paired_image[:, :half]
 92
 93    # The right half renders the SRF segmentation as a pink overlay on a white background.
 94    # The seam between the concatenated halves introduces a single column of black JPEG
 95    # compression bleed at the left edge of the mask half, which we remove before thresholding.
 96    mask_rgb = paired_image[:, half:].copy()
 97    mask_rgb[:, 0] = 255
 98    mask = (np.abs(255 - mask_rgb.astype(int)).sum(axis=-1) > 30).astype("uint8")
 99
100    imageio.imwrite(image_path, raw)
101    imageio.imwrite(gt_path, mask)
102
103    return image_path, gt_path
104
105
106def get_csc_fundus_segmentation_paths(
107    path: Union[os.PathLike, str],
108    split: Literal["train", "test"],
109    grader: Literal["grader1", "grader2", "grader3"] = "grader1",
110    download: bool = False,
111) -> Tuple[List[str], List[str]]:
112    """Get paths to the CSC Fundus Segmentation data.
113
114    Args:
115        path: Filepath to a folder where the data is downloaded for further processing.
116        split: The choice of data split.
117        grader: The choice of annotator whose segmentations are used.
118        download: Whether to download the data if it is not present.
119
120    Returns:
121        List of filepaths for the image data.
122        List of filepaths for the label data.
123    """
124    assert split in ["train", "test"], f"'{split}' is not a valid split."
125
126    data_dir = get_csc_fundus_segmentation_data(path=path, grader=grader, download=download)
127
128    paired_paths = natsorted(glob(os.path.join(data_dir, split, "*.jpg")))
129    assert len(paired_paths) > 0, f"No images found for split '{split}' and grader '{grader}'."
130
131    image_dir = os.path.join(data_dir, "preprocessed", split, "images")
132    gt_dir = os.path.join(data_dir, "preprocessed", split, "masks")
133
134    image_paths, gt_paths = [], []
135    for paired_path in paired_paths:
136        image_path, gt_path = _split_paired_image(paired_path, image_dir, gt_dir)
137        image_paths.append(image_path)
138        gt_paths.append(gt_path)
139
140    return image_paths, gt_paths
141
142
143def get_csc_fundus_segmentation_dataset(
144    path: Union[os.PathLike, str],
145    patch_shape: Tuple[int, int],
146    split: Literal["train", "test"],
147    grader: Literal["grader1", "grader2", "grader3"] = "grader1",
148    resize_inputs: bool = False,
149    download: bool = False,
150    **kwargs
151) -> Dataset:
152    """Get the CSC Fundus Segmentation dataset for subretinal fluid segmentation in fundus photographs.
153
154    Args:
155        path: Filepath to a folder where the downloaded data will be saved.
156        patch_shape: The patch shape to use for training.
157        split: The choice of data split.
158        grader: The choice of annotator whose segmentations are used.
159        resize_inputs: Whether to resize the inputs to the expected patch shape.
160        download: Whether to download the data if it is not present.
161        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
162
163    Returns:
164        The segmentation dataset.
165    """
166    image_paths, gt_paths = get_csc_fundus_segmentation_paths(
167        path=path, split=split, grader=grader, download=download
168    )
169
170    if resize_inputs:
171        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": True}
172        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
173            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs,
174        )
175
176    return torch_em.default_segmentation_dataset(
177        raw_paths=image_paths,
178        raw_key=None,
179        label_paths=gt_paths,
180        label_key=None,
181        patch_shape=patch_shape,
182        is_seg_dataset=False,
183        **kwargs
184    )
185
186
187def get_csc_fundus_segmentation_loader(
188    path: Union[os.PathLike, str],
189    batch_size: int,
190    patch_shape: Tuple[int, int],
191    split: Literal["train", "test"],
192    grader: Literal["grader1", "grader2", "grader3"] = "grader1",
193    resize_inputs: bool = False,
194    download: bool = False,
195    **kwargs
196) -> DataLoader:
197    """Get the CSC Fundus Segmentation dataloader for subretinal fluid segmentation in fundus photographs.
198
199    Args:
200        path: Filepath to a folder where the downloaded data will be saved.
201        batch_size: The batch size for training.
202        patch_shape: The patch shape to use for training.
203        split: The choice of data split.
204        grader: The choice of annotator whose segmentations are used.
205        resize_inputs: Whether to resize the inputs to the expected patch shape.
206        download: Whether to download the data if it is not present.
207        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
208
209    Returns:
210        The DataLoader.
211    """
212    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
213    dataset = get_csc_fundus_segmentation_dataset(
214        path, patch_shape, split, grader, resize_inputs, download, **ds_kwargs
215    )
216    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URL = 'https://data.mendeley.com/public-files/datasets/4k64fwnp4k/files/{}/file_downloaded'
FNAMES_TO_IDS = {'Grader_1.zip': 'e53c7210-9e09-4e0e-8f36-87103128fe38', 'Grader_2.zip': '2b551de5-1855-47cf-88cf-6f55eb26d001', 'Grader_3.zip': 'fd6214bf-a33a-413c-948c-7430beebe286'}
CHECKSUMS = {'Grader_1.zip': 'e9820a9cd2b13490f972e2c58b316b0ab62dccef00564d1c1cc768e55658b435', 'Grader_2.zip': '78ca3f61b58cf85ef2619b031c6c4a4a95f9af7f8bcbc567e9d295f6ead1120e', 'Grader_3.zip': 'a2c9699125c056cb8f46d073a72f93eec1e3a57b77370eaa15ed1a3475dba07a'}
def get_csc_fundus_segmentation_data( path: Union[os.PathLike, str], grader: Literal['grader1', 'grader2', 'grader3'], download: bool = False) -> str:
46def get_csc_fundus_segmentation_data(
47    path: Union[os.PathLike, str], grader: Literal["grader1", "grader2", "grader3"], download: bool = False
48) -> str:
49    """Download the CSC Fundus Segmentation dataset.
50
51    Args:
52        path: Filepath to a folder where the data is downloaded for further processing.
53        grader: The choice of annotator whose segmentations are used.
54        download: Whether to download the data if it is not present.
55
56    Returns:
57        Filepath where the data is downloaded.
58    """
59    assert grader in ["grader1", "grader2", "grader3"], f"'{grader}' is not a valid grader choice."
60
61    fname = f"Grader_{grader[-1]}.zip"
62    data_dir = os.path.join(path, Path(fname).stem)
63    if os.path.exists(data_dir):
64        return data_dir
65
66    os.makedirs(path, exist_ok=True)
67
68    zip_path = os.path.join(path, fname)
69    util.download_source(
70        path=zip_path, url=URL.format(FNAMES_TO_IDS[fname]), download=download, checksum=CHECKSUMS[fname]
71    )
72    util.unzip(zip_path=zip_path, dst=data_dir)
73
74    return data_dir

Download the CSC Fundus Segmentation dataset.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • grader: The choice of annotator whose segmentations are used.
  • download: Whether to download the data if it is not present.
Returns:

Filepath where the data is downloaded.

def get_csc_fundus_segmentation_paths( path: Union[os.PathLike, str], split: Literal['train', 'test'], grader: Literal['grader1', 'grader2', 'grader3'] = 'grader1', download: bool = False) -> Tuple[List[str], List[str]]:
107def get_csc_fundus_segmentation_paths(
108    path: Union[os.PathLike, str],
109    split: Literal["train", "test"],
110    grader: Literal["grader1", "grader2", "grader3"] = "grader1",
111    download: bool = False,
112) -> Tuple[List[str], List[str]]:
113    """Get paths to the CSC Fundus Segmentation data.
114
115    Args:
116        path: Filepath to a folder where the data is downloaded for further processing.
117        split: The choice of data split.
118        grader: The choice of annotator whose segmentations are used.
119        download: Whether to download the data if it is not present.
120
121    Returns:
122        List of filepaths for the image data.
123        List of filepaths for the label data.
124    """
125    assert split in ["train", "test"], f"'{split}' is not a valid split."
126
127    data_dir = get_csc_fundus_segmentation_data(path=path, grader=grader, download=download)
128
129    paired_paths = natsorted(glob(os.path.join(data_dir, split, "*.jpg")))
130    assert len(paired_paths) > 0, f"No images found for split '{split}' and grader '{grader}'."
131
132    image_dir = os.path.join(data_dir, "preprocessed", split, "images")
133    gt_dir = os.path.join(data_dir, "preprocessed", split, "masks")
134
135    image_paths, gt_paths = [], []
136    for paired_path in paired_paths:
137        image_path, gt_path = _split_paired_image(paired_path, image_dir, gt_dir)
138        image_paths.append(image_path)
139        gt_paths.append(gt_path)
140
141    return image_paths, gt_paths

Get paths to the CSC Fundus Segmentation data.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • split: The choice of data split.
  • grader: The choice of annotator whose segmentations are used.
  • 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_csc_fundus_segmentation_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int], split: Literal['train', 'test'], grader: Literal['grader1', 'grader2', 'grader3'] = 'grader1', resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
144def get_csc_fundus_segmentation_dataset(
145    path: Union[os.PathLike, str],
146    patch_shape: Tuple[int, int],
147    split: Literal["train", "test"],
148    grader: Literal["grader1", "grader2", "grader3"] = "grader1",
149    resize_inputs: bool = False,
150    download: bool = False,
151    **kwargs
152) -> Dataset:
153    """Get the CSC Fundus Segmentation dataset for subretinal fluid segmentation in fundus photographs.
154
155    Args:
156        path: Filepath to a folder where the downloaded data will be saved.
157        patch_shape: The patch shape to use for training.
158        split: The choice of data split.
159        grader: The choice of annotator whose segmentations are used.
160        resize_inputs: Whether to resize the inputs to the expected patch shape.
161        download: Whether to download the data if it is not present.
162        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
163
164    Returns:
165        The segmentation dataset.
166    """
167    image_paths, gt_paths = get_csc_fundus_segmentation_paths(
168        path=path, split=split, grader=grader, download=download
169    )
170
171    if resize_inputs:
172        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": True}
173        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
174            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs,
175        )
176
177    return torch_em.default_segmentation_dataset(
178        raw_paths=image_paths,
179        raw_key=None,
180        label_paths=gt_paths,
181        label_key=None,
182        patch_shape=patch_shape,
183        is_seg_dataset=False,
184        **kwargs
185    )

Get the CSC Fundus Segmentation dataset for subretinal fluid segmentation in fundus photographs.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • patch_shape: The patch shape to use for training.
  • split: The choice of data split.
  • grader: The choice of annotator whose segmentations are used.
  • resize_inputs: Whether to resize the inputs to the expected 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_csc_fundus_segmentation_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, int], split: Literal['train', 'test'], grader: Literal['grader1', 'grader2', 'grader3'] = 'grader1', resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
188def get_csc_fundus_segmentation_loader(
189    path: Union[os.PathLike, str],
190    batch_size: int,
191    patch_shape: Tuple[int, int],
192    split: Literal["train", "test"],
193    grader: Literal["grader1", "grader2", "grader3"] = "grader1",
194    resize_inputs: bool = False,
195    download: bool = False,
196    **kwargs
197) -> DataLoader:
198    """Get the CSC Fundus Segmentation dataloader for subretinal fluid segmentation in fundus photographs.
199
200    Args:
201        path: Filepath to a folder where the downloaded data will be saved.
202        batch_size: The batch size for training.
203        patch_shape: The patch shape to use for training.
204        split: The choice of data split.
205        grader: The choice of annotator whose segmentations are used.
206        resize_inputs: Whether to resize the inputs to the expected patch shape.
207        download: Whether to download the data if it is not present.
208        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
209
210    Returns:
211        The DataLoader.
212    """
213    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
214    dataset = get_csc_fundus_segmentation_dataset(
215        path, patch_shape, split, grader, resize_inputs, download, **ds_kwargs
216    )
217    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the CSC Fundus Segmentation dataloader for subretinal fluid segmentation in fundus photographs.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • batch_size: The batch size for training.
  • patch_shape: The patch shape to use for training.
  • split: The choice of data split.
  • grader: The choice of annotator whose segmentations are used.
  • resize_inputs: Whether to resize the inputs to the expected 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.