torch_em.data.datasets.light_microscopy.synthmt

The SynthMT dataset contains interference reflection microscopy (IRM) images of microtubules with segmentation annotations. It is available from two sources, selected with the 'source' argument:

  • 'synthetic_irm': 6,600 synthetically generated 512x512 RGB images with per-instance binary masks, designed to train foundation models (e.g. SAM) for automated in vitro microtubule analysis.
  • 'real_irm': the IRM_InVitroMT dataset with 237 real images (206 train and 31 test) with manually annotated binary microtubule masks, acquired with four microscopes.

The synthetic data is located at https://huggingface.co/datasets/HTW-KI-Werkstatt/SynthMT. This dataset is from the publication https://doi.org/10.64898/2026.01.09.698597. The real data is located at https://doi.org/10.5281/zenodo.20597920 and is distributed under a CC BY 4.0 license. Please cite the publication if you use this dataset in your research.

  1"""The SynthMT dataset contains interference reflection microscopy (IRM) images of microtubules
  2with segmentation annotations. It is available from two sources, selected with the 'source' argument:
  3- 'synthetic_irm': 6,600 synthetically generated 512x512 RGB images with per-instance binary masks,
  4  designed to train foundation models (e.g. SAM) for automated in vitro microtubule analysis.
  5- 'real_irm': the IRM_InVitroMT dataset with 237 real images (206 train and 31 test) with manually annotated
  6  binary microtubule masks, acquired with four microscopes.
  7
  8The synthetic data is located at https://huggingface.co/datasets/HTW-KI-Werkstatt/SynthMT.
  9This dataset is from the publication https://doi.org/10.64898/2026.01.09.698597.
 10The real data is located at https://doi.org/10.5281/zenodo.20597920 and is distributed under a CC BY 4.0 license.
 11Please cite the publication if you use this dataset in your research.
 12"""
 13
 14import os
 15from glob import glob
 16from typing import Union, Tuple, List, Literal, Optional
 17
 18import numpy as np
 19
 20from torch.utils.data import Dataset, DataLoader
 21
 22import torch_em
 23
 24from .. import util
 25
 26
 27URL = "https://huggingface.co/datasets/HTW-KI-Werkstatt/SynthMT/resolve/main/data/{FILENAME}"
 28NUM_PARQUET_FILES = 8
 29
 30REAL_URLS = {
 31    "train": "https://zenodo.org/records/20597920/files/Training_dataset.zip",
 32    "test": "https://zenodo.org/records/20597920/files/Test_dataset.zip",
 33}
 34REAL_CHECKSUMS = {
 35    "train": "99ce37c0ed886f905b8cb3dc26af060ec71277db50a1dbba37dbed9839e09e37",
 36    "test": "bd11f99abcdebaf3784e18c33dcfd75e6386416cfd33b1f1567ed8c4a771d711",
 37}
 38REAL_DIRS = {
 39    "train": ("Training_dataset", "Training_source", "Training_target"),
 40    "test": ("Test_dataset", "Prediction_source", "Prediction_target"),
 41}
 42
 43
 44def _download_parquets(path, download):
 45    """Download all parquet files for the dataset."""
 46    parquet_dir = os.path.join(path, "parquets")
 47    os.makedirs(parquet_dir, exist_ok=True)
 48
 49    for i in range(NUM_PARQUET_FILES):
 50        fname = f"train-{i:05d}-of-{NUM_PARQUET_FILES:05d}.parquet"
 51        fpath = os.path.join(parquet_dir, fname)
 52        if not os.path.exists(fpath):
 53            url = URL.format(FILENAME=fname)
 54            util.download_source(path=fpath, url=url, download=download, checksum=None)
 55
 56    return parquet_dir
 57
 58
 59def _create_images_from_parquets(path):
 60    """Extract images and instance labels from parquet files and save as TIF."""
 61    import imageio.v3 as imageio
 62    import pandas as pd
 63    from io import BytesIO
 64    from PIL import Image
 65    from tqdm import tqdm
 66
 67    image_dir = os.path.join(path, "images")
 68    label_dir = os.path.join(path, "labels")
 69    os.makedirs(image_dir, exist_ok=True)
 70    os.makedirs(label_dir, exist_ok=True)
 71
 72    parquet_dir = os.path.join(path, "parquets")
 73    parquet_files = sorted(glob(os.path.join(parquet_dir, "*.parquet")))
 74
 75    for pfile in tqdm(parquet_files, desc="Processing parquet files"):
 76        df = pd.read_parquet(pfile)
 77        for _, row in df.iterrows():
 78            sample_id = row["id"]
 79            img_path = os.path.join(image_dir, f"{sample_id}.tif")
 80            lbl_path = os.path.join(label_dir, f"{sample_id}.tif")
 81
 82            if os.path.exists(img_path) and os.path.exists(lbl_path):
 83                continue
 84
 85            # Decode the image.
 86            img = Image.open(BytesIO(row["image"]["bytes"])).convert("RGB")
 87            img_arr = np.array(img)
 88
 89            # Decode instance masks and merge into a single label map.
 90            masks = row["mask"]
 91            instances = np.zeros(img_arr.shape[:2], dtype="uint32")
 92            for i, mask_entry in enumerate(masks, start=1):
 93                mask = np.array(Image.open(BytesIO(mask_entry["bytes"])).convert("L"))
 94                instances[mask > 0] = i
 95
 96            imageio.imwrite(img_path, img_arr, compression="zlib")
 97            imageio.imwrite(lbl_path, instances, compression="zlib")
 98
 99
