torch_em.data.datasets.medical.tear_meniscus

The Tear Meniscus dataset contains annotations for pixel-level segmentation of the lower tear meniscus in external-eye colour and infrared images.

The dataset comprises 1693 colour images and 1739 infrared images, collected from five clinical centres across China. The initial annotations were produced with a human-computer-interactive approach and were subsequently reviewed and corrected by a senior ophthalmologist, so the ground truth is clinician-reviewed. The shipped label maps are binary (0 = background, 1 = tear meniscus); some of them are stored as 3-channel (RGB) images with all channels holding the same grayscale value, which this module reduces to a single-channel binary mask. The raw images also mix formats (RGBA, RGB and grayscale across the different centres), which this module normalizes to 3-channel RGB.

The dataset is located at https://doi.org/10.6084/m9.figshare.28650536.v2 (CC BY 4.0).

This dataset is from the publication https://doi.org/10.1038/s41597-025-06460-0. Please cite it if you use this dataset in your research.

  1"""The Tear Meniscus dataset contains annotations for pixel-level segmentation of the lower tear meniscus
  2in external-eye colour and infrared images.
  3
  4The dataset comprises 1693 colour images and 1739 infrared images, collected from five clinical centres
  5across China. The initial annotations were produced with a human-computer-interactive approach and were
  6subsequently reviewed and corrected by a senior ophthalmologist, so the ground truth is clinician-reviewed.
  7The shipped label maps are binary (0 = background, 1 = tear meniscus); some of them are stored as
  83-channel (RGB) images with all channels holding the same grayscale value, which this module reduces to
  9a single-channel binary mask. The raw images also mix formats (RGBA, RGB and grayscale across the
 10different centres), which this module normalizes to 3-channel RGB.
 11
 12The dataset is located at https://doi.org/10.6084/m9.figshare.28650536.v2 (CC BY 4.0).
 13
 14This dataset is from the publication https://doi.org/10.1038/s41597-025-06460-0.
 15Please cite it if you use this dataset in your research.
 16"""
 17
 18import os
 19from glob import glob
 20from tqdm import tqdm
 21from pathlib import Path
 22from natsort import natsorted
 23from typing import Union, Tuple, Literal, List
 24
 25import numpy as np
 26import imageio.v3 as imageio
 27
 28from torch.utils.data import Dataset, DataLoader
 29
 30import torch_em
 31
 32from .. import util
 33
 34
 35URL = "https://ndownloader.figshare.com/files/56406179"
 36CHECKSUM = "b4614214e69098b09160f4713c5e0f11fcd6a7a158a5217ca9cd15ba2687f170"
 37
 38LABEL_IDS = {"background": 0, "tear_meniscus": 1}
 39
 40MODALITIES = {"colour": "Colour", "infrared": "Infrared"}
 41
 42
 43def get_tear_meniscus_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 44    """Download the Tear Meniscus dataset.
 45
 46    Args:
 47        path: Filepath to a folder where the data is downloaded for further processing.
 48        download: Whether to download the data if it is not present.
 49
 50    Returns:
 51        Filepath where the data is downloaded.
 52    """
 53    data_dir = os.path.join(path, "Open DataSet2")
 54    if os.path.exists(data_dir):
 55        return data_dir
 56
 57    os.makedirs(path, exist_ok=True)
 58
 59    zip_path = os.path.join(path, "Open DataSet.zip")
 60    util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM)
 61    util.unzip(zip_path=zip_path, dst=path)
 62
 63    return data_dir
 64
 65
 66def _preprocess_images(raw_paths: List[str]) -> List[str]:
 67    """Normalize the (RGBA / RGB / grayscale) raw images to 3-channel RGB."""
 68    preprocessed_paths = []
 69    for raw_path in tqdm(raw_paths, desc="Preprocessing images"):
 70        parent = Path(raw_path).parent.parent
 71        preprocessed_dir = os.path.join(parent, "Original_rgb")
 72        os.makedirs(preprocessed_dir, exist_ok=True)
 73
 74        preprocessed_path = os.path.join(preprocessed_dir, f"{Path(raw_path).stem}.tif")
 75        preprocessed_paths.append(preprocessed_path)
 76        if os.path.exists(preprocessed_path):
 77            continue
 78
 79        raw = imageio.imread(raw_path)
 80        if raw.ndim == 2:
 81            raw = np.repeat(raw[..., None], 3, axis=-1)
 82        else:
 83            raw = raw[..., :3]
 84        imageio.imwrite(preprocessed_path, raw, compression="zlib")
 85
 86    return preprocessed_paths
 87
 88
 89def _preprocess_labels(raw_label_paths: List[str]) -> List[str]:
 90    """Reduce the (partially 3-channel) label images to single-channel binary masks."""
 91    preprocessed_paths = []
 92    for label_path in tqdm(raw_label_paths, desc="Preprocessing labels"):
 93        parent = Path(label_path).parent.parent
 94        preprocessed_dir = os.path.join(parent, "Label_binary")
 95        os.makedirs(preprocessed_dir, exist_ok=True)
 96
 97        preprocessed_path = os.path.join(preprocessed_dir, f"{Path(label_path).stem}.tif")
 98        preprocessed_paths.append(preprocessed_path)
 99        if os.path.exists(preprocessed_path):
