torch_em.data.datasets.medical.autopet_organ

The AutoPET-Organ dataset contains annotations for 11 organs in whole-body FDG-PET scans.

The dataset consists of 100 studies of the AutoPET collection with organ annotations that were added and expert-examined for the SegAnyPET publication, so the annotations are downloaded from there and the scans are taken from the existing autopet loader. The label ids are 1: liver, 2: kidney (left), 3: kidney (right), 4: heart, 5: spleen, 6: aorta, 7: lung lower lobe (left), 8: lung lower lobe (right), 9: lung upper lobe (left), 10: lung upper lobe (right), 11: lung middle lobe (right). See also CLASS_IDS.

NOTE: The release stores the right middle lobe under two ids, 11 and 12, which are perfectly complementary: of the 100 studies, 64 use id 11, 36 use id 12, and no study uses both or neither. The two carry the same structure, matching in side, in position relative to the other organs of the same study, and in size. 'harmonize' maps id 12 onto id 11 so that one organ has one id, which is the default because the raw ids split one class in two and make a broken training target.

NOTE: The source describes a prostate annotation, but no study of this release contains a twelfth structure once ids 11 and 12 are merged, so the annotations cover 11 organs rather than 12.

NOTE: The label ids were assigned by measuring each annotation in the data, since the source lists the organs without their ids. Every study is stored in LPS orientation, and the ids follow from the side and the position of each annotation relative to the other organs of the same study.

The annotations are located at https://github.com/YichiZhang98/SegAnyPET and the scans at https://autopet.grand-challenge.org. This dataset is from the publication https://doi.org/10.48550/arXiv.2502.14351. Please cite it, and the AutoPET publication https://doi.org/10.1038/s41597-022-01718-3, if you use this dataset in your research.

  1"""The AutoPET-Organ dataset contains annotations for 11 organs in whole-body FDG-PET scans.
  2
  3The dataset consists of 100 studies of the AutoPET collection with organ annotations that were added and
  4expert-examined for the SegAnyPET publication, so the annotations are downloaded from there and the
  5scans are taken from the existing autopet loader. The label ids are 1: liver, 2: kidney (left),
  63: kidney (right), 4: heart, 5: spleen, 6: aorta, 7: lung lower lobe (left), 8: lung lower lobe (right),
  79: lung upper lobe (left), 10: lung upper lobe (right), 11: lung middle lobe (right).
  8See also `CLASS_IDS`.
  9
 10NOTE: The release stores the right middle lobe under two ids, 11 and 12, which are perfectly
 11complementary: of the 100 studies, 64 use id 11, 36 use id 12, and no study uses both or neither. The
 12two carry the same structure, matching in side, in position relative to the other organs of the same
 13study, and in size. 'harmonize' maps id 12 onto id 11 so that one organ has one id, which is the
 14default because the raw ids split one class in two and make a broken training target.
 15
 16NOTE: The source describes a prostate annotation, but no study of this release contains a twelfth
 17structure once ids 11 and 12 are merged, so the annotations cover 11 organs rather than 12.
 18
 19NOTE: The label ids were assigned by measuring each annotation in the data, since the source lists the
 20organs without their ids. Every study is stored in LPS orientation, and the ids follow from the side and
 21the position of each annotation relative to the other organs of the same study.
 22
 23The annotations are located at https://github.com/YichiZhang98/SegAnyPET and the scans at
 24https://autopet.grand-challenge.org.
 25This dataset is from the publication https://doi.org/10.48550/arXiv.2502.14351.
 26Please cite it, and the AutoPET publication https://doi.org/10.1038/s41597-022-01718-3, if you use this
 27dataset in your research.
 28"""
 29
 30import os
 31import re
 32from glob import glob
 33from natsort import natsorted
 34from typing import Union, Optional, Tuple, List
 35
 36import numpy as np
 37
 38from torch.utils.data import Dataset, DataLoader
 39
 40import torch_em
 41
 42from .autopet import get_autopet_data
 43from .. import util
 44
 45
 46URL = "https://github.com/YichiZhang98/SegAnyPET/raw/main/AutoPET-OrganlabelsTr.zip"
 47
 48CHECKSUM = "8794137a0f8df0bc192aa8bb98009b3bc706e4591cbe8f289f8d38e1c5574142"
 49
 50CLASS_NAMES = [
 51    "liver", "kidney_left", "kidney_right", "heart", "spleen", "aorta",
 52    "lung_lower_lobe_left", "lung_lower_lobe_right", "lung_upper_lobe_left", "lung_upper_lobe_right",
 53    "lung_middle_lobe_right",
 54]
 55"""The organs of the AutoPET-Organ dataset. The label id of an organ is its 1-based index."""
 56
 57CLASS_IDS = {name: i + 1 for i, name in enumerate(CLASS_NAMES)}
 58"""Mapping from the organ name to its label id."""
 59
 60MIDDLE_LOBE_ALIAS = 12
 61"""The second id that the release uses for the right middle lobe, which `harmonize` maps onto id 11."""
 62
 63
 64def _prepare_labels(label_path, out_path, harmonize):
 65    """Store the annotation under a filename without a dot in its stem, optionally harmonizing its ids.
 66
 67    Two of the study names abbreviate 'nativ und mit' as 'nativ u. mit', and the dot of that abbreviation
 68    makes the '.nii.gz' extension unrecognizable to the file readers.
 69    """
 70    import nibabel as nib
 71
 72    if os.path.exists(out_path):
 73        return out_path
 74
 75    image = nib.load(label_path)
 76    labels = np.asarray(image.dataobj)
 77    if harmonize:
 78        labels = np.where(labels == MIDDLE_LOBE_ALIAS, CLASS_IDS["lung_middle_lobe_right"], labels)
 79
 80    nib.save(nib.Nifti1Image(labels.astype("uint8"), image.affine, image.header), out_path)
 81    return out_path
 82
 83
 84def get_autopet_organ_data(
 85    path: Union[os.PathLike, str], autopet_path: Optional[Union[os.PathLike, str]] = None, download: bool = False
 86) -> Tuple[str, str]:
 87    """Download the AutoPET-Organ dataset.
 88
 89    Args:
 90        path: Filepath to a folder where the data is downloaded for further processing.
 91        autopet_path: Filepath to an existing AutoPET download. The scans are downloaded to `path` if it
 92            is not given, which takes several hundred gigabytes.
 93        download: Whether to download the data if it is not present.
 94
 95    Returns:
 96        Filepath where the annotations are downloaded.
 97        Filepath where the scans are downloaded.
 98    """
 99    label_dir = os.path.join(path, "AutoPET-OrganlabelsTr")