100def _get_real_data(path, split, download):
101    data_dir, _, _ = REAL_DIRS[split]
102    real_dir = os.path.join(path, "real_irm")
103    if os.path.exists(os.path.join(real_dir, data_dir)):
104        return real_dir
105
106    os.makedirs(real_dir, exist_ok=True)
107    zip_path = os.path.join(real_dir, f"{data_dir}.zip")
108    util.download_source(path=zip_path, url=REAL_URLS[split], download=download, checksum=REAL_CHECKSUMS[split])
109    util.unzip(zip_path=zip_path, dst=real_dir)
110    return real_dir
111
112
113def get_synthmt_data(
114    path: Union[os.PathLike, str],
115    source: Literal["synthetic_irm", "real_irm"],
116    split: Optional[Literal["train", "test"]] = None,
117    download: bool = False,
118) -> str:
119    """Download the SynthMT dataset.
120
121    Args:
122        path: Filepath to a folder where the downloaded data will be saved.
123        source: The data source. Either 'synthetic_irm' or 'real_irm'.
124        split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
125        download: Whether to download the data if it is not present.
126
127    Returns:
128        The filepath to the directory with the data.
129    """
130    assert source in ("synthetic_irm", "real_irm"), f"'{source}' is not a valid data source."
131
132    if source == "real_irm":
133        assert split in REAL_URLS, f"'{split}' is not a valid data split of the real source."
134        return _get_real_data(path, split, download)
135
136    assert split is None, "The synthetic source has no data splits."
137    _download_parquets(path, download)
138
139    image_dir = os.path.join(path, "images")
140    label_dir = os.path.join(path, "labels")
141    if not os.path.exists(image_dir) or not os.path.exists(label_dir):
142        _create_images_from_parquets(path)
143
144    return path
145
146
147def get_synthmt_paths(
148    path: Union[os.PathLike, str],
149    source: Literal["synthetic_irm", "real_irm"],
150    split: Optional[Literal["train", "test"]] = None,
151    download: bool = False,
152) -> Tuple[List[str], List[str]]:
153    """Get paths to the SynthMT data.
154
155    Args:
156        path: Filepath to a folder where the downloaded data will be saved.
157        source: The data source. Either 'synthetic_irm' or 'real_irm'.
158        split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
159        download: Whether to download the data if it is not present.
160
161    Returns:
162        List of filepaths for the image data.
163        List of filepaths for the label data.
164    """
165    from natsort import natsorted
166
167    data_path = get_synthmt_data(path, source, split, download)
168
169    if source == "real_irm":
170        data_dir, image_dir, label_dir = REAL_DIRS[split]
171        image_paths = natsorted(glob(os.path.join(data_path, data_dir, image_dir, "*.tif")))
172        label_paths = natsorted(glob(os.path.join(data_path, data_dir, label_dir, "*.tif")))
173        assert [os.path.basename(p) for p in image_paths] == [os.path.basename(p) for p in label_paths]
174    else:
175        image_paths = natsorted(glob(os.path.join(data_path, "images", "*.tif")))
176        label_paths = natsorted(glob(os.path.join(data_path, "labels", "*.tif")))
177
178    assert len(image_paths) == len(label_paths) and len(image_paths) > 0
179
180    return image_paths, label_paths
181
182
183def get_synthmt_dataset(
184    path: Union[os.PathLike, str],
185    patch_shape: Tuple[int, int],
186    source: Literal["synthetic_irm", "real_irm"],
187    split: Optional[Literal["train", "test"]] = None,
188    download: bool = False,
189    **kwargs,
190) -> Dataset:
191    """Get the SynthMT dataset for microtubule segmentation.
192
193    The synthetic source provides instance labels, the real source provides binary labels.
194
195    Args:
196        path: Filepath to a folder where the downloaded data will be saved.
197        patch_shape: The patch shape to use for training.
198        source: The data source. Either 'synthetic_irm' or 'real_irm'.
199        split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
200        download: Whether to download the data if it is not present.
201        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
202
203    Returns:
204        The segmentation dataset.
205    """
206    image_paths, label_paths = get_synthmt_paths(path, source, split, download)
207
208    if source == "real_irm":
209        kwargs = util.update_kwargs(kwargs, "label_transform", torch_em.transform.label.labels_to_binary)
210    else:
211        kwargs, _ = util.add_instance_label_transform(kwargs, add_binary_target=True)
212    kwargs = util.update_kwargs(kwargs, "ndim", 2)
213
214    return torch_em.default_segmentation_dataset(
215        raw_paths=image_paths,
216        raw_key=None,
217        label_paths=label_paths,
218        label_key=None,
219        patch_shape=patch_shape,
220        is_seg_dataset=False,
221        **kwargs,
222    )
223
224
225def get_synthmt_loader(
226    path: Union[os.PathLike, str],
227    batch_size: int,
228    patch_shape: Tuple[int, int],
229    source: Literal["synthetic_irm", "real_irm"],
230    split: Optional[Literal["train", "test"]] = None,
231    download: bool = False,
232    **kwargs,
233) -> DataLoader:
234    """Get the SynthMT dataloader for microtubule segmentation.
235
236    Args:
237        path: Filepath to a folder where the downloaded data will be saved.
238        batch_size: The batch size for training.
239        patch_shape: The patch shape to use for training.
240        source: The data source. Either 'synthetic_irm' or 'real_irm'.
241        split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
242        download: Whether to download the data if it is not present.
243        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
244
245    Returns:
246        The DataLoader.
247    """
248    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
249    dataset = get_synthmt_dataset(path, patch_shape, source, split, download, **ds_kwargs)
250    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
URL = 'https://huggingface.co/datasets/HTW-KI-Werkstatt/SynthMT/resolve/main/data/{FILENAME}'
NUM_PARQUET_FILES = 8
REAL_URLS = {'train': 'https://zenodo.org/records/20597920/files/Training_dataset.zip', 'test': 'https://zenodo.org/records/20597920/files/Test_dataset.zip'}
REAL_CHECKSUMS = {'train': '99ce37c0ed886f905b8cb3dc26af060ec71277db50a1dbba37dbed9839e09e37', 'test': 'bd11f99abcdebaf3784e18c33dcfd75e6386416cfd33b1f1567ed8c4a771d711'}
REAL_DIRS = {'train': ('Training_dataset', 'Training_source', 'Training_target'), 'test': ('Test_dataset', 'Prediction_source', 'Prediction_target')}
def get_synthmt_data( path: Union[os.PathLike, str], source: Literal['synthetic_irm', 'real_irm'], split: Optional[Literal['train', 'test']] = None, download: bool = False) -> str:
114def get_synthmt_data(
115    path: Union[os.PathLike, str],
116    source: Literal["synthetic_irm", "real_irm"],
117    split: Optional[Literal["train", "test"]] = None,
118    download: bool = False,
119) -> str:
120    """Download the SynthMT dataset.
121
122    Args:
123        path: Filepath to a folder where the downloaded data will be saved.
124        source: The data source. Either 'synthetic_irm' or 'real_irm'.
125        split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
126        download: Whether to download the data if it is not present.
127
128    Returns:
129        The filepath to the directory with the data.
130    """
131    assert source in ("synthetic_irm", "real_irm"), f"'{source}' is not a valid data source."
132
133    if source == "real_irm":
134        assert split in REAL_URLS, f"'{split}' is not a valid data split of the real source."
135        return _get_real_data(path, split, download)
136
137    assert split is None, "The synthetic source has no data splits."
138    _download_parquets(path, download)
139
140    image_dir = os.path.join(path, "images")
141    label_dir = os.path.join(path, "labels")
142    if not os.path.exists(image_dir) or not os.path.exists(label_dir):
143        _create_images_from_parquets(path)
144
145    return path

