torch_em.data.datasets.histopathology.puma

The PUMA dataset contains annotations for nucleus and tissue segmentation in melanoma H&E stained histopathology images.

This dataset is located at https://zenodo.org/records/13859989. This is part of the PUMA Grand Challenge: https://puma.grand-challenge.org/. The dataset is from the publication https://doi.org/10.1093/gigascience/giaf011. Please cite them if you use this dataset for your research.

  1"""The PUMA dataset contains annotations for nucleus and tissue segmentation
  2in melanoma H&E stained histopathology images.
  3
  4This dataset is located at https://zenodo.org/records/13859989.
  5This is part of the PUMA Grand Challenge: https://puma.grand-challenge.org/.
  6The dataset is from the publication https://doi.org/10.1093/gigascience/giaf011.
  7Please cite them if you use this dataset for your research.
  8"""
  9
 10import os
 11from glob import glob
 12from tqdm import tqdm
 13from pathlib import Path
 14from natsort import natsorted
 15from typing import Union, Literal, List, Tuple
 16
 17import json
 18import numpy as np
 19import pandas as pd
 20import imageio.v3 as imageio
 21from sklearn.model_selection import train_test_split
 22
 23from torch.utils.data import Dataset, DataLoader
 24
 25import torch_em
 26
 27from .. import util
 28
 29
 30URL = {
 31    "data": "https://zenodo.org/records/15050523/files/01_training_dataset_tif_ROIs.zip",
 32    "annotations": {
 33        "nuclei": "https://zenodo.org/records/15050523/files/01_training_dataset_geojson_nuclei.zip",
 34        "tissue": "https://zenodo.org/records/15050523/files/01_training_dataset_geojson_tissue.zip",
 35    }
 36}
 37
 38CHECKSUM = {
 39    "data": "af48b879f8ff7e74b84a7114924881606f13f108aa0f9bcc21d3593b717ee022",
 40    "annotations": {
 41        "nuclei": "eda271225900d6de0759e0281f3731a570e09f2adab58bd36425b9d2dfad91a0",
 42        "tissue": "fc2835135cc28324f52eac131327f0f12c554c0b1f334a108bf4b65e0f18c42b",
 43    }
 44}
 45
 46NUCLEI_CLASS_DICT = {
 47    "nuclei_stroma": 1,
 48    "nuclei_tumor": 2,
 49    "nuclei_plasma_cell": 3,
 50    "nuclei_histiocyte": 4,
 51    "nuclei_lymphocyte": 5,
 52    "nuclei_melanophage": 6,
 53    "nuclei_neutrophil": 7,
 54    "nuclei_endothelium": 8,
 55    "nuclei_epithelium": 9,
 56    "nuclei_apoptosis": 10,
 57}
 58
 59TISSUE_CLASS_DICT = {
 60    "tissue_stroma": 1,
 61    "tissue_tumor": 2,
 62    "tissue_epidermis": 3,
 63    "tissue_blood_vessel": 4,
 64    "tissue_necrosis": 5,
 65    "tissue_white_background": 6,
 66}
 67
 68CLASS_DICT = {
 69    "nuclei": NUCLEI_CLASS_DICT,
 70    "tissue": TISSUE_CLASS_DICT,
 71}
 72
 73
 74def _get_classes_per_image(path, annotations):
 75    "Maps each ROI image id to the set of class ids present in its annotation"
 76    ann_dir = os.path.join(path, "annotations", annotations, f"01_training_dataset_geojson_{annotations}")
 77    ann_paths = glob(os.path.join(ann_dir, "*.geojson"))
 78    assert ann_paths, f"Could not find any '{annotations}' annotations in '{ann_dir}'."
 79
 80    class_dict = CLASS_DICT[annotations]
 81    classes_per_image = {}
 82    for ann_path in ann_paths:
 83        image_id = os.path.basename(ann_path).replace(f"_{annotations}.geojson", "")
 84        with open(ann_path) as f:
 85            feature_collection = json.load(f)
 86        classes_per_image[image_id] = {
 87            class_dict[feat["properties"]["classification"]["name"]]
 88            for feat in feature_collection["features"]
 89        }
 90    return classes_per_image
 91
 92
 93def _split_covers_all_classes(split_ids, classes_per_image, all_classes):
 94    "Checks that every class is present in at least one image of every split"
 95    for ids in split_ids.values():
 96        covered = set()
 97        for image_id in ids:
 98            covered.update(classes_per_image.get(image_id, set()))
 99        if not all_classes.issubset(covered):
