Spaces:
Running
Running
import numpy as np | |
import tifffile as tif | |
import skimage.io as io | |
from typing import Optional, Sequence, Union | |
from monai.config import DtypeLike, PathLike, KeysCollection | |
from monai.utils import ensure_tuple | |
from monai.data.utils import is_supported_format, optional_import, ensure_tuple_rep | |
from monai.data.image_reader import ImageReader, NumpyReader | |
from monai.transforms import LoadImage, LoadImaged | |
from monai.utils.enums import PostFix | |
DEFAULT_POST_FIX = PostFix.meta() | |
itk, has_itk = optional_import("itk", allow_namespace_pkg=True) | |
__all__ = [ | |
"CustomLoadImaged", | |
"CustomLoadImageD", | |
"CustomLoadImageDict", | |
"CustomLoadImage", | |
] | |
class CustomLoadImage(LoadImage): | |
""" | |
Load image file or files from provided path based on reader. | |
If reader is not specified, this class automatically chooses readers | |
based on the supported suffixes and in the following order: | |
- User-specified reader at runtime when calling this loader. | |
- User-specified reader in the constructor of `LoadImage`. | |
- Readers from the last to the first in the registered list. | |
- Current default readers: (nii, nii.gz -> NibabelReader), (png, jpg, bmp -> PILReader), | |
(npz, npy -> NumpyReader), (nrrd -> NrrdReader), (DICOM file -> ITKReader). | |
[!Caution] This overriding replaces the original ITK with Custom UnifiedITKReader. | |
""" | |
def __init__( | |
self, | |
reader=None, | |
image_only: bool = False, | |
dtype: DtypeLike = np.float32, | |
ensure_channel_first: bool = False, | |
*args, | |
**kwargs, | |
) -> None: | |
super(CustomLoadImage, self).__init__( | |
reader, image_only, dtype, ensure_channel_first, *args, **kwargs | |
) | |
# Adding TIFFReader. Although ITK Reader supports ".tiff" files, sometimes fails to load images. | |
self.readers = [] | |
self.register(UnifiedITKReader(*args, **kwargs)) | |
class CustomLoadImaged(LoadImaged): | |
""" | |
Dictionary-based wrapper of `CustomLoadImage`. | |
""" | |
def __init__( | |
self, | |
keys: KeysCollection, | |
reader: Optional[Union[ImageReader, str]] = None, | |
dtype: DtypeLike = np.float32, | |
meta_keys: Optional[KeysCollection] = None, | |
meta_key_postfix: str = DEFAULT_POST_FIX, | |
overwriting: bool = False, | |
image_only: bool = False, | |
ensure_channel_first: bool = False, | |
simple_keys=False, | |
allow_missing_keys: bool = False, | |
*args, | |
**kwargs, | |
) -> None: | |
super(CustomLoadImaged, self).__init__( | |
keys, | |
reader, | |
dtype, | |
meta_keys, | |
meta_key_postfix, | |
overwriting, | |
image_only, | |
ensure_channel_first, | |
simple_keys, | |
allow_missing_keys, | |
*args, | |
**kwargs, | |
) | |
# Assign CustomLoader | |
self._loader = CustomLoadImage( | |
reader, image_only, dtype, ensure_channel_first, *args, **kwargs | |
) | |
if not isinstance(meta_key_postfix, str): | |
raise TypeError( | |
f"meta_key_postfix must be a str but is {type(meta_key_postfix).__name__}." | |
) | |
self.meta_keys = ( | |
ensure_tuple_rep(None, len(self.keys)) | |
if meta_keys is None | |
else ensure_tuple(meta_keys) | |
) | |
if len(self.keys) != len(self.meta_keys): | |
raise ValueError("meta_keys should have the same length as keys.") | |
self.meta_key_postfix = ensure_tuple_rep(meta_key_postfix, len(self.keys)) | |
self.overwriting = overwriting | |
class UnifiedITKReader(NumpyReader): | |
""" | |
Unified Reader to read ".tif" and ".tiff files". | |
As the tifffile reads the images as numpy arrays, it inherits from the NumpyReader. | |
""" | |
def __init__( | |
self, channel_dim: Optional[int] = None, **kwargs, | |
): | |
super(UnifiedITKReader, self).__init__(channel_dim=channel_dim, **kwargs) | |
self.kwargs = kwargs | |
self.channel_dim = channel_dim | |
def verify_suffix(self, filename: Union[Sequence[PathLike], PathLike]) -> bool: | |
"""Verify whether the file format is supported by TIFF Reader.""" | |
suffixes: Sequence[str] = ["tif", "tiff", "png", "jpg", "bmp", "jpeg",] | |
return has_itk or is_supported_format(filename, suffixes) | |
def read(self, data: Union[Sequence[PathLike], PathLike], **kwargs): | |
"""Read Images from the file.""" | |
img_ = [] | |
filenames: Sequence[PathLike] = ensure_tuple(data) | |
kwargs_ = self.kwargs.copy() | |
kwargs_.update(kwargs) | |
for name in filenames: | |
name = f"{name}" | |
if name.endswith(".tif") or name.endswith(".tiff"): | |
_obj = tif.imread(name) | |
else: | |
try: | |
_obj = itk.imread(name, **kwargs_) | |
_obj = itk.array_view_from_image(_obj, keep_axes=False) | |
except: | |
_obj = io.imread(name) | |
if len(_obj.shape) == 2: | |
_obj = np.repeat(np.expand_dims(_obj, axis=-1), 3, axis=-1) | |
elif len(_obj.shape) == 3 and _obj.shape[-1] > 3: | |
_obj = _obj[:, :, :3] | |
else: | |
pass | |
img_.append(_obj) | |
return img_ if len(filenames) > 1 else img_[0] | |
CustomLoadImageD = CustomLoadImageDict = CustomLoadImaged | |