Skip to content

2743 - no extension check for user specified reader #2751

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/source/data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ Generic Interfaces
.. autoclass:: ImageDataset
:members:
:special-members: __getitem__

`NPZDictItemDataset`
~~~~~~~~~~~~~~~~~~~~
.. autoclass:: NPZDictItemDataset
Expand Down Expand Up @@ -108,6 +108,11 @@ Patch-based dataset
Image reader
------------

ImageReader
~~~~~~~~~~~
.. autoclass:: ImageReader
:members:

ITKReader
~~~~~~~~~
.. autoclass:: ITKReader
Expand Down
47 changes: 36 additions & 11 deletions monai/data/image_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,30 @@


class ImageReader(ABC):
"""Abstract class to define interface APIs to load image files.
users need to call `read` to load image and then use `get_data`
to get the image data and properties from meta data.
"""
An abstract class defines APIs to load image files.

Typical usage of an implementation of this class is:

.. code-block:: python

image_reader = MyImageReader()
img_obj = image_reader.read(path_to_image)
img_data, meta_data = image_reader.get_data(img_obj)

- The `read` call converts image filenames into image objects,
- The `get_data` call fetches the image data, as well as meta data.
- A reader should implement `verify_suffix` with the logic of checking the input filename
by the filename extensions.

"""

@abstractmethod
def verify_suffix(self, filename: Union[Sequence[str], str]) -> bool:
"""
Verify whether the specified file or files format is supported by current reader.
Verify whether the specified `filename` is supported by the current reader.
This method should return True if the reader is able to read the format suggested by the
`filename`.

Args:
filename: file name or a list of file names to read.
Expand All @@ -67,7 +81,7 @@ def verify_suffix(self, filename: Union[Sequence[str], str]) -> bool:
def read(self, data: Union[Sequence[str], str], **kwargs) -> Union[Sequence[Any], Any]:
"""
Read image data from specified file or files.
Note that it returns the raw data, so different readers return different image data type.
Note that it returns a data object or a sequence of data objects.

Args:
data: file name or a list of file names to read.
Expand All @@ -80,7 +94,8 @@ def read(self, data: Union[Sequence[str], str], **kwargs) -> Union[Sequence[Any]
def get_data(self, img) -> Tuple[np.ndarray, Dict]:
"""
Extract data array and meta data from loaded image and return them.
This function must return 2 objects, first is numpy array of image data, second is dict of meta data.
This function must return two objects, the first is a numpy array of image data,
the second is a dictionary of meta data.

Args:
img: an image object loaded from an image file or a list of image objects.
Expand Down Expand Up @@ -124,7 +139,7 @@ def _stack_images(image_list: List, meta_dict: Dict):
class ITKReader(ImageReader):
"""
Load medical images based on ITK library.
All the supported image formats can be found:
All the supported image formats can be found at:
https://github.com/InsightSoftwareConsortium/ITK/tree/master/Modules/IO
The loaded data array will be in C order, for example, a 3D image NumPy
array index order will be `CDWH`.
Expand Down Expand Up @@ -396,7 +411,10 @@ def _get_meta_dict(self, img) -> Dict:

"""
# swap to little endian as PyTorch doesn't support big endian
header = img.header.as_byteswapped("<")
try:
header = img.header.as_byteswapped("<")
except ValueError:
header = img.header
return dict(header)

def _get_affine(self, img):
Expand All @@ -419,11 +437,18 @@ def _get_spatial_shape(self, img):

"""
# swap to little endian as PyTorch doesn't support big endian
header = img.header.as_byteswapped("<")
ndim = header["dim"][0]
try:
header = img.header.as_byteswapped("<")
except ValueError:
header = img.header
dim = header.get("dim", None)
if dim is None:
dim = header.get("dims") # mgh format?
dim = np.insert(dim, 0, 3)
ndim = dim[0]
spatial_rank = min(ndim, 3)
# the img data should have no channel dim or the last dim is channel
return np.asarray(header["dim"][1 : spatial_rank + 1])
return np.asarray(dim[1 : spatial_rank + 1])

def _get_array_data(self, img):
"""
Expand Down
3 changes: 2 additions & 1 deletion monai/data/nifti_saver.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from pathlib import Path
from typing import Dict, Optional, Union

import numpy as np
Expand Down Expand Up @@ -36,7 +37,7 @@ class NiftiSaver:

def __init__(
self,
output_dir: str = "./",
output_dir: Union[Path, str] = "./",
output_postfix: str = "seg",
output_ext: str = ".nii.gz",
resample: bool = True,
Expand Down
3 changes: 2 additions & 1 deletion monai/data/png_saver.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from pathlib import Path
from typing import Dict, Optional, Union

import numpy as np
Expand All @@ -33,7 +34,7 @@ class PNGSaver:

def __init__(
self,
output_dir: str = "./",
output_dir: Union[Path, str] = "./",
output_postfix: str = "seg",
output_ext: str = ".png",
resample: bool = True,
Expand Down
6 changes: 4 additions & 2 deletions monai/data/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from copy import deepcopy
from functools import reduce
from itertools import product, starmap
from pathlib import PurePath
from pathlib import Path, PurePath
from typing import Any, Dict, Generator, Iterable, List, Mapping, Optional, Sequence, Tuple, Union

import numpy as np
Expand Down Expand Up @@ -492,6 +492,8 @@ def correct_nifti_header_if_necessary(img_nii):
Args:
img_nii: nifti image object
"""
if img_nii.header.get("dim") is None:
return img_nii # not nifti?
dim = img_nii.header["dim"][0]
if dim >= 5:
return img_nii # do nothing for high-dimensional array
Expand Down Expand Up @@ -677,7 +679,7 @@ def to_affine_nd(r: Union[np.ndarray, int], affine: np.ndarray) -> np.ndarray:
def create_file_basename(
postfix: str,
input_file_name: str,
folder_path: str,
folder_path: Union[Path, str],
data_root_dir: str = "",
separate_folder: bool = True,
patch_index: Optional[int] = None,
Expand Down
2 changes: 1 addition & 1 deletion monai/transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@
)
from .inverse import InvertibleTransform
from .inverse_batch_transform import BatchInverseTransform, Decollated
from .io.array import LoadImage, SaveImage
from .io.array import SUPPORTED_READERS, LoadImage, SaveImage
from .io.dictionary import LoadImaged, LoadImageD, LoadImageDict, SaveImaged, SaveImageD, SaveImageDict
from .nvtx import (
Mark,
Expand Down
Loading