torch_em.data.datasets.medical.cc_tumor_heterogeneity

The CC-Tumor-Heterogeneity (CCTH) dataset contains annotations for cervix and tumor segmentation in T2-weighted MRI of patients with advanced cervical cancer.

It consists of 68 T2-weighted MRI volumes (67 sagittal, 1 axial) of 23 patients, acquired at up to three time points during radiochemotherapy. Each volume comes with two contours drawn in MIM: the uterus / cervix (ROI with magenta display color) and the tumor inside of it (ROI with cyan display color), which are distributed as DICOM RTSTRUCT. This module downloads only the RTSTRUCT series and the T2-weighted MRI series they reference (not the DCE / DWI MRI and PET/CT of the collection), rasterizes the contours onto the MRI grid (see torch_em.data.datasets.util.rasterize_rtstruct) and stores images and labels in hdf5 files. The semantic label ids are: 1: tumor, 2: cervix / uterus (excluding the tumor).

NOTE: This requires the pydicom python package.

The dataset is located at https://www.cancerimagingarchive.net/collection/cc-tumor-heterogeneity/.

This dataset is from the publication https://doi.org/10.1016/j.ijrobp.2020.02.001. The data was released at https://doi.org/10.7937/TCIA.2022.6X0F-2S2T. Please cite it if you use this dataset in your research.

  1"""The CC-Tumor-Heterogeneity (CCTH) dataset contains annotations for cervix and tumor segmentation in T2-weighted
  2MRI of patients with advanced cervical cancer.
  3
  4It consists of 68 T2-weighted MRI volumes (67 sagittal, 1 axial) of 23 patients, acquired at up to three time
  5points during radiochemotherapy. Each volume comes with two contours drawn in MIM: the uterus / cervix (ROI with
  6magenta display color) and the tumor inside of it (ROI with cyan display color), which are distributed as DICOM
  7RTSTRUCT. This module downloads only the RTSTRUCT series and the T2-weighted MRI series they reference (not the
  8DCE / DWI MRI and PET/CT of the collection), rasterizes the contours onto the MRI grid
  9(see `torch_em.data.datasets.util.rasterize_rtstruct`) and stores images and labels in hdf5 files.
 10The semantic label ids are: 1: tumor, 2: cervix / uterus (excluding the tumor).
 11
 12NOTE: This requires the pydicom python package.
 13
 14The dataset is located at https://www.cancerimagingarchive.net/collection/cc-tumor-heterogeneity/.
 15
 16This dataset is from the publication https://doi.org/10.1016/j.ijrobp.2020.02.001.
 17The data was released at https://doi.org/10.7937/TCIA.2022.6X0F-2S2T.
 18Please cite it if you use this dataset in your research.
 19"""
 20
 21import os
 22import csv
 23import requests
 24from glob import glob
 25from tqdm import tqdm
 26from natsort import natsorted
 27from typing import Union, Tuple, List
 28
 29from torch.utils.data import Dataset, DataLoader
 30
 31import torch_em
 32
 33from .. import util
 34
 35
 36COLLECTION = "CC-Tumor-Heterogeneity"
 37
 38# The DICOM series are downloaded individually from TCIA via the NBIA REST API.
 39URL = util.NBIA_API_URL + "getSeries"
 40CHECKSUM = None
 41
 42LABEL_IDS = {"tumor": 1, "cervix": 2}
 43
 44# The two ROIs of each RTSTRUCT have the same name and are only distinguished by their display color.
 45ROI_COLORS = {(0, 235, 235): LABEL_IDS["tumor"], (255, 0, 255): LABEL_IDS["cervix"]}
 46
 47
 48def _read_rtstruct_info(rtstruct_path):
 49    """Get the referenced series UID and the mapping from ROI numbers to label ids."""
 50    import pydicom
 51
 52    rtstruct = pydicom.dcmread(rtstruct_path, stop_before_pixels=True)
 53    referenced_series = str(
 54        rtstruct.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[0]
 55        .RTReferencedSeriesSequence[0].SeriesInstanceUID
 56    )
 57    roi_labels = {}
 58    for roi_contour in rtstruct.ROIContourSequence:
 59        color = tuple(int(v) for v in roi_contour.ROIDisplayColor)
 60        assert color in ROI_COLORS, f"Unexpected ROI color {color} in {rtstruct_path}."
 61        roi_labels[int(roi_contour.ReferencedROINumber)] = ROI_COLORS[color]
 62    assert sorted(roi_labels.values()) == sorted(LABEL_IDS.values()), f"Unexpected ROIs in {rtstruct_path}."
 63    roi_names = {int(roi.ROINumber): str(roi.ROIName) for roi in rtstruct.StructureSetROISequence}
 64    return referenced_series, roi_labels, roi_names
 65
 66
 67def _preprocess_cc_tumor_heterogeneity(dicom_dir, csv_path, preprocessed_dir):
 68    import h5py
 69
 70    with open(csv_path, "r") as f:
 71        rtstruct_series = {row["Series UID"]: row["Subject ID"] for row in csv.DictReader(f)}
 72
 73    os.makedirs(preprocessed_dir, exist_ok=True)
 74    for series_uid, subject_id in tqdm(sorted(rtstruct_series.items()), desc="Preprocess CC-Tumor-Heterogeneity"):
 75        rtstruct_path = glob(os.path.join(dicom_dir, series_uid, "*.dcm"))[0]
 76        referenced_series, roi_labels, roi_names = _read_rtstruct_info(rtstruct_path)
 77        # The ROI names encode the imaging plane and time point, e.g. 'Ut-MRT2-Sag-1'.
 78        time_point = roi_names[1].replace("Ut-MRT2-", "")
 79        out_path = os.path.join(preprocessed_dir, f"{subject_id}_{time_point}.h5")
 80        if os.path.exists(out_path):
 81            continue
 82
 83        volume, geometry = util.load_dicom_series(os.path.join(dicom_dir, referenced_series))
 84        labels = util.rasterize_rtstruct(
 85            rtstruct_path, geometry, volume.shape, lambda roi_number, roi_name: roi_labels[roi_number]
 86        )
 87
 88        with h5py.File(out_path, "w") as f:
 89            f.create_dataset("raw", data=volume, compression="gzip")
 90            f.create_dataset("labels", data=labels, compression="gzip")
 91
 92
 93def get_cc_tumor_heterogeneity_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 94    """Download the CC-Tumor-Heterogeneity dataset.
 95
 96    Args:
 97        path: Filepath to a folder where the data is downloaded for further processing.
 98        download: Whether to download the data if it is not present.
 99
100    Returns:
101        Filepath where the preprocessed data is stored.
102    """
103    # NOTE: The preprocessing below skips volumes that were converted already, so an interrupted run resumes.
104    preprocessed_dir = os.path.join(path, "preprocessed")
105    dicom_dir = os.path.join(path, "dicom")
106    if not os.path.exists(dicom_dir) and not download:
107        raise RuntimeError(f"Cannot find the data at {path}, but download was set to False.")
108    os.makedirs(path, exist_ok=True)
109
110    # Download the RTSTRUCT series of the collection, and then the T2-weighted MRI series they reference.
111    # The series metadata are written after all series are downloaded, so their presence means it is complete.
112    csv_path = os.path.join(path, "cc_tumor_heterogeneity_rtstruct.csv")
113    image_csv_path = os.path.join(path, "cc_tumor_heterogeneity_images")
114    if not os.path.exists(csv_path) or not os.path.exists(f"{image_csv_path}.csv"):
115        response = requests.get(URL, params={"Collection": COLLECTION, "Modality": "RTSTRUCT"})
116        response.raise_for_status()
117        rtstruct_uids = sorted(series["SeriesInstanceUID"] for series in response.json())
118        csv_path = util.download_tcia_series(rtstruct_uids, dicom_dir, csv_path[:-len(".csv")])
119
120        image_uids = sorted(
121            _read_rtstruct_info(glob(os.path.join(dicom_dir, uid, "*.dcm"))[0])[0] for uid in rtstruct_uids
122        )
123        util.download_tcia_series(image_uids, dicom_dir, image_csv_path)
124
125    _preprocess_cc_tumor_heterogeneity(dicom_dir, csv_path, preprocessed_dir)
126    return preprocessed_dir
127
128
129def get_cc_tumor_heterogeneity_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
130    """Get paths to the CC-Tumor-Heterogeneity data.
131
132    Args:
133        path: Filepath to a folder where the data is downloaded for further processing.
134        download: Whether to download the data if it is not present.
135
136    Returns:
137        List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels').
138    """
139    data_dir = get_cc_tumor_heterogeneity_data(path, download)
140    volume_paths = natsorted(glob(os.path.join(data_dir, "*.h5")))
141    return volume_paths
142
143
144def get_cc_tumor_heterogeneity_dataset(
145    path: Union[os.PathLike, str],
146    patch_shape: Tuple[int, ...],
147    resize_inputs: bool = False,
148    download: bool = False,
149    **kwargs
150) -> Dataset:
151    """Get the CC-Tumor-Heterogeneity dataset for cervix and tumor segmentation in T2-weighted MRI.
152
153    Args:
154        path: Filepath to a folder where the data is downloaded for further processing.
155        patch_shape: The patch shape to use for training.
156        resize_inputs: Whether to resize inputs to the desired patch shape.
157        download: Whether to download the data if it is not present.
158        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
159
160    Returns:
161        The segmentation dataset.
162    """
163    volume_paths = get_cc_tumor_heterogeneity_paths(path, download)
164
165    if resize_inputs:
166        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
167        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
168            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
169        )
170
171    return torch_em.default_segmentation_dataset(
172        raw_paths=volume_paths,
173        raw_key="raw",
174        label_paths=volume_paths,
175        label_key="labels",
176        patch_shape=patch_shape,
177        is_seg_dataset=True,
178        **kwargs
179    )
180
181
182def get_cc_tumor_heterogeneity_loader(
183    path: Union[os.PathLike, str],
184    batch_size: int,
185    patch_shape: Tuple[int, ...],
186    resize_inputs: bool = False,
187    download: bool = False,
188    **kwargs
189) -> DataLoader:
190    """Get the CC-Tumor-Heterogeneity dataloader for cervix and tumor segmentation in T2-weighted MRI.
191
192    Args:
193        path: Filepath to a folder where the data is downloaded for further processing.
194        batch_size: The batch size for training.
195        patch_shape: The patch shape to use for training.
196        resize_inputs: Whether to resize inputs to the desired patch shape.
197        download: Whether to download the data if it is not present.
198        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
199
200    Returns:
201        The DataLoader.
202    """
203    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
204    dataset = get_cc_tumor_heterogeneity_dataset(path, patch_shape, resize_inputs, download, **ds_kwargs)
205    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
COLLECTION = 'CC-Tumor-Heterogeneity'
URL = 'https://services.cancerimagingarchive.net/nbia-api/services/v1/getSeries'
CHECKSUM = None
LABEL_IDS = {'tumor': 1, 'cervix': 2}
ROI_COLORS = {(0, 235, 235): 1, (255, 0, 255): 2}
def get_cc_tumor_heterogeneity_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 94def get_cc_tumor_heterogeneity_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 95    """Download the CC-Tumor-Heterogeneity dataset.
 96
 97    Args:
 98        path: Filepath to a folder where the data is downloaded for further processing.
 99        download: Whether to download the data if it is not present.
100
101    Returns:
102        Filepath where the preprocessed data is stored.
103    """
104    # NOTE: The preprocessing below skips volumes that were converted already, so an interrupted run resumes.
105    preprocessed_dir = os.path.join(path, "preprocessed")
106    dicom_dir = os.path.join(path, "dicom")
107    if not os.path.exists(dicom_dir) and not download:
108        raise RuntimeError(f"Cannot find the data at {path}, but download was set to False.")
109    os.makedirs(path, exist_ok=True)
110
111    # Download the RTSTRUCT series of the collection, and then the T2-weighted MRI series they reference.
112    # The series metadata are written after all series are downloaded, so their presence means it is complete.
113    csv_path = os.path.join(path, "cc_tumor_heterogeneity_rtstruct.csv")
114    image_csv_path = os.path.join(path, "cc_tumor_heterogeneity_images")
115    if not os.path.exists(csv_path) or not os.path.exists(f"{image_csv_path}.csv"):
116        response = requests.get(URL, params={"Collection": COLLECTION, "Modality": "RTSTRUCT"})
117        response.raise_for_status()
118        rtstruct_uids = sorted(series["SeriesInstanceUID"] for series in response.json())
119        csv_path = util.download_tcia_series(rtstruct_uids, dicom_dir, csv_path[:-len(".csv")])
120
121        image_uids = sorted(
122            _read_rtstruct_info(glob(os.path.join(dicom_dir, uid, "*.dcm"))[0])[0] for uid in rtstruct_uids
123        )
124        util.download_tcia_series(image_uids, dicom_dir, image_csv_path)
125
126    _preprocess_cc_tumor_heterogeneity(dicom_dir, csv_path, preprocessed_dir)
127    return preprocessed_dir

