torch_em.data.datasets.medical.resect

The RESECT dataset contains pre-operative MRI and intra-operative 3D ultrasound of 23 patients with low-grade gliomas, together with annotations from the RESECT-SEG extension.

For every patient, three intra-operative ultrasound (US) volumes were acquired: before, during and after tumor resection. The annotations (RESECT-SEG, an extension of the CuRIOUS 2022 challenge labels) are:

  • 'tumor': the tumor in the US volume before resection and in the pre-operative FLAIR MRI (23 cases each),
  • 'resection': the resection cavity in the US volumes during (21 cases) and after (22 cases) resection,
  • 'sulci': the cerebral sulci in all three US phases (23 cases each),
  • 'falx': the cerebral falx in the US volumes (7 to 8 cases per phase). All labels are binary (0: background, 1: structure).

The image volumes are located at https://doi.org/10.11582/2017.00004 (NIRD research data archive, CC BY 4.0) and the annotations at https://osf.io/jv8bk/ (CC BY-NC-SA 4.0). Only the volumes needed for the chosen source, phase and structure are downloaded.

This dataset is from the publications https://doi.org/10.1002/mp.12268 (RESECT) and https://doi.org/10.1002/mp.17317 (RESECT-SEG annotations). Please cite them if you use this dataset in your research.

  1"""The RESECT dataset contains pre-operative MRI and intra-operative 3D ultrasound of 23 patients
  2with low-grade gliomas, together with annotations from the RESECT-SEG extension.
  3
  4For every patient, three intra-operative ultrasound (US) volumes were acquired: before, during and after
  5tumor resection. The annotations (RESECT-SEG, an extension of the CuRIOUS 2022 challenge labels) are:
  6- 'tumor': the tumor in the US volume before resection and in the pre-operative FLAIR MRI (23 cases each),
  7- 'resection': the resection cavity in the US volumes during (21 cases) and after (22 cases) resection,
  8- 'sulci': the cerebral sulci in all three US phases (23 cases each),
  9- 'falx': the cerebral falx in the US volumes (7 to 8 cases per phase).
 10All labels are binary (0: background, 1: structure).
 11
 12The image volumes are located at https://doi.org/10.11582/2017.00004 (NIRD research data archive, CC BY 4.0)
 13and the annotations at https://osf.io/jv8bk/ (CC BY-NC-SA 4.0). Only the volumes needed for the chosen
 14source, phase and structure are downloaded.
 15
 16This dataset is from the publications https://doi.org/10.1002/mp.12268 (RESECT) and
 17https://doi.org/10.1002/mp.17317 (RESECT-SEG annotations).
 18Please cite them if you use this dataset in your research.
 19"""
 20
 21import os
 22from glob import glob
 23from natsort import natsorted
 24from typing import Union, Tuple, Literal, List, Optional
 25
 26from torch.utils.data import Dataset, DataLoader
 27
 28import torch_em
 29
 30from .. import util
 31
 32
 33IMAGE_URL = "https://data.archive.sigma2.no/dataset/5686d8fa-2003-4837-8e66-8e887fabe21e/download/RESECT/NIFTI"
 34# The folder zip is generated on-the-fly by OSF, hence the checksum of the archive is not reliable.
 35LABEL_URL = "https://files.osf.io/v1/resources/jv8bk/providers/osfstorage/64cd20819cbf033b051e46c8/?zip="
 36
 37CASE_IDS = [1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 23, 24, 25, 26, 27]
 38
 39STRUCTURES = {
 40    "before": ["tumor", "sulci", "falx"],
 41    "during": ["resection", "sulci", "falx"],
 42    "after": ["resection", "sulci", "falx"],
 43    "MRI": ["tumor"],
 44}
 45
 46
 47def _get_structure(source, phase, structure):
 48    if source not in ["US", "MRI"]:
 49        raise ValueError(f"'{source}' is not a valid source. Choose either 'US' or 'MRI'.")
 50    if source == "US" and phase not in ["before", "during", "after"]:
 51        raise ValueError(f"'{phase}' is not a valid phase. Choose one of 'before', 'during' or 'after'.")
 52
 53    valid_structures = STRUCTURES["MRI" if source == "MRI" else phase]
 54    if structure is None:
 55        structure = valid_structures[0]
 56    if structure not in valid_structures:
 57        raise ValueError(
 58            f"'{structure}' is not a valid structure for source '{source}' and phase '{phase}'. "
 59            f"Choose one of {valid_structures}."
 60        )
 61    return structure
 62
 63
 64def get_resect_data(
 65    path: Union[os.PathLike, str],
 66    source: Literal["US", "MRI"] = "US",
 67    phase: Literal["before", "during", "after"] = "before",
 68    download: bool = False,
 69) -> Tuple[str, str]:
 70    """Download the RESECT dataset.
 71
 72    Args:
 73        path: Filepath to a folder where the data is downloaded for further processing.
 74        source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
 75        phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection.
 76            Ignored for the 'MRI' source.
 77        download: Whether to download the data if it is not present.
 78
 79    Returns:
 80        Filepath to the folder with the image volumes.
 81        Filepath to the folder with the label volumes.
 82    """
 83    _get_structure(source, phase, None)
 84
 85    label_dir = os.path.join(path, "labels")
 86    if not os.path.exists(label_dir):
 87        os.makedirs(path, exist_ok=True)
 88        zip_path = os.path.join(path, "RESECT-Segmentation.zip")
 89        util.download_source(path=zip_path, url=LABEL_URL, download=download, checksum=None)
 90        util.unzip(zip_path=zip_path, dst=label_dir)
 91
 92    image_dir = os.path.join(path, "images", source)
 93    os.makedirs(image_dir, exist_ok=True)
 94    for case_id in CASE_IDS:
 95        fname = f"Case{case_id}-US-{phase}.nii.gz" if source == "US" else f"Case{case_id}-FLAIR.nii.gz"
 96        url = f"{IMAGE_URL}/Case{case_id}/{source}/{fname}"
 97        util.download_source(path=os.path.join(image_dir, fname), url=url, download=download, checksum=None)
 98
 99    return image_dir, label_dir