100    if not os.path.exists(label_dir):
101        os.makedirs(path, exist_ok=True)
102        zip_path = os.path.join(path, "AutoPET-OrganlabelsTr.zip")
103        util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM)
104        util.unzip(zip_path=zip_path, dst=path, remove=False)
105
106    # The scans are the whole-body PET studies of the AutoPET collection, which are several hundred
107    # gigabytes, so an existing download of them is used when it is given.
108    autopet_path = path if autopet_path is None else autopet_path
109    get_autopet_data(path=autopet_path, download=download)
110    image_dir = os.path.join(autopet_path, "AutoPET-II", "FDG-PET-CT-Lesions")
111
112    return label_dir, image_dir
113
114
115def get_autopet_organ_paths(
116    path: Union[os.PathLike, str],
117    harmonize: bool = True,
118    autopet_path: Optional[Union[os.PathLike, str]] = None,
119    download: bool = False,
120) -> Tuple[List[str], List[str]]:
121    """Get paths to the AutoPET-Organ data.
122
123    Args:
124        path: Filepath to a folder where the data is downloaded for further processing.
125        harmonize: Whether to map the second id of the right middle lobe onto its first one.
126        autopet_path: Filepath to an existing AutoPET download.
127        download: Whether to download the data if it is not present.
128
129    Returns:
130        List of filepaths for the image data.
131        List of filepaths for the label data.
132    """
133    label_dir, image_dir = get_autopet_organ_data(path, autopet_path, download)
134
135    prepared_dir = os.path.join(path, "harmonized" if harmonize else "prepared")
136    os.makedirs(prepared_dir, exist_ok=True)
137
138    raw_paths, label_paths = [], []
139    for label_path in natsorted(glob(os.path.join(label_dir, "*.nii.gz"))):
140        # The annotations are named '<patient id>_<study id>_.nii.gz' for the study folder they belong to.
141        stem = os.path.basename(label_path)[:-len(".nii.gz")].rstrip("_")
142        match = re.match(r"^(PETCT_[0-9a-f]+)_(.+)$", stem)
143        if match is None:
144            continue
145
146        image_path = os.path.join(image_dir, match.group(1), match.group(2), "SUV.nii.gz")
147        if not os.path.exists(image_path):
148            continue
149
150        out_path = os.path.join(prepared_dir, f"{stem.replace('.', '')}.nii.gz")
151        raw_paths.append(image_path)
152        label_paths.append(_prepare_labels(label_path, out_path, harmonize))
153
154    assert len(raw_paths) == len(label_paths) and len(raw_paths) > 0
155
156    return raw_paths, label_paths
157
158
159def get_autopet_organ_dataset(
160    path: Union[os.PathLike, str],
161    patch_shape: Tuple[int, ...],
162    harmonize: bool = True,
163    autopet_path: Optional[Union[os.PathLike, str]] = None,
164    resize_inputs: bool = False,
165    download: bool = False,
166    **kwargs
167) -> Dataset:
168    """Get the AutoPET-Organ dataset for organ segmentation in whole-body PET.
169
170    Args:
171        path: Filepath to a folder where the data is downloaded for further processing.
172        patch_shape: The patch shape to use for training.
173        harmonize: Whether to map the second id of the right middle lobe onto its first one.
174        autopet_path: Filepath to an existing AutoPET download.
175        resize_inputs: Whether to resize inputs to the desired patch shape.
176        download: Whether to download the data if it is not present.
177        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
178
179    Returns:
180        The segmentation dataset.
181    """
182    raw_paths, label_paths = get_autopet_organ_paths(path, harmonize, autopet_path, download)
183
184    if resize_inputs:
185        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
186        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
187            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
188        )
189
190    return torch_em.default_segmentation_dataset(
191        raw_paths=raw_paths,
192        raw_key="data",
193        label_paths=label_paths,
194        label_key="data",
195        patch_shape=patch_shape,
196        is_seg_dataset=True,
197        **kwargs
198    )
199
200
201def get_autopet_organ_loader(
202    path: Union[os.PathLike, str],
203    batch_size: int,
204    patch_shape: Tuple[int, ...],
205    harmonize: bool = True,
206    autopet_path: Optional[Union[os.PathLike, str]] = None,
207    resize_inputs: bool = False,
208    download: bool = False,
209    **kwargs
210) -> DataLoader:
211    """Get the AutoPET-Organ dataloader for organ segmentation in whole-body PET.
212
213    Args:
214        path: Filepath to a folder where the data is downloaded for further processing.
215        batch_size: The batch size for training.
216        patch_shape: The patch shape to use for training.
217        harmonize: Whether to map the second id of the right middle lobe onto its first one.
218        autopet_path: Filepath to an existing AutoPET download.
219        resize_inputs: Whether to resize inputs to the desired patch shape.
220        download: Whether to download the data if it is not present.
221        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
222
223    Returns:
224        The DataLoader.
225    """
226    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
227    dataset = get_autopet_organ_dataset(
228        path, patch_shape, harmonize, autopet_path, resize_inputs, download, **ds_kwargs
229    )
230    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URL = 'https://github.com/YichiZhang98/SegAnyPET/raw/main/AutoPET-OrganlabelsTr.zip'
CHECKSUM = '8794137a0f8df0bc192aa8bb98009b3bc706e4591cbe8f289f8d38e1c5574142'
CLASS_NAMES = ['liver', 'kidney_left', 'kidney_right', 'heart', 'spleen', 'aorta', 'lung_lower_lobe_left', 'lung_lower_lobe_right', 'lung_upper_lobe_left', 'lung_upper_lobe_right', 'lung_middle_lobe_right']