100            return False
101    return True
102
103
104def _make_random_split(metastatic_ids, primary_ids, random_state):
105    # Create random splits per dataset: 20% for test, then 15% of the train set for val.
106    train_ids, test_ids = train_test_split(metastatic_ids, test_size=0.2, random_state=random_state)
107    train_ids, val_ids = train_test_split(train_ids, test_size=0.15, random_state=random_state)
108    # Do same as above for 'primary' samples.
109    ptrain_ids, ptest_ids = train_test_split(primary_ids, test_size=0.2, random_state=random_state)
110    ptrain_ids, pval_ids = train_test_split(ptrain_ids, test_size=0.15, random_state=random_state)
111    train_ids = train_ids + ptrain_ids
112    val_ids = val_ids + pval_ids
113    test_ids = test_ids + ptest_ids
114    return {"train": train_ids, "val": val_ids, "test": test_ids}
115
116
117def _create_split_csv(path, annotations, split, random_state=42, max_split_attempts=200):
118    "This creates a split saved to a .csv file in the dataset directory"
119    csv_path = os.path.join(path, "puma_split.csv")
120
121    if os.path.exists(csv_path):
122        df = pd.read_csv(csv_path)
123        df[split] = df[split].apply(lambda x: json.loads(x.replace("'", '"')))  # ensures all items from column in list.
124        split_list = df.iloc[0][split]
125    else:
126        print(f"Creating a new split file at '{csv_path}'.")
127        # NOTE: The ids are sorted, as 'train_test_split' is order sensitive and 'glob' does not
128        # guarantee a stable order, i.e. the fixed 'random_state' alone does not reproduce the split.
129        metastatic_ids = natsorted(
130            os.path.basename(image).split(".")[0]
131            for image in glob(os.path.join(path, "data", "01_training_dataset_tif_ROIs", "*metastatic*"))
132        )
133        primary_ids = natsorted(
134            os.path.basename(image).split(".")[0]
135            for image in glob(os.path.join(path, "data", "01_training_dataset_tif_ROIs", "*primary*"))
136        )
137
138        # The ROIs (and hence this split) are shared across annotation levels, so a split validated
139        # for one level can still drop a rare class (e.g. 'tissue_white_background') from the other.
140        coverage_checks = []
141        for level, class_dict in CLASS_DICT.items():
142            coverage_checks.append((set(class_dict.values()), _get_classes_per_image(path, level)))
143
144        split_ids, candidate = None, None
145        for attempt in range(max_split_attempts):
146            candidate = _make_random_split(metastatic_ids, primary_ids, random_state=random_state + attempt)
147            if all(
148                _split_covers_all_classes(candidate, classes_per_image, all_classes)
149                for all_classes, classes_per_image in coverage_checks
150            ):
151                split_ids = candidate
152                break
153
154        if split_ids is None:
155            print(
156                f"Warning: could not find a split covering all classes within {max_split_attempts} attempts "
157                f"(seeds {random_state}-{random_state + max_split_attempts - 1}). Using the last candidate "
158                "anyway, please check rare classes manually."
159            )
160            split_ids = candidate
161
162        df = pd.DataFrame.from_dict([split_ids])
163        df.to_csv(csv_path, index=False)
164
165        split_list = split_ids[split]
166
167    return split_list
168
169
170def _preprocess_inputs(path, annotations, split):
171    import h5py
172    try:
173        import geopandas as gpd
174    except ModuleNotFoundError:
175        raise RuntimeError("Please install 'geopandas': 'conda install -c conda-forge geopandas'.")
176
177    try:
178        from rasterio.features import rasterize
179        from rasterio.transform import from_bounds
180    except ModuleNotFoundError:
181        raise RuntimeError("Please install 'rasterio': 'conda install -c conda-forge rasterio'.")
182
183    annotation_paths = glob(
184        os.path.join(path, "annotations", annotations, f"01_training_dataset_geojson_{annotations}", "*.geojson")
185    )
186    roi_dir = os.path.join(path, "data", "01_training_dataset_tif_ROIs")
187    preprocessed_dir = os.path.join(path, split, "preprocessed")
188    os.makedirs(preprocessed_dir, exist_ok=True)
189
190    split_list = _create_split_csv(path, annotations, split)
191    print(f"The data split '{split}' has '{len(split_list)}' samples!")
192
193    for ann_path in tqdm(annotation_paths, desc=f"Preprocessing '{annotations}'"):
194        fname = os.path.basename(ann_path).replace(f"_{annotations}.geojson", ".tif")
195        image_path = os.path.join(roi_dir, fname)
196
197        # Handle inconsistent extension for sample 103 (.tiff instead of .tif).
198        if not os.path.exists(image_path):
199            image_path = image_path + "f"  # Retrying with .tiff
200
201        if os.path.basename(image_path).split(".")[0] not in split_list:
202            continue
203
204        assert os.path.exists(image_path), image_path
205
206        volume_path = os.path.join(preprocessed_dir, Path(fname).with_suffix(".h5"))
207        gdf = gpd.read_file(ann_path)
208        minx, miny, maxx, maxy = gdf.total_bounds
209
210        width, height = 1024, 1024  # roi shape
211        transform = from_bounds(minx, miny, maxx, maxy, width, height)
212
213        # Extract class ids mapped to each class name. Depending on the geopandas/pyogrio version,
214        # this property comes back either as a JSON-encoded string or already parsed into a dict.
215        class_dict = CLASS_DICT[annotations]
216        classification = gdf["classification"].apply(lambda x: json.loads(x) if isinstance(x, str) else x)
217        class_ids = [class_dict[cls_entry["name"]] for cls_entry in classification]
218        semantic_shapes = ((geom, unique_id) for geom, unique_id in zip(gdf.geometry, class_ids))
219        semantic_mask = rasterize(
220            semantic_shapes, out_shape=(height, width), transform=transform, fill=0, dtype=np.uint8
221        )
222
223        gdf['id'] = range(1, len(gdf) + 1)
224        instance_shapes = ((geom, unique_id) for geom, unique_id in zip(gdf.geometry, gdf['id']))
225        instance_mask = rasterize(
226            instance_shapes, out_shape=(height, width), transform=transform, fill=0, dtype=np.int32
227        )
228
229        # Transform labels to match expected orientation
230        instance_mask = np.flip(instance_mask)
231        instance_mask = np.fliplr(instance_mask)
232
233        semantic_mask = np.flip(semantic_mask)
234        semantic_mask = np.fliplr(semantic_mask)
235
236        image = imageio.imread(image_path)
237        image = image[..., :-1].transpose(2, 0, 1)
238
239        with h5py.File(volume_path, "a") as f:
240            if "raw" not in f.keys():
241                f.create_dataset("raw", data=image, compression="gzip")
242
243            if f"labels/instances/{annotations}" not in f.keys():
244                f.create_dataset(f"labels/instances/{annotations}", data=instance_mask, compression="gzip")
245
246            if f"labels/semantic/{annotations}" not in f.keys():
247                f.create_dataset(f"labels/semantic/{annotations}", data=semantic_mask, compression="gzip")
248
249
250def _annotations_are_stored(data_dir, annotations):
251    import h5py
252    volume_paths = glob(os.path.join(data_dir, "preprocessed", "*.h5"))
253    if not volume_paths:
254        return
255    f = h5py.File(volume_paths[0], "r")
256    return f"labels/instances/{annotations}" in f.keys()
257
258
259def get_puma_data(
260    path: Union[os.PathLike, str],
261    split: Literal["train", "val", "test"],
262    annotations: Literal['nuclei', 'tissue'] = "nuclei",
263    download: bool = False,
264) -> str:
265    """Download the PUMA data.
266
267    Args:
268        path: Filepath to a folder where the downloaded data will be saved.
269        split: The choice of data split.
270        annotations: The choice of annotations.
271        download: Whether to download the data if it is not present.
272
273    Returns:
274        Filepath where the dataset is downloaded and stored for further preprocessing.
275    """
276    if annotations not in ["nuclei", "tissue"]:
277        raise ValueError(f"'{annotations}' is not a valid annotation for the data.")
278
279    data_dir = os.path.join(path, split)
280    if os.path.exists(data_dir) and _annotations_are_stored(data_dir, annotations):
281        return data_dir
282
283    os.makedirs(path, exist_ok=True)
284
285    if not os.path.exists(os.path.join(path, "data")):
286        # Download the data.
287        zip_path = os.path.join(path, "roi.zip")
288        util.download_source(path=zip_path, url=URL["data"], download=download, checksum=CHECKSUM["data"])
289        util.unzip(zip_path=zip_path, dst=os.path.join(path, "data"))
290
291    # Download the annotations. All levels are fetched, as the split is shared across them and is
292    # validated against each level (the geojson files are small).
293    for level in CLASS_DICT:
294        annotation_dir = os.path.join(path, "annotations", level)
295        if os.path.exists(annotation_dir):
296            continue
297
298        zip_path = os.path.join(path, "annotations.zip")
299        util.download_source(
300            path=zip_path,
301            url=URL["annotations"][level],
302            download=download,
303            checksum=CHECKSUM["annotations"][level]
304        )
305        util.unzip(zip_path=zip_path, dst=annotation_dir)
306
307    _preprocess_inputs(path, annotations, split)
308
309    return data_dir
310
311
312def get_puma_paths(
313    path: Union[os.PathLike, str],
314    split: Literal["train", "val", "test"],
315    annotations: Literal['nuclei', 'tissue'] = "nuclei",
316    download: bool = False
317) -> List[str]:
318    """Get paths to the PUMA dataset.
319
320    Args:
321        path: Filepath to a folder where the downloaded data will be saved.
322        split: The choice of data split.
323        annotations: The choice of annotations.
324        download: Whether to download the data if it is not present.
325
326    Returns:
327        List of filepaths for the input data.
328    """
329    data_dir = get_puma_data(path, split, annotations, download)
330    volume_paths = natsorted(glob(os.path.join(data_dir, "preprocessed", "*.h5")))
331    return volume_paths
332
333
334def get_puma_dataset(
335    path: Union[os.PathLike, str],
336    patch_shape: Tuple[int, int],
337    split: Literal["train", "val", "test"],
338    annotations: Literal['nuclei', 'tissue'] = "nuclei",
339    label_choice: Literal["instances", "semantic"] = "instances",
340    resize_inputs: bool = False,
341    download: bool = False,
342    **kwargs
343) -> Dataset:
344    """Get the PUMA dataset for nuclei and tissue segmentation.
345
346    Args:
347        path: Filepath to a folder where the downloaded data will be saved.
348        patch_shape: The patch shape to use for training.
349        split: The choice of data split.
350        annotations: The choice of annotations.
351        label_choice: The choice of segmentation type.
352        resize_inputs: Whether to resize the inputs.
353        download: Whether to download the data if it is not present.
354        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
355
356    Returns:
357        The segmentation dataset.
358    """
359    volume_paths = get_puma_paths(path, split, annotations, download)
360
361    if resize_inputs:
362        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": True}
363        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
364            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
365        )
366
367    return torch_em.default_segmentation_dataset(
368        raw_paths=volume_paths,
369        raw_key="raw",
370        label_paths=volume_paths,
371        label_key=f"labels/{label_choice}/{annotations}",
372        patch_shape=patch_shape,
373        with_channels=True,
374        is_seg_dataset=True,
375        ndim=2,
376        **kwargs
377    )
378
379
380def get_puma_loader(
381    path: Union[os.PathLike, str],
382    batch_size: int,
383    patch_shape: Tuple[int, int],
384    split: Literal["train", "val", "test"],
385    annotations: Literal['nuclei', 'tissue'] = "nuclei",
386    label_choice: Literal["instances", "semantic"] = "instances",
387    resize_inputs: bool = False,
388    download: bool = False,
389    **kwargs
390) -> DataLoader:
391    """Get the PUMA dataloader for nuclei and tissue segmentation.
392
393    Args:
394        path: Filepath to a folder where the downloaded data will be saved.
395        batch_size: The batch size for training.
396        patch_shape: The patch shape to use for training.
397        split: The choice of data split.
398        annotations: The choice of annotations.
399        label_choice: The choice of segmentation type.
400        resize_inputs: Whether to resize the inputs.
401        download: Whether to download the data if it is not present.
402        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
403
404    Returns:
405        The DataLoader.
406    """
407    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
408    dataset = get_puma_dataset(
409        path, patch_shape, split, annotations, label_choice, resize_inputs, download, **ds_kwargs
410    )
411    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URL = {'data': 'https://zenodo.org/records/15050523/files/01_training_dataset_tif_ROIs.zip', 'annotations': {'nuclei': 'https://zenodo.org/records/15050523/files/01_training_dataset_geojson_nuclei.zip', 'tissue': 'https://zenodo.org/records/15050523/files/01_training_dataset_geojson_tissue.zip'}}
CHECKSUM = {'data': 'af48b879f8ff7e74b84a7114924881606f13f108aa0f9bcc21d3593b717ee022', 'annotations': {'nuclei': 'eda271225900d6de0759e0281f3731a570e09f2adab58bd36425b9d2dfad91a0', 'tissue': 'fc2835135cc28324f52eac131327f0f12c554c0b1f334a108bf4b65e0f18c42b'}}
NUCLEI_CLASS_DICT = {'nuclei_stroma': 1, 'nuclei_tumor': 2, 'nuclei_plasma_cell': 3, 'nuclei_histiocyte': 4, 'nuclei_lymphocyte': 5, 'nuclei_melanophage': 6, 'nuclei_neutrophil': 7, 'nuclei_endothelium': 8, 'nuclei_epithelium': 9, 'nuclei_apoptosis': 10}
TISSUE_CLASS_DICT = {'tissue_stroma': 1, 'tissue_tumor': 2, 'tissue_epidermis': 3, 'tissue_blood_vessel': 4, 'tissue_necrosis': 5, 'tissue_white_background': 6}
CLASS_DICT = {'nuclei': {'nuclei_stroma': 1, 'nuclei_tumor': 2, 'nuclei_plasma_cell': 3, 'nuclei_histiocyte': 4, 'nuclei_lymphocyte': 5, 'nuclei_melanophage': 6, 'nuclei_neutrophil': 7, 'nuclei_endothelium': 8, 'nuclei_epithelium': 9, 'nuclei_apoptosis': 10}, 'tissue': {'tissue_stroma': 1, 'tissue_tumor': 2, 'tissue_epidermis': 3, 'tissue_blood_vessel': 4, 'tissue_necrosis': 5, 'tissue_white_background': 6}}
def get_puma_data( path: Union[os.PathLike, str], split: Literal['train', 'val', 'test'], annotations: Literal['nuclei', 'tissue'] = 'nuclei', download: bool = False) -> str:
260def get_puma_data(
261    path: Union[os.PathLike, str],
262    split: Literal["train", "val", "test"],
263    annotations: Literal['nuclei', 'tissue'] = "nuclei",
264    download: bool = False,
265) -> str:
266    """Download the PUMA data.
267
268    Args:
269        path: Filepath to a folder where the downloaded data will be saved.
270        split: The choice of data split.
271        annotations: The choice of annotations.
272        download: Whether to download the data if it is not present.
273
274    Returns:
275        Filepath where the dataset is downloaded and stored for further preprocessing.
276    """
277    if annotations not in ["nuclei", "tissue"]:
278        raise ValueError(f"'{annotations}' is not a valid annotation for the data.")
279
280    data_dir = os.path.join(path, split)
281    if os.path.exists(data_dir) and _annotations_are_stored(data_dir, annotations):
282        return data_dir
283
284    os.makedirs(path, exist_ok=True)
285
286    if not os.path.exists(os.path.join(path, "data")):
287        # Download the data.
288        zip_path = os.path.join(path, "roi.zip")
289        util.download_source(path=zip_path, url=URL["data"], download=download, checksum=CHECKSUM["data"])
290        util.unzip(zip_path=zip_path, dst=os.path.join(path, "data"))
291
292    # Download the annotations. All levels are fetched, as the split is shared across them and is
293    # validated against each level (the geojson files are small).
294    for level in CLASS_DICT:
295        annotation_dir = os.path.join(path, "annotations", level)
296        if os.path.exists(annotation_dir):
297            continue
298
299        zip_path = os.path.join(path, "annotations.zip")
300        util.download_source(
301            path=zip_path,
302            url=URL["annotations"][level],
303            download=download,
304            checksum=CHECKSUM["annotations"][level]
305        )
306        util.unzip(zip_path=zip_path, dst=annotation_dir)
307
308    _preprocess_inputs(path, annotations, split)
309
310    return data_dir