100
101
102def get_resect_paths(
103    path: Union[os.PathLike, str],
104    source: Literal["US", "MRI"] = "US",
105    phase: Literal["before", "during", "after"] = "before",
106    structure: Optional[Literal["tumor", "resection", "sulci", "falx"]] = None,
107    download: bool = False,
108) -> Tuple[List[str], List[str]]:
109    """Get paths to the RESECT data.
110
111    Args:
112        path: Filepath to a folder where the data is downloaded for further processing.
113        source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
114        phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection.
115            Ignored for the 'MRI' source.
116        structure: The annotated structure. One of 'tumor' (US before resection and MRI), 'resection'
117            (resection cavity, US during and after resection), 'sulci' or 'falx' (US, all phases).
118            By default, 'tumor' for 'before' and 'MRI' and 'resection' for 'during' and 'after'.
119        download: Whether to download the data if it is not present.
120
121    Returns:
122        List of filepaths for the image data.
123        List of filepaths for the label data.
124    """
125    structure = _get_structure(source, phase, structure)
126    image_dir, label_dir = get_resect_data(path, source, phase, download)
127
128    prefix = f"US-{phase}" if source == "US" else "FLAIR"
129    label_paths = natsorted(glob(os.path.join(label_dir, "Case*", f"Case*-{prefix}-{structure}.nii.gz")))
130    raw_paths = [
131        os.path.join(image_dir, os.path.basename(p).replace(f"-{structure}.nii.gz", ".nii.gz")) for p in label_paths
132    ]
133    assert all(os.path.exists(p) for p in raw_paths), "Some image volumes are missing."
134    assert len(raw_paths) == len(label_paths) and len(raw_paths) > 0
135
136    return raw_paths, label_paths
137
138
139def get_resect_dataset(
140    path: Union[os.PathLike, str],
141    patch_shape: Tuple[int, ...],
142    source: Literal["US", "MRI"] = "US",
143    phase: Literal["before", "during", "after"] = "before",
144    structure: Optional[Literal["tumor", "resection", "sulci", "falx"]] = None,
145    resize_inputs: bool = False,
146    download: bool = False,
147    **kwargs
148) -> Dataset:
149    """Get the RESECT dataset for brain tumor, resection cavity, sulci and falx segmentation.
150
151    Args:
152        path: Filepath to a folder where the data is downloaded for further processing.
153        patch_shape: The patch shape to use for training.
154        source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
155        phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection.
156            Ignored for the 'MRI' source.
157        structure: The annotated structure. One of 'tumor' (US before resection and MRI), 'resection'
158            (resection cavity, US during and after resection), 'sulci' or 'falx' (US, all phases).
159            By default, 'tumor' for 'before' and 'MRI' and 'resection' for 'during' and 'after'.
160        resize_inputs: Whether to resize inputs to the desired patch shape.
161        download: Whether to download the data if it is not present.
162        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
163
164    Returns:
165        The segmentation dataset.
166    """
167    raw_paths, label_paths = get_resect_paths(path, source, phase, structure, download)
168
169    if resize_inputs:
170        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
171        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
172            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
173        )
174
175    return torch_em.default_segmentation_dataset(
176        raw_paths=raw_paths,
177        raw_key="data",
178        label_paths=label_paths,
179        label_key="data",
180        patch_shape=patch_shape,
181        is_seg_dataset=True,
182        **kwargs
183    )
184
185
186def get_resect_loader(
187    path: Union[os.PathLike, str],
188    batch_size: int,
189    patch_shape: Tuple[int, ...],
190    source: Literal["US", "MRI"] = "US",
191    phase: Literal["before", "during", "after"] = "before",
192    structure: Optional[Literal["tumor", "resection", "sulci", "falx"]] = None,
193    resize_inputs: bool = False,
194    download: bool = False,
195    **kwargs
196) -> DataLoader:
197    """Get the RESECT dataloader for brain tumor, resection cavity, sulci and falx segmentation.
198
199    Args:
200        path: Filepath to a folder where the data is downloaded for further processing.
201        batch_size: The batch size for training.
202        patch_shape: The patch shape to use for training.
203        source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
204        phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection.
205            Ignored for the 'MRI' source.
206        structure: The annotated structure. One of 'tumor' (US before resection and MRI), 'resection'
207            (resection cavity, US during and after resection), 'sulci' or 'falx' (US, all phases).
208            By default, 'tumor' for 'before' and 'MRI' and 'resection' for 'during' and 'after'.
209        resize_inputs: Whether to resize inputs to the desired patch shape.
210        download: Whether to download the data if it is not present.
211        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
212
213    Returns:
214        The DataLoader.
215    """
216    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
217    dataset = get_resect_dataset(path, patch_shape, source, phase, structure, resize_inputs, download, **ds_kwargs)
218    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)
IMAGE_URL = 'https://data.archive.sigma2.no/dataset/5686d8fa-2003-4837-8e66-8e887fabe21e/download/RESECT/NIFTI'
LABEL_URL = 'https://files.osf.io/v1/resources/jv8bk/providers/osfstorage/64cd20819cbf033b051e46c8/?zip='
CASE_IDS = [1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 23, 24, 25, 26, 27]
STRUCTURES = {'before': ['tumor', 'sulci', 'falx'], 'during': ['resection', 'sulci', 'falx'], 'after': ['resection', 'sulci', 'falx'], 'MRI': ['tumor']}
def get_resect_data( path: Union[os.PathLike, str], source: Literal['US', 'MRI'] = 'US', phase: Literal['before', 'during', 'after'] = 'before', download: bool = False) -> Tuple[str, str]:
 65def get_resect_data(
 66    path: Union[os.PathLike, str],
 67    source: Literal["US", "MRI"] = "US",
 68    phase: Literal["before", "during", "after"] = "before",
 69    download: bool = False,
 70) -> Tuple[str, str]:
 71    """Download the RESECT dataset.
 72
 73    Args:
 74        path: Filepath to a folder where the data is downloaded for further processing.
 75        source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
 76        phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection.
 77            Ignored for the 'MRI' source.
 78        download: Whether to download the data if it is not present.
 79
 80    Returns:
 81        Filepath to the folder with the image volumes.
 82        Filepath to the folder with the label volumes.
 83    """
 84    _get_structure(source, phase, None)
 85
 86    label_dir = os.path.join(path, "labels")
 87    if not os.path.exists(label_dir):
 88        os.makedirs(path, exist_ok=True)
 89        zip_path = os.path.join(path, "RESECT-Segmentation.zip")
 90        util.download_source(path=zip_path, url=LABEL_URL, download=download, checksum=None)
 91        util.unzip(zip_path=zip_path, dst=label_dir)
 92
 93    image_dir = os.path.join(path, "images", source)
 94    os.makedirs(image_dir, exist_ok=True)
 95    for case_id in CASE_IDS:
 96        fname = f"Case{case_id}-US-{phase}.nii.gz" if source == "US" else f"Case{case_id}-FLAIR.nii.gz"
 97        url = f"{IMAGE_URL}/Case{case_id}/{source}/{fname}"
 98        util.download_source(path=os.path.join(image_dir, fname), url=url, download=download, checksum=None)
 99
