torch_em.data.datasets.medical.ski10
The SKI10 dataset contains annotations for knee bone and cartilage segmentation in MRI.
The data was curated for the SKI10 challenge (Segmentation of Knee Images 2010, https://ski10.grand-challenge.org),
which was held at MICCAI 2010. It consists of the 100 annotated training MRI of the challenge, which come from the
surgical planning program of Biomet Inc. The four annotated structures are the femoral bone and cartilage and the
tibial bone and cartilage, see LABEL_IDS. The 50 test MRI of the challenge were distributed without annotations
and are not included here.
NOTE: The official challenge is closed and its data cannot be downloaded from the challenge website anymore. This module uses the redistribution at https://huggingface.co/datasets/YongchengYAO/SKI10 (CC BY-NC-SA 4.0), which converted the original 'mhd' / 'raw' volumes to nifti without changing the image or mask values.
The volumes are stored with the slice axis last (the in-plane resolution is around 0.4 mm and the slice thickness is 1 mm), so they are converted to hdf5 volumes with the slice axis first (the keys are 'raw' and 'labels') by this module.
This dataset is from the publication 'Segmentation of Knee Images: A Grand Challenge' by Heimann et al., MICCAI Workshop on Medical Image Analysis for the Clinic (2010), which has no DOI. The challenge papers are collected at https://doi.org/10.5281/zenodo.4781231. Please cite it if you use this dataset in your research.
1"""The SKI10 dataset contains annotations for knee bone and cartilage segmentation in MRI. 2 3The data was curated for the SKI10 challenge (Segmentation of Knee Images 2010, https://ski10.grand-challenge.org), 4which was held at MICCAI 2010. It consists of the 100 annotated training MRI of the challenge, which come from the 5surgical planning program of Biomet Inc. The four annotated structures are the femoral bone and cartilage and the 6tibial bone and cartilage, see `LABEL_IDS`. The 50 test MRI of the challenge were distributed without annotations 7and are not included here. 8 9NOTE: The official challenge is closed and its data cannot be downloaded from the challenge website anymore. 10This module uses the redistribution at https://huggingface.co/datasets/YongchengYAO/SKI10 (CC BY-NC-SA 4.0), 11which converted the original 'mhd' / 'raw' volumes to nifti without changing the image or mask values. 12 13The volumes are stored with the slice axis last (the in-plane resolution is around 0.4 mm and the slice 14thickness is 1 mm), so they are converted to hdf5 volumes with the slice axis first (the keys are 'raw' and 15'labels') by this module. 16 17This dataset is from the publication 'Segmentation of Knee Images: A Grand Challenge' by Heimann et al., 18MICCAI Workshop on Medical Image Analysis for the Clinic (2010), which has no DOI. The challenge papers are 19collected at https://doi.org/10.5281/zenodo.4781231. 20Please cite it if you use this dataset in your research. 21""" 22 23import os 24from glob import glob 25from tqdm import tqdm 26from natsort import natsorted 27from typing import Union, Tuple, List 28 29import numpy as np 30 31from torch.utils.data import Dataset, DataLoader 32 33import torch_em 34 35from .. import util 36 37 38URL = "https://huggingface.co/datasets/YongchengYAO/SKI10/resolve/main/SKI10.zip" 39CHECKSUM = "d367c1c68143f450e4cad92111afc064f116372442a4816718e20ea556507bc8" 40 41LABEL_IDS = { 42 "background": 0, "femur_bone": 1, "femur_cartilage": 2, "tibia_bone": 3, "tibia_cartilage": 4, 43} 44 45N_VOLUMES = 100 46 47 48def _preprocess_inputs(data_dir, preprocessed_dir): 49 import h5py 50 import nibabel as nib 51 52 image_paths = natsorted(glob(os.path.join(data_dir, "image", "image-*.nii"))) 53 os.makedirs(preprocessed_dir, exist_ok=True) 54 55 for image_path in tqdm(image_paths, desc="Preprocessing the SKI10 volumes"): 56 case_id = os.path.basename(image_path)[len("image-"):-len(".nii")] 57 volume_path = os.path.join(preprocessed_dir, f"ski10_{case_id}.h5") 58 if os.path.exists(volume_path): 59 continue 60 61 label_path = os.path.join(data_dir, "label", f"labels-{case_id}.nii") 62 63 # The transpose maps the nifti axis order (X, Y, Z) to the (Z, Y, X) order used for the volumes, 64 # so that the first axis is the slice axis of the acquisition. 65 raw = np.asarray(nib.load(image_path).dataobj).T 66 labels = np.asarray(nib.load(label_path).dataobj).T.astype("uint8") 67 68 # The file is written to a temporary path first, so that an interrupted run leaves no corrupt file. 69 with h5py.File(f"{volume_path}.tmp", "w") as f: 70 f.create_dataset("raw", data=raw, compression="gzip") 71 f.create_dataset("labels", data=labels, compression="gzip") 72 73 os.rename(f"{volume_path}.tmp", volume_path) 74 75 76def get_ski10_data(path: Union[os.PathLike, str], download: bool = False) -> str: 77 """Download the SKI10 dataset. 78 79 Args: 80 path: Filepath to a folder where the data is downloaded for further processing. 81 download: Whether to download the data if it is not present. 82 83 Returns: 84 Filepath where the preprocessed data is stored. 85 """ 86 preprocessed_dir = os.path.join(path, "preprocessed") 87 if len(glob(os.path.join(preprocessed_dir, "*.h5"))) == N_VOLUMES: 88 return preprocessed_dir 89 90 os.makedirs(path, exist_ok=True) 91 92 data_dir = os.path.join(path, "SKI10") 93 if not os.path.exists(data_dir): 94 zip_path = os.path.join(path, "SKI10.zip") 95 util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM) 96 util.unzip(zip_path=zip_path, dst=path) 97 98 _preprocess_inputs(data_dir, preprocessed_dir) 99 return preprocessed_dir 100 101 102def get_ski10_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]: 103 """Get paths to the SKI10 data. 104 105 Args: 106 path: Filepath to a folder where the data is downloaded for further processing. 107 download: Whether to download the data if it is not present. 108 109 Returns: 110 List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels'). 111 """ 112 data_dir = get_ski10_data(path, download) 113 volume_paths = natsorted(glob(os.path.join(data_dir, "ski10_*.h5"))) 114 assert len(volume_paths) > 0 115 116 return volume_paths 117 118 119def get_ski10_dataset( 120 path: Union[os.PathLike, str], 121 patch_shape: Tuple[int, ...], 122 resize_inputs: bool = False, 123 download: bool = False, 124 **kwargs 125) -> Dataset: 126 """Get the SKI10 dataset for knee bone and cartilage segmentation. 127 128 Args: 129 path: Filepath to a folder where the data is downloaded for further processing. 130 patch_shape: The patch shape to use for training. 131 resize_inputs: Whether to resize inputs to the desired patch shape. 132 download: Whether to download the data if it is not present. 133 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`. 134 135 Returns: 136 The segmentation dataset. 137 """ 138 volume_paths = get_ski10_paths(path, download) 139 140 if resize_inputs: 141 resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False} 142 kwargs, patch_shape = util.update_kwargs_for_resize_trafo( 143 kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs 144 ) 145 146 return torch_em.default_segmentation_dataset( 147 raw_paths=volume_paths, 148 raw_key="raw", 149 label_paths=volume_paths, 150 label_key="labels", 151 patch_shape=patch_shape, 152 is_seg_dataset=True, 153 **kwargs 154 ) 155 156 157def get_ski10_loader( 158 path: Union[os.PathLike, str], 159 batch_size: int, 160 patch_shape: Tuple[int, ...], 161 resize_inputs: bool = False, 162 download: bool = False, 163 **kwargs 164) -> DataLoader: 165 """Get the SKI10 dataloader for knee bone and cartilage segmentation. 166 167 Args: 168 path: Filepath to a folder where the data is downloaded for further processing. 169 batch_size: The batch size for training. 170 patch_shape: The patch shape to use for training. 171 resize_inputs: Whether to resize inputs to the desired patch shape. 172 download: Whether to download the data if it is not present. 173 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader. 174 175 Returns: 176 The DataLoader. 177 """ 178 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 179 dataset = get_ski10_dataset(path, patch_shape, resize_inputs, download, **ds_kwargs) 180 return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
77def get_ski10_data(path: Union[os.PathLike, str], download: bool = False) -> str: 78 """Download the SKI10 dataset. 79 80 Args: 81 path: Filepath to a folder where the data is downloaded for further processing. 82 download: Whether to download the data if it is not present. 83 84 Returns: 85 Filepath where the preprocessed data is stored. 86 """ 87 preprocessed_dir = os.path.join(path, "preprocessed") 88 if len(glob(os.path.join(preprocessed_dir, "*.h5"))) == N_VOLUMES: 89 return preprocessed_dir 90 91 os.makedirs(path, exist_ok=True) 92 93 data_dir = os.path.join(path, "SKI10") 94 if not os.path.exists(data_dir): 95 zip_path = os.path.join(path, "SKI10.zip") 96 util.download_source(path=zip_path, url=URL, download=download, checksum=CHECKSUM) 97 util.unzip(zip_path=zip_path, dst=path) 98 99 _preprocess_inputs(data_dir, preprocessed_dir) 100 return preprocessed_dir
Download the SKI10 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.
103def get_ski10_paths(path: Union[os.PathLike, str], download: bool = False) -> List[str]: 104 """Get paths to the SKI10 data. 105 106 Args: 107 path: Filepath to a folder where the data is downloaded for further processing. 108 download: Whether to download the data if it is not present. 109 110 Returns: 111 List of filepaths for the hdf5 files, which contain the image data ('raw') and the label data ('labels'). 112 """ 113 data_dir = get_ski10_data(path, download) 114 volume_paths = natsorted(glob(os.path.join(data_dir, "ski10_*.h5"))) 115 assert len(volume_paths) > 0 116 117 return volume_paths
Get paths to the SKI10 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').
120def get_ski10_dataset( 121 path: Union[os.PathLike, str], 122 patch_shape: Tuple[int, ...], 123 resize_inputs: bool = False, 124 download: bool = False, 125 **kwargs 126) -> Dataset: 127 """Get the SKI10 dataset for knee bone and cartilage segmentation. 128 129 Args: 130 path: Filepath to a folder where the data is downloaded for further processing. 131 patch_shape: The patch shape to use for training. 132 resize_inputs: Whether to resize inputs to the desired patch shape. 133 download: Whether to download the data if it is not present. 134 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`. 135 136 Returns: 137 The segmentation dataset. 138 """ 139 volume_paths = get_ski10_paths(path, download) 140 141 if resize_inputs: 142 resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False} 143 kwargs, patch_shape = util.update_kwargs_for_resize_trafo( 144 kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs 145 ) 146 147 return torch_em.default_segmentation_dataset( 148 raw_paths=volume_paths, 149 raw_key="raw", 150 label_paths=volume_paths, 151 label_key="labels", 152 patch_shape=patch_shape, 153 is_seg_dataset=True, 154 **kwargs 155 )
Get the SKI10 dataset for knee bone and cartilage segmentation.
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.
158def get_ski10_loader( 159 path: Union[os.PathLike, str], 160 batch_size: int, 161 patch_shape: Tuple[int, ...], 162 resize_inputs: bool = False, 163 download: bool = False, 164 **kwargs 165) -> DataLoader: 166 """Get the SKI10 dataloader for knee bone and cartilage segmentation. 167 168 Args: 169 path: Filepath to a folder where the data is downloaded for further processing. 170 batch_size: The batch size for training. 171 patch_shape: The patch shape to use for training. 172 resize_inputs: Whether to resize inputs to the desired patch shape. 173 download: Whether to download the data if it is not present. 174 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader. 175 176 Returns: 177 The DataLoader. 178 """ 179 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 180 dataset = get_ski10_dataset(path, patch_shape, resize_inputs, download, **ds_kwargs) 181 return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
Get the SKI10 dataloader for knee bone and cartilage 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.
- 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.