The organs of the AutoPET-Organ dataset. The label id of an organ is its 1-based index.

CLASS_IDS = {'liver': 1, 'kidney_left': 2, 'kidney_right': 3, 'heart': 4, 'spleen': 5, 'aorta': 6, 'lung_lower_lobe_left': 7, 'lung_lower_lobe_right': 8, 'lung_upper_lobe_left': 9, 'lung_upper_lobe_right': 10, 'lung_middle_lobe_right': 11}

Mapping from the organ name to its label id.

MIDDLE_LOBE_ALIAS = 12

The second id that the release uses for the right middle lobe, which harmonize maps onto id 11.

def get_autopet_organ_data( path: Union[os.PathLike, str], autopet_path: Union[os.PathLike, str, NoneType] = None, download: bool = False) -> Tuple[str, str]:
 85def get_autopet_organ_data(
 86    path: Union[os.PathLike, str], autopet_path: Optional[Union[os.PathLike, str]] = None, download: bool = False
 87) -> Tuple[str, str]:
 88    """Download the AutoPET-Organ dataset.
 89
 90    Args:
 91        path: Filepath to a folder where the data is downloaded for further processing.
 92        autopet_path: Filepath to an existing AutoPET download. The scans are downloaded to `path` if it
 93            is not given, which takes several hundred gigabytes.
 94        download: Whether to download the data if it is not present.
 95
 96    Returns:
 97        Filepath where the annotations are downloaded.
 98        Filepath where the scans are downloaded.
 99    """
