torch_em.data.datasets.medical.pengwin

The PENGWIN dataset contains annotation for pelvic bone fracture and fragments in CT and X-Ray images.

The original (v1) release is from the challenge: https://pengwin.grand-challenge.org/pengwin/. This release is related to the publication: https://doi.org/10.1007/978-3-031-43996-4_30.

The updated (v2) release, PENGWIN26, is from the challenge: https://pengwin2026.grand-challenge.org/. It ships 340 labeled real clinical CT cases with per-fragment instance ids, collected across seven institutions: https://zenodo.org/records/19732767 (DOI: https://doi.org/10.5281/zenodo.19732767). The simulated fracture cases for the separate reduction planning task are not covered by this loader.

Please cite the respective publication(s) if you use this dataset for your research.

  1"""The PENGWIN dataset contains annotation for pelvic bone fracture and
  2fragments in CT and X-Ray images.
  3
  4The original (v1) release is from the challenge: https://pengwin.grand-challenge.org/pengwin/.
  5This release is related to the publication: https://doi.org/10.1007/978-3-031-43996-4_30.
  6
  7The updated (v2) release, PENGWIN26, is from the challenge: https://pengwin2026.grand-challenge.org/.
  8It ships 340 labeled real clinical CT cases with per-fragment instance ids, collected across seven
  9institutions: https://zenodo.org/records/19732767 (DOI: https://doi.org/10.5281/zenodo.19732767).
 10The simulated fracture cases for the separate reduction planning task are not covered by this loader.
 11
 12Please cite the respective publication(s) if you use this dataset for your research.
 13"""
 14
 15import os
 16from glob import glob
 17from natsort import natsorted
 18from typing import Union, Tuple, Literal, List
 19
 20from torch.utils.data import Dataset, DataLoader
 21
 22import torch_em
 23
 24from .. import util
 25
 26
 27URLS = {
 28    "CT": [
 29        "https://zenodo.org/records/10927452/files/PENGWIN_CT_train_images_part1.zip",  # inputs part 1
 30        "https://zenodo.org/records/10927452/files/PENGWIN_CT_train_images_part2.zip",  # inputs part 2
 31        "https://zenodo.org/records/10927452/files/PENGWIN_CT_train_labels.zip",  # labels
 32    ],
 33    "X-Ray": ["https://zenodo.org/records/10913196/files/train.zip"]
 34}
 35
 36CHECKSUMS = {
 37    "CT": [
 38        "e2e9f99798960607ffced1fbdeee75a626c41bf859eaf4125029a38fac6b7609",  # inputs part 1
 39        "19f3cdc5edd1daf9324c70f8ba683eed054f6ed8f2b1cc59dbd80724f8f0bbb2",  # inputs part 2
 40        "c4d3857e02d3ee5d0df6c8c918dd3cf5a7c9419135f1ec089b78215f37c6665c"  # labels
 41    ],
 42    "X-Ray": ["48d107979eb929a3c61da4e75566306a066408954cf132907bda570f2a7de725"]
 43}
 44
 45TARGET_DIRS = {
 46    "CT": ["CT/images", "CT/images", "CT/labels"],
 47    "X-Ray": ["X-Ray"]
 48}
 49
 50MODALITIES = ["CT", "X-Ray"]
 51
 52URLS_V2 = {
 53    "CT": [
 54        "https://zenodo.org/records/19732767/files/PENGWIN26_task1_2_train_part1.zip",
 55        "https://zenodo.org/records/19732767/files/PENGWIN26_task1_2_train_part2.zip",
 56        "https://zenodo.org/records/19732767/files/PENGWIN26_task1_2_train_part3.zip",
 57        "https://zenodo.org/records/19732767/files/PENGWIN26_task1_2_train_part4.zip",
 58    ]
 59}
 60
 61CHECKSUMS_V2 = {
 62    "CT": [
 63        "a9b047c7796164a4cb47f2083b67da2502070dfc7241fe42ec291a065dae0b19",
 64        "71aa69cb5620c9a5d4186de0c0e25f7bfe491b1123b4be3d18b62b4b9c267213",
 65        "26a7055993730d57f1365d3ab5e001841a048f57baa398f178a2d1298b2db636",
 66        "ff79294be9eb1827d8042c3171c460b829252e5a33c689ba856284c31518fa5d",
 67    ]
 68}
 69
 70
 71def get_pengwin_data(
 72    path: Union[os.PathLike, str],
 73    modality: Literal["CT", "X-Ray"],
 74    download: bool = False,
 75    version: Literal["v1", "v2"] = "v1",
 76) -> str:
 77    """Download the PENGWIN dataset.
 78
 79    Args:
 80        path: Filepath to a folder where the data is downloaded for further processing.
 81        modality: The choice of modality for inputs.
 82        download: Whether to download the data if it is not present.
 83        version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or
 84            'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
 85
 86    Returns:
 87        Filepath where the data is downloaded.
 88    """
 89    if not isinstance(modality, str) and modality in MODALITIES:
 90        raise ValueError(f"'{modality}' is not a valid modality. Please choose from {MODALITIES}.")
 91
 92    if version == "v2" and modality != "CT":
 93        raise ValueError("The 'v2' (PENGWIN26) release only provides the 'CT' modality.")
 94
 95    data_dir = os.path.join(path, "data")
 96    exists_dir = os.path.join(data_dir, modality) if version == "v1" else os.path.join(data_dir, modality, version)
 97    if os.path.exists(exists_dir):
 98        return data_dir
 99