100    return image_dir, label_dir

Download the RESECT dataset.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
  • phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection. Ignored for the 'MRI' source.
  • download: Whether to download the data if it is not present.
Returns:

Filepath to the folder with the image volumes. Filepath to the folder with the label volumes.

def get_resect_paths( path: Union[os.PathLike, str], source: Literal['US', 'MRI'] = 'US', phase: Literal['before', 'during', 'after'] = 'before', structure: Optional[Literal['tumor', 'resection', 'sulci', 'falx']] = None, download: bool = False) -> Tuple[List[str], List[str]]:
103def get_resect_paths(
104    path: Union[os.PathLike, str],
105    source: Literal["US", "MRI"] = "US",
106    phase: Literal["before", "during", "after"] = "before",
107    structure: Optional[Literal["tumor", "resection", "sulci", "falx"]] = None,
108    download: bool = False,
109) -> Tuple[List[str], List[str]]:
110    """Get paths to the RESECT data.
111
112    Args:
113        path: Filepath to a folder where the data is downloaded for further processing.
114        source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
115        phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection.
116            Ignored for the 'MRI' source.
117        structure: The annotated structure. One of 'tumor' (US before resection and MRI), 'resection'
118            (resection cavity, US during and after resection), 'sulci' or 'falx' (US, all phases).
119            By default, 'tumor' for 'before' and 'MRI' and 'resection' for 'during' and 'after'.
120        download: Whether to download the data if it is not present.
121
122    Returns:
123        List of filepaths for the image data.
124        List of filepaths for the label data.
125    """
126    structure = _get_structure(source, phase, structure)
127    image_dir, label_dir = get_resect_data(path, source, phase, download)
128
129    prefix = f"US-{phase}" if source == "US" else "FLAIR"
130    label_paths = natsorted(glob(os.path.join(label_dir, "Case*", f"Case*-{prefix}-{structure}.nii.gz")))
131    raw_paths = [
132        os.path.join(image_dir, os.path.basename(p).replace(f"-{structure}.nii.gz", ".nii.gz")) for p in label_paths
133    ]
134    assert all(os.path.exists(p) for p in raw_paths), "Some image volumes are missing."
135    assert len(raw_paths) == len(label_paths) and len(raw_paths) > 0
136
137    return raw_paths, label_paths