Download the CC-Tumor-Heterogeneity 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 preprocessed data is stored.

def get_cc_tumor_heterogeneity_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
130def get_cc_tumor_heterogeneity_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]:
131    """Get paths to the CC-Tumor-Heterogeneity data.
132
133    Args:
134        path: Filepath to a folder where the data is downloaded for further processing.
135        download: Whether to download the data if it is not present.
136
137    Returns:
138        List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels').
139    """
140    data_dir = get_cc_tumor_heterogeneity_data(path, download)
141    volume_paths = natsorted(glob(os.path.join(data_dir, "*.h5")))
142    return volume_paths

Get paths to the CC-Tumor-Heterogeneity data.

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:

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

def get_cc_tumor_heterogeneity_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, ...], resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
145def get_cc_tumor_heterogeneity_dataset(
146    path: Union[os.PathLike, str],
147    patch_shape: Tuple[int, ...],
148    resize_inputs: bool = False,
149    download: bool = False,
150    **kwargs
151) -> Dataset:
152    """Get the CC-Tumor-Heterogeneity dataset for cervix and tumor segmentation in T2-weighted MRI.
153
154    Args:
155        path: Filepath to a folder where the data is downloaded for further processing.
156        patch_shape: The patch shape to use for training.
157        resize_inputs: Whether to resize inputs to the desired patch shape.
158        download: Whether to download the data if it is not present.
159        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
160
161    Returns:
162        The segmentation dataset.
163    """
164    volume_paths = get_cc_tumor_heterogeneity_paths(path, download)
165
166    if resize_inputs:
167        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
168        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
169            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
170        )
171
172    return torch_em.default_segmentation_dataset(
173        raw_paths=volume_paths,
174        raw_key="raw",
175        label_paths=volume_paths,
176        label_key="labels",
177        patch_shape=patch_shape,
178        is_seg_dataset=True,
179        **kwargs
180    )

Get the CC-Tumor-Heterogeneity dataset for cervix and tumor segmentation in T2-weighted MRI.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • 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_cc_tumor_heterogeneity_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:
183def get_cc_tumor_heterogeneity_loader(
184    path: Union[os.PathLike, str],
185    batch_size: int,
186    patch_shape: Tuple[int, ...],
187    resize_inputs: bool = False,
188    download: bool = False,
189    **kwargs
190) -> DataLoader:
191    """Get the CC-Tumor-Heterogeneity dataloader for cervix and tumor segmentation in T2-weighted MRI.
192
193    Args:
194        path: Filepath to a folder where the data is downloaded for further processing.
195        batch_size: The batch size for training.
196        patch_shape: The patch shape to use for training.
197        resize_inputs: Whether to resize inputs to the desired patch shape.
198        download: Whether to download the data if it is not present.
199        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
200
201    Returns:
202        The DataLoader.
203    """
204    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
205    dataset = get_cc_tumor_heterogeneity_dataset(path, patch_shape, resize_inputs, download, **ds_kwargs)
206    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the CC-Tumor-Heterogeneity dataloader for cervix and tumor segmentation in T2-weighted MRI.

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.
  • 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.