torch_em.data.datasets.medical.tn3k

The TN3K dataset contains annotations for thyroid nodule segmentation in ultrasound images.

The dataset consists of 3493 thyroid ultrasound images (2879 in the 'trainval' split and 614 in the 'test' split) collected from 2421 patients across multiple devices and views, with pixel-level nodule masks. The masks are binary: 0 for background and 1 for thyroid nodule.

The dataset is hosted at https://github.com/haifangong/TRFE-Net-for-thyroid-nodule-segmentation, which bundles it together with the TG3K (thyroid gland) dataset and a copy of DDTI inside a single archive. We download it from the Google Drive mirror linked in that repository's README.

This dataset is from the publications https://doi.org/10.1016/j.compbiomed.2022.106389 and https://doi.org/10.1109/ISBI48211.2021.9434087. Please cite them if you use this dataset for your research.

  1"""The TN3K dataset contains annotations for thyroid nodule segmentation in ultrasound images.
  2
  3The dataset consists of 3493 thyroid ultrasound images (2879 in the 'trainval' split and 614 in the
  4'test' split) collected from 2421 patients across multiple devices and views, with pixel-level nodule
  5masks. The masks are binary: 0 for background and 1 for thyroid nodule.
  6
  7The dataset is hosted at https://github.com/haifangong/TRFE-Net-for-thyroid-nodule-segmentation, which
  8bundles it together with the TG3K (thyroid gland) dataset and a copy of DDTI inside a single archive.
  9We download it from the Google Drive mirror linked in that repository's README.
 10
 11This dataset is from the publications https://doi.org/10.1016/j.compbiomed.2022.106389 and
 12https://doi.org/10.1109/ISBI48211.2021.9434087. Please cite them if you use this dataset for your research.
 13"""
 14
 15import os
 16from glob import glob
 17from natsort import natsorted
 18from typing import Union, Tuple, Literal, List
 19
 20import imageio.v3 as imageio
 21
 22from torch.utils.data import Dataset, DataLoader
 23
 24import torch_em
 25
 26from .. import util
 27
 28
 29GDRIVE_ID = "1reHyY5eTZ5uePXMVMzFOq5j3eFOSp50F"
 30CHECKSUM = "0ba1770076dab01b1f8fd661a227d80982168bc433de61c80fd266427b20cf60"
 31
 32
 33def get_tn3k_data(path: Union[os.PathLike, str], download: bool = False) -> str:
 34    """Download the TN3K dataset.
 35
 36    Args:
 37        path: Filepath to a folder where the data is downloaded for further processing.
 38        download: Whether to download the data if it is not present.
 39
 40    Returns:
 41        Filepath where the data is downloaded.
 42    """
 43    data_dir = os.path.join(path, "Thyroid Dataset", "tn3k")
 44    if os.path.exists(data_dir):
 45        return data_dir
 46
 47    os.makedirs(path, exist_ok=True)
 48
 49    zip_path = os.path.join(path, "tn3k.zip")
 50    util.download_source_gdrive(
 51        path=zip_path, url=f"https://drive.google.com/uc?id={GDRIVE_ID}", download=download, checksum=CHECKSUM,
 52    )
 53    util.unzip(zip_path=zip_path, dst=path)
 54
 55    return data_dir
 56
 57
 58def get_tn3k_paths(
 59    path: Union[os.PathLike, str], split: Literal["trainval", "test"], download: bool = False
 60) -> Tuple[List[str], List[str]]:
 61    """Get paths to the TN3K data.
 62
 63    Args:
 64        path: Filepath to a folder where the data is downloaded for further processing.
 65        split: The choice of data split. Either 'trainval' or 'test'.
 66        download: Whether to download the data if it is not present.
 67
 68    Returns:
 69        List of filepaths for the image data.
 70        List of filepaths for the label data.
 71    """
 72    data_dir = get_tn3k_data(path=path, download=download)
 73
 74    if split not in ["trainval", "test"]:
 75        raise ValueError(f"'{split}' is not a valid split.")
 76
 77    image_paths = natsorted(glob(os.path.join(data_dir, f"{split}-image", "*.jpg")))
 78    mask_paths = natsorted(glob(os.path.join(data_dir, f"{split}-mask", "*.jpg")))
 79
 80    if len(image_paths) == 0 or len(image_paths) != len(mask_paths):
 81        raise RuntimeError("Something went wrong with fetching the image and label paths.")
 82
 83    neu_gt_dir = os.path.join(data_dir, f"{split}-mask", "preprocessed")
 84    os.makedirs(neu_gt_dir, exist_ok=True)
 85
 86    gt_paths = []
 87    for mask_path in mask_paths:
 88        gt_path = os.path.join(neu_gt_dir, os.path.basename(mask_path).replace(".jpg", ".tif"))
 89        gt_paths.append(gt_path)
 90        if os.path.exists(gt_path):
 91            continue
 92
 93        # The masks are stored as jpegs, which introduces compression artifacts around otherwise binary values.
 94        mask = (imageio.imread(mask_path) > 127).astype("uint8")
 95        imageio.imwrite(gt_path, mask, compression="zlib")
 96
 97    return image_paths, gt_paths
 98
 99