Get paths to the RESECT data.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
  • phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection. Ignored for the 'MRI' source.
  • structure: The annotated structure. One of 'tumor' (US before resection and MRI), 'resection' (resection cavity, US during and after resection), 'sulci' or 'falx' (US, all phases). By default, 'tumor' for 'before' and 'MRI' and 'resection' for 'during' and 'after'.
  • 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_resect_dataset( path: Union[os.PathLike, str], patch_shape: Tuple[int, ...], source: Literal['US', 'MRI'] = 'US', phase: Literal['before', 'during', 'after'] = 'before', structure: Optional[Literal['tumor', 'resection', 'sulci', 'falx']] = None, resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataset.Dataset:
140def get_resect_dataset(
141    path: Union[os.PathLike, str],
142    patch_shape: Tuple[int, ...],
143    source: Literal["US", "MRI"] = "US",
144    phase: Literal["before", "during", "after"] = "before",
145    structure: Optional[Literal["tumor", "resection", "sulci", "falx"]] = None,
146    resize_inputs: bool = False,
147    download: bool = False,
148    **kwargs
149) -> Dataset:
150    """Get the RESECT dataset for brain tumor, resection cavity, sulci and falx segmentation.
151
152    Args:
153        path: Filepath to a folder where the data is downloaded for further processing.
154        patch_shape: The patch shape to use for training.
155        source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
156        phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection.
157            Ignored for the 'MRI' source.
158        structure: The annotated structure. One of 'tumor' (US before resection and MRI), 'resection'
159            (resection cavity, US during and after resection), 'sulci' or 'falx' (US, all phases).
160            By default, 'tumor' for 'before' and 'MRI' and 'resection' for 'during' and 'after'.
161        resize_inputs: Whether to resize inputs to the desired patch shape.
162        download: Whether to download the data if it is not present.
163        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset`.
164
165    Returns:
166        The segmentation dataset.
167    """
168    raw_paths, label_paths = get_resect_paths(path, source, phase, structure, download)
169
170    if resize_inputs:
171        resize_kwargs = {"patch_shape": patch_shape, "is_rgb": False}
172        kwargs, patch_shape = util.update_kwargs_for_resize_trafo(
173            kwargs=kwargs, patch_shape=patch_shape, resize_inputs=resize_inputs, resize_kwargs=resize_kwargs
174        )
175
176    return torch_em.default_segmentation_dataset(
177        raw_paths=raw_paths,
178        raw_key="data",
179        label_paths=label_paths,
180        label_key="data",
181        patch_shape=patch_shape,
182        is_seg_dataset=True,
183        **kwargs
184    )

Get the RESECT dataset for brain tumor, resection cavity, sulci and falx segmentation.

Arguments:
  • path: Filepath to a folder where the data is downloaded for further processing.
  • patch_shape: The patch shape to use for training.
  • source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
  • phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection. Ignored for the 'MRI' source.
  • structure: The annotated structure. One of 'tumor' (US before resection and MRI), 'resection' (resection cavity, US during and after resection), 'sulci' or 'falx' (US, all phases). By default, 'tumor' for 'before' and 'MRI' and 'resection' for 'during' and 'after'.
  • resize_inputs: Whether to resize inputs to the desired patch shape.
  • 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_resect_loader( path: Union[os.PathLike, str], batch_size: int, patch_shape: Tuple[int, ...], source: Literal['US', 'MRI'] = 'US', phase: Literal['before', 'during', 'after'] = 'before', structure: Optional[Literal['tumor', 'resection', 'sulci', 'falx']] = None, resize_inputs: bool = False, download: bool = False, **kwargs) -> torch.utils.data.dataloader.DataLoader:
187def get_resect_loader(
188    path: Union[os.PathLike, str],
189    batch_size: int,
190    patch_shape: Tuple[int, ...],
191    source: Literal["US", "MRI"] = "US",
192    phase: Literal["before", "during", "after"] = "before",
193    structure: Optional[Literal["tumor", "resection", "sulci", "falx"]] = None,
194    resize_inputs: bool = False,
195    download: bool = False,
196    **kwargs
197) -> DataLoader:
198    """Get the RESECT dataloader for brain tumor, resection cavity, sulci and falx segmentation.
199
200    Args:
201        path: Filepath to a folder where the data is downloaded for further processing.
202        batch_size: The batch size for training.
203        patch_shape: The patch shape to use for training.
204        source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
205        phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection.
206            Ignored for the 'MRI' source.
207        structure: The annotated structure. One of 'tumor' (US before resection and MRI), 'resection'
208            (resection cavity, US during and after resection), 'sulci' or 'falx' (US, all phases).
209            By default, 'tumor' for 'before' and 'MRI' and 'resection' for 'during' and 'after'.
210        resize_inputs: Whether to resize inputs to the desired patch shape.
211        download: Whether to download the data if it is not present.
212        kwargs: Additional keyword arguments for `torch_em.default_segmentation_dataset` or for the PyTorch DataLoader.
213
214    Returns:
215        The DataLoader.
216    """
217    ds_kwargs, loader_kwargs = util.split_kwargs(torch_em.default_segmentation_dataset, **kwargs)
218    dataset = get_resect_dataset(path, patch_shape, source, phase, structure, resize_inputs, download, **ds_kwargs)
219    return torch_em.get_data_loader(dataset, batch_size, **loader_kwargs)

Get the RESECT dataloader for brain tumor, resection cavity, sulci and falx 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.
  • source: The imaging source. Either 'US' (intra-operative ultrasound) or 'MRI' (pre-operative FLAIR).
  • phase: The surgical phase of the ultrasound volumes. Either 'before', 'during' or 'after' resection. Ignored for the 'MRI' source.
  • structure: The annotated structure. One of 'tumor' (US before resection and MRI), 'resection' (resection cavity, US during and after resection), 'sulci' or 'falx' (US, all phases). By default, 'tumor' for 'before' and 'MRI' and 'resection' for 'during' and 'after'.
  • resize_inputs: Whether to resize inputs to the desired patch shape.
  • 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.