torch_em.data.datasets.light_microscopy.livecell
The LIVECell dataset contains phase-contrast microscopy images and annotations for cell segmentations for 8 different cell lines.
This dataset is described in the publication https://doi.org/10.1038/s41592-021-01249-6. Please cite it if you use this dataset in your research.
1"""The LIVECell dataset contains phase-contrast microscopy images 2and annotations for cell segmentations for 8 different cell lines. 3 4This dataset is described in the publication https://doi.org/10.1038/s41592-021-01249-6. 5Please cite it if you use this dataset in your research. 6""" 7 8import os 9import json 10import requests 11from tqdm import tqdm 12from shutil import copyfileobj 13from typing import List, Optional, Sequence, Tuple, Union 14 15import numpy as np 16import imageio.v3 as imageio 17 18import torch 19from torch.utils.data import Dataset, DataLoader 20 21import torch_em 22 23from .. import util 24from ... import ImageCollectionDataset 25 26try: 27 from pycocotools.coco import COCO 28except ImportError: 29 COCO = None 30 31URLS = { 32 "images": "http://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/images.zip", 33 "train": ("http://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/annotations/" 34 "LIVECell/livecell_coco_train.json"), 35 "val": ("http://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/annotations/" 36 "LIVECell/livecell_coco_val.json"), 37 "test": ("http://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/annotations/" 38 "LIVECell/livecell_coco_test.json") 39} 40# TODO 41CHECKSUM = None 42 43CELL_TYPES = ['A172', 'BT474', 'BV2', 'Huh7', 'MCF7', 'SHSY5Y', 'SkBr3', 'SKOV3'] 44 45 46# TODO use download flag 47def _download_annotation_file(path, split, download): 48 annotation_file = os.path.join(path, f"{split}.json") 49 if not os.path.exists(annotation_file): 50 url = URLS[split] 51 print("Downloading livecell annotation file from", url) 52 with requests.get(url, stream=True) as r: 53 with open(annotation_file, 'wb') as f: 54 copyfileobj(r.raw, f) 55 return annotation_file 56 57 58def _annotations_to_instances(coco, image_metadata, category_ids): 59 import bioimage_cpp as bic 60 61 # create and save the segmentation 62 annotation_ids = coco.getAnnIds(imgIds=image_metadata["id"], catIds=category_ids) 63 annotations = coco.loadAnns(annotation_ids) 64 assert len(annotations) <= np.iinfo("uint16").max 65 shape = (image_metadata["height"], image_metadata["width"]) 66 seg = np.zeros(shape, dtype="uint32") 67 68 # sort annotations by size, except for iscrowd which go first 69 # we do this to minimize small noise from overlapping multi annotations 70 # (see below) 71 sizes = [ann["area"] if ann["iscrowd"] == 0 else 1 for ann in annotations] 72 sorting = np.argsort(sizes) 73 annotations = [annotations[i] for i in sorting] 74 75 for seg_id, annotation in enumerate(annotations, 1): 76 mask = coco.annToMask(annotation).astype("bool") 77 assert mask.shape == seg.shape 78 seg[mask] = seg_id 79 80 # some images have multiple masks per object with slightly different foreground 81 # this causes small noise objects we need to filter 82 min_size = 50 83 seg_ids, sizes = np.unique(seg, return_counts=True) 84 seg[np.isin(seg, seg_ids[sizes < min_size])] = 0 85 86 seg, _, _ = bic.segmentation.relabel_sequential(seg) 87 88 return seg.astype("uint16") 89 90 91def _create_segmentations_from_annotations(annotation_file, image_folder, seg_folder, cell_types, split): 92 # Use a per-split and per-cell_types cache to avoid reloading the COCO JSON when data is already prepared. 93 # The split must be part of the key: train and val share the same seg_folder, so a key without it 94 # would return the paths of whichever split was processed first for both. 95 cache_key = "all" if cell_types is None else "_".join(sorted(cell_types)) 96 cache_file = os.path.join(seg_folder, f"seg_paths_{split}_{cache_key}.json") 97 if os.path.exists(cache_file): 98 with open(cache_file) as f: 99 cached = json.load(f) 100 image_paths = [os.path.join(seg_folder, fname) for fname in cached["image_paths"]] 101 seg_paths = [os.path.join(seg_folder, fname) for fname in cached["seg_paths"]] 102 return image_paths, seg_paths 103 104 if COCO is None: 105 raise ModuleNotFoundError( 106 "'pycocotools' is required for processing the LIVECell ground-truth. " 107 "Install it with 'conda install -c conda-forge pycocotools'." 108 ) 109 110 coco = COCO(annotation_file) 111 category_ids = coco.getCatIds(catNms=["cell"]) 112 image_ids = coco.getImgIds(catIds=category_ids) 113 114 image_paths, seg_paths = [], [] 115 for image_id in tqdm(image_ids, desc="creating livecell segmentations from coco-style annotations"): 116 # get the path for the image data and make sure the corresponding image exists 117 image_metadata = coco.loadImgs(image_id)[0] 118 file_name = image_metadata["file_name"] 119 120 # if cell_type names are given we only select file names that match a cell_type 121 if cell_types is not None and (not any([cell_type in file_name for cell_type in cell_types])): 122 continue 123 124 sub_folder = file_name.split("_")[0] 125 image_path = os.path.join(image_folder, sub_folder, file_name) 126 # something changed in the image layout? we keep the old version around in case this changes back... 127 if not os.path.exists(image_path): 128 image_path = os.path.join(image_folder, file_name) 129 assert os.path.exists(image_path), image_path 130 image_paths.append(image_path) 131 132 # get the output path 133 out_folder = os.path.join(seg_folder, sub_folder) 134 os.makedirs(out_folder, exist_ok=True) 135 seg_path = os.path.join(out_folder, file_name) 136 seg_paths.append(seg_path) 137 if os.path.exists(seg_path): 138 continue 139 140 seg = _annotations_to_instances(coco, image_metadata, category_ids) 141 imageio.imwrite(seg_path, seg) 142 143 assert len(image_paths) == len(seg_paths) 144 assert len(image_paths) > 0, \ 145 f"No matching image paths were found. Did you pass invalid cell type names ({cell_types})?" 146 147 cache_dir = os.path.dirname(cache_file) 148 image_paths_rel = [os.path.relpath(image_path, start=cache_dir) for image_path in image_paths] 149 seg_paths_rel = [os.path.relpath(seg_path, start=cache_dir) for seg_path in seg_paths] 150 with open(cache_file, "w") as f: 151 json.dump({"image_paths": image_paths_rel, "seg_paths": seg_paths_rel}, f) 152 153 return image_paths, seg_paths 154 155 156def _download_livecell_annotations(path, split, download, cell_types, label_path): 157 annotation_file = _download_annotation_file(path, split, download) 158 if split == "test": 159 split_name = "livecell_test_images" 160 else: 161 split_name = "livecell_train_val_images" 162 163 image_folder = os.path.join(path, "images", split_name) 164 seg_folder = os.path.join(path, "annotations", split_name) if label_path is None\ 165 else os.path.join(label_path, "annotations", split_name) 166 167 assert os.path.exists(image_folder), image_folder 168 169 return _create_segmentations_from_annotations(annotation_file, image_folder, seg_folder, cell_types, split) 170 171 172def get_livecell_data(path: Union[os.PathLike], download: bool = False): 173 """Download the LIVECell dataset. 174 175 Args: 176 path: Filepath to a folder where the downloaded data will be saved. 177 download: Whether to download the data if it is not present. 178 """ 179 os.makedirs(path, exist_ok=True) 180 image_path = os.path.join(path, "images") 181 182 if os.path.exists(image_path): 183 return 184 185 url = URLS["images"] 186 checksum = CHECKSUM 187 zip_path = os.path.join(path, "livecell.zip") 188 util.download_source(zip_path, url, download, checksum) 189 util.unzip(zip_path, path, True) 190 191 192def get_livecell_paths( 193 path: Union[os.PathLike, str], 194 split: str, 195 download: bool = False, 196 cell_types: Optional[Sequence[str]] = None, 197 label_path: Optional[Union[os.PathLike, str]] = None 198) -> Tuple[List[str], List[str]]: 199 """Get paths to the LIVECell data. 200 201 Args: 202 path: Filepath to a folder where the downloaded data will be saved. 203 split: The data split to use. Either 'train', 'val' or 'test'. 204 download: Whether to download the data if it is not present. 205 cell_types: The cell types for which to get the data paths. 206 label_path: Optional path for loading the label data. 207 208 Returns: 209 List of filepaths for the image data. 210 List of filepaths for the label data. 211 """ 212 get_livecell_data(path, download) 213 image_paths, seg_paths = _download_livecell_annotations(path, split, download, cell_types, label_path) 214 return image_paths, seg_paths 215 216 217def get_livecell_dataset( 218 path: Union[os.PathLike, str], 219 split: str, 220 patch_shape: Tuple[int, int], 221 download: bool = False, 222 offsets: Optional[List[List[int]]] = None, 223 boundaries: bool = False, 224 binary: bool = False, 225 cell_types: Optional[Sequence[str]] = None, 226 label_path: Optional[Union[os.PathLike, str]] = None, 227 label_dtype=torch.int64, 228 **kwargs 229) -> Dataset: 230 """Get the LIVECell dataset for segmenting cells in phase-contrast microscopy. 231 232 Args: 233 path: Filepath to a folder where the downloaded data will be saved. 234 split: The data split to use. Either 'train', 'val' or 'test'. 235 patch_shape: The patch shape to use for training. 236 download: Whether to download the data if it is not present. 237 offsets: Offset values for affinity computation used as target. 238 boundaries: Whether to compute boundaries as the target. 239 binary: Whether to use a binary segmentation target. 240 cell_types: The cell types for which to get the data paths. 241 label_path: Optional path for loading the label data. 242 label_dtype: The datatype of the label data. 243 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`. 244 245 Returns: 246 The segmentation dataset. 247 """ 248 assert split in ("train", "val", "test") 249 if cell_types is not None: 250 assert isinstance(cell_types, (list, tuple)), \ 251 f"cell_types must be passed as a list or tuple instead of {cell_types}" 252 253 image_paths, seg_paths = get_livecell_paths(path, split, download, cell_types, label_path) 254 255 kwargs = util.ensure_transforms(ndim=2, **kwargs) 256 kwargs, label_dtype = util.add_instance_label_transform( 257 kwargs, add_binary_target=True, label_dtype=label_dtype, offsets=offsets, boundaries=boundaries, binary=binary 258 ) 259 260 return ImageCollectionDataset( 261 raw_image_paths=image_paths, 262 label_image_paths=seg_paths, 263 patch_shape=patch_shape, 264 label_dtype=label_dtype, 265 **kwargs 266 ) 267 268 269def get_livecell_loader( 270 path: Union[os.PathLike, str], 271 split: str, 272 patch_shape: Tuple[int, int], 273 batch_size: int, 274 download: bool = False, 275 offsets: Optional[List[List[int]]] = None, 276 boundaries: bool = False, 277 binary: bool = False, 278 cell_types: Optional[Sequence[str]] = None, 279 label_path: Optional[Union[os.PathLike, str]] = None, 280 label_dtype=torch.int64, 281 **kwargs 282) -> DataLoader: 283 """Get the LIVECell dataloader for segmenting cells in phase-contrast microscopy. 284 285 Args: 286 path: Filepath to a folder where the downloaded data will be saved. 287 split: The data split to use. Either 'train', 'val' or 'test'. 288 patch_shape: The patch shape to use for training. 289 batch_size: The batch size for training. 290 download: Whether to download the data if it is not present. 291 offsets: Offset values for affinity computation used as target. 292 boundaries: Whether to compute boundaries as the target. 293 binary: Whether to use a binary segmentation target. 294 cell_types: The cell types for which to get the data paths. 295 label_path: Optional path for loading the label data. 296 label_dtype: The datatype of the label data. 297 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader. 298 299 Returns: 300 The DataLoader. 301 """ 302 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 303 dataset = get_livecell_dataset( 304 path, split, patch_shape, download=download, offsets=offsets, boundaries=boundaries, binary=binary, 305 cell_types=cell_types, label_path=label_path, label_dtype=label_dtype, **ds_kwargs 306 ) 307 return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URLS =
{'images': 'http://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/images.zip', 'train': 'http://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/annotations/LIVECell/livecell_coco_train.json', 'val': 'http://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/annotations/LIVECell/livecell_coco_val.json', 'test': 'http://livecell-dataset.s3.eu-central-1.amazonaws.com/LIVECell_dataset_2021/annotations/LIVECell/livecell_coco_test.json'}
CHECKSUM =
None
CELL_TYPES =
['A172', 'BT474', 'BV2', 'Huh7', 'MCF7', 'SHSY5Y', 'SkBr3', 'SKOV3']
def
get_livecell_data(path: os.PathLike, download: bool = False):
173def get_livecell_data(path: Union[os.PathLike], download: bool = False): 174 """Download the LIVECell dataset. 175 176 Args: 177 path: Filepath to a folder where the downloaded data will be saved. 178 download: Whether to download the data if it is not present. 179 """ 180 os.makedirs(path, exist_ok=True) 181 image_path = os.path.join(path, "images") 182 183 if os.path.exists(image_path): 184 return 185 186 url = URLS["images"] 187 checksum = CHECKSUM 188 zip_path = os.path.join(path, "livecell.zip") 189 util.download_source(zip_path, url, download, checksum) 190 util.unzip(zip_path, path, True)
Download the LIVECell 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.
def
get_livecell_paths( path: Union[os.PathLike, str], split: str, download: bool = False, cell_types: Optional[Sequence[str]] = None, label_path: Union[os.PathLike, str, NoneType] = None) -> Tuple[List[str], List[str]]:
193def get_livecell_paths( 194 path: Union[os.PathLike, str], 195 split: str, 196 download: bool = False, 197 cell_types: Optional[Sequence[str]] = None, 198 label_path: Optional[Union[os.PathLike, str]] = None 199) -> Tuple[List[str], List[str]]: 200 """Get paths to the LIVECell data. 201 202 Args: 203 path: Filepath to a folder where the downloaded data will be saved. 204 split: The data split to use. Either 'train', 'val' or 'test'. 205 download: Whether to download the data if it is not present. 206 cell_types: The cell types for which to get the data paths. 207 label_path: Optional path for loading the label data. 208 209 Returns: 210 List of filepaths for the image data. 211 List of filepaths for the label data. 212 """ 213 get_livecell_data(path, download) 214 image_paths, seg_paths = _download_livecell_annotations(path, split, download, cell_types, label_path) 215 return image_paths, seg_paths
Get paths to the LIVECell data.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- split: The data split to use. Either 'train', 'val' or 'test'.
- download: Whether to download the data if it is not present.
- cell_types: The cell types for which to get the data paths.
- label_path: Optional path for loading the label data.
Returns:
List of filepaths for the image data. List of filepaths for the label data.
def
get_livecell_dataset( path: Union[os.PathLike, str], split: str, patch_shape: Tuple[int, int], download: bool = False, offsets: Optional[List[List[int]]] = None, boundaries: bool = False, binary: bool = False, cell_types: Optional[Sequence[str]] = None, label_path: Union[os.PathLike, str, NoneType] = None, label_dtype=torch.int64, **kwargs) -> torch.utils.data.dataset.Dataset:
218def get_livecell_dataset( 219 path: Union[os.PathLike, str], 220 split: str, 221 patch_shape: Tuple[int, int], 222 download: bool = False, 223 offsets: Optional[List[List[int]]] = None, 224 boundaries: bool = False, 225 binary: bool = False, 226 cell_types: Optional[Sequence[str]] = None, 227 label_path: Optional[Union[os.PathLike, str]] = None, 228 label_dtype=torch.int64, 229 **kwargs 230) -> Dataset: 231 """Get the LIVECell dataset for segmenting cells in phase-contrast microscopy. 232 233 Args: 234 path: Filepath to a folder where the downloaded data will be saved. 235 split: The data split to use. Either 'train', 'val' or 'test'. 236 patch_shape: The patch shape to use for training. 237 download: Whether to download the data if it is not present. 238 offsets: Offset values for affinity computation used as target. 239 boundaries: Whether to compute boundaries as the target. 240 binary: Whether to use a binary segmentation target. 241 cell_types: The cell types for which to get the data paths. 242 label_path: Optional path for loading the label data. 243 label_dtype: The datatype of the label data. 244 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`. 245 246 Returns: 247 The segmentation dataset. 248 """ 249 assert split in ("train", "val", "test") 250 if cell_types is not None: 251 assert isinstance(cell_types, (list, tuple)), \ 252 f"cell_types must be passed as a list or tuple instead of {cell_types}" 253 254 image_paths, seg_paths = get_livecell_paths(path, split, download, cell_types, label_path) 255 256 kwargs = util.ensure_transforms(ndim=2, **kwargs) 257 kwargs, label_dtype = util.add_instance_label_transform( 258 kwargs, add_binary_target=True, label_dtype=label_dtype, offsets=offsets, boundaries=boundaries, binary=binary 259 ) 260 261 return ImageCollectionDataset( 262 raw_image_paths=image_paths, 263 label_image_paths=seg_paths, 264 patch_shape=patch_shape, 265 label_dtype=label_dtype, 266 **kwargs 267 )
Get the LIVECell dataset for segmenting cells in phase-contrast microscopy.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- split: The data split to use. Either 'train', 'val' or 'test'.
- patch_shape: The patch shape to use for training.
- download: Whether to download the data if it is not present.
- 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.
- cell_types: The cell types for which to get the data paths.
- label_path: Optional path for loading the label data.
- label_dtype: The datatype of the label data.
- kwargs: Additional keyword arguments for
torch_em.default_segmentation_dataset.
Returns:
The segmentation dataset.
def
get_livecell_loader( path: Union[os.PathLike, str], split: str, patch_shape: Tuple[int, int], batch_size: int, download: bool = False, offsets: Optional[List[List[int]]] = None, boundaries: bool = False, binary: bool = False, cell_types: Optional[Sequence[str]] = None, label_path: Union[os.PathLike, str, NoneType] = None, label_dtype=torch.int64, **kwargs) -> torch.utils.data.dataloader.DataLoader:
270def get_livecell_loader( 271 path: Union[os.PathLike, str], 272 split: str, 273 patch_shape: Tuple[int, int], 274 batch_size: int, 275 download: bool = False, 276 offsets: Optional[List[List[int]]] = None, 277 boundaries: bool = False, 278 binary: bool = False, 279 cell_types: Optional[Sequence[str]] = None, 280 label_path: Optional[Union[os.PathLike, str]] = None, 281 label_dtype=torch.int64, 282 **kwargs 283) -> DataLoader: 284 """Get the LIVECell dataloader for segmenting cells in phase-contrast microscopy. 285 286 Args: 287 path: Filepath to a folder where the downloaded data will be saved. 288 split: The data split to use. Either 'train', 'val' or 'test'. 289 patch_shape: The patch shape to use for training. 290 batch_size: The batch size for training. 291 download: Whether to download the data if it is not present. 292 offsets: Offset values for affinity computation used as target. 293 boundaries: Whether to compute boundaries as the target. 294 binary: Whether to use a binary segmentation target. 295 cell_types: The cell types for which to get the data paths. 296 label_path: Optional path for loading the label data. 297 label_dtype: The datatype of the label data. 298 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader. 299 300 Returns: 301 The DataLoader. 302 """ 303 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 304 dataset = get_livecell_dataset( 305 path, split, patch_shape, download=download, offsets=offsets, boundaries=boundaries, binary=binary, 306 cell_types=cell_types, label_path=label_path, label_dtype=label_dtype, **ds_kwargs 307 ) 308 return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
Get the LIVECell dataloader for segmenting cells in phase-contrast microscopy.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- split: The data split to use. Either 'train', 'val' or 'test'.
- patch_shape: The patch shape to use for training.
- batch_size: The batch size for training.
- download: Whether to download the data if it is not present.
- 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.
- cell_types: The cell types for which to get the data paths.
- label_path: Optional path for loading the label data.
- label_dtype: The datatype of the label data.
- kwargs: Additional keyword arguments for
torch_em.default_segmentation_datasetor for the PyTorch DataLoader.
Returns:
The DataLoader.