torch_em.data.datasets.medical.orcascore

The orCaScore dataset contains annotations for coronary artery calcification in cardiac CT.

The data was curated for the orCaScore challenge (https://orcascore.grand-challenge.org), which was held at MICCAI 2014 and evaluates automatic coronary artery calcium scoring. The training set consists of 32 patients, 8 from each of four CT scanner vendors, and the test set of 40 patients without a reference standard. Each examination consists of a non-contrast enhanced CT image (the file suffix 'CTI') and a contrast enhanced CTA image (the file suffix 'CTAI'). The reference standard (the file suffix 'R') was annotated on the non-contrast CT images only, so this module pairs the labels with the CT images.

The reference standard labels every lesion with intensities above 130 HU by the coronary artery it belongs to, see LABEL_IDS: 1 = left anterior descending artery (calcifications of the left main coronary artery are labeled as LAD as well), 2 = left circumflex artery, 3 = right coronary artery.

NOTE: The data is only handed out to registered participants and the challenge has been closed for new registrations since August 2025, so it cannot be downloaded automatically. There is no openly published copy of it: the data was behind a signed confidentiality agreement from the start, and the original challenge website 'orcascore.isi.uu.nl' no longer resolves. To obtain the data, please follow these steps:

  • Ask the challenge organizers for access to the training data, either through https://orcascore.grand-challenge.org or by writing to 'j.m.wolterink@utwente.nl'.
  • Place the four downloaded archives 'Train_V1.rar', 'Train_V2.rar', 'Train_V3.rar' and 'Train_V4.rar' in the folder passed as 'path'. This module extracts them, or picks up the extracted MetaImage files ('TRVPCTI.mhd' with the matching '.raw' or '.zraw') if they are already extracted.

NOTE: The filenames and the archive names are taken from the archived download page of the original challenge website, http://web.archive.org/web/20180418103434/http://orcascore.isi.uu.nl:80/download/. It documents 'TR' for the training set (the test set uses 'TE'), 'V' for the vendor, 'P' for the patient, the suffix 'CTI' for the non-contrast CT, 'CTAI' for the CTA and 'R' for the reference standard, and the archive sizes 390 MiB, 815 MiB, 500 MiB and 608 MiB for the four training archives.

The MetaImage volumes are converted to hdf5 volumes (the keys are 'raw' and 'labels') by this module.

The dataset is located at https://orcascore.grand-challenge.org.

This dataset is from the publication https://doi.org/10.1118/1.4945696. Please cite it if you use this dataset in your research.

  1"""The orCaScore dataset contains annotations for coronary artery calcification in cardiac CT.
  2
  3The data was curated for the orCaScore challenge (https://orcascore.grand-challenge.org), which was held at
  4MICCAI 2014 and evaluates automatic coronary artery calcium scoring. The training set consists of 32 patients,
  58 from each of four CT scanner vendors, and the test set of 40 patients without a reference standard.
  6Each examination consists of a non-contrast enhanced CT image (the file suffix 'CTI') and a contrast enhanced
  7CTA image (the file suffix 'CTAI'). The reference standard (the file suffix 'R') was annotated on the
  8non-contrast CT images only, so this module pairs the labels with the CT images.
  9
 10The reference standard labels every lesion with intensities above 130 HU by the coronary artery it belongs to,
 11see `LABEL_IDS`: 1 = left anterior descending artery (calcifications of the left main coronary artery are
 12labeled as LAD as well), 2 = left circumflex artery, 3 = right coronary artery.
 13
 14NOTE: The data is only handed out to registered participants and the challenge has been closed for new
 15registrations since August 2025, so it cannot be downloaded automatically. There is no openly published
 16copy of it: the data was behind a signed confidentiality agreement from the start, and the original
 17challenge website 'orcascore.isi.uu.nl' no longer resolves. To obtain the data, please follow these steps:
 18- Ask the challenge organizers for access to the training data, either through
 19  https://orcascore.grand-challenge.org or by writing to 'j.m.wolterink@utwente.nl'.
 20- Place the four downloaded archives 'Train_V1.rar', 'Train_V2.rar', 'Train_V3.rar' and 'Train_V4.rar' in the
 21  folder passed as 'path'. This module extracts them, or picks up the extracted MetaImage files
 22  ('TRV<vendor>P<patient>CTI.mhd' with the matching '.raw' or '.zraw') if they are already extracted.
 23
 24NOTE: The filenames and the archive names are taken from the archived download page of the original
 25challenge website, http://web.archive.org/web/20180418103434/http://orcascore.isi.uu.nl:80/download/. It
 26documents 'TR' for the training set (the test set uses 'TE'), 'V' for the vendor, 'P' for the patient,
 27the suffix 'CTI' for the non-contrast CT, 'CTAI' for the CTA and 'R' for the reference standard, and the
 28archive sizes 390 MiB, 815 MiB, 500 MiB and 608 MiB for the four training archives.
 29
 30The MetaImage volumes are converted to hdf5 volumes (the keys are 'raw' and 'labels') by this module.
 31
 32The dataset is located at https://orcascore.grand-challenge.org.
 33
 34This dataset is from the publication https://doi.org/10.1118/1.4945696.
 35Please cite it if you use this dataset in your research.
 36"""
 37
 38import os
 39import zlib
 40from glob import glob
 41from tqdm import tqdm
 42from natsort import natsorted
 43from typing import Union, Tuple, List
 44
 45import numpy as np
 46
 47from torch.utils.data import Dataset, DataLoader
 48
 49import torch_em
 50
 51from .. import util
 52
 53
 54LABEL_IDS = {"background": 0, "LAD": 1, "LCX": 2, "RCA": 3}
 55
 56ARCHIVE_NAMES = ["Train_V1.rar", "Train_V2.rar", "Train_V3.rar", "Train_V4.rar"]
 57
 58N_VOLUMES = 32
 59
 60METAIMAGE_DTYPES = {
 61    "MET_CHAR": "int8",
 62    "MET_UCHAR": "uint8",
 63    "MET_SHORT": "int16",
 64    "MET_USHORT": "uint16",
 65    "MET_INT": "int32",
 66    "MET_UINT": "uint32",
 67    "MET_FLOAT": "float32",
 68    "MET_DOUBLE": "float64",
 69}
 70
 71
 72def _read_metaimage(mhd_path):
 73    """Read a MetaImage volume (a '.mhd' header with a '.raw' or a zlib compressed '.zraw' data file)."""
 74    header = {}
 75    with open(mhd_path, "r") as f:
 76        for line in f:
 77            if "=" not in line:
 78                continue
 79            key, value = line.split("=", 1)
 80            header[key.strip()] = value.strip()
 81
 82    shape = [int(v) for v in header["DimSize"].split()][::-1]  # The data is stored with the first axis last.
 83    dtype = np.dtype(METAIMAGE_DTYPES[header["ElementType"]])
 84    dtype = dtype.newbyteorder(">" if header.get("BinaryDataByteOrderMSB", "False") == "True" else "<")
 85
 86    with open(os.path.join(os.path.dirname(mhd_path), header["ElementDataFile"]), "rb") as f:
 87        data = f.read()
 88    if header.get("CompressedData", "False") == "True":
 89        data = zlib.decompress(data)
 90
 91    return np.frombuffer(data, dtype=dtype).reshape(shape)
 92
 93
 94def _preprocess_inputs(data_dir, preprocessed_dir):
 95    import h5py
 96
 97    label_paths = natsorted(glob(os.path.join(data_dir, "**", "TRV*P*R.mhd"), recursive=True))
 98    os.makedirs(preprocessed_dir, exist_ok=True)
 99
100    for label_path in tqdm(label_paths, desc="Preprocessing the orCaScore cases"):
101        case_id = os.path.basename(label_path)[:-len("R.mhd")]
102        volume_path = os.path.join(preprocessed_dir, f"{case_id}.h5")
103        if os.path.exists(volume_path):
104            continue
105
106        raw = _read_metaimage(os.path.join(os.path.dirname(label_path), f"{case_id}CTI.mhd"))
107        labels = _read_metaimage(label_path)
108
109        # The file is written to a temporary path first, so that an interrupted run leaves no corrupt file.
110        with h5py.File(f"{volume_path}.tmp", "w") as f:
111            f.create_dataset("raw", data=raw, compression="gzip")
112            f.create_dataset("labels", data=labels.astype("uint8"), compression="gzip")
113
114        os.rename(f"{volume_path}.tmp", volume_path)
115
116
117def get_orcascore_data(path: Union[os.PathLike, str], download: bool = False) -> str:
118    """Obtain the orCaScore dataset.
119
120    Args:
121        path: Filepath to a folder where the manually downloaded data is stored.
122        download: Whether to download the data if it is not present. The data cannot be downloaded
123            automatically, so this raises if the data has not been downloaded manually.
124
125    Returns:
126        Filepath where the preprocessed data is stored.
127    """
128    preprocessed_dir = os.path.join(path, "preprocessed")
129    if len(glob(os.path.join(preprocessed_dir, "*.h5"))) == N_VOLUMES:
130        return preprocessed_dir
131
132    if not glob(os.path.join(path, "**", "TRV*P*R.mhd"), recursive=True):
133        archive_paths = [os.path.join(path, name) for name in ARCHIVE_NAMES]
134        if not any(os.path.exists(p) for p in archive_paths):
135            msg = "'torch_em' cannot download this dataset, because the orCaScore data is only handed out to "
136            msg += "registered participants and the challenge has been closed for new registrations. Please ask "
137            msg += "the organizers at 'https://orcascore.grand-challenge.org' or at 'j.m.wolterink@utwente.nl' for "
138            msg += f"the training data and place {ARCHIVE_NAMES} in '{path}'."
139            raise NotImplementedError(msg)
140
141        for archive_path in archive_paths:
142            util.unzip_rarfile(rar_path=archive_path, dst=path, remove=False)
143
144    _preprocess_inputs(path, preprocessed_dir)
145    return preprocessed_dir
146
147
148def get_orcascore_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
149    """Get paths to the orCaScore data.
150
151    Args:
152        path: Filepath to a folder where the manually downloaded data is stored.
153        download: Whether to download the data if it is not present.
154
155    Returns:
156        List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels').
157    """
158    data_dir = get_orcascore_data(path, download)
159    return natsorted(glob(os.path.join(data_dir, "*.h5")))
160
161
162def get_orcascore_dataset(
163    path: Union[os.PathLike, str],
164    patch_shape: Tuple[int, ...],
165    resize_inputs: bool = False,
166    download: bool = False,
167    **kwargs
168) -> Dataset:
169    """Get the orCaScore dataset for coronary artery calcification segmentation.
170
171    Args:
172        path: Filepath to a folder where the manually downloaded data is stored.
173        patch_shape: The patch shape to use for training.
174        resize_inputs: Whether to resize inputs to the desired patch shape.
175        download: Whether to download the data if it is not present.
176        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
177
178    Returns:
179        The segmentation dataset.
180    """
181    volume_paths = get_orcascore_paths(path, download)
182
183    if resize_inputs:
184        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
185        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
186            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
187        )
188
189    return torch_em.default_segmentation_dataset(
190        raw_paths=volume_paths,
191        raw_key="raw",
192        label_paths=volume_paths,
193        label_key="labels",
194        patch_shape=patch_shape,
195        is_seg_dataset=True,
196        **kwargs
197    )
198
199
200def get_orcascore_loader(
201    path: Union[os.PathLike, str],
202    batch_size: int,
203    patch_shape: Tuple[int, ...],
204    resize_inputs: bool = False,
205    download: bool = False,
206    **kwargs
207) -> DataLoader:
208    """Get the orCaScore dataloader for coronary artery calcification segmentation.
209
210    Args:
211        path: Filepath to a folder where the manually downloaded data is stored.
212        batch_size: The batch size for training.
213        patch_shape: The patch shape to use for training.
214        resize_inputs: Whether to resize inputs to the desired patch shape.
215        download: Whether to download the data if it is not present.
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_orcascore_dataset(path, patch_shape, resize_inputs, download, **ds_kwargs)
223    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
LABEL_IDS = {'background': 0, 'LAD': 1, 'LCX': 2, 'RCA': 3}
ARCHIVE_NAMES = ['Train_V1.rar', 'Train_V2.rar', 'Train_V3.rar', 'Train_V4.rar']
N_VOLUMES = 32
METAIMAGE_DTYPES = {'MET_CHAR': 'int8', 'MET_UCHAR': 'uint8', 'MET_SHORT': 'int16', 'MET_USHORT': 'uint16', 'MET_INT': 'int32', 'MET_UINT': 'uint32', 'MET_FLOAT': 'float32', 'MET_DOUBLE': 'float64'}
def get_orcascore_data(path: Union[os.PathLike, str], download: bool = False) -> str:
118def get_orcascore_data(path: Union[os.PathLike, str], download: bool = False) -> str:
119    """Obtain the orCaScore dataset.
120
121    Args:
122        path: Filepath to a folder where the manually downloaded data is stored.
123        download: Whether to download the data if it is not present. The data cannot be downloaded
124            automatically, so this raises if the data has not been downloaded manually.
125
126    Returns:
127        Filepath where the preprocessed data is stored.
128    """
129    preprocessed_dir = os.path.join(path, "preprocessed")
130    if len(glob(os.path.join(preprocessed_dir, "*.h5"))) == N_VOLUMES:
131        return preprocessed_dir
132
133    if not glob(os.path.join(path, "**", "TRV*P*R.mhd"), recursive=True):
134        archive_paths = [os.path.join(path, name) for name in ARCHIVE_NAMES]
135        if not any(os.path.exists(p) for p in archive_paths):
136            msg = "'torch_em' cannot download this dataset, because the orCaScore data is only handed out to "
137            msg += "registered participants and the challenge has been closed for new registrations. Please ask "
138            msg += "the organizers at 'https://orcascore.grand-challenge.org' or at 'j.m.wolterink@utwente.nl' for "
139            msg += f"the training data and place {ARCHIVE_NAMES} in '{path}'."
140            raise NotImplementedError(msg)
141
142        for archive_path in archive_paths:
143            util.unzip_rarfile(rar_path=archive_path, dst=path, remove=False)
144
145    _preprocess_inputs(path, preprocessed_dir)
146    return preprocessed_dir

Obtain the orCaScore dataset.

Arguments:
  • path: Filepath to a folder where the manually downloaded data is stored.
  • download: Whether to download the data if it is not present. The data cannot be downloaded automatically, so this raises if the data has not been downloaded manually.
Returns:

Filepath where the preprocessed data is stored.

def get_orcascore_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
149def get_orcascore_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
150    """Get paths to the orCaScore data.
151
152    Args:
153        path: Filepath to a folder where the manually downloaded data is stored.
154        download: Whether to download the data if it is not present.
155
156    Returns:
157        List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels').
158    """
159    data_dir = get_orcascore_data(path, download)
160    return natsorted(glob(os.path.join(data_dir, "*.h5")))

Get paths to the orCaScore data.

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

List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels').

def get_orcascore_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, ...], resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
163def get_orcascore_dataset(
164    path: Union[os.PathLike, str],
165    patch_shape: Tuple[int, ...],
166    resize_inputs: bool = False,
167    download: bool = False,
168    **kwargs
169) -> Dataset:
170    """Get the orCaScore dataset for coronary artery calcification segmentation.
171
172    Args:
173        path: Filepath to a folder where the manually downloaded data is stored.
174        patch_shape: The patch shape to use for training.
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    volume_paths = get_orcascore_paths(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=volume_paths,
192        raw_key="raw",
193        label_paths=volume_paths,
194        label_key="labels",
195        patch_shape=patch_shape,
196        is_seg_dataset=True,
197        **kwargs
198    )

Get the orCaScore dataset for coronary artery calcification segmentation.

Arguments:
  • path: Filepath to a folder where the manually downloaded data is stored.
  • patch_shape: The patch shape to use for training.
  • 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_orcascore_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, ...], resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
201def get_orcascore_loader(
202    path: Union[os.PathLike, str],
203    batch_size: int,
204    patch_shape: Tuple[int, ...],
205    resize_inputs: bool = False,
206    download: bool = False,
207    **kwargs
208) -> DataLoader:
209    """Get the orCaScore dataloader for coronary artery calcification segmentation.
210
211    Args:
212        path: Filepath to a folder where the manually downloaded data is stored.
213        batch_size: The batch size for training.
214        patch_shape: The patch shape to use for training.
215        resize_inputs: Whether to resize inputs to the desired patch shape.
216        download: Whether to download the data if it is not present.
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_orcascore_dataset(path, patch_shape, resize_inputs, download, **ds_kwargs)
224    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the orCaScore dataloader for coronary artery calcification segmentation.

Arguments:
  • path: Filepath to a folder where the manually downloaded data is stored.
  • batch_size: The batch size for training.
  • patch_shape: The patch shape to use for training.
  • 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.