torch_em.data.datasets.medical.rider_lung
The RIDER Lung CT dataset contains annotations for lung tumor segmentation in same-day repeat CT scans of non-small cell lung cancer patients.
It consists of 59 CT volumes (test and retest scan of 31 patients, with one scan missing for three patients)
with a delineation of the primary gross tumor volume by a radiation oncologist, which is distributed as DICOM
RTSTRUCT in the 'RIDER-LungCT-Seg' analysis result of the collection. Each RTSTRUCT contains a manual
delineation ('GTVp_torch_em.data.datasets.util.rasterize_rtstruct)
and stores the CT ('raw') and the binary tumor labels ('labels/manual' and 'labels/auto') in hdf5 files.
NOTE: This requires the pydicom python package.
The dataset is located at https://www.cancerimagingarchive.net/collection/rider-lung-ct/ and the segmentations at https://www.cancerimagingarchive.net/analysis-result/rider-lungct-seg/.
This dataset is from the publications https://doi.org/10.1148/radiol.2522081593 (images) and https://doi.org/10.1038/ncomms5006 (segmentations). The data was released at https://doi.org/10.7937/K9/TCIA.2015.U1X8A5NR and https://doi.org/10.7937/tcia.2020.jit9grk8. Please cite them if you use this dataset in your research.
1"""The RIDER Lung CT dataset contains annotations for lung tumor segmentation in same-day repeat CT scans 2of non-small cell lung cancer patients. 3 4It consists of 59 CT volumes (test and retest scan of 31 patients, with one scan missing for three patients) 5with a delineation of the primary gross tumor volume by a radiation oncologist, which is distributed as DICOM 6RTSTRUCT in the 'RIDER-LungCT-Seg' analysis result of the collection. Each RTSTRUCT contains a manual 7delineation ('GTVp_<scan>_man') and the result of an in-house autosegmentation method ('GTVp_<scan>_auto'). 8This module rasterizes the contours onto the CT grid (see `torch_em.data.datasets.util.rasterize_rtstruct`) 9and stores the CT ('raw') and the binary tumor labels ('labels/manual' and 'labels/auto') in hdf5 files. 10 11NOTE: This requires the pydicom python package. 12 13The dataset is located at https://www.cancerimagingarchive.net/collection/rider-lung-ct/ and the segmentations 14at https://www.cancerimagingarchive.net/analysis-result/rider-lungct-seg/. 15 16This dataset is from the publications https://doi.org/10.1148/radiol.2522081593 (images) 17and https://doi.org/10.1038/ncomms5006 (segmentations). 18The data was released at https://doi.org/10.7937/K9/TCIA.2015.U1X8A5NR and https://doi.org/10.7937/tcia.2020.jit9grk8. 19Please cite them if you use this dataset in your research. 20""" 21 22import os 23import csv 24from glob import glob 25from tqdm import tqdm 26from natsort import natsorted 27from typing import Union, Tuple, List, Literal 28 29import numpy as np 30 31from torch.utils.data import Dataset, DataLoader 32 33import torch_em 34 35from .. import util 36 37 38URLS = { 39 "images": "https://www.cancerimagingarchive.net/wp-content/uploads/RIDER-Lung-CT-Original-Scans-for-Leonard-Wee-Feb-10-2020-.tcia", # noqa 40 "labels": "https://www.cancerimagingarchive.net/wp-content/uploads/RIDER-Lung-CT-RTSTRUCTS-DICOM-SEGS-Leonard-Wee-Feb-10-2020.tcia", # noqa 41} 42 43# The DICOM series are downloaded individually from TCIA. 44CHECKSUMS = {"images": None, "labels": None} 45 46ANNOTATIONS = ["manual", "auto"] 47 48 49def _get_referenced_series(rtstruct_path): 50 import pydicom 51 52 rtstruct = pydicom.dcmread(rtstruct_path, stop_before_pixels=True) 53 return str( 54 rtstruct.ReferencedFrameOfReferenceSequence[0].RTReferencedStudySequence[0] 55 .RTReferencedSeriesSequence[0].SeriesInstanceUID 56 ) 57 58 59def _preprocess_rider_lung(dicom_dir, csv_path, preprocessed_dir): 60 import h5py 61 62 with open(csv_path, "r") as f: 63 rows = list(csv.DictReader(f)) 64 rtstruct_series = { 65 row["Series UID"]: (row["Subject ID"], row["Series Description"].lower()) for row in rows 66 if row["Modality"] == "RTSTRUCT" 67 } 68 69 os.makedirs(preprocessed_dir, exist_ok=True) 70 for series_uid, (subject_id, scan) in tqdm(sorted(rtstruct_series.items()), desc="Preprocess RIDER Lung CT"): 71 assert scan in ("test", "retest"), f"Unexpected RTSTRUCT series description: {scan}" 72 out_path = os.path.join(preprocessed_dir, f"{subject_id}_{scan}.h5") 73 if os.path.exists(out_path): 74 continue 75 76 rtstruct_path = glob(os.path.join(dicom_dir, series_uid, "*.dcm"))[0] 77 ct_dir = os.path.join(dicom_dir, _get_referenced_series(rtstruct_path)) 78 volume, geometry = util.load_dicom_series(ct_dir) 79 volume = np.round(volume).astype("int16") 80 81 with h5py.File(out_path, "w") as f: 82 f.create_dataset("raw", data=volume, compression="gzip") 83 for annotation in ANNOTATIONS: 84 roi_labels = {f"GTVp_{scan}_{'man' if annotation == 'manual' else 'auto'}": 1} 85 labels = util.rasterize_rtstruct(rtstruct_path, geometry, volume.shape, roi_labels) 86 # Not all RTSTRUCTs contain both delineations, e.g. RIDER-2016615262 has no 'auto' retest contour. 87 if labels.any(): 88 f.create_dataset(f"labels/{annotation}", data=labels, compression="gzip") 89 90 91def get_rider_lung_data(path: Union[os.PathLike, str], download: bool = False) -> str: 92 """Download the RIDER Lung CT dataset. 93 94 Args: 95 path: Filepath to a folder where the data is downloaded for further processing. 96 download: Whether to download the data if it is not present. 97 98 Returns: 99 Filepath where the preprocessed data is stored. 100 """ 101 # NOTE: The preprocessing below skips volumes that were converted already, so an interrupted run resumes. 102 preprocessed_dir = os.path.join(path, "preprocessed") 103 os.makedirs(path, exist_ok=True) 104 105 # Download the annotated CT series and the RTSTRUCT (and SEG) series from the two TCIA manifests. 106 # The series metadata are written after all series are downloaded, so their presence means it is complete. 107 dicom_dir = os.path.join(path, "dicom") 108 for name, url in URLS.items(): 109 csv_filename = os.path.join(path, f"rider_lung_{name}") 110 if not os.path.exists(f"{csv_filename}.csv"): 111 util.download_source_tcia( 112 path=os.path.join(path, os.path.basename(url)), url=url, dst=dicom_dir, 113 csv_filename=csv_filename, download=download, 114 ) 115 116 _preprocess_rider_lung(dicom_dir, os.path.join(path, "rider_lung_labels.csv"), preprocessed_dir) 117 return preprocessed_dir 118 119 120def get_rider_lung_paths( 121 path: Union[os.PathLike, str], annotation: Literal["manual", "auto"] = "manual", download: bool = False 122) -> List[str]: 123 """Get paths to the RIDER Lung CT data. 124 125 Args: 126 path: Filepath to a folder where the data is downloaded for further processing. 127 annotation: The tumor delineation to use as labels, either the 'manual' delineation by a radiation 128 oncologist or the result of the 'auto' segmentation method. 129 download: Whether to download the data if it is not present. 130 131 Returns: 132 List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data 133 ('labels/manual' and 'labels/auto'). 134 """ 135 import h5py 136 137 assert annotation in ANNOTATIONS, f"'{annotation}' is not a valid annotation. Choose one of {ANNOTATIONS}." 138 data_dir = get_rider_lung_data(path, download) 139 volume_paths = natsorted(glob(os.path.join(data_dir, "*.h5"))) 140 141 # A few volumes do not have both delineations, they are filtered out for the respective annotation. 142 def _has_annotation(volume_path): 143 with h5py.File(volume_path, "r") as f: 144 return f"labels/{annotation}" in f 145 146 return [volume_path for volume_path in volume_paths if _has_annotation(volume_path)] 147 148 149def get_rider_lung_dataset( 150 path: Union[os.PathLike, str], 151 patch_shape: Tuple[int, ...], 152 annotation: Literal["manual", "auto"] = "manual", 153 resize_inputs: bool = False, 154 download: bool = False, 155 **kwargs 156) -> Dataset: 157 """Get the RIDER Lung CT dataset for lung tumor segmentation. 158 159 Args: 160 path: Filepath to a folder where the data is downloaded for further processing. 161 patch_shape: The patch shape to use for training. 162 annotation: The tumor delineation to use as labels, either the 'manual' delineation by a radiation 163 oncologist or the result of the 'auto' segmentation method. 164 resize_inputs: Whether to resize inputs to the desired patch shape. 165 download: Whether to download the data if it is not present. 166 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`. 167 168 Returns: 169 The segmentation dataset. 170 """ 171 volume_paths = get_rider_lung_paths(path, annotation, download) 172 173 if resize_inputs: 174 resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False} 175 kwargs, patch_shape = util.update_kwargs_for_resize_trafo( 176 kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs 177 ) 178 179 return torch_em.default_segmentation_dataset( 180 raw_paths=volume_paths, 181 raw_key="raw", 182 label_paths=volume_paths, 183 label_key=f"labels/{annotation}", 184 patch_shape=patch_shape, 185 is_seg_dataset=True, 186 **kwargs 187 ) 188 189 190def get_rider_lung_loader( 191 path: Union[os.PathLike, str], 192 batch_size: int, 193 patch_shape: Tuple[int, ...], 194 annotation: Literal["manual", "auto"] = "manual", 195 resize_inputs: bool = False, 196 download: bool = False, 197 **kwargs 198) -> DataLoader: 199 """Get the RIDER Lung CT dataloader for lung tumor segmentation. 200 201 Args: 202 path: Filepath to a folder where the data is downloaded for further processing. 203 batch_size: The batch size for training. 204 patch_shape: The patch shape to use for training. 205 annotation: The tumor delineation to use as labels, either the 'manual' delineation by a radiation 206 oncologist or the result of the 'auto' segmentation method. 207 resize_inputs: Whether to resize inputs to the desired patch shape. 208 download: Whether to download the data if it is not present. 209 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader. 210 211 Returns: 212 The DataLoader. 213 """ 214 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 215 dataset = get_rider_lung_dataset(path, patch_shape, annotation, resize_inputs, download, **ds_kwargs) 216 return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
92def get_rider_lung_data(path: Union[os.PathLike, str], download: bool = False) -> str: 93 """Download the RIDER Lung CT dataset. 94 95 Args: 96 path: Filepath to a folder where the data is downloaded for further processing. 97 download: Whether to download the data if it is not present. 98 99 Returns: 100 Filepath where the preprocessed data is stored. 101 """ 102 # NOTE: The preprocessing below skips volumes that were converted already, so an interrupted run resumes. 103 preprocessed_dir = os.path.join(path, "preprocessed") 104 os.makedirs(path, exist_ok=True) 105 106 # Download the annotated CT series and the RTSTRUCT (and SEG) series from the two TCIA manifests. 107 # The series metadata are written after all series are downloaded, so their presence means it is complete. 108 dicom_dir = os.path.join(path, "dicom") 109 for name, url in URLS.items(): 110 csv_filename = os.path.join(path, f"rider_lung_{name}") 111 if not os.path.exists(f"{csv_filename}.csv"): 112 util.download_source_tcia( 113 path=os.path.join(path, os.path.basename(url)), url=url, dst=dicom_dir, 114 csv_filename=csv_filename, download=download, 115 ) 116 117 _preprocess_rider_lung(dicom_dir, os.path.join(path, "rider_lung_labels.csv"), preprocessed_dir) 118 return preprocessed_dir
Download the RIDER Lung CT 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.
121def get_rider_lung_paths( 122 path: Union[os.PathLike, str], annotation: Literal["manual", "auto"] = "manual", download: bool = False 123) -> List[str]: 124 """Get paths to the RIDER Lung CT data. 125 126 Args: 127 path: Filepath to a folder where the data is downloaded for further processing. 128 annotation: The tumor delineation to use as labels, either the 'manual' delineation by a radiation 129 oncologist or the result of the 'auto' segmentation method. 130 download: Whether to download the data if it is not present. 131 132 Returns: 133 List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data 134 ('labels/manual' and 'labels/auto'). 135 """ 136 import h5py 137 138 assert annotation in ANNOTATIONS, f"'{annotation}' is not a valid annotation. Choose one of {ANNOTATIONS}." 139 data_dir = get_rider_lung_data(path, download) 140 volume_paths = natsorted(glob(os.path.join(data_dir, "*.h5"))) 141 142 # A few volumes do not have both delineations, they are filtered out for the respective annotation. 143 def _has_annotation(volume_path): 144 with h5py.File(volume_path, "r") as f: 145 return f"labels/{annotation}" in f 146 147 return [volume_path for volume_path in volume_paths if _has_annotation(volume_path)]
Get paths to the RIDER Lung CT data.
Arguments:
- path: Filepath to a folder where the data is downloaded for further processing.
- annotation: The tumor delineation to use as labels, either the 'manual' delineation by a radiation oncologist or the result of the 'auto' segmentation method.
- 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/manual' and 'labels/auto').
150def get_rider_lung_dataset( 151 path: Union[os.PathLike, str], 152 patch_shape: Tuple[int, ...], 153 annotation: Literal["manual", "auto"] = "manual", 154 resize_inputs: bool = False, 155 download: bool = False, 156 **kwargs 157) -> Dataset: 158 """Get the RIDER Lung CT dataset for lung tumor segmentation. 159 160 Args: 161 path: Filepath to a folder where the data is downloaded for further processing. 162 patch_shape: The patch shape to use for training. 163 annotation: The tumor delineation to use as labels, either the 'manual' delineation by a radiation 164 oncologist or the result of the 'auto' segmentation method. 165 resize_inputs: Whether to resize inputs to the desired patch shape. 166 download: Whether to download the data if it is not present. 167 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`. 168 169 Returns: 170 The segmentation dataset. 171 """ 172 volume_paths = get_rider_lung_paths(path, annotation, download) 173 174 if resize_inputs: 175 resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False} 176 kwargs, patch_shape = util.update_kwargs_for_resize_trafo( 177 kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs 178 ) 179 180 return torch_em.default_segmentation_dataset( 181 raw_paths=volume_paths, 182 raw_key="raw", 183 label_paths=volume_paths, 184 label_key=f"labels/{annotation}", 185 patch_shape=patch_shape, 186 is_seg_dataset=True, 187 **kwargs 188 )
Get the RIDER Lung CT dataset for lung tumor segmentation.
Arguments:
- path: Filepath to a folder where the data is downloaded for further processing.
- patch_shape: The patch shape to use for training.
- annotation: The tumor delineation to use as labels, either the 'manual' delineation by a radiation oncologist or the result of the 'auto' segmentation method.
- 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.
191def get_rider_lung_loader( 192 path: Union[os.PathLike, str], 193 batch_size: int, 194 patch_shape: Tuple[int, ...], 195 annotation: Literal["manual", "auto"] = "manual", 196 resize_inputs: bool = False, 197 download: bool = False, 198 **kwargs 199) -> DataLoader: 200 """Get the RIDER Lung CT dataloader for lung tumor segmentation. 201 202 Args: 203 path: Filepath to a folder where the data is downloaded for further processing. 204 batch_size: The batch size for training. 205 patch_shape: The patch shape to use for training. 206 annotation: The tumor delineation to use as labels, either the 'manual' delineation by a radiation 207 oncologist or the result of the 'auto' segmentation method. 208 resize_inputs: Whether to resize inputs to the desired patch shape. 209 download: Whether to download the data if it is not present. 210 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader. 211 212 Returns: 213 The DataLoader. 214 """ 215 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 216 dataset = get_rider_lung_dataset(path, patch_shape, annotation, resize_inputs, download, **ds_kwargs) 217 return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
Get the RIDER Lung CT dataloader for lung tumor 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.
- annotation: The tumor delineation to use as labels, either the 'manual' delineation by a radiation oncologist or the result of the 'auto' segmentation method.
- 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_datasetor for the PyTorch DataLoader.
Returns:
The DataLoader.