Download the SynthMT dataset.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • source: The data source. Either 'synthetic_irm' or 'real_irm'.
  • split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
  • download: Whether to download the data if it is not present.
Returns:

The filepath to the directory with the data.

def get_synthmt_paths( path: Union[os.PathLike, str], source: Literal['synthetic_irm', 'real_irm'], split: Optional[Literal['train', 'test']] = None, download: bool = False) -> Tuple[List[str], List[str]]:
148def get_synthmt_paths(
149    path: Union[os.PathLike, str],
150    source: Literal["synthetic_irm", "real_irm"],
151    split: Optional[Literal["train", "test"]] = None,
152    download: bool = False,
153) -> Tuple[List[str], List[str]]:
154    """Get paths to the SynthMT data.
155
156    Args:
157        path: Filepath to a folder where the downloaded data will be saved.
158        source: The data source. Either 'synthetic_irm' or 'real_irm'.
159        split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
160        download: Whether to download the data if it is not present.
161
162    Returns:
163        List of filepaths for the image data.
164        List of filepaths for the label data.
165    """
166    from natsort import natsorted
167
168    data_path = get_synthmt_data(path, source, split, download)
169
170    if source == "real_irm":
171        data_dir, image_dir, label_dir = REAL_DIRS[split]
172        image_paths = natsorted(glob(os.path.join(data_path, data_dir, image_dir, "*.tif")))
173        label_paths = natsorted(glob(os.path.join(data_path, data_dir, label_dir, "*.tif")))
174        assert [os.path.basename(p) for p in image_paths] == [os.path.basename(p) for p in label_paths]
175    else:
176        image_paths = natsorted(glob(os.path.join(data_path, "images", "*.tif")))
177        label_paths = natsorted(glob(os.path.join(data_path, "labels", "*.tif")))
178
179    assert len(image_paths) == len(label_paths) and len(image_paths) > 0
180
181    return image_paths, label_paths