100    label_dir = os.path.join(path, "AutoPET-OrganlabelsTr")
101    if not os.path.exists(label_dir):
102        os.makedirs(path, exist_ok=True)
103        zip_path = os.path.join(path, "AutoPET-OrganlabelsTr.zip")
104        util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM)
105        util.unzip(zip_path=zip_path, dst=path, remove=False)
106
107    # The scans are the whole-body PET studies of the AutoPET collection, which are several hundred
108    # gigabytes, so an existing download of them is used when it is given.
109    autopet_path = path if autopet_path is None else autopet_path
110    get_autopet_data(path=autopet_path, download=download)
111    image_dir = os.path.join(autopet_path, "AutoPET-II", "FDG-PET-CT-Lesions")
112
113    return label_dir, image_dir

Download the AutoPET-Organ dataset.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • autopet_path: Filepath to an existing AutoPET download. The scans are downloaded to path if it is not given, which takes several hundred gigabytes.
  • download: Whether to download the data if it is not present.
Returns:

Filepath where the annotations are downloaded. Filepath where the scans are downloaded.

def get_autopet_organ_paths( path: Union[os.PathLike, str], harmonize: bool = True, autopet_path: Union[os.PathLike, str, NoneType] = None, download: bool = False) -> Tuple[List[str], List[str]]:
116def get_autopet_organ_paths(
117    path: Union[os.PathLike, str],
118    harmonize: bool = True,
119    autopet_path: Optional[Union[os.PathLike, str]] = None,
120    download: bool = False,
121) -> Tuple[List[str], List[str]]:
122    """Get paths to the AutoPET-Organ data.
123
124    Args:
125        path: Filepath to a folder where the data is downloaded for further processing.
126        harmonize: Whether to map the second id of the right middle lobe onto its first one.
127        autopet_path: Filepath to an existing AutoPET download.
128        download: Whether to download the data if it is not present.
129
130    Returns:
131        List of filepaths for the image data.
132        List of filepaths for the label data.
133    """
134    label_dir, image_dir = get_autopet_organ_data(path, autopet_path, download)
135
136    prepared_dir = os.path.join(path, "harmonized" if harmonize else "prepared")
137    os.makedirs(prepared_dir, exist_ok=True)
138
139    raw_paths, label_paths = [], []
140    for label_path in natsorted(glob(os.path.join(label_dir, "*.nii.gz"))):
141        # The annotations are named '<patient id>_<study id>_.nii.gz' for the study folder they belong to.
142        stem = os.path.basename(label_path)[:-len(".nii.gz")].rstrip("_")
143        match = re.match(r"^(PETCT_[0-9a-f]+)_(.+)$", stem)
144        if match is None:
145            continue
146
147        image_path = os.path.join(image_dir, match.group(1), match.group(2), "SUV.nii.gz")
148        if not os.path.exists(image_path):
149            continue
150
151        out_path = os.path.join(prepared_dir, f"{stem.replace('.', '')}.nii.gz")
152        raw_paths.append(image_path)
153        label_paths.append(_prepare_labels(label_path, out_path, harmonize))
154
155    assert len(raw_paths) == len(label_paths) and len(raw_paths) > 0
156
157    return raw_paths, label_paths

