torch_em.data.datasets.histopathology.spatialproteomics_bnhl

This dataset contains cell instance segmentation annotations for highly multiplexed immunofluorescence imaging of reactive lymph nodes and various B cell Non-Hodgkin lymphomas (BNHL), acquired with the Akoya Phenocycler.

The data is from the publication https://doi.org/10.1038/s41592-026-03155-1 and hosted on the BioImage Archive at https://www.ebi.ac.uk/biostudies/studies/S-BIAD2100. Please cite it if you use this dataset in your research.

This loader covers the 250 tissue microarray (TMA) cores. Each core is a 3000x3000 pixel crop with 56 immunofluorescence channels and a matching per-cell instance segmentation mask, generated with cellpose on the DAPI channel. The BioImage Archive record also hosts 5 whole-slide images (24-63 GB each), which are out of scope for this loader.

On first use, each TMA core is converted into a single HDF5 file with the following layout: - 'raw/all': the (56, H, W) stack of all channels. - 'raw/channels/': each individual channel (H, W), see CHANNELS for the full list. - 'labels/instances': the instance segmentation.

  1"""This dataset contains cell instance segmentation annotations for highly multiplexed
  2immunofluorescence imaging of reactive lymph nodes and various B cell Non-Hodgkin lymphomas (BNHL),
  3acquired with the Akoya Phenocycler.
  4
  5The data is from the publication https://doi.org/10.1038/s41592-026-03155-1 and hosted on the
  6BioImage Archive at https://www.ebi.ac.uk/biostudies/studies/S-BIAD2100. Please cite it if you use
  7this dataset in your research.
  8
  9This loader covers the 250 tissue microarray (TMA) cores. Each core is a 3000x3000 pixel crop with
 1056 immunofluorescence channels and a matching per-cell instance segmentation mask, generated with
 11cellpose on the DAPI channel. The BioImage Archive record also hosts 5 whole-slide images
 12(24-63 GB each), which are out of scope for this loader.
 13
 14On first use, each TMA core is converted into a single HDF5 file with the following layout:
 15    - 'raw/all': the (56, H, W) stack of all channels.
 16    - 'raw/channels/<channel>': each individual channel (H, W), see `CHANNELS` for the full list.
 17    - 'labels/instances': the instance segmentation.
 18"""
 19
 20import os
 21from glob import glob
 22from typing import List, Optional, Sequence, Tuple, Union
 23
 24import pandas as pd
 25
 26from torch.utils.data import Dataset, DataLoader
 27
 28import torch_em
 29
 30from .. import util
 31
 32
 33BASE_URL = "https://ftp.ebi.ac.uk/biostudies/fire/S-BIAD/100/S-BIAD2100/Files/data_for_publication"
 34MANIFEST_URL = f"{BASE_URL}/derived_data/tma_sample_overview.csv"
 35MANIFEST_CHECKSUM = "c2bb69d29af94c09cb7cd5b3b10e3acfdffb6beea1d7e8dd46b93cc30f55b0f8"
 36
 37CHANNELS = (
 38    "DAPI", "Helios", "CD10", "TCF7/TCF1", "PD-L1", "BCL-6", "FOXP3", "CD69", "Perforin", "CD19",
 39    "LAG3", "CD21", "CD62L", "c-myc", "CD138", "CD15", "BCL-2", "CD56", "IKZF3", "CD25", "NOXA",
 40    "Tim3", "Serpin B9", "Podoplanin", "CD38", "SPARC", "ICOS", "CXCR5", "CD163", "FADD", "p53",
 41    "Collagen IV", "CD4", "CD7", "Kappa", "CD20", "CD34", "PAX5", "PD-1", "CD45RA", "CD11b",
 42    "Lambda", "CD57", "CD11c", "CD90", "HLA DR", "CD68", "CD31", "CD45", "CD3", "Cytokeratin",
 43    "CD45RO", "CD8", "Granzyme B", "CD79a", "Ki-67",
 44)
 45
 46
 47def _sanitize_channel(name):
 48    return name.replace("/", "-")
 49
 50
 51def _get_manifest(path, download):
 52    manifest_path = os.path.join(path, "tma_sample_overview.csv")
 53    util.download_source(manifest_path, MANIFEST_URL, download, MANIFEST_CHECKSUM)
 54    return pd.read_csv(manifest_path, index_col=0)
 55
 56
 57def _convert_sample(zip_path, output_path):
 58    import h5py
 59    import zarr
 60
 61    store = zarr.storage.ZipStore(zip_path, mode="r")
 62    group = zarr.open_group(store=store, mode="r")
 63    channels = [str(c) for c in group["channels"][:]]
 64    if channels != list(CHANNELS):
 65        raise RuntimeError(f"Unexpected channel order in {zip_path}.")
 66
 67    raw = group["_image_raw"][:]
 68    instances = group["_segmentation"][:]
 69    store.close()
 70
 71    tmp_path = output_path + ".tmp"
 72    with h5py.File(tmp_path, "w") as f:
 73        f.create_dataset("raw/all", data=raw, compression="gzip", chunks=(len(CHANNELS), 512, 512))
 74        for i, name in enumerate(CHANNELS):
 75            f.create_dataset(f"raw/channels/{_sanitize_channel(name)}", data=raw[i], compression="gzip")
 76        f.create_dataset("labels/instances", data=instances, compression="gzip")
 77
 78    os.replace(tmp_path, output_path)
 79
 80
 81def get_spatialproteomics_bnhl_data(
 82    path: Union[os.PathLike, str],
 83    samples: Optional[Sequence[str]] = None,
 84    download: bool = False,
 85) -> str:
 86    """Download and preprocess the spatialproteomics BNHL tissue microarray (TMA) data.
 87
 88    Args:
 89        path: Filepath to a folder where the downloaded data will be saved.
 90        samples: The TMA sample ids to prepare. By default all 250 TMA cores are prepared, which
 91            requires downloading about 60 GB of data.
 92        download: Whether to download the data if it is not present.
 93
 94    Returns:
 95        Filepath to the folder where the preprocessed data is stored.
 96    """
 97    os.makedirs(path, exist_ok=True)
 98    manifest = _get_manifest(path, download)
 99    valid_samples = set(manifest["sample_id"])