Download the PUMA data.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • split: The choice of data split.
  • annotations: The choice of annotations.
  • download: Whether to download the data if it is not present.
Returns:

Filepath where the dataset is downloaded and stored for further preprocessing.

def get_puma_paths( path: Union[os.PathLike, str], split: Literal['train', 'val', 'test'], annotations: Literal['nuclei', 'tissue'] = 'nuclei', download: bool = False) -> List[str]:
313def get_puma_paths(
314    path: Union[os.PathLike, str],
315    split: Literal["train", "val", "test"],
316    annotations: Literal['nuclei', 'tissue'] = "nuclei",
317    download: bool = False
318) -> List[str]:
319    """Get paths to the PUMA dataset.
320
321    Args:
322        path: Filepath to a folder where the downloaded data will be saved.
323        split: The choice of data split.
324        annotations: The choice of annotations.
325        download: Whether to download the data if it is not present.
326
327    Returns:
328        List of filepaths for the input data.
329    """
330    data_dir = get_puma_data(path, split, annotations, download)
331    volume_paths = natsorted(glob(os.path.join(data_dir, "preprocessed", "*.h5")))
332    return volume_paths

Get paths to the PUMA dataset.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • split: The choice of data split.
  • annotations: The choice of annotations.
  • download: Whether to download the data if it is not present.