100    os.makedirs(path, exist_ok=True)
101
102    if version == "v1":
103        for url, checksum, dst_dir in zip(URLS[modality], CHECKSUMS[modality], TARGET_DIRS[modality]):
104            zip_path = os.path.join(path, os.path.split(url)[-1])
105            util.download_source(path=zip_path, url=url, download=download, checksum=checksum)
106            util.unzip(zip_path=zip_path, dst=os.path.join(data_dir, dst_dir))
107    else:  # v2
108        dst_dir = os.path.join(data_dir, modality, version)
109        for url, checksum in zip(URLS_V2[modality], CHECKSUMS_V2[modality]):
110            zip_path = os.path.join(path, os.path.split(url)[-1])
111            util.download_source(path=zip_path, url=url, download=download, checksum=checksum)
112            util.unzip(zip_path=zip_path, dst=dst_dir)
113
114    return data_dir
115
116
117def get_pengwin_paths(
118    path: Union[os.PathLike, str],
119    modality: Literal["CT", "X-Ray"],
120    download: bool = False,
121    version: Literal["v1", "v2"] = "v1",
122) -> Tuple[List[str], List[str]]:
123    """Get paths to the PENGWIN data.
124
125    Args:
126        path: Filepath to a folder where the data is downloaded for further processing.
127        modality: The choice of modality for inputs.
128        download: Whether to download the data if it is not present.
129        version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or
130            'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
131
132    Returns:
133        List of filepaths for the image data.
134        List of filepaths for the label data.
135    """
136    data_dir = get_pengwin_data(path=path, modality=modality, download=download, version=version)
137
138    if version == "v2":
139        base_dir = os.path.join(data_dir, modality, version)
140        image_paths = natsorted(glob(os.path.join(base_dir, "**", "image.mha"), recursive=True))
141        gt_paths = natsorted(glob(os.path.join(base_dir, "**", "label.mha"), recursive=True))
142    elif modality == "CT":
143        image_paths = natsorted(glob(os.path.join(data_dir, modality, "images", "*.mha")))
144        gt_paths = natsorted(glob(os.path.join(data_dir, modality, "labels", "*.mha")))
145    else:  # X-Ray
146        base_dir = os.path.join(data_dir, modality, "train")
147        image_paths = natsorted(glob(os.path.join(base_dir, "input", "images", "*.tif")))
148        gt_paths = natsorted(glob(os.path.join(base_dir, "output", "images", "*.tif")))
149
150    return image_paths, gt_paths
151
152
153def get_pengwin_dataset(
154    path: Union[os.PathLike, str],
155    patch_shape: Tuple[int, ...],
156    modality: Literal["CT", "X-Ray"],
157    resize_inputs: bool = False,
158    download: bool = False,
159    version: Literal["v1", "v2"] = "v1",
160    **kwargs
161) -> Dataset:
162    """Get the PENGWIN dataset for pelvic fracture segmentation.
163
164    Args:
165        path: Filepath to a folder where the data is downloaded for further processing.
166        patch_shape: The patch shape to use for training.
167        modality: The choice of modality for inputs.
168        resize_inputs: Whether to resize inputs to the desired patch shape.
169        download: Whether to download the data if it is not present.
170        version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or
171            'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
172        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
173
174    Returns:
175        The segmentation dataset.
176    """
177    image_paths, gt_paths = get_pengwin_paths(path=path, modality=modality, download=download, version=version)
178
179    if resize_inputs:
180        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
181        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
182            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
183        )
184
185    return torch_em.default_segmentation_dataset(
186        raw_paths=image_paths,
187        raw_key=None,
188        label_paths=gt_paths,
189        label_key=None,
190        patch_shape=patch_shape,
191        **kwargs
192    )
193
194
195def get_pengwin_loader(
196    path: Union[os.PathLike, str],
197    batch_size: int,
198    patch_shape: Tuple[int, ...],
199    modality: Literal["CT", "X-Ray"],
200    resize_inputs: bool = False,
201    download: bool = False,
202    version: Literal["v1", "v2"] = "v1",
203    **kwargs
204) -> DataLoader:
205    """Get the PENGWIN dataloader for pelvic fracture segmentation.
206
207    Args:
208        path: Filepath to a folder where the data is downloaded for further processing.
209        batch_size: The batch size for training.
210        patch_shape: The patch shape to use for training.
211        modality: The choice of modality for inputs.
212        resize_inputs: Whether to resize inputs to the desired patch shape.
213        download: Whether to download the data if it is not present.
214        version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or
215            'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
216        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
217
218    Returns:
219        The DataLoader.
220    """
221    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
222    dataset = get_pengwin_dataset(path, patch_shape, modality, resize_inputs, download, version, **ds_kwargs)
223    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URLS = {'CT': ['https://zenodo.org/records/10927452/files/PENGWIN_CT_train_images_part1.zip', 'https://zenodo.org/records/10927452/files/PENGWIN_CT_train_images_part2.zip', 'https://zenodo.org/records/10927452/files/PENGWIN_CT_train_labels.zip'], 'X-Ray': ['https://zenodo.org/records/10913196/files/train.zip']}
CHECKSUMS = {'CT': ['e2e9f99798960607ffced1fbdeee75a626c41bf859eaf4125029a38fac6b7609', '19f3cdc5edd1daf9324c70f8ba683eed054f6ed8f2b1cc59dbd80724f8f0bbb2', 'c4d3857e02d3ee5d0df6c8c918dd3cf5a7c9419135f1ec089b78215f37c6665c'], 'X-Ray': ['48d107979eb929a3c61da4e75566306a066408954cf132907bda570f2a7de725']}
TARGET_DIRS = {'CT': ['CT/images', 'CT/images', 'CT/labels'], 'X-Ray': ['X-Ray']}
MODALITIES = ['CT', 'X-Ray']
URLS_V2 = {'CT': ['https://zenodo.org/records/19732767/files/PENGWIN26_task1_2_train_part1.zip', 'https://zenodo.org/records/19732767/files/PENGWIN26_task1_2_train_part2.zip', 'https://zenodo.org/records/19732767/files/PENGWIN26_task1_2_train_part3.zip', 'https://zenodo.org/records/19732767/files/PENGWIN26_task1_2_train_part4.zip']}
CHECKSUMS_V2 = {'CT': ['a9b047c7796164a4cb47f2083b67da2502070dfc7241fe42ec291a065dae0b19', '71aa69cb5620c9a5d4186de0c0e25f7bfe491b1123b4be3d18b62b4b9c267213', '26a7055993730d57f1365d3ab5e001841a048f57baa398f178a2d1298b2db636', 'ff79294be9eb1827d8042c3171c460b829252e5a33c689ba856284c31518fa5d']}
def get_pengwin_data( path: Union[os.PathLike, str], modality: Literal['CT', 'X-Ray'], download: bool = False, version: Literal['v1', 'v2'] = 'v1') -> str:
 72def get_pengwin_data(
 73    path: Union[os.PathLike, str],
 74    modality: Literal["CT", "X-Ray"],
 75    download: bool = False,
 76    version: Literal["v1", "v2"] = "v1",
 77) -> str:
 78    """Download the PENGWIN dataset.
 79
 80    Args:
 81        path: Filepath to a folder where the data is downloaded for further processing.
 82        modality: The choice of modality for inputs.
 83        download: Whether to download the data if it is not present.
 84        version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or
 85            'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
 86
 87    Returns:
 88        Filepath where the data is downloaded.
 89    """
 90    if not isinstance(modality, str) and modality in MODALITIES:
 91        raise ValueError(f"'{modality}' is not a valid modality. Please choose from {MODALITIES}.")
 92
 93    if version == "v2" and modality != "CT":
 94        raise ValueError("The 'v2' (PENGWIN26) release only provides the 'CT' modality.")
 95
 96    data_dir = os.path.join(path, "data")
 97    exists_dir = os.path.join(data_dir, modality) if version == "v1" else os.path.join(data_dir, modality, version)
 98    if os.path.exists(exists_dir):
 99        return data_dir