100
101    if samples is None:
102        samples = sorted(valid_samples)
103    else:
104        invalid = sorted(set(samples) - valid_samples)
105        if invalid:
106            raise ValueError(f"Invalid sample id(s) {invalid}.")
107
108    preprocessed_dir = os.path.join(path, "preprocessed")
109    os.makedirs(preprocessed_dir, exist_ok=True)
110
111    zip_dir = os.path.join(path, "tmas")
112    os.makedirs(zip_dir, exist_ok=True)
113
114    for sample_id in samples:
115        output_path = os.path.join(preprocessed_dir, f"{sample_id}.h5")
116        if os.path.exists(output_path):
117            continue
118
119        zip_path = os.path.join(zip_dir, f"{sample_id}.zarr.zip")
120        util.download_source(zip_path, f"{BASE_URL}/tmas/{sample_id}.zarr.zip", download, checksum=None)
121        _convert_sample(zip_path, output_path)
122
123    return preprocessed_dir
124
125
126def get_spatialproteomics_bnhl_paths(
127    path: Union[os.PathLike, str],
128    samples: Optional[Sequence[str]] = None,
129    download: bool = False,
130) -> List[str]:
131    """Get paths to the preprocessed spatialproteomics BNHL TMA data.
132
133    Args:
134        path: Filepath to a folder where the downloaded data will be saved.
135        samples: The TMA sample ids to load. By default all 250 TMA cores are loaded.
136        download: Whether to download the data if it is not present.
137
138    Returns:
139        List of filepaths to the preprocessed HDF5 files.
140    """
141    preprocessed_dir = get_spatialproteomics_bnhl_data(path, samples, download)
142    if samples is None:
143        paths = sorted(glob(os.path.join(preprocessed_dir, "*.h5")))
144    else:
145        paths = [os.path.join(preprocessed_dir, f"{sample_id}.h5") for sample_id in samples]
146
147    missing = [p for p in paths if not os.path.exists(p)]
148    if missing:
149        raise RuntimeError(f"Could not find the data at {missing}.")
150
151    return paths
152
153
154def get_spatialproteomics_bnhl_dataset(
155    path: Union[os.PathLike, str],
156    patch_shape: Tuple[int, int],
157    samples: Optional[Sequence[str]] = None,
158    channel: str = "all",
159    download: bool = False,
160    resize_inputs: bool = False,
161    **kwargs
162) -> Dataset:
163    """Get the spatialproteomics BNHL dataset for cell instance segmentation in multiplexed
164    immunofluorescence images of B cell Non-Hodgkin lymphomas.
165
166    Args:
167        path: Filepath to a folder where the downloaded data will be saved.
168        patch_shape: The patch shape to use for training.
169        samples: The TMA sample ids to load. By default all 250 TMA cores are loaded.
170        channel: The raw input. Either 'all' for the full (56, H, W) channel stack, or the name of
171            a single channel, see `CHANNELS` for the full list, e.g. 'CD20'.
172        download: Whether to download the data if it is not present.
173        resize_inputs: Whether to resize the input images.
174        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
175
176    Returns:
177        The segmentation dataset.
178    """
179    if channel == "all":
180        raw_key, with_channels = "raw/all", True
181    elif channel in CHANNELS:
182        raw_key, with_channels = f"raw/channels/{_sanitize_channel(channel)}", False
183    else:
184        raise ValueError(f"'{channel}' is not a valid channel. Choose 'all' or one of {CHANNELS}.")
185
186    paths = get_spatialproteomics_bnhl_paths(path, samples, download)
187
188    if resize_inputs:
189        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
190        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
191            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
192        )
193
194    return torch_em.default_segmentation_dataset(
195        raw_paths=paths,
196        raw_key=raw_key,
197        label_paths=paths,
198        label_key="labels/instances",
199        patch_shape=patch_shape,
200        is_seg_dataset=True,
201        with_channels=with_channels,
202        ndim=2,
203        **kwargs
204    )
205
206
207def get_spatialproteomics_bnhl_loader(
208    path: Union[os.PathLike, str],
209    patch_shape: Tuple[int, int],
210    batch_size: int,
211    samples: Optional[Sequence[str]] = None,
212    channel: str = "all",
213    download: bool = False,
214    resize_inputs: bool = False,
215    **kwargs
216) -> DataLoader:
217    """Get the spatialproteomics BNHL dataloader for cell instance segmentation in multiplexed
218    immunofluorescence images of B cell Non-Hodgkin lymphomas.
219
220    Args:
221        path: Filepath to a folder where the downloaded data will be saved.
222        patch_shape: The patch shape to use for training.
223        batch_size: The batch size for training.
224        samples: The TMA sample ids to load. By default all 250 TMA cores are loaded.
225        channel: The raw input. Either 'all' for the full (56, H, W) channel stack, or the name of
226            a single channel, see `CHANNELS` for the full list, e.g. 'CD20'.
227        download: Whether to download the data if it is not present.
228        resize_inputs: Whether to resize the input images.
229        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the
230            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_spatialproteomics_bnhl_dataset(
237        path, patch_shape, samples=samples, channel=channel, download=download,
238        resize_inputs=resize_inputs, **ds_kwargs
239    )
240    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
BASE_URL = 'https://ftp.ebi.ac.uk/biostudies/fire/S-BIAD/100/S-BIAD2100/Files/data_for_publication'
MANIFEST_URL = 'https://ftp.ebi.ac.uk/biostudies/fire/S-BIAD/100/S-BIAD2100/Files/data_for_publication/derived_data/tma_sample_overview.csv'
MANIFEST_CHECKSUM = 'c2bb69d29af94c09cb7cd5b3b10e3acfdffb6beea1d7e8dd46b93cc30f55b0f8'
CHANNELS = ('DAPI', 'Helios', 'CD10', 'TCF7/TCF1', 'PD-L1', 'BCL-6', 'FOXP3', 'CD69', 'Perforin', 'CD19', 'LAG3', 'CD21', 'CD62L', 'c-myc', 'CD138', 'CD15', 'BCL-2', 'CD56', 'IKZF3', 'CD25', 'NOXA', 'Tim3', 'Serpin B9', 'Podoplanin', 'CD38', 'SPARC', 'ICOS', 'CXCR5', 'CD163', 'FADD', 'p53', 'Collagen IV', 'CD4', 'CD7', 'Kappa', 'CD20', 'CD34', 'PAX5', 'PD-1', 'CD45RA', 'CD11b', 'Lambda', 'CD57', 'CD11c', 'CD90', 'HLA DR', 'CD68', 'CD31', 'CD45', 'CD3', 'Cytokeratin', 'CD45RO', 'CD8', 'Granzyme B', 'CD79a', 'Ki-67')
def get_spatialproteomics_bnhl_data( path: Union[os.PathLike, str], samples: Optional[Sequence[str]] = None, download: bool = False) -> str:
 82def get_spatialproteomics_bnhl_data(
 83    path: Union[os.PathLike, str],
 84    samples: Optional[Sequence[str]] = None,
 85    download: bool = False,
 86) -> str:
 87    """Download and preprocess the spatialproteomics BNHL tissue microarray (TMA) data.
 88
 89    Args:
 90        path: Filepath to a folder where the downloaded data will be saved.
 91        samples: The TMA sample ids to prepare. By default all 250 TMA cores are prepared, which
 92            requires downloading about 60 GB of data.
 93        download: Whether to download the data if it is not present.
 94
 95    Returns:
 96        Filepath to the folder where the preprocessed data is stored.
 97    """
 98    os.makedirs(path, exist_ok=True)
 99    manifest = _get_manifest(path, download)
100    valid_samples = set(manifest["sample_id"])
101
102    if samples is None:
103        samples = sorted(valid_samples)
104    else:
105        invalid = sorted(set(samples) - valid_samples)
106        if invalid:
107            raise ValueError(f"Invalid sample id(s) {invalid}.")
108
109    preprocessed_dir = os.path.join(path, "preprocessed")
110    os.makedirs(preprocessed_dir, exist_ok=True)
111
112    zip_dir = os.path.join(path, "tmas")
113    os.makedirs(zip_dir, exist_ok=True)
114
115    for sample_id in samples:
116        output_path = os.path.join(preprocessed_dir, f"{sample_id}.h5")
117        if os.path.exists(output_path):
118            continue
119
120        zip_path = os.path.join(zip_dir, f"{sample_id}.zarr.zip")
121        util.download_source(zip_path, f"{BASE_URL}/tmas/{sample_id}.zarr.zip", download, checksum=None)
122        _convert_sample(zip_path, output_path)
123
124    return preprocessed_dir

Download and preprocess the spatialproteomics BNHL tissue microarray (TMA) data.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • samples: The TMA sample ids to prepare. By default all 250 TMA cores are prepared, which requires downloading about 60 GB of data.
  • download: Whether to download the data if it is not present.
Returns:

Filepath to the folder where the preprocessed data is stored.

def get_spatialproteomics_bnhl_paths( path: Union[os.PathLike, str], samples: Optional[Sequence[str]] = None, download: bool = False) -> List[str]:
127def get_spatialproteomics_bnhl_paths(
128    path: Union[os.PathLike, str],
129    samples: Optional[Sequence[str]] = None,
130    download: bool = False,
131) -> List[str]:
132    """Get paths to the preprocessed spatialproteomics BNHL TMA data.
133
134    Args:
135        path: Filepath to a folder where the downloaded data will be saved.
136        samples: The TMA sample ids to load. By default all 250 TMA cores are loaded.
137        download: Whether to download the data if it is not present.
138
139    Returns:
140        List of filepaths to the preprocessed HDF5 files.
141    """
142    preprocessed_dir = get_spatialproteomics_bnhl_data(path, samples, download)
143    if samples is None:
144        paths = sorted(glob(os.path.join(preprocessed_dir, "*.h5")))
145    else:
146        paths = [os.path.join(preprocessed_dir, f"{sample_id}.h5") for sample_id in samples]
147
148    missing = [p for p in paths if not os.path.exists(p)]
149    if missing:
150        raise RuntimeError(f"Could not find the data at {missing}.")
151
152    return paths

Get paths to the preprocessed spatialproteomics BNHL TMA data.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • samples: The TMA sample ids to load. By default all 250 TMA cores are loaded.
  • download: Whether to download the data if it is not present.
Returns:

List of filepaths to the preprocessed HDF5 files.

def get_spatialproteomics_bnhl_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int], samples: Optional[Sequence[str]] = None, channel: str = 'all', download: bool = False, resize_inputs: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
155def get_spatialproteomics_bnhl_dataset(
156    path: Union[os.PathLike, str],
157    patch_shape: Tuple[int, int],
158    samples: Optional[Sequence[str]] = None,
159    channel: str = "all",
160    download: bool = False,
161    resize_inputs: bool = False,
162    **kwargs
163) -> Dataset:
164    """Get the spatialproteomics BNHL dataset for cell instance segmentation in multiplexed
165    immunofluorescence images of B cell Non-Hodgkin lymphomas.
166
167    Args:
168        path: Filepath to a folder where the downloaded data will be saved.
169        patch_shape: The patch shape to use for training.
170        samples: The TMA sample ids to load. By default all 250 TMA cores are loaded.
171        channel: The raw input. Either 'all' for the full (56, H, W) channel stack, or the name of
172            a single channel, see `CHANNELS` for the full list, e.g. 'CD20'.
173        download: Whether to download the data if it is not present.
174        resize_inputs: Whether to resize the input images.
175        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
176
177    Returns:
178        The segmentation dataset.
179    """
180    if channel == "all":
181        raw_key, with_channels = "raw/all", True
182    elif channel in CHANNELS:
183        raw_key, with_channels = f"raw/channels/{_sanitize_channel(channel)}", False
184    else:
185        raise ValueError(f"'{channel}' is not a valid channel. Choose 'all' or one of {CHANNELS}.")
186
187    paths = get_spatialproteomics_bnhl_paths(path, samples, download)
188
189    if resize_inputs:
190        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
191        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
192            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
193        )
194
195    return torch_em.default_segmentation_dataset(
196        raw_paths=paths,
197        raw_key=raw_key,
198        label_paths=paths,
199        label_key="labels/instances",
200        patch_shape=patch_shape,
201        is_seg_dataset=True,
202        with_channels=with_channels,
203        ndim=2,
204        **kwargs
205    )

Get the spatialproteomics BNHL dataset for cell instance segmentation in multiplexed immunofluorescence images of B cell Non-Hodgkin lymphomas.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • patch_shape: The patch shape to use for training.
  • samples: The TMA sample ids to load. By default all 250 TMA cores are loaded.
  • channel: The raw input. Either 'all' for the full (56, H, W) channel stack, or the name of a single channel, see CHANNELS for the full list, e.g. 'CD20'.
  • download: Whether to download the data if it is not present.
  • resize_inputs: Whether to resize the input images.
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset.
Returns:

The segmentation dataset.

def get_spatialproteomics_bnhl_loader( path: Union[os.PathLike, str], patch_shape: Tuple[int, int], batch_size: int, samples: Optional[Sequence[str]] = None, channel: str = 'all', download: bool = False, resize_inputs: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
208def get_spatialproteomics_bnhl_loader(
209    path: Union[os.PathLike, str],
210    patch_shape: Tuple[int, int],
211    batch_size: int,
212    samples: Optional[Sequence[str]] = None,
213    channel: str = "all",
214    download: bool = False,
215    resize_inputs: bool = False,
216    **kwargs
217) -> DataLoader:
218    """Get the spatialproteomics BNHL dataloader for cell instance segmentation in multiplexed
219    immunofluorescence images of B cell Non-Hodgkin lymphomas.
220
221    Args:
222        path: Filepath to a folder where the downloaded data will be saved.
223        patch_shape: The patch shape to use for training.
224        batch_size: The batch size for training.
225        samples: The TMA sample ids to load. By default all 250 TMA cores are loaded.
226        channel: The raw input. Either 'all' for the full (56, H, W) channel stack, or the name of
227            a single channel, see `CHANNELS` for the full list, e.g. 'CD20'.
228        download: Whether to download the data if it is not present.
229        resize_inputs: Whether to resize the input images.
230        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the
231            PyTorch DataLoader.
232
233    Returns:
234        The DataLoader.
235    """
236    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
237    dataset = get_spatialproteomics_bnhl_dataset(
238        path, patch_shape, samples=samples, channel=channel, download=download,
239        resize_inputs=resize_inputs, **ds_kwargs
240    )
241    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the spatialproteomics BNHL dataloader for cell instance segmentation in multiplexed immunofluorescence images of B cell Non-Hodgkin lymphomas.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • patch_shape: The patch shape to use for training.
  • batch_size: The batch size for training.
  • samples: The TMA sample ids to load. By default all 250 TMA cores are loaded.
  • channel: The raw input. Either 'all' for the full (56, H, W) channel stack, or the name of a single channel, see CHANNELS for the full list, e.g. 'CD20'.
  • download: Whether to download the data if it is not present.
  • resize_inputs: Whether to resize the input images.
  • kwargs: Additional keyword arguments for torch_em.default_segmentation_dataset or for the PyTorch DataLoader.
Returns:

The DataLoader.