torch_em.data.datasets.medical.polypgen
The PolypGen dataset contains annotations for polyp detection and segmentation in colonoscopy images and video sequence frames, collected from six different medical centres across Europe and Africa.
NOTE: The dataset is hosted on Synapse. Downloading it requires the 'synapseclient' python library and a Synapse account with an authentication token stored in the '~/.synapseConfig' file. See 'get_polypgen_data' for details.
The Synapse project 'syn26376615' has two versions of the multi-centre archive ('PolypGen2021_MultiCenterData_v2.zip' and '..._v3.zip'); this module downloads both (via 'download_source_synapse') but only extracts and uses the newer v3 archive, which unpacks into a 'data_C/images_C' and 'data_C/masks_C' folder per centre (confirmed by inspecting the real archive contents, not assumed).
The dataset is from the publication https://doi.org/10.1038/s41597-023-01981-y. Please cite it if you use this dataset for your research.
1"""The PolypGen dataset contains annotations for polyp detection and segmentation in 2colonoscopy images and video sequence frames, collected from six different medical 3centres across Europe and Africa. 4 5NOTE: The dataset is hosted on Synapse. Downloading it requires the 'synapseclient' 6python library and a Synapse account with an authentication token stored in the 7'~/.synapseConfig' file. See 'get_polypgen_data' for details. 8 9The Synapse project 'syn26376615' has two versions of the multi-centre archive 10('PolypGen2021_MultiCenterData_v2.zip' and '..._v3.zip'); this module downloads both 11(via 'download_source_synapse') but only extracts and uses the newer v3 archive, which 12unpacks into a 'data_C<i>/images_C<i>' and 'data_C<i>/masks_C<i>' folder per centre 13(confirmed by inspecting the real archive contents, not assumed). 14 15The dataset is from the publication https://doi.org/10.1038/s41597-023-01981-y. 16Please cite it if you use this dataset for your research. 17""" 18 19import os 20from glob import glob 21from tqdm import tqdm 22from pathlib import Path 23from natsort import natsorted 24from typing import Union, Tuple, List, Optional, Literal 25 26import numpy as np 27import imageio.v3 as imageio 28 29from torch.utils.data import Dataset, DataLoader 30 31import torch_em 32 33from .. import util 34 35 36ENTITY = "syn26376615" 37 38CENTERS = ["C1", "C2", "C3", "C4", "C5", "C6"] 39 40 41def get_polypgen_data(path: Union[os.PathLike, str], download: bool = False) -> str: 42 """Download the PolypGen dataset. 43 44 The dataset is located at https://www.synapse.org/Synapse:syn26376615. 45 46 Follow the instructions below to get access to the dataset. 47 - Create a free account at https://www.synapse.org. 48 - Generate a personal access token and store it in a '~/.synapseConfig' file, see 49 https://python-docs.synapse.org/tutorials/authentication/ for details. 50 - Install the 'synapseclient' python library. 51 52 Args: 53 path: Filepath to a folder where the data is downloaded for further processing. 54 download: Whether to download the data if it is not present. 55 56 Returns: 57 Filepath where the data is downloaded. 58 """ 59 extracted_dir = os.path.join(path, "PolypGen2021_MultiCenterData_v3") 60 if os.path.exists(extracted_dir): 61 return path 62 63 os.makedirs(path, exist_ok=True) 64 util.download_source_synapse(path=path, entity=ENTITY, download=download) 65 66 zip_path = os.path.join(path, "PolypGen2021_MultiCenterData_v3.zip") 67 util.unzip(zip_path=zip_path, dst=path, remove=False) 68 69 return path 70 71 72def get_polypgen_paths( 73 path: Union[os.PathLike, str], 74 center: Optional[Literal["C1", "C2", "C3", "C4", "C5", "C6"]] = None, 75 download: bool = False, 76) -> Tuple[List[str], List[str]]: 77 """Get paths to the PolypGen data. 78 79 Args: 80 path: Filepath to a folder where the data is downloaded for further processing. 81 center: The choice of a specific centre's data. By default, loads data from all centres. 82 download: Whether to download the data if it is not present. 83 84 Returns: 85 List of filepaths for the image data. 86 List of filepaths for the label data. 87 """ 88 data_dir = get_polypgen_data(path, download) 89 90 centers = CENTERS if center is None else [center] 91 92 image_paths, gt_paths = [], [] 93 for this_center in centers: 94 this_image_paths = natsorted( 95 glob(os.path.join(data_dir, "**", f"images_{this_center}", "*.jpg"), recursive=True) 96 ) 97 for image_path in this_image_paths: 98 # Masks live in a sibling 'masks_C<i>' folder and their filename has an extra '_mask' suffix, 99 # e.g. 'images_C1/100H0050.jpg' pairs with 'masks_C1/100H0050_mask.jpg'. 100 mask_dir = os.path.dirname(image_path).replace(f"images_{this_center}", f"masks_{this_center}") 101 gt_path = os.path.join(mask_dir, f"{Path(image_path).stem}_mask.jpg") 102 if not os.path.exists(gt_path): 103 continue 104 105 image_paths.append(image_path) 106 gt_paths.append(gt_path) 107 108 # The masks are lossily JPEG-compressed grayscale images replicated across 3 channels (background 109 # near 0, foreground near 255), not the single-channel binary masks 'ImageCollectionDataset' expects. 110 # They are binarized once here and cached as '.tif' files next to the original masks. 111 neu_gt_paths = [] 112 for gt_path in tqdm(gt_paths, desc="Preprocessing PolypGen masks"): 113 neu_gt_path = os.path.join(os.path.dirname(gt_path), f"{Path(gt_path).stem}.tif") 114 neu_gt_paths.append(neu_gt_path) 115 if os.path.exists(neu_gt_path): 116 continue 117 118 gt = imageio.imread(gt_path) 119 if gt.ndim == 3: # Some centres' masks are single-channel grayscale rather than RGB. 120 gt = np.mean(gt, axis=-1) 121 gt = (gt > 128).astype("uint8") 122 imageio.imwrite(neu_gt_path, gt, compression="zlib") 123 gt_paths = neu_gt_paths 124 125 assert len(image_paths) == len(gt_paths) and len(image_paths) > 0, ( 126 "No image-mask pairs were found. The expected per-centre 'images_C<i>' / 'masks_C<i>' folder layout " 127 "may not match the actual structure of the downloaded PolypGen data. Please inspect the data at " 128 f"'{data_dir}' and update the search pattern in 'get_polypgen_paths' accordingly." 129 ) 130 131 return image_paths, gt_paths 132 133 134def get_polypgen_dataset( 135 path: Union[os.PathLike, str], 136 patch_shape: Tuple[int, int], 137 center: Optional[Literal["C1", "C2", "C3", "C4", "C5", "C6"]] = None, 138 resize_inputs: bool = False, 139 download: bool = False, 140 **kwargs 141) -> Dataset: 142 """Get the PolypGen dataset for polyp segmentation in colonoscopy images. 143 144 Args: 145 path: Filepath to a folder where the data is downloaded for further processing. 146 patch_shape: The patch shape to use for training. 147 center: The choice of a specific centre's data. By default, loads data from all centres. 148 resize_inputs: Whether to resize inputs to the desired patch shape. 149 download: Whether to download the data if it is not present. 150 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`. 151 152 Returns: 153 The segmentation dataset. 154 """ 155 image_paths, gt_paths = get_polypgen_paths(path, center, download) 156 157 if resize_inputs: 158 resize_kwargs = {"patch_shape": patch_shape, "is_rgb": True} 159 kwargs, patch_shape = util.update_kwargs_for_resize_trafo( 160 kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs 161 ) 162 163 return torch_em.default_segmentation_dataset( 164 raw_paths=image_paths, 165 raw_key=None, 166 label_paths=gt_paths, 167 label_key=None, 168 patch_shape=patch_shape, 169 is_seg_dataset=False, 170 **kwargs 171 ) 172 173 174def get_polypgen_loader( 175 path: Union[os.PathLike, str], 176 batch_size: int, 177 patch_shape: Tuple[int, int], 178 center: Optional[Literal["C1", "C2", "C3", "C4", "C5", "C6"]] = None, 179 resize_inputs: bool = False, 180 download: bool = False, 181 **kwargs 182) -> DataLoader: 183 """Get the PolypGen dataloader for polyp segmentation in colonoscopy images. 184 185 Args: 186 path: Filepath to a folder where the data is downloaded for further processing. 187 batch_size: The batch size for training. 188 patch_shape: The patch shape to use for training. 189 center: The choice of a specific centre's data. By default, loads data from all centres. 190 resize_inputs: Whether to resize inputs to the desired patch shape. 191 download: Whether to download the data if it is not present. 192 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader. 193 194 Returns: 195 The DataLoader. 196 """ 197 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 198 dataset = get_polypgen_dataset(path, patch_shape, center, resize_inputs, download, **ds_kwargs) 199 return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
42def get_polypgen_data(path: Union[os.PathLike, str], download: bool = False) -> str: 43 """Download the PolypGen dataset. 44 45 The dataset is located at https://www.synapse.org/Synapse:syn26376615. 46 47 Follow the instructions below to get access to the dataset. 48 - Create a free account at https://www.synapse.org. 49 - Generate a personal access token and store it in a '~/.synapseConfig' file, see 50 https://python-docs.synapse.org/tutorials/authentication/ for details. 51 - Install the 'synapseclient' python library. 52 53 Args: 54 path: Filepath to a folder where the data is downloaded for further processing. 55 download: Whether to download the data if it is not present. 56 57 Returns: 58 Filepath where the data is downloaded. 59 """ 60 extracted_dir = os.path.join(path, "PolypGen2021_MultiCenterData_v3") 61 if os.path.exists(extracted_dir): 62 return path 63 64 os.makedirs(path, exist_ok=True) 65 util.download_source_synapse(path=path, entity=ENTITY, download=download) 66 67 zip_path = os.path.join(path, "PolypGen2021_MultiCenterData_v3.zip") 68 util.unzip(zip_path=zip_path, dst=path, remove=False) 69 70 return path
Download the PolypGen dataset.
The dataset is located at https://www.synapse.org/Synapse:syn26376615.
Follow the instructions below to get access to the dataset.
- Create a free account at https://www.synapse.org.
- Generate a personal access token and store it in a '~/.synapseConfig' file, see https://python-docs.synapse.org/tutorials/authentication/ for details.
- Install the 'synapseclient' python library.
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 data is downloaded.
73def get_polypgen_paths( 74 path: Union[os.PathLike, str], 75 center: Optional[Literal["C1", "C2", "C3", "C4", "C5", "C6"]] = None, 76 download: bool = False, 77) -> Tuple[List[str], List[str]]: 78 """Get paths to the PolypGen data. 79 80 Args: 81 path: Filepath to a folder where the data is downloaded for further processing. 82 center: The choice of a specific centre's data. By default, loads data from all centres. 83 download: Whether to download the data if it is not present. 84 85 Returns: 86 List of filepaths for the image data. 87 List of filepaths for the label data. 88 """ 89 data_dir = get_polypgen_data(path, download) 90 91 centers = CENTERS if center is None else [center] 92 93 image_paths, gt_paths = [], [] 94 for this_center in centers: 95 this_image_paths = natsorted( 96 glob(os.path.join(data_dir, "**", f"images_{this_center}", "*.jpg"), recursive=True) 97 ) 98 for image_path in this_image_paths: 99 # Masks live in a sibling 'masks_C<i>' folder and their filename has an extra '_mask' suffix, 100 # e.g. 'images_C1/100H0050.jpg' pairs with 'masks_C1/100H0050_mask.jpg'. 101 mask_dir = os.path.dirname(image_path).replace(f"images_{this_center}", f"masks_{this_center}") 102 gt_path = os.path.join(mask_dir, f"{Path(image_path).stem}_mask.jpg") 103 if not os.path.exists(gt_path): 104 continue 105 106 image_paths.append(image_path) 107 gt_paths.append(gt_path) 108 109 # The masks are lossily JPEG-compressed grayscale images replicated across 3 channels (background 110 # near 0, foreground near 255), not the single-channel binary masks 'ImageCollectionDataset' expects. 111 # They are binarized once here and cached as '.tif' files next to the original masks. 112 neu_gt_paths = [] 113 for gt_path in tqdm(gt_paths, desc="Preprocessing PolypGen masks"): 114 neu_gt_path = os.path.join(os.path.dirname(gt_path), f"{Path(gt_path).stem}.tif") 115 neu_gt_paths.append(neu_gt_path) 116 if os.path.exists(neu_gt_path): 117 continue 118 119 gt = imageio.imread(gt_path) 120 if gt.ndim == 3: # Some centres' masks are single-channel grayscale rather than RGB. 121 gt = np.mean(gt, axis=-1) 122 gt = (gt > 128).astype("uint8") 123 imageio.imwrite(neu_gt_path, gt, compression="zlib") 124 gt_paths = neu_gt_paths 125 126 assert len(image_paths) == len(gt_paths) and len(image_paths) > 0, ( 127 "No image-mask pairs were found. The expected per-centre 'images_C<i>' / 'masks_C<i>' folder layout " 128 "may not match the actual structure of the downloaded PolypGen data. Please inspect the data at " 129 f"'{data_dir}' and update the search pattern in 'get_polypgen_paths' accordingly." 130 ) 131 132 return image_paths, gt_paths
Get paths to the PolypGen data.
Arguments:
- path: Filepath to a folder where the data is downloaded for further processing.
- center: The choice of a specific centre's data. By default, loads data from all centres.
- 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.
135def get_polypgen_dataset( 136 path: Union[os.PathLike, str], 137 patch_shape: Tuple[int, int], 138 center: Optional[Literal["C1", "C2", "C3", "C4", "C5", "C6"]] = None, 139 resize_inputs: bool = False, 140 download: bool = False, 141 **kwargs 142) -> Dataset: 143 """Get the PolypGen dataset for polyp segmentation in colonoscopy images. 144 145 Args: 146 path: Filepath to a folder where the data is downloaded for further processing. 147 patch_shape: The patch shape to use for training. 148 center: The choice of a specific centre's data. By default, loads data from all centres. 149 resize_inputs: Whether to resize inputs to the desired patch shape. 150 download: Whether to download the data if it is not present. 151 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`. 152 153 Returns: 154 The segmentation dataset. 155 """ 156 image_paths, gt_paths = get_polypgen_paths(path, center, download) 157 158 if resize_inputs: 159 resize_kwargs = {"patch_shape": patch_shape, "is_rgb": True} 160 kwargs, patch_shape = util.update_kwargs_for_resize_trafo( 161 kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs 162 ) 163 164 return torch_em.default_segmentation_dataset( 165 raw_paths=image_paths, 166 raw_key=None, 167 label_paths=gt_paths, 168 label_key=None, 169 patch_shape=patch_shape, 170 is_seg_dataset=False, 171 **kwargs 172 )
Get the PolypGen dataset for polyp segmentation in colonoscopy images.
Arguments:
- path: Filepath to a folder where the data is downloaded for further processing.
- patch_shape: The patch shape to use for training.
- center: The choice of a specific centre's data. By default, loads data from all centres.
- 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.
175def get_polypgen_loader( 176 path: Union[os.PathLike, str], 177 batch_size: int, 178 patch_shape: Tuple[int, int], 179 center: Optional[Literal["C1", "C2", "C3", "C4", "C5", "C6"]] = None, 180 resize_inputs: bool = False, 181 download: bool = False, 182 **kwargs 183) -> DataLoader: 184 """Get the PolypGen dataloader for polyp segmentation in colonoscopy images. 185 186 Args: 187 path: Filepath to a folder where the data is downloaded for further processing. 188 batch_size: The batch size for training. 189 patch_shape: The patch shape to use for training. 190 center: The choice of a specific centre's data. By default, loads data from all centres. 191 resize_inputs: Whether to resize inputs to the desired patch shape. 192 download: Whether to download the data if it is not present. 193 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader. 194 195 Returns: 196 The DataLoader. 197 """ 198 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 199 dataset = get_polypgen_dataset(path, patch_shape, center, resize_inputs, download, **ds_kwargs) 200 return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
Get the PolypGen dataloader for polyp segmentation in colonoscopy images.
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.
- center: The choice of a specific centre's data. By default, loads data from all centres.
- 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.