100def get_tn3k_dataset(
101    path: Union[os.PathLike, str],
102    patch_shape: Tuple[int, int],
103    split: Literal["trainval", "test"],
104    resize_inputs: bool = False,
105    download: bool = False,
106    **kwargs
107) -> Dataset:
108    """Get the TN3K dataset for thyroid nodule segmentation.
109
110    Args:
111        path: Filepath to a folder where the data is downloaded for further processing.
112        patch_shape: The patch shape to use for training.
113        split: The choice of data split. Either 'trainval' or 'test'.
114        resize_inputs: Whether to resize the inputs.
115        download: Whether to download the data if it is not present.
116        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
117
118    Returns:
119        The segmentation dataset.
120    """
121    image_paths, gt_paths = get_tn3k_paths(path, split, download)
122
123    if resize_inputs:
124        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
125        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
126            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
127        )
128
129    return torch_em.default_segmentation_dataset(
130        raw_paths=image_paths,
131        raw_key=None,
132        label_paths=gt_paths,
133        label_key=None,
134        patch_shape=patch_shape,
135        is_seg_dataset=False,
136        **kwargs
137    )
138
139
140def get_tn3k_loader(
141    path: Union[os.PathLike, str],
142    batch_size: int,
143    patch_shape: Tuple[int, int],
144    split: Literal["trainval", "test"],
145    resize_inputs: bool = False,
146    download: bool = False,
147    **kwargs
148) -> DataLoader:
149    """Get the TN3K dataloader for thyroid nodule segmentation.
150
151    Args:
152        path: Filepath to a folder where the data is downloaded for further processing.
153        batch_size: The batch size for training.
154        patch_shape: The patch shape to use for training.
155        split: The choice of data split. Either 'trainval' or 'test'.
156        resize_inputs: Whether to resize the inputs.
157        download: Whether to download the data if it is not present.
158        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
159
160    Returns:
161        The DataLoader.
162    """
163    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
164    dataset = get_tn3k_dataset(path, patch_shape, split, resize_inputs, download, **ds_kwargs)
165    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
GDRIVE_ID = '1reHyY5eTZ5uePXMVMzFOq5j3eFOSp50F'
CHECKSUM = '0ba1770076dab01b1f8fd661a227d80982168bc433de61c80fd266427b20cf60'
def get_tn3k_data(path: Union[os.PathLike, str], download: bool = False) -> str:
34def get_tn3k_data(path: Union[os.PathLike, str], download: bool = False) -> str:
35    """Download the TN3K dataset.
36
37    Args:
38        path: Filepath to a folder where the data is downloaded for further processing.
39        download: Whether to download the data if it is not present.
40
41    Returns:
42        Filepath where the data is downloaded.
43    """
44    data_dir = os.path.join(path, "Thyroid Dataset", "tn3k")
45    if os.path.exists(data_dir):
46        return data_dir
47
48    os.makedirs(path, exist_ok=True)
49
50    zip_path = os.path.join(path, "tn3k.zip")
51    util.download_source_gdrive(
52        path=zip_path, url=f"https://drive.google.com/uc?id={GDRIVE_ID}", download=download, checksum=CHECKSUM,
53    )
54    util.unzip(zip_path=zip_path, dst=path)
55
56    return data_dir

Download the TN3K dataset.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • download: Whether to download the data if it is not present.
Returns:

Filepath where the data is downloaded.

