Source code for health_multimodal.image.data.io

#  -------------------------------------------------------------------------------------------
#  Copyright (c) Microsoft Corporation. All rights reserved.
#  Licensed under the MIT License (MIT). See LICENSE in the repo root for license information.
#  -------------------------------------------------------------------------------------------

from pathlib import Path
from typing import Optional, Tuple

import numpy as np
import pydicom as dicom
from PIL import Image
import SimpleITK as sitk
from skimage import io


[docs]def remap_to_uint8(array: np.ndarray, percentiles: Optional[Tuple[float, float]] = None) -> np.ndarray: """Remap values in input so the output range is :math:`[0, 255]`. Percentiles can be used to specify the range of values to remap. This is useful to discard outliers in the input data. :param array: Input array. :param percentiles: Percentiles of the input values that will be mapped to ``0`` and ``255``. Passing ``None`` is equivalent to using percentiles ``(0, 100)`` (but faster). :returns: Array with ``0`` and ``255`` as minimum and maximum values. """ array = array.astype(float) if percentiles is not None: len_percentiles = len(percentiles) if len_percentiles != 2: message = 'The value for percentiles should be a sequence of length 2,' f' but has length {len_percentiles}' raise ValueError(message) a, b = percentiles if a >= b: raise ValueError(f'Percentiles must be in ascending order, but a sequence "{percentiles}" was passed') if a < 0 or b > 100: raise ValueError(f'Percentiles must be in the range [0, 100], but a sequence "{percentiles}" was passed') cutoff: np.ndarray = np.percentile(array, percentiles) array = np.clip(array, *cutoff) array -= array.min() array /= array.max() array *= 255 return array.astype(np.uint8)
[docs]def load_image(path: Path) -> Image.Image: """Load an image from disk. The image values are remapped to :math:`[0, 255]` and cast to 8-bit unsigned integers. :param path: Path to image. :returns: Image as ``Pillow`` ``Image``. """ # Although ITK supports JPEG and PNG, we use Pillow for consistency with older trained models if path.suffix in [".jpg", ".jpeg", ".png"]: image = io.imread(path) elif path.suffixes == [".nii", ".gz"]: image = sitk.GetArrayFromImage(sitk.ReadImage(str(path))) if image.shape[0] == 1: image = np.squeeze(image, axis=0) assert image.ndim == 2 elif path.suffix == ".dcm": image = dicom.dcmread(path).pixel_array else: raise ValueError(f"Image type not supported, filename was: {path}") image = remap_to_uint8(image) return Image.fromarray(image).convert("L")