torch_em.data.datasets.light_microscopy.fusionx
FusionX contains light microscopy images of mono- and multi-nucleated cells with instance segmentation annotations, curated for quantifying cell-to-cell fusion.
The dataset is located at https://zenodo.org/records/18596465 and is licensed under CC BY 4.0. A handful of annotated images are missing from the released archives; these are skipped automatically when building the paths.
1"""FusionX contains light microscopy images of mono- and multi-nucleated cells with 2instance segmentation annotations, curated for quantifying cell-to-cell fusion. 3 4The dataset is located at https://zenodo.org/records/18596465 and is licensed under 5CC BY 4.0. A handful of annotated images are missing from the released archives; these 6are skipped automatically when building the paths. 7""" 8 9import os 10from glob import glob 11from natsort import natsorted 12from typing import List, Literal, Optional, Tuple, Union 13 14from torch.utils.data import Dataset, DataLoader 15 16import torch_em 17 18from .. import util 19 20 21URL = "https://zenodo.org/records/18596465/files/{}?download=1" 22 23CHECKSUMS = { 24 "train_png.7z": "214988031e094b0aa9a181b52404a2eaef95bbe3f4921a6c92ac0c912faf7d70", 25 "train_png.json": "216e4a33860b46f6712ddf1317db7d89cd77337477630af46f425cfb562c97d6", 26 "test_png.7z": "f9769dc8a6587214d0f8919311fc13771def75830f04ba95b15289edfb8ed8d9", 27 "test_png.json": "244063f5a5599af6983e8c601fea476b26e1b31defdd2acfb634d16aa86f5cc7", 28} 29 30SPLITS = {"train": "train_png.json", "test": "test_png.json"} 31 32 33def _create_segmentations_from_coco_annotations(path, split): 34 """Convert COCO mask annotations to instance segmentation masks.""" 35 import numpy as np 36 import imageio.v3 as imageio 37 from tqdm import tqdm 38 39 try: 40 from pycocotools.coco import COCO 41 except ImportError: 42 raise ImportError( 43 "'pycocotools' is required for processing the FusionX ground-truth. " 44 "Install it with 'conda install -c conda-forge pycocotools'." 45 ) 46 47 image_dir = os.path.join(path, f"{split}_png") 48 label_dir = os.path.join(path, "labels", split) 49 if os.path.exists(label_dir): 50 label_paths = natsorted(glob(os.path.join(label_dir, "*.tif"))) 51 if len(label_paths) > 0: 52 image_paths = [ 53 os.path.join(image_dir, f"{os.path.splitext(os.path.basename(p))[0]}.png") for p in label_paths 54 ] 55 return natsorted(image_paths), label_paths 56 57 os.makedirs(label_dir, exist_ok=True) 58 59 ann_file = os.path.join(path, SPLITS[split]) 60 coco = COCO(ann_file) 61 62 image_paths, label_paths = [], [] 63 for image_id in tqdm(coco.getImgIds(), desc=f"Creating FusionX segmentations ({split})"): 64 image_metadata = coco.loadImgs(image_id)[0] 65 file_name = image_metadata["file_name"] 66 67 image_path = os.path.join(image_dir, file_name) 68 if not os.path.exists(image_path): 69 continue # A few annotated images are missing from the released archive. 70 image_paths.append(image_path) 71 72 label_path = os.path.join(label_dir, f"{os.path.splitext(file_name)[0]}.tif") 73 label_paths.append(label_path) 74 if os.path.exists(label_path): 75 continue 76 77 annotations = coco.loadAnns(coco.getAnnIds(imgIds=image_id)) 78 shape = (image_metadata["height"], image_metadata["width"]) 79 seg = np.zeros(shape, dtype="uint32") 80 81 # Paint the largest cells first, so smaller overlapping cells stay visible on top. 82 masks = [coco.annToMask(a).astype(bool) for a in annotations] 83 sorting = np.argsort([m.sum() for m in masks])[::-1] 84 for seg_id, idx in enumerate(sorting, 1): 85 seg[masks[idx]] = seg_id 86 87 imageio.imwrite(label_path, seg.astype("uint16"), compression="zlib") 88 89 assert len(image_paths) == len(label_paths) and len(image_paths) > 0 90 return natsorted(image_paths), natsorted(label_paths) 91 92 93def get_fusionx_data(path: Union[os.PathLike, str], download: bool = False) -> str: 94 """Download the FusionX dataset. 95 96 Args: 97 path: Filepath to a folder where the downloaded data will be saved. 98 download: Whether to download the data if it is not present. 99 100 Returns: 101 The filepath to the folder where the data is stored. 102 """ 103 if os.path.exists(os.path.join(path, "train_png")) and os.path.exists(os.path.join(path, "test_png")): 104 return path 105 106 if not download: 107 raise RuntimeError(f"Cannot find the data at {path}, but 'download' is set to False.") 108 109 os.makedirs(path, exist_ok=True) 110 for fname, checksum in CHECKSUMS.items(): 111 fpath = os.path.join(path, fname) 112 util.download_source(fpath, URL.format(fname), download, checksum=checksum) 113 if fname.endswith(".7z"): 114 util.unzip_7z(fpath, path, remove=True) 115 116 return path 117 118 119def get_fusionx_paths( 120 path: Union[os.PathLike, str], split: Literal["train", "test"], download: bool = False, 121) -> Tuple[List[str], List[str]]: 122 """Get paths to the FusionX data. 123 124 Args: 125 path: Filepath to a folder where the downloaded data will be saved. 126 split: The data split to use. Either 'train' or 'test'. 127 download: Whether to download the data if it is not present. 128 129 Returns: 130 List of filepaths for the image data. 131 List of filepaths for the label data. 132 """ 133 assert split in SPLITS, f"'{split}' is not a valid split. Choose from {list(SPLITS.keys())}." 134 data_dir = get_fusionx_data(path, download) 135 return _create_segmentations_from_coco_annotations(data_dir, split) 136 137 138def get_fusionx_dataset( 139 path: Union[os.PathLike, str], 140 patch_shape: Tuple[int, int], 141 split: Literal["train", "test"], 142 offsets: Optional[List[List[int]]] = None, 143 boundaries: bool = False, 144 binary: bool = False, 145 download: bool = False, 146 **kwargs, 147) -> Dataset: 148 """Get the FusionX dataset for cell instance segmentation. 149 150 Args: 151 path: Filepath to a folder where the downloaded data will be saved. 152 patch_shape: The patch shape to use for training. 153 split: The data split to use. Either 'train' or 'test'. 154 offsets: Offset values for affinity computation used as target. 155 boundaries: Whether to compute boundaries as the target. 156 binary: Whether to use a binary segmentation target. 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 image_paths, label_paths = get_fusionx_paths(path, split, download) 164 165 kwargs, _ = util.add_instance_label_transform( 166 kwargs, add_binary_target=True, offsets=offsets, boundaries=boundaries, binary=binary 167 ) 168 kwargs = util.update_kwargs(kwargs, "ndim", 2) 169 170 return torch_em.default_segmentation_dataset( 171 raw_paths=image_paths, 172 raw_key=None, 173 label_paths=label_paths, 174 label_key=None, 175 patch_shape=patch_shape, 176 is_seg_dataset=False, 177 with_channels=True, 178 **kwargs, 179 ) 180 181 182def get_fusionx_loader( 183 path: Union[os.PathLike, str], 184 batch_size: int, 185 patch_shape: Tuple[int, int], 186 split: Literal["train", "test"], 187 offsets: Optional[List[List[int]]] = None, 188 boundaries: bool = False, 189 binary: bool = False, 190 download: bool = False, 191 **kwargs, 192) -> DataLoader: 193 """Get the FusionX dataloader for cell instance segmentation. 194 195 Args: 196 path: Filepath to a folder where the downloaded data will be saved. 197 batch_size: The batch size for training. 198 patch_shape: The patch shape to use for training. 199 split: The data split to use. Either 'train' or 'test'. 200 offsets: Offset values for affinity computation used as target. 201 boundaries: Whether to compute boundaries as the target. 202 binary: Whether to use a binary segmentation target. 203 download: Whether to download the data if it is not present. 204 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or the PyTorch DataLoader. 205 206 Returns: 207 The DataLoader. 208 """ 209 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 210 dataset = get_fusionx_dataset( 211 path=path, 212 patch_shape=patch_shape, 213 split=split, 214 offsets=offsets, 215 boundaries=boundaries, 216 binary=binary, 217 download=download, 218 **ds_kwargs, 219 ) 220 return torch_em.get_data_loader(dataset=dataset, batch_size=batch_size, **loader_kwargs)
URL =
'https://zenodo.org/records/18596465/files/{}?download=1'
CHECKSUMS =
{'train_png.7z': '214988031e094b0aa9a181b52404a2eaef95bbe3f4921a6c92ac0c912faf7d70', 'train_png.json': '216e4a33860b46f6712ddf1317db7d89cd77337477630af46f425cfb562c97d6', 'test_png.7z': 'f9769dc8a6587214d0f8919311fc13771def75830f04ba95b15289edfb8ed8d9', 'test_png.json': '244063f5a5599af6983e8c601fea476b26e1b31defdd2acfb634d16aa86f5cc7'}
SPLITS =
{'train': 'train_png.json', 'test': 'test_png.json'}
def
get_fusionx_data(path: Union[os.PathLike, str], download: bool = False) -> str:
94def get_fusionx_data(path: Union[os.PathLike, str], download: bool = False) -> str: 95 """Download the FusionX dataset. 96 97 Args: 98 path: Filepath to a folder where the downloaded data will be saved. 99 download: Whether to download the data if it is not present. 100 101 Returns: 102 The filepath to the folder where the data is stored. 103 """ 104 if os.path.exists(os.path.join(path, "train_png")) and os.path.exists(os.path.join(path, "test_png")): 105 return path 106 107 if not download: 108 raise RuntimeError(f"Cannot find the data at {path}, but 'download' is set to False.") 109 110 os.makedirs(path, exist_ok=True) 111 for fname, checksum in CHECKSUMS.items(): 112 fpath = os.path.join(path, fname) 113 util.download_source(fpath, URL.format(fname), download, checksum=checksum) 114 if fname.endswith(".7z"): 115 util.unzip_7z(fpath, path, remove=True) 116 117 return path
Download the FusionX dataset.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- download: Whether to download the data if it is not present.
Returns:
The filepath to the folder where the data is stored.
def
get_fusionx_paths( path: Union[os.PathLike, str], split: Literal['train', 'test'], download: bool = False) -> Tuple[List[str], List[str]]:
120def get_fusionx_paths( 121 path: Union[os.PathLike, str], split: Literal["train", "test"], download: bool = False, 122) -> Tuple[List[str], List[str]]: 123 """Get paths to the FusionX data. 124 125 Args: 126 path: Filepath to a folder where the downloaded data will be saved. 127 split: The data split to use. Either 'train' or 'test'. 128 download: Whether to download the data if it is not present. 129 130 Returns: 131 List of filepaths for the image data. 132 List of filepaths for the label data. 133 """ 134 assert split in SPLITS, f"'{split}' is not a valid split. Choose from {list(SPLITS.keys())}." 135 data_dir = get_fusionx_data(path, download) 136 return _create_segmentations_from_coco_annotations(data_dir, split)
Get paths to the FusionX data.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- split: The data split to use. Either 'train' or 'test'.
- 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.
def
get_fusionx_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int], split: Literal['train', 'test'], offsets: Optional[List[List[int]]] = None, boundaries: bool = False, binary: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
139def get_fusionx_dataset( 140 path: Union[os.PathLike, str], 141 patch_shape: Tuple[int, int], 142 split: Literal["train", "test"], 143 offsets: Optional[List[List[int]]] = None, 144 boundaries: bool = False, 145 binary: bool = False, 146 download: bool = False, 147 **kwargs, 148) -> Dataset: 149 """Get the FusionX dataset for cell instance segmentation. 150 151 Args: 152 path: Filepath to a folder where the downloaded data will be saved. 153 patch_shape: The patch shape to use for training. 154 split: The data split to use. Either 'train' or 'test'. 155 offsets: Offset values for affinity computation used as target. 156 boundaries: Whether to compute boundaries as the target. 157 binary: Whether to use a binary segmentation target. 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 image_paths, label_paths = get_fusionx_paths(path, split, download) 165 166 kwargs, _ = util.add_instance_label_transform( 167 kwargs, add_binary_target=True, offsets=offsets, boundaries=boundaries, binary=binary 168 ) 169 kwargs = util.update_kwargs(kwargs, "ndim", 2) 170 171 return torch_em.default_segmentation_dataset( 172 raw_paths=image_paths, 173 raw_key=None, 174 label_paths=label_paths, 175 label_key=None, 176 patch_shape=patch_shape, 177 is_seg_dataset=False, 178 with_channels=True, 179 **kwargs, 180 )
Get the FusionX dataset for cell instance segmentation.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- patch_shape: The patch shape to use for training.
- split: The data split to use. Either 'train' or 'test'.
- offsets: Offset values for affinity computation used as target.
- boundaries: Whether to compute boundaries as the target.
- binary: Whether to use a binary segmentation target.
- 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_fusionx_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, int], split: Literal['train', 'test'], offsets: Optional[List[List[int]]] = None, boundaries: bool = False, binary: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
183def get_fusionx_loader( 184 path: Union[os.PathLike, str], 185 batch_size: int, 186 patch_shape: Tuple[int, int], 187 split: Literal["train", "test"], 188 offsets: Optional[List[List[int]]] = None, 189 boundaries: bool = False, 190 binary: bool = False, 191 download: bool = False, 192 **kwargs, 193) -> DataLoader: 194 """Get the FusionX dataloader for cell instance segmentation. 195 196 Args: 197 path: Filepath to a folder where the downloaded data will be saved. 198 batch_size: The batch size for training. 199 patch_shape: The patch shape to use for training. 200 split: The data split to use. Either 'train' or 'test'. 201 offsets: Offset values for affinity computation used as target. 202 boundaries: Whether to compute boundaries as the target. 203 binary: Whether to use a binary segmentation target. 204 download: Whether to download the data if it is not present. 205 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or the PyTorch DataLoader. 206 207 Returns: 208 The DataLoader. 209 """ 210 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 211 dataset = get_fusionx_dataset( 212 path=path, 213 patch_shape=patch_shape, 214 split=split, 215 offsets=offsets, 216 boundaries=boundaries, 217 binary=binary, 218 download=download, 219 **ds_kwargs, 220 ) 221 return torch_em.get_data_loader(dataset=dataset, batch_size=batch_size, **loader_kwargs)
Get the FusionX dataloader for cell instance segmentation.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- batch_size: The batch size for training.
- patch_shape: The patch shape to use for training.
- split: The data split to use. Either 'train' or 'test'.
- offsets: Offset values for affinity computation used as target.
- boundaries: Whether to compute boundaries as the target.
- binary: Whether to use a binary segmentation target.
- download: Whether to download the data if it is not present.
- kwargs: Additional keyword arguments for
torch_em.default_segmentation_datasetor the PyTorch DataLoader.
Returns:
The DataLoader.