Skip to content

Commit a7d4574

Browse files
authored
2743 - no extension check for user specified reader (#2751)
* no extension check for user specified reader; allow Path object
1 parent a6cf9b6 commit a7d4574

File tree

13 files changed

+244
-98
lines changed

13 files changed

+244
-98
lines changed

docs/source/data.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Generic Interfaces
7474
.. autoclass:: ImageDataset
7575
:members:
7676
:special-members: __getitem__
77-
77+
7878
`NPZDictItemDataset`
7979
~~~~~~~~~~~~~~~~~~~~
8080
.. autoclass:: NPZDictItemDataset
@@ -108,6 +108,11 @@ Patch-based dataset
108108
Image reader
109109
------------
110110

111+
ImageReader
112+
~~~~~~~~~~~
113+
.. autoclass:: ImageReader
114+
:members:
115+
111116
ITKReader
112117
~~~~~~~~~
113118
.. autoclass:: ITKReader

monai/data/image_reader.py

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,30 @@
4545

4646

4747
class ImageReader(ABC):
48-
"""Abstract class to define interface APIs to load image files.
49-
users need to call `read` to load image and then use `get_data`
50-
to get the image data and properties from meta data.
48+
"""
49+
An abstract class defines APIs to load image files.
50+
51+
Typical usage of an implementation of this class is:
52+
53+
.. code-block:: python
54+
55+
image_reader = MyImageReader()
56+
img_obj = image_reader.read(path_to_image)
57+
img_data, meta_data = image_reader.get_data(img_obj)
58+
59+
- The `read` call converts image filenames into image objects,
60+
- The `get_data` call fetches the image data, as well as meta data.
61+
- A reader should implement `verify_suffix` with the logic of checking the input filename
62+
by the filename extensions.
5163
5264
"""
5365

5466
@abstractmethod
5567
def verify_suffix(self, filename: Union[Sequence[str], str]) -> bool:
5668
"""
57-
Verify whether the specified file or files format is supported by current reader.
69+
Verify whether the specified `filename` is supported by the current reader.
70+
This method should return True if the reader is able to read the format suggested by the
71+
`filename`.
5872
5973
Args:
6074
filename: file name or a list of file names to read.
@@ -67,7 +81,7 @@ def verify_suffix(self, filename: Union[Sequence[str], str]) -> bool:
6781
def read(self, data: Union[Sequence[str], str], **kwargs) -> Union[Sequence[Any], Any]:
6882
"""
6983
Read image data from specified file or files.
70-
Note that it returns the raw data, so different readers return different image data type.
84+
Note that it returns a data object or a sequence of data objects.
7185
7286
Args:
7387
data: file name or a list of file names to read.
@@ -80,7 +94,8 @@ def read(self, data: Union[Sequence[str], str], **kwargs) -> Union[Sequence[Any]
8094
def get_data(self, img) -> Tuple[np.ndarray, Dict]:
8195
"""
8296
Extract data array and meta data from loaded image and return them.
83-
This function must return 2 objects, first is numpy array of image data, second is dict of meta data.
97+
This function must return two objects, the first is a numpy array of image data,
98+
the second is a dictionary of meta data.
8499
85100
Args:
86101
img: an image object loaded from an image file or a list of image objects.
@@ -124,7 +139,7 @@ def _stack_images(image_list: List, meta_dict: Dict):
124139
class ITKReader(ImageReader):
125140
"""
126141
Load medical images based on ITK library.
127-
All the supported image formats can be found:
142+
All the supported image formats can be found at:
128143
https://github.com/InsightSoftwareConsortium/ITK/tree/master/Modules/IO
129144
The loaded data array will be in C order, for example, a 3D image NumPy
130145
array index order will be `CDWH`.
@@ -396,7 +411,10 @@ def _get_meta_dict(self, img) -> Dict:
396411
397412
"""
398413
# swap to little endian as PyTorch doesn't support big endian
399-
header = img.header.as_byteswapped("<")
414+
try:
415+
header = img.header.as_byteswapped("<")
416+
except ValueError:
417+
header = img.header
400418
return dict(header)
401419

402420
def _get_affine(self, img):
@@ -419,11 +437,18 @@ def _get_spatial_shape(self, img):
419437
420438
"""
421439
# swap to little endian as PyTorch doesn't support big endian
422-
header = img.header.as_byteswapped("<")
423-
ndim = header["dim"][0]
440+
try:
441+
header = img.header.as_byteswapped("<")
442+
except ValueError:
443+
header = img.header
444+
dim = header.get("dim", None)
445+
if dim is None:
446+
dim = header.get("dims") # mgh format?
447+
dim = np.insert(dim, 0, 3)
448+
ndim = dim[0]
424449
spatial_rank = min(ndim, 3)
425450
# the img data should have no channel dim or the last dim is channel
426-
return np.asarray(header["dim"][1 : spatial_rank + 1])
451+
return np.asarray(dim[1 : spatial_rank + 1])
427452

428453
def _get_array_data(self, img):
429454
"""

monai/data/nifti_saver.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# See the License for the specific language governing permissions and
1010
# limitations under the License.
1111

12+
from pathlib import Path
1213
from typing import Dict, Optional, Union
1314

1415
import numpy as np
@@ -36,7 +37,7 @@ class NiftiSaver:
3637

3738
def __init__(
3839
self,
39-
output_dir: str = "./",
40+
output_dir: Union[Path, str] = "./",
4041
output_postfix: str = "seg",
4142
output_ext: str = ".nii.gz",
4243
resample: bool = True,

monai/data/png_saver.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
# See the License for the specific language governing permissions and
1010
# limitations under the License.
1111

12+
from pathlib import Path
1213
from typing import Dict, Optional, Union
1314

1415
import numpy as np
@@ -33,7 +34,7 @@ class PNGSaver:
3334

3435
def __init__(
3536
self,
36-
output_dir: str = "./",
37+
output_dir: Union[Path, str] = "./",
3738
output_postfix: str = "seg",
3839
output_ext: str = ".png",
3940
resample: bool = True,

monai/data/utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from copy import deepcopy
2020
from functools import reduce
2121
from itertools import product, starmap
22-
from pathlib import PurePath
22+
from pathlib import Path, PurePath
2323
from typing import Any, Dict, Generator, Iterable, List, Mapping, Optional, Sequence, Tuple, Union
2424

2525
import numpy as np
@@ -492,6 +492,8 @@ def correct_nifti_header_if_necessary(img_nii):
492492
Args:
493493
img_nii: nifti image object
494494
"""
495+
if img_nii.header.get("dim") is None:
496+
return img_nii # not nifti?
495497
dim = img_nii.header["dim"][0]
496498
if dim >= 5:
497499
return img_nii # do nothing for high-dimensional array
@@ -677,7 +679,7 @@ def to_affine_nd(r: Union[np.ndarray, int], affine: np.ndarray) -> np.ndarray:
677679
def create_file_basename(
678680
postfix: str,
679681
input_file_name: str,
680-
folder_path: str,
682+
folder_path: Union[Path, str],
681683
data_root_dir: str = "",
682684
separate_folder: bool = True,
683685
patch_index: Optional[int] = None,

monai/transforms/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@
193193
)
194194
from .inverse import InvertibleTransform
195195
from .inverse_batch_transform import BatchInverseTransform, Decollated
196-
from .io.array import LoadImage, SaveImage
196+
from .io.array import SUPPORTED_READERS, LoadImage, SaveImage
197197
from .io.dictionary import LoadImaged, LoadImageD, LoadImageDict, SaveImaged, SaveImageD, SaveImageDict
198198
from .nvtx import (
199199
Mark,

0 commit comments

Comments
 (0)