Get paths to the SynthMT data.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • source: The data source. Either 'synthetic_irm' or 'real_irm'.
  • split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
  • 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_synthmt_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int], source: Literal['synthetic_irm', 'real_irm'], split: Optional[Literal['train', 'test']] = None, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
184def get_synthmt_dataset(
185    path: Union[os.PathLike, str],
186    patch_shape: Tuple[int, int],
187    source: Literal["synthetic_irm", "real_irm"],
188    split: Optional[Literal["train", "test"]] = None,
189    download: bool = False,
190    **kwargs,
191) -> Dataset:
192    """Get the SynthMT dataset for microtubule segmentation.
193
194    The synthetic source provides instance labels, the real source provides binary labels.
195
196    Args:
197        path: Filepath to a folder where the downloaded data will be saved.
198        patch_shape: The patch shape to use for training.
199        source: The data source. Either 'synthetic_irm' or 'real_irm'.
200        split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
201        download: Whether to download the data if it is not present.
202        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
203
204    Returns:
205        The segmentation dataset.
206    """
207    image_paths, label_paths = get_synthmt_paths(path, source, split, download)
208
209    if source == "real_irm":
210        kwargs = util.update_kwargs(kwargs, "label_transform", torch_em.transform.label.labels_to_binary)
211    else:
212        kwargs, _ = util.add_instance_label_transform(kwargs, add_binary_target=True)
213    kwargs = util.update_kwargs(kwargs, "ndim", 2)
214
215    return torch_em.default_segmentation_dataset(
216        raw_paths=image_paths,
217        raw_key=None,
218        label_paths=label_paths,
219        label_key=None,
220        patch_shape=patch_shape,
221        is_seg_dataset=False,
222        **kwargs,
223    )

Get the SynthMT dataset for microtubule segmentation.

The synthetic source provides instance labels, the real source provides binary labels.

Arguments:
  • path: Filepath to a folder where the downloaded data will be saved.
  • patch_shape: The patch shape to use for training.
  • source: The data source. Either 'synthetic_irm' or 'real_irm'.
  • split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
  • 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_synthmt_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, int], source: Literal['synthetic_irm', 'real_irm'], split: Optional[Literal['train', 'test']] = None, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
226def get_synthmt_loader(
227    path: Union[os.PathLike, str],
228    batch_size: int,
229    patch_shape: Tuple[int, int],
230    source: Literal["synthetic_irm", "real_irm"],
231    split: Optional[Literal["train", "test"]] = None,
232    download: bool = False,
233    **kwargs,
234) -> DataLoader:
235    """Get the SynthMT dataloader for microtubule segmentation.
236
237    Args:
238        path: Filepath to a folder where the downloaded data will be saved.
239        batch_size: The batch size for training.
240        patch_shape: The patch shape to use for training.
241        source: The data source. Either 'synthetic_irm' or 'real_irm'.
242        split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
243        download: Whether to download the data if it is not present.
244        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
245
246    Returns:
247        The DataLoader.
248    """
249    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
250    dataset = get_synthmt_dataset(path, patch_shape, source, split, download, **ds_kwargs)
251    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the SynthMT dataloader for microtubule 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.
  • source: The data source. Either 'synthetic_irm' or 'real_irm'.
  • split: The data split of the real source. Either 'train' or 'test'. Must be None for the synthetic source.
  • 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.