100
101    os.makedirs(path, exist_ok=True)
102
103    if version == "v1":
104        for url, checksum, dst_dir in zip(URLS[modality], CHECKSUMS[modality], TARGET_DIRS[modality]):
105            zip_path = os.path.join(path, os.path.split(url)[-1])
106            util.download_source(path=zip_path, url=url, download=download, checksum=checksum)
107            util.unzip(zip_path=zip_path, dst=os.path.join(data_dir, dst_dir))
108    else:  # v2
109        dst_dir = os.path.join(data_dir, modality, version)
110        for url, checksum in zip(URLS_V2[modality], CHECKSUMS_V2[modality]):
111            zip_path = os.path.join(path, os.path.split(url)[-1])
112            util.download_source(path=zip_path, url=url, download=download, checksum=checksum)
113            util.unzip(zip_path=zip_path, dst=dst_dir)
114
115    return data_dir

Download the PENGWIN dataset.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • modality: The choice of modality for inputs.
  • download: Whether to download the data if it is not present.
  • version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or 'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
Returns:

Filepath where the data is downloaded.

def get_pengwin_paths( path: Union[os.PathLike, str], modality: Literal['CT', 'X-Ray'], download: bool = False, version: Literal['v1', 'v2'] = 'v1') -> Tuple[List[str], List[str]]:
118def get_pengwin_paths(
119    path: Union[os.PathLike, str],
120    modality: Literal["CT", "X-Ray"],
121    download: bool = False,
122    version: Literal["v1", "v2"] = "v1",
123) -> Tuple[List[str], List[str]]:
124    """Get paths to the PENGWIN data.
125
126    Args:
127        path: Filepath to a folder where the data is downloaded for further processing.
128        modality: The choice of modality for inputs.
129        download: Whether to download the data if it is not present.
130        version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or
131            'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
132
133    Returns:
134        List of filepaths for the image data.
135        List of filepaths for the label data.
136    """
137    data_dir = get_pengwin_data(path=path, modality=modality, download=download, version=version)
138
139    if version == "v2":
140        base_dir = os.path.join(data_dir, modality, version)
141        image_paths = natsorted(glob(os.path.join(base_dir, "**", "image.mha"), recursive=True))
142        gt_paths = natsorted(glob(os.path.join(base_dir, "**", "label.mha"), recursive=True))
143    elif modality == "CT":
144        image_paths = natsorted(glob(os.path.join(data_dir, modality, "images", "*.mha")))
145        gt_paths = natsorted(glob(os.path.join(data_dir, modality, "labels", "*.mha")))
146    else:  # X-Ray
147        base_dir = os.path.join(data_dir, modality, "train")
148        image_paths = natsorted(glob(os.path.join(base_dir, "input", "images", "*.tif")))
149        gt_paths = natsorted(glob(os.path.join(base_dir, "output", "images", "*.tif")))
150
151    return image_paths, gt_paths

