torch_em.data.datasets.electron_microscopy.parlakgul_liver_eval
The Parlakgul liver evaluation dataset contains manually annotated FIB-SEM crops of mouse liver
for mitochondria and lipid droplets. They are the independent evaluation volumes of the model-generated
segmentations of the Parlakgul liver dataset (see parlakgul_liver.py).
For each of the four liver samples (6461, 6464, 9430, 1857) there are three crops of 25 x 150 x 150 voxels (z, y, x) per organelle, at 8 nm isotropic resolution. Every crop comes with:
- the raw image,
- the manual annotation (instance ids, 0 is background; for a few crops the annotation is semantic),
- the instance segmentation predicted by the authors' model for the same crop (ids are arbitrary).
This dataset is from the publication https://doi.org/10.1038/s41586-022-04518-2. Please cite it if you use this dataset in your research.
The data is publicly available at https://www.ebi.ac.uk/empiar/EMPIAR-10791/.
1"""The Parlakgul liver evaluation dataset contains manually annotated FIB-SEM crops of mouse liver 2for mitochondria and lipid droplets. They are the independent evaluation volumes of the model-generated 3segmentations of the Parlakgul liver dataset (see `parlakgul_liver.py`). 4 5For each of the four liver samples (6461, 6464, 9430, 1857) there are three crops of 25 x 150 x 150 voxels 6(z, y, x) per organelle, at 8 nm isotropic resolution. Every crop comes with: 7- the raw image, 8- the manual annotation (instance ids, 0 is background; for a few crops the annotation is semantic), 9- the instance segmentation predicted by the authors' model for the same crop (ids are arbitrary). 10 11This dataset is from the publication https://doi.org/10.1038/s41586-022-04518-2. 12Please cite it if you use this dataset in your research. 13 14The data is publicly available at https://www.ebi.ac.uk/empiar/EMPIAR-10791/. 15""" 16 17import io 18import os 19import re 20import shutil 21import zipfile 22from typing import List, Literal, Tuple, Union 23 24import numpy as np 25from torch.utils.data import DataLoader, Dataset 26 27import torch_em 28from .. import util 29 30 31URL = ( 32 "https://ftp.ebi.ac.uk/empiar/world_availability/10791/data/" 33 "Parlakgul%20et%20al%20-%20Regulation%20of%20liver%20subcellular%20architecture%20controls%20metabolic%20" 34 "homeostasis/FIB-SEM%20Raw%20and%20Segmentation%20Data/Segmentation%20Evaluation/" 35 "Manual%20annotation%20-%20Prediction%20-%20Raw%20Slices.zip" 36) 37CHECKSUM = "bfb94ec5818a5b25e7ae380e624a29df366e32a66a674ad71030312951c9775f" 38ZIP_NAME = "Manual annotation - Prediction - Raw Slices.zip" 39 40SAMPLES = ("6461", "6464", "9430", "1857") 41LABEL_CHOICES = {"mito": "mitochondria", "lipid_droplet": "lipid droplets"} 42N_EVALUATION_VOLUMES = 3 43 44 45def _read_tiff_stack(archive, name): 46 """Read all slices of a nested zip archive as a (z, y, x) array, ordered by the slice number.""" 47 from PIL import Image 48 49 with zipfile.ZipFile(io.BytesIO(archive.read(name))) as zf: 50 members = [ 51 info.filename for info in zf.infolist() 52 if info.filename.lower().endswith((".tif", ".tiff")) and "__MACOSX" not in info.filename 53 ] 54 members.sort(key=lambda f: int(re.search(r"(\d+)\.tiff?$", f.lower()).group(1))) 55 return np.stack([np.array(Image.open(io.BytesIO(zf.read(f)))) for f in members]) 56 57 58def _find_member(names, sample, organelle, index, kind): 59 # The archives are not named consistently: the manual annotation of 6464 is called 'manual evaluation'. 60 pattern = { 61 "raw": r"raw", "labels": r"manual (annotation|evaluation)", "prediction": r"prediction", 62 }[kind] 63 regex = re.compile(rf"^{sample}[ _].*{organelle} evaluation 00{index} {pattern}\.zip$") 64 matches = [n for n in names if regex.match(os.path.basename(n))] 65 if len(matches) != 1: 66 raise RuntimeError(f"Expected one archive for {sample}, {organelle}, {index}, {kind}, found {matches}.") 67 return matches[0] 68 69 70def get_parlakgul_liver_eval_data( 71 path: Union[os.PathLike, str], 72 sample: Literal["6461", "6464", "9430", "1857"] = "6461", 73 label_choice: Literal["mito", "lipid_droplet"] = "mito", 74 download: bool = False, 75) -> List[str]: 76 """Download the Parlakgul liver evaluation crops and convert them to zarr stores. 77 78 Args: 79 path: Filepath to a folder where the downloaded data will be saved. 80 sample: Which liver sample to use. One of "6461" (lean), "6464" (obese 1), 81 "9430" (obese 2), "1857" (obese Climp63). 82 label_choice: The organelle. One of "mito" or "lipid_droplet". 83 download: Whether to download the data if it is not present. 84 85 Returns: 86 The filepaths to the zarr stores of the three evaluation crops. 87 """ 88 import zarr 89 from zarr.codecs import BloscCodec 90 91 if sample not in SAMPLES: 92 raise ValueError(f"'{sample}' is not a valid sample. Choose from {SAMPLES}.") 93 if label_choice not in LABEL_CHOICES: 94 raise ValueError(f"'{label_choice}' is not a valid label_choice. Choose from {list(LABEL_CHOICES)}.") 95 organelle = LABEL_CHOICES[label_choice] 96 97 os.makedirs(path, exist_ok=True) 98 zarr_paths = [ 99 os.path.join(path, f"{sample}_{label_choice}_{i:03d}.zarr") for i in range(1, N_EVALUATION_VOLUMES + 1) 100 ] 101 if all(os.path.exists(p) for p in zarr_paths): 102 return zarr_paths 103 104 zip_path = os.path.join(path, ZIP_NAME) 105 util.download_source(zip_path, URL, download, checksum=CHECKSUM) 106 107 with zipfile.ZipFile(zip_path) as archive: 108 names = [n for n in archive.namelist() if n.endswith(".zip") and "__MACOSX" not in n] 109 for index, zarr_path in enumerate(zarr_paths, start=1): 110 if os.path.exists(zarr_path): 111 continue 112 113 stacks = { 114 kind: _read_tiff_stack(archive, _find_member(names, sample, organelle, index, kind)) 115 for kind in ("raw", "labels", "prediction") 116 } 117 # One crop (6461 mito 003) is annotated with 255 instead of 1. 118 if stacks["labels"].max() == 255: 119 stacks["labels"] = (stacks["labels"] > 0).astype("uint8") 120 121 tmp_path = f"{zarr_path}.incomplete" 122 if os.path.exists(tmp_path): 123 shutil.rmtree(tmp_path) 124 root = zarr.open_group(tmp_path, mode="w") 125 root.attrs.update({"sample": sample, "label_choice": label_choice, "resolution_nm": [8, 8, 8]}) 126 for name, data in stacks.items(): 127 arr = root.create_array( 128 name, shape=data.shape, chunks=data.shape, dtype=data.dtype, 129 compressors=BloscCodec(cname="zstd", clevel=6), 130 ) 131 arr[:] = data 132 os.rename(tmp_path, zarr_path) 133 134 return zarr_paths 135 136 137def get_parlakgul_liver_eval_paths( 138 path: Union[os.PathLike, str], 139 samples: Union[str, List[str]] = "6461", 140 label_choice: Literal["mito", "lipid_droplet"] = "mito", 141 download: bool = False, 142) -> List[str]: 143 """Get paths to the Parlakgul liver evaluation zarr stores. 144 145 Args: 146 path: Filepath to a folder where the downloaded data will be saved. 147 samples: The liver sample(s) to use. One or several of "6461", "6464", "9430", "1857". 148 label_choice: The organelle. One of "mito" or "lipid_droplet". 149 download: Whether to download the data if it is not present. 150 151 Returns: 152 The filepaths to the zarr stores, three per sample. 153 """ 154 samples = [samples] if isinstance(samples, str) else samples 155 return [p for sample in samples for p in get_parlakgul_liver_eval_data(path, sample, label_choice, download)] 156 157 158def get_parlakgul_liver_eval_dataset( 159 path: Union[os.PathLike, str], 160 patch_shape: Tuple[int, int, int], 161 samples: Union[str, List[str]] = "6461", 162 label_choice: Literal["mito", "lipid_droplet"] = "mito", 163 semantic: bool = False, 164 return_prediction: bool = False, 165 download: bool = False, 166 **kwargs, 167) -> Dataset: 168 """Get the Parlakgul liver evaluation dataset for mitochondria or lipid droplet segmentation. 169 170 Args: 171 path: Filepath to a folder where the downloaded data will be saved. 172 patch_shape: The patch shape (z, y, x). The crops have the shape (25, 150, 150). 173 samples: The liver sample(s) to use. One or several of "6461", "6464", "9430", "1857". 174 label_choice: The organelle. One of "mito" or "lipid_droplet". 175 semantic: Whether to return the binary foreground mask instead of the instance ids. 176 return_prediction: Whether to return the model prediction instead of the manual annotation. 177 This is meant to compute the evaluation scores of the original segmentations. 178 download: Whether to download the data if it is not present. 179 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`. 180 181 Returns: 182 The segmentation dataset. 183 """ 184 assert len(patch_shape) == 3 185 186 paths = get_parlakgul_liver_eval_paths(path, samples, label_choice, download) 187 188 kwargs = util.update_kwargs(kwargs, "is_seg_dataset", True) 189 if semantic: 190 kwargs = util.update_kwargs(kwargs, "label_transform", _binarize) 191 192 return torch_em.default_segmentation_dataset( 193 raw_paths=paths, 194 raw_key="raw", 195 label_paths=paths, 196 label_key="prediction" if return_prediction else "labels", 197 patch_shape=patch_shape, 198 **kwargs, 199 ) 200 201 202def _binarize(labels): 203 return (labels > 0).astype("float32") 204 205 206def get_parlakgul_liver_eval_loader( 207 path: Union[os.PathLike, str], 208 patch_shape: Tuple[int, int, int], 209 batch_size: int, 210 samples: Union[str, List[str]] = "6461", 211 label_choice: Literal["mito", "lipid_droplet"] = "mito", 212 semantic: bool = False, 213 return_prediction: bool = False, 214 download: bool = False, 215 **kwargs, 216) -> DataLoader: 217 """Get the DataLoader for the Parlakgul liver evaluation dataset. 218 219 Args: 220 path: Filepath to a folder where the downloaded data will be saved. 221 patch_shape: The patch shape (z, y, x). The crops have the shape (25, 150, 150). 222 batch_size: The batch size. 223 samples: The liver sample(s) to use. One or several of "6461", "6464", "9430", "1857". 224 label_choice: The organelle. One of "mito" or "lipid_droplet". 225 semantic: Whether to return the binary foreground mask instead of the instance ids. 226 return_prediction: Whether to return the model prediction instead of the manual annotation. 227 download: Whether to download the data if it is not present. 228 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` 229 or for the PyTorch DataLoader. 230 231 Returns: 232 The DataLoader. 233 """ 234 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 235 dataset = get_parlakgul_liver_eval_dataset( 236 path, patch_shape, samples=samples, label_choice=label_choice, semantic=semantic, 237 return_prediction=return_prediction, download=download, **ds_kwargs, 238 ) 239 return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
71def get_parlakgul_liver_eval_data( 72 path: Union[os.PathLike, str], 73 sample: Literal["6461", "6464", "9430", "1857"] = "6461", 74 label_choice: Literal["mito", "lipid_droplet"] = "mito", 75 download: bool = False, 76) -> List[str]: 77 """Download the Parlakgul liver evaluation crops and convert them to zarr stores. 78 79 Args: 80 path: Filepath to a folder where the downloaded data will be saved. 81 sample: Which liver sample to use. One of "6461" (lean), "6464" (obese 1), 82 "9430" (obese 2), "1857" (obese Climp63). 83 label_choice: The organelle. One of "mito" or "lipid_droplet". 84 download: Whether to download the data if it is not present. 85 86 Returns: 87 The filepaths to the zarr stores of the three evaluation crops. 88 """ 89 import zarr 90 from zarr.codecs import BloscCodec 91 92 if sample not in SAMPLES: 93 raise ValueError(f"'{sample}' is not a valid sample. Choose from {SAMPLES}.") 94 if label_choice not in LABEL_CHOICES: 95 raise ValueError(f"'{label_choice}' is not a valid label_choice. Choose from {list(LABEL_CHOICES)}.") 96 organelle = LABEL_CHOICES[label_choice] 97 98 os.makedirs(path, exist_ok=True) 99 zarr_paths = [ 100 os.path.join(path, f"{sample}_{label_choice}_{i:03d}.zarr") for i in range(1, N_EVALUATION_VOLUMES + 1) 101 ] 102 if all(os.path.exists(p) for p in zarr_paths): 103 return zarr_paths 104 105 zip_path = os.path.join(path, ZIP_NAME) 106 util.download_source(zip_path, URL, download, checksum=CHECKSUM) 107 108 with zipfile.ZipFile(zip_path) as archive: 109 names = [n for n in archive.namelist() if n.endswith(".zip") and "__MACOSX" not in n] 110 for index, zarr_path in enumerate(zarr_paths, start=1): 111 if os.path.exists(zarr_path): 112 continue 113 114 stacks = { 115 kind: _read_tiff_stack(archive, _find_member(names, sample, organelle, index, kind)) 116 for kind in ("raw", "labels", "prediction") 117 } 118 # One crop (6461 mito 003) is annotated with 255 instead of 1. 119 if stacks["labels"].max() == 255: 120 stacks["labels"] = (stacks["labels"] > 0).astype("uint8") 121 122 tmp_path = f"{zarr_path}.incomplete" 123 if os.path.exists(tmp_path): 124 shutil.rmtree(tmp_path) 125 root = zarr.open_group(tmp_path, mode="w") 126 root.attrs.update({"sample": sample, "label_choice": label_choice, "resolution_nm": [8, 8, 8]}) 127 for name, data in stacks.items(): 128 arr = root.create_array( 129 name, shape=data.shape, chunks=data.shape, dtype=data.dtype, 130 compressors=BloscCodec(cname="zstd", clevel=6), 131 ) 132 arr[:] = data 133 os.rename(tmp_path, zarr_path) 134 135 return zarr_paths
Download the Parlakgul liver evaluation crops and convert them to zarr stores.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- sample: Which liver sample to use. One of "6461" (lean), "6464" (obese 1), "9430" (obese 2), "1857" (obese Climp63).
- label_choice: The organelle. One of "mito" or "lipid_droplet".
- download: Whether to download the data if it is not present.
Returns:
The filepaths to the zarr stores of the three evaluation crops.
138def get_parlakgul_liver_eval_paths( 139 path: Union[os.PathLike, str], 140 samples: Union[str, List[str]] = "6461", 141 label_choice: Literal["mito", "lipid_droplet"] = "mito", 142 download: bool = False, 143) -> List[str]: 144 """Get paths to the Parlakgul liver evaluation zarr stores. 145 146 Args: 147 path: Filepath to a folder where the downloaded data will be saved. 148 samples: The liver sample(s) to use. One or several of "6461", "6464", "9430", "1857". 149 label_choice: The organelle. One of "mito" or "lipid_droplet". 150 download: Whether to download the data if it is not present. 151 152 Returns: 153 The filepaths to the zarr stores, three per sample. 154 """ 155 samples = [samples] if isinstance(samples, str) else samples 156 return [p for sample in samples for p in get_parlakgul_liver_eval_data(path, sample, label_choice, download)]
Get paths to the Parlakgul liver evaluation zarr stores.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- samples: The liver sample(s) to use. One or several of "6461", "6464", "9430", "1857".
- label_choice: The organelle. One of "mito" or "lipid_droplet".
- download: Whether to download the data if it is not present.
Returns:
The filepaths to the zarr stores, three per sample.
159def get_parlakgul_liver_eval_dataset( 160 path: Union[os.PathLike, str], 161 patch_shape: Tuple[int, int, int], 162 samples: Union[str, List[str]] = "6461", 163 label_choice: Literal["mito", "lipid_droplet"] = "mito", 164 semantic: bool = False, 165 return_prediction: bool = False, 166 download: bool = False, 167 **kwargs, 168) -> Dataset: 169 """Get the Parlakgul liver evaluation dataset for mitochondria or lipid droplet segmentation. 170 171 Args: 172 path: Filepath to a folder where the downloaded data will be saved. 173 patch_shape: The patch shape (z, y, x). The crops have the shape (25, 150, 150). 174 samples: The liver sample(s) to use. One or several of "6461", "6464", "9430", "1857". 175 label_choice: The organelle. One of "mito" or "lipid_droplet". 176 semantic: Whether to return the binary foreground mask instead of the instance ids. 177 return_prediction: Whether to return the model prediction instead of the manual annotation. 178 This is meant to compute the evaluation scores of the original segmentations. 179 download: Whether to download the data if it is not present. 180 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`. 181 182 Returns: 183 The segmentation dataset. 184 """ 185 assert len(patch_shape) == 3 186 187 paths = get_parlakgul_liver_eval_paths(path, samples, label_choice, download) 188 189 kwargs = util.update_kwargs(kwargs, "is_seg_dataset", True) 190 if semantic: 191 kwargs = util.update_kwargs(kwargs, "label_transform", _binarize) 192 193 return torch_em.default_segmentation_dataset( 194 raw_paths=paths, 195 raw_key="raw", 196 label_paths=paths, 197 label_key="prediction" if return_prediction else "labels", 198 patch_shape=patch_shape, 199 **kwargs, 200 )
Get the Parlakgul liver evaluation dataset for mitochondria or lipid droplet segmentation.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- patch_shape: The patch shape (z, y, x). The crops have the shape (25, 150, 150).
- samples: The liver sample(s) to use. One or several of "6461", "6464", "9430", "1857".
- label_choice: The organelle. One of "mito" or "lipid_droplet".
- semantic: Whether to return the binary foreground mask instead of the instance ids.
- return_prediction: Whether to return the model prediction instead of the manual annotation. This is meant to compute the evaluation scores of the original segmentations.
- 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.
207def get_parlakgul_liver_eval_loader( 208 path: Union[os.PathLike, str], 209 patch_shape: Tuple[int, int, int], 210 batch_size: int, 211 samples: Union[str, List[str]] = "6461", 212 label_choice: Literal["mito", "lipid_droplet"] = "mito", 213 semantic: bool = False, 214 return_prediction: bool = False, 215 download: bool = False, 216 **kwargs, 217) -> DataLoader: 218 """Get the DataLoader for the Parlakgul liver evaluation dataset. 219 220 Args: 221 path: Filepath to a folder where the downloaded data will be saved. 222 patch_shape: The patch shape (z, y, x). The crops have the shape (25, 150, 150). 223 batch_size: The batch size. 224 samples: The liver sample(s) to use. One or several of "6461", "6464", "9430", "1857". 225 label_choice: The organelle. One of "mito" or "lipid_droplet". 226 semantic: Whether to return the binary foreground mask instead of the instance ids. 227 return_prediction: Whether to return the model prediction instead of the manual annotation. 228 download: Whether to download the data if it is not present. 229 kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` 230 or for the PyTorch DataLoader. 231 232 Returns: 233 The DataLoader. 234 """ 235 ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs) 236 dataset = get_parlakgul_liver_eval_dataset( 237 path, patch_shape, samples=samples, label_choice=label_choice, semantic=semantic, 238 return_prediction=return_prediction, download=download, **ds_kwargs, 239 ) 240 return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
Get the DataLoader for the Parlakgul liver evaluation dataset.
Arguments:
- path: Filepath to a folder where the downloaded data will be saved.
- patch_shape: The patch shape (z, y, x). The crops have the shape (25, 150, 150).
- batch_size: The batch size.
- samples: The liver sample(s) to use. One or several of "6461", "6464", "9430", "1857".
- label_choice: The organelle. One of "mito" or "lipid_droplet".
- semantic: Whether to return the binary foreground mask instead of the instance ids.
- return_prediction: Whether to return the model prediction instead of the manual annotation.
- 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.