100            continue
101
102        label = imageio.imread(label_path)
103        if label.ndim == 3:
104            label = label[..., 0]
105        label = (label > 127).astype("uint8")
106        imageio.imwrite(preprocessed_path, label, compression="zlib")
107
108    return preprocessed_paths
109
110
111def get_tear_meniscus_paths(
112    path: Union[os.PathLike, str],
113    modality: Literal["colour", "infrared"] = "colour",
114    download: bool = False,
115) -> Tuple[List[str], List[str]]:
116    """Get paths to the Tear Meniscus data.
117
118    Args:
119        path: Filepath to a folder where the data is downloaded for further processing.
120        modality: The imaging modality. Either 'colour' or 'infrared'.
121        download: Whether to download the data if it is not present.
122
123    Returns:
124        List of filepaths for the image data.
125        List of filepaths for the label data.
126    """
127    data_dir = get_tear_meniscus_data(path, download)
128
129    if modality not in MODALITIES:
130        raise ValueError(f"'{modality}' is not a valid modality. Please choose one of {list(MODALITIES)}.")
131    prefix = MODALITIES[modality]
132
133    raw_image_paths = natsorted(glob(os.path.join(data_dir, f"{prefix}*", "Original", "*.PNG")))
134    raw_label_paths = [p.replace(f"{os.sep}Original{os.sep}", f"{os.sep}Label{os.sep}") for p in raw_image_paths]
135
136    assert len(raw_image_paths) > 0, f"Could not find any images in '{data_dir}'."
137    for label_path in raw_label_paths:
138        assert os.path.exists(label_path), label_path
139
140    image_paths = _preprocess_images(raw_image_paths)
141    label_paths = _preprocess_labels(raw_label_paths)
142
143    return image_paths, label_paths
144
145
146def get_tear_meniscus_dataset(
147    path: Union[os.PathLike, str],
148    patch_shape: Tuple[int, int],
149    modality: Literal["colour", "infrared"] = "colour",
150    resize_inputs: bool = False,
151    download: bool = False,
152    **kwargs
153) -> Dataset:
154    """Get the Tear Meniscus dataset for segmentation of the tear meniscus.
155
156    Args:
157        path: Filepath to a folder where the data is downloaded for further processing.
158        patch_shape: The patch shape to use for training.
159        modality: The imaging modality. Either 'colour' or 'infrared'.
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, label_paths = get_tear_meniscus_paths(path, modality, download)
168
169    if resize_inputs:
170        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": True}
171        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
172            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
173        )
174
175    return torch_em.default_segmentation_dataset(
176        raw_paths=image_paths,
177        raw_key=None,
178        label_paths=label_paths,
179        label_key=None,
180        patch_shape=patch_shape,
181        is_seg_dataset=False,
182        **kwargs
183    )
184
185
186def get_tear_meniscus_loader(
187    path: Union[os.PathLike, str],
188    batch_size: int,
189    patch_shape: Tuple[int, int],
190    modality: Literal["colour", "infrared"] = "colour",
191    resize_inputs: bool = False,
192    download: bool = False,
193    **kwargs
194) -> DataLoader:
195    """Get the Tear Meniscus dataloader for segmentation of the tear meniscus.
196
197    Args:
198        path: Filepath to a folder where the data is downloaded for further processing.
199        batch_size: The batch size for training.
200        patch_shape: The patch shape to use for training.
201        modality: The imaging modality. Either 'colour' or 'infrared'.
202        resize_inputs: Whether to resize the inputs to the expected patch shape.
203        download: Whether to download the data if it is not present.
204        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
205
206    Returns:
207        The DataLoader.
208    """
209    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
210    dataset = get_tear_meniscus_dataset(path, patch_shape, modality, resize_inputs, download, **ds_kwargs)
211    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URL = 'https://ndownloader.figshare.com/files/56406179'
CHECKSUM = 'b4614214e69098b09160f4713c5e0f11fcd6a7a158a5217ca9cd15ba2687f170'
LABEL_IDS = {'background': 0, 'tear_meniscus': 1}
MODALITIES = {'colour': 'Colour', 'infrared': 'Infrared'}
def get_tear_meniscus_data(path: Union[os.PathLike, str], download: bool = False) -> str:
44def get_tear_meniscus_data(path: Union[os.PathLike, str], download: bool = False) -> str:
45    """Download the Tear Meniscus dataset.
46
47    Args:
48        path: Filepath to a folder where the data is downloaded for further processing.
49        download: Whether to download the data if it is not present.
50
51    Returns:
52        Filepath where the data is downloaded.
53    """
54    data_dir = os.path.join(path, "Open DataSet2")
55    if os.path.exists(data_dir):
56        return data_dir
57
58    os.makedirs(path, exist_ok=True)
59
60    zip_path = os.path.join(path, "Open DataSet.zip")
61    util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM)
62    util.unzip(zip_path=zip_path, dst=path)
63
64    return data_dir

Download the Tear Meniscus 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 where the data is downloaded.

def get_tear_meniscus_paths( path: Union[os.PathLike, str], modality: Literal['colour', 'infrared'] = 'colour', download: bool = False) -> Tuple[List[str], List[str]]:
112def get_tear_meniscus_paths(
113    path: Union[os.PathLike, str],
114    modality: Literal["colour", "infrared"] = "colour",
115    download: bool = False,
116) -> Tuple[List[str], List[str]]:
117    """Get paths to the Tear Meniscus data.
118
119    Args:
120        path: Filepath to a folder where the data is downloaded for further processing.
121        modality: The imaging modality. Either 'colour' or 'infrared'.
122        download: Whether to download the data if it is not present.
123
124    Returns:
125        List of filepaths for the image data.
126        List of filepaths for the label data.
127    """
128    data_dir = get_tear_meniscus_data(path, download)
129
130    if modality not in MODALITIES:
131        raise ValueError(f"'{modality}' is not a valid modality. Please choose one of {list(MODALITIES)}.")
132    prefix = MODALITIES[modality]
133
134    raw_image_paths = natsorted(glob(os.path.join(data_dir, f"{prefix}*", "Original", "*.PNG")))
135    raw_label_paths = [p.replace(f"{os.sep}Original{os.sep}", f"{os.sep}Label{os.sep}") for p in raw_image_paths]
136
137    assert len(raw_image_paths) > 0, f"Could not find any images in '{data_dir}'."
138    for label_path in raw_label_paths:
139        assert os.path.exists(label_path), label_path
140
141    image_paths = _preprocess_images(raw_image_paths)
142    label_paths = _preprocess_labels(raw_label_paths)
143
144    return image_paths, label_paths

Get paths to the Tear Meniscus data.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • modality: The imaging modality. Either 'colour' or 'infrared'.
  • 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_tear_meniscus_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int], modality: Literal['colour', 'infrared'] = 'colour', resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
147def get_tear_meniscus_dataset(
148    path: Union[os.PathLike, str],
149    patch_shape: Tuple[int, int],
150    modality: Literal["colour", "infrared"] = "colour",
151    resize_inputs: bool = False,
152    download: bool = False,
153    **kwargs
154) -> Dataset:
155    """Get the Tear Meniscus dataset for segmentation of the tear meniscus.
156
157    Args:
158        path: Filepath to a folder where the data is downloaded for further processing.
159        patch_shape: The patch shape to use for training.
160        modality: The imaging modality. Either 'colour' or 'infrared'.
161        resize_inputs: Whether to resize the inputs to the expected patch shape.
162        download: Whether to download the data if it is not present.
163        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
164
165    Returns:
166        The segmentation dataset.
167    """
168    image_paths, label_paths = get_tear_meniscus_paths(path, modality, download)
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=label_paths,
180        label_key=None,
181        patch_shape=patch_shape,
182        is_seg_dataset=False,
183        **kwargs
184    )

Get the Tear Meniscus dataset for segmentation of the tear meniscus.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • patch_shape: The patch shape to use for training.
  • modality: The imaging modality. Either 'colour' or 'infrared'.
  • 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_tear_meniscus_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, int], modality: Literal['colour', 'infrared'] = 'colour', resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
187def get_tear_meniscus_loader(
188    path: Union[os.PathLike, str],
189    batch_size: int,
190    patch_shape: Tuple[int, int],
191    modality: Literal["colour", "infrared"] = "colour",
192    resize_inputs: bool = False,
193    download: bool = False,
194    **kwargs
195) -> DataLoader:
196    """Get the Tear Meniscus dataloader for segmentation of the tear meniscus.
197
198    Args:
199        path: Filepath to a folder where the data is downloaded for further processing.
200        batch_size: The batch size for training.
201        patch_shape: The patch shape to use for training.
202        modality: The imaging modality. Either 'colour' or 'infrared'.
203        resize_inputs: Whether to resize the inputs to the expected patch shape.
204        download: Whether to download the data if it is not present.
205        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
206
207    Returns:
208        The DataLoader.
209    """
210    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
211    dataset = get_tear_meniscus_dataset(path, patch_shape, modality, resize_inputs, download, **ds_kwargs)
212    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the Tear Meniscus dataloader for segmentation of the tear meniscus.

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.
  • modality: The imaging modality. Either 'colour' or 'infrared'.
  • 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.