Get paths to the PENGWIN data.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • modality: The choice of modality for inputs.
  • download: Whether to download the data if it is not present.
  • version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or 'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
Returns:

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

def get_pengwin_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, ...], modality: Literal['CT', 'X-Ray'], resize_inputs: bool = False, download: bool = False, version: Literal['v1', 'v2'] = 'v1', **kwargs) -> torch.utils.data.dataset.Dataset:
154def get_pengwin_dataset(
155    path: Union[os.PathLike, str],
156    patch_shape: Tuple[int, ...],
157    modality: Literal["CT", "X-Ray"],
158    resize_inputs: bool = False,
159    download: bool = False,
160    version: Literal["v1", "v2"] = "v1",
161    **kwargs
162) -> Dataset:
163    """Get the PENGWIN dataset for pelvic fracture segmentation.
164
165    Args:
166        path: Filepath to a folder where the data is downloaded for further processing.
167        patch_shape: The patch shape to use for training.
168        modality: The choice of modality for inputs.
169        resize_inputs: Whether to resize inputs to the desired patch shape.
170        download: Whether to download the data if it is not present.
171        version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or
172            'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
173        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
174
175    Returns:
176        The segmentation dataset.
177    """
178    image_paths, gt_paths = get_pengwin_paths(path=path, modality=modality, download=download, version=version)
179
180    if resize_inputs:
181        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
182        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
183            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
184        )
185
186    return torch_em.default_segmentation_dataset(
187        raw_paths=image_paths,
188        raw_key=None,
189        label_paths=gt_paths,
190        label_key=None,
191        patch_shape=patch_shape,
192        **kwargs
193    )