Get paths to the AutoPET-Organ data.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • harmonize: Whether to map the second id of the right middle lobe onto its first one.
  • autopet_path: Filepath to an existing AutoPET download.
  • 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_autopet_organ_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, ...], harmonize: bool = True, autopet_path: Union[os.PathLike, str, NoneType] = None, resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
160def get_autopet_organ_dataset(
161    path: Union[os.PathLike, str],
162    patch_shape: Tuple[int, ...],
163    harmonize: bool = True,
164    autopet_path: Optional[Union[os.PathLike, str]] = None,
165    resize_inputs: bool = False,
166    download: bool = False,
167    **kwargs
168) -> Dataset:
169    """Get the AutoPET-Organ dataset for organ segmentation in whole-body PET.
170
171    Args:
172        path: Filepath to a folder where the data is downloaded for further processing.
173        patch_shape: The patch shape to use for training.
174        harmonize: Whether to map the second id of the right middle lobe onto its first one.
175        autopet_path: Filepath to an existing AutoPET download.
176        resize_inputs: Whether to resize inputs to the desired patch shape.
177        download: Whether to download the data if it is not present.
178        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
179
180    Returns:
181        The segmentation dataset.
182    """
183    raw_paths, label_paths = get_autopet_organ_paths(path, harmonize, autopet_path, download)
184
185    if resize_inputs:
186        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
187        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
188            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
189        )
190
191    return torch_em.default_segmentation_dataset(
192        raw_paths=raw_paths,
193        raw_key="data",
194        label_paths=label_paths,
195        label_key="data",
196        patch_shape=patch_shape,
197        is_seg_dataset=True,
198        **kwargs
199    )

Get the AutoPET-Organ dataset for organ segmentation in whole-body PET.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • patch_shape: The patch shape to use for training.
  • harmonize: Whether to map the second id of the right middle lobe onto its first one.
  • autopet_path: Filepath to an existing AutoPET download.
  • resize_inputs: Whether to resize inputs to the desired 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_autopet_organ_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, ...], harmonize: bool = True, autopet_path: Union[os.PathLike, str, NoneType] = None, resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
202def get_autopet_organ_loader(
203    path: Union[os.PathLike, str],
204    batch_size: int,
205    patch_shape: Tuple[int, ...],
206    harmonize: bool = True,
207    autopet_path: Optional[Union[os.PathLike, str]] = None,
208    resize_inputs: bool = False,
209    download: bool = False,
210    **kwargs
211) -> DataLoader:
212    """Get the AutoPET-Organ dataloader for organ segmentation in whole-body PET.
213
214    Args:
215        path: Filepath to a folder where the data is downloaded for further processing.
216        batch_size: The batch size for training.
217        patch_shape: The patch shape to use for training.
218        harmonize: Whether to map the second id of the right middle lobe onto its first one.
219        autopet_path: Filepath to an existing AutoPET download.
220        resize_inputs: Whether to resize inputs to the desired patch shape.
221        download: Whether to download the data if it is not present.
222        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
223
224    Returns:
225        The DataLoader.
226    """
227    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
228    dataset = get_autopet_organ_dataset(
229        path, patch_shape, harmonize, autopet_path, resize_inputs, download, **ds_kwargs
230    )
231    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the AutoPET-Organ dataloader for organ segmentation in whole-body PET.

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.
  • harmonize: Whether to map the second id of the right middle lobe onto its first one.
  • autopet_path: Filepath to an existing AutoPET download.
  • resize_inputs: Whether to resize inputs to the desired 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.