def get_tn3k_paths( path: Union[os.PathLike, str], split: Literal['trainval', 'test'], download: bool = False) -> Tuple[List[str], List[str]]:
59def get_tn3k_paths(
60    path: Union[os.PathLike, str], split: Literal["trainval", "test"], download: bool = False
61) -> Tuple[List[str], List[str]]:
62    """Get paths to the TN3K data.
63
64    Args:
65        path: Filepath to a folder where the data is downloaded for further processing.
66        split: The choice of data split. Either 'trainval' or 'test'.
67        download: Whether to download the data if it is not present.
68
69    Returns:
70        List of filepaths for the image data.
71        List of filepaths for the label data.
72    """
73    data_dir = get_tn3k_data(path=path, download=download)
74
75    if split not in ["trainval", "test"]:
76        raise ValueError(f"'{split}' is not a valid split.")
77
78    image_paths = natsorted(glob(os.path.join(data_dir, f"{split}-image", "*.jpg")))
79    mask_paths = natsorted(glob(os.path.join(data_dir, f"{split}-mask", "*.jpg")))
80
81    if len(image_paths) == 0 or len(image_paths) != len(mask_paths):
82        raise RuntimeError("Something went wrong with fetching the image and label paths.")
83
84    neu_gt_dir = os.path.join(data_dir, f"{split}-mask", "preprocessed")
85    os.makedirs(neu_gt_dir, exist_ok=True)
86
87    gt_paths = []
88    for mask_path in mask_paths:
89        gt_path = os.path.join(neu_gt_dir, os.path.basename(mask_path).replace(".jpg", ".tif"))
90        gt_paths.append(gt_path)
91        if os.path.exists(gt_path):
92            continue
93
94        # The masks are stored as jpegs, which introduces compression artifacts around otherwise binary values.
95        mask = (imageio.imread(mask_path) > 127).astype("uint8")
96        imageio.imwrite(gt_path, mask, compression="zlib")
97
98    return image_paths, gt_paths

Get paths to the TN3K data.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • split: The choice of data split. Either 'trainval' or 'test'.
  • 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_tn3k_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, int], split: Literal['trainval', 'test'], resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
101def get_tn3k_dataset(
102    path: Union[os.PathLike, str],
103    patch_shape: Tuple[int, int],
104    split: Literal["trainval", "test"],
105    resize_inputs: bool = False,
106    download: bool = False,
107    **kwargs
108) -> Dataset:
109    """Get the TN3K dataset for thyroid nodule segmentation.
110
111    Args:
112        path: Filepath to a folder where the data is downloaded for further processing.
113        patch_shape: The patch shape to use for training.
114        split: The choice of data split. Either 'trainval' or 'test'.
115        resize_inputs: Whether to resize the inputs.
116        download: Whether to download the data if it is not present.
117        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
118
119    Returns:
120        The segmentation dataset.
121    """
122    image_paths, gt_paths = get_tn3k_paths(path, split, download)
123
124    if resize_inputs:
125        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
126        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
127            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
128        )
129
130    return torch_em.default_segmentation_dataset(
131        raw_paths=image_paths,
132        raw_key=None,
133        label_paths=gt_paths,
134        label_key=None,
135        patch_shape=patch_shape,
136        is_seg_dataset=False,
137        **kwargs
138    )

Get the TN3K dataset for thyroid nodule segmentation.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • patch_shape: The patch shape to use for training.
  • split: The choice of data split. Either 'trainval' or 'test'.
  • 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_tn3k_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, int], split: Literal['trainval', 'test'], resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
141def get_tn3k_loader(
142    path: Union[os.PathLike, str],
143    batch_size: int,
144    patch_shape: Tuple[int, int],
145    split: Literal["trainval", "test"],
146    resize_inputs: bool = False,
147    download: bool = False,
148    **kwargs
149) -> DataLoader:
150    """Get the TN3K dataloader for thyroid nodule segmentation.
151
152    Args:
153        path: Filepath to a folder where the data is downloaded for further processing.
154        batch_size: The batch size for training.
155        patch_shape: The patch shape to use for training.
156        split: The choice of data split. Either 'trainval' or 'test'.
157        resize_inputs: Whether to resize the inputs.
158        download: Whether to download the data if it is not present.
159        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
160
161    Returns:
162        The DataLoader.
163    """
164    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
165    dataset = get_tn3k_dataset(path, patch_shape, split, resize_inputs, download, **ds_kwargs)
166    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the TN3K dataloader for thyroid nodule segmentation.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • batch_size: The batch size for training.
  • patch_shape: The patch shape to use for training.
  • split: The choice of data split. Either 'trainval' or 'test'.
  • 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.