Returns:

List of filepaths for the input data.

def get_puma_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int], split: Literal['train', 'val', 'test'], annotations: Literal['nuclei', 'tissue'] = 'nuclei', label_choice: Literal['instances', 'semantic'] = 'instances', resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
335def get_puma_dataset(
336    path: Union[os.PathLike, str],
337    patch_shape: Tuple[int, int],
338    split: Literal["train", "val", "test"],
339    annotations: Literal['nuclei', 'tissue'] = "nuclei",
340    label_choice: Literal["instances", "semantic"] = "instances",
341    resize_inputs: bool = False,
342    download: bool = False,
343    **kwargs
344) -> Dataset:
345    """Get the PUMA dataset for nuclei and tissue segmentation.
346
347    Args:
348        path: Filepath to a folder where the downloaded data will be saved.
349        patch_shape: The patch shape to use for training.
350        split: The choice of data split.
351        annotations: The choice of annotations.
352        label_choice: The choice of segmentation type.
353        resize_inputs: Whether to resize the inputs.
354        download: Whether to download the data if it is not present.
355        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
356
357    Returns:
358        The segmentation dataset.
359    """
360    volume_paths = get_puma_paths(path, split, annotations, download)
361
362    if resize_inputs:
363        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": True}
364        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
365            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
366        )
367
368    return torch_em.default_segmentation_dataset(
369        raw_paths=volume_paths,
370        raw_key="raw",
371        label_paths=volume_paths,
372        label_key=f"labels/{label_choice}/{annotations}",
373        patch_shape=patch_shape,
374        with_channels=True,
375        is_seg_dataset=True,
376        ndim=2,
377        **kwargs
378    )