Get the PENGWIN dataset for pelvic fracture segmentation.

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 choice of modality for inputs.
  • resize_inputs: Whether to resize inputs to the desired patch shape.
  • download: Whether to download the data if it is not present.
  • version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or 'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset.
Returns:

The segmentation dataset.

def get_pengwin_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, ...], modality: Literal['CT', 'X-Ray'], resize_inputs: bool = False, download: bool = False, version: Literal['v1', 'v2'] = 'v1', **kwargs) -> torch.utils.data.dataloader.DataLoader:
196def get_pengwin_loader(
197    path: Union[os.PathLike, str],
198    batch_size: int,
199    patch_shape: Tuple[int, ...],
200    modality: Literal["CT", "X-Ray"],
201    resize_inputs: bool = False,
202    download: bool = False,
203    version: Literal["v1", "v2"] = "v1",
204    **kwargs
205) -> DataLoader:
206    """Get the PENGWIN dataloader for pelvic fracture segmentation.
207
208    Args:
209        path: Filepath to a folder where the data is downloaded for further processing.
210        batch_size: The batch size for training.
211        patch_shape: The patch shape to use for training.
212        modality: The choice of modality for inputs.
213        resize_inputs: Whether to resize inputs to the desired patch shape.
214        download: Whether to download the data if it is not present.
215        version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or
216            'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
217        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
218
219    Returns:
220        The DataLoader.
221    """
222    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
223    dataset = get_pengwin_dataset(path, patch_shape, modality, resize_inputs, download, version, **ds_kwargs)
224    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the PENGWIN dataloader for pelvic fracture segmentation.

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 choice of modality for inputs.
  • resize_inputs: Whether to resize inputs to the desired patch shape.
  • download: Whether to download the data if it is not present.
  • version: The version of the dataset. Either 'v1' (original PENGWIN release, CT and X-Ray) or 'v2' (PENGWIN26, 340 labeled real clinical CT cases from seven institutions).
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset or for the PyTorch DataLoader.
Returns:

The DataLoader.