Get the PUMA dataset for nuclei and tissue 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 choice of data split.
  • annotations: The choice of annotations.
  • label_choice: The choice of segmentation type.
  • resize_inputs: Whether to resize the inputs.
  • 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_puma_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, int], split: Literal['train', 'val', 'test'], annotations: Literal['nuclei', 'tissue'] = 'nuclei', label_choice: Literal['instances', 'semantic'] = 'instances', resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
381def get_puma_loader(
382    path: Union[os.PathLike, str],
383    batch_size: int,
384    patch_shape: Tuple[int, int],
385    split: Literal["train", "val", "test"],
386    annotations: Literal['nuclei', 'tissue'] = "nuclei",
387    label_choice: Literal["instances", "semantic"] = "instances",
388    resize_inputs: bool = False,
389    download: bool = False,
390    **kwargs
391) -> DataLoader:
392    """Get the PUMA dataloader for nuclei and tissue segmentation.
393
394    Args:
395        path: Filepath to a folder where the downloaded data will be saved.
396        batch_size: The batch size for training.
397        patch_shape: The patch shape to use for training.
398        split: The choice of data split.
399        annotations: The choice of annotations.
400        label_choice: The choice of segmentation type.
401        resize_inputs: Whether to resize the inputs.
402        download: Whether to download the data if it is not present.
403        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
404
405    Returns:
406        The DataLoader.
407    """
408    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
409    dataset = get_puma_dataset(
410        path, patch_shape, split, annotations, label_choice, resize_inputs, download, **ds_kwargs
411    )
412    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the PUMA dataloader for nuclei and tissue 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 choice of data split.
  • annotations: The choice of annotations.
  • label_choice: The choice of segmentation type.
  • resize_inputs: Whether to resize the inputs.
  • download: Whether to download the data if it is not present.
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset or for the PyTorch DataLoader.
Returns:

The DataLoader.