-
Notifications
You must be signed in to change notification settings - Fork 1.5k
2620 3595 Writer backend selector, deprecating nifti_saver/writer, png_saver/writer #3773
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
Changes from all commits
33f0348
0df2424
677cb10
05b94ad
aa7af63
7439019
f86ce2b
9292690
01853a6
2c09f8e
0152543
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,7 @@ | |
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| from typing import TYPE_CHECKING, Mapping, Optional, Sequence, Union | ||
| from typing import TYPE_CHECKING, Dict, Mapping, Optional, Sequence, Union | ||
|
|
||
| import numpy as np | ||
|
|
||
|
|
@@ -22,13 +22,15 @@ | |
| GridSampleMode, | ||
| GridSamplePadMode, | ||
| InterpolateMode, | ||
| OptionalImportError, | ||
| convert_data_type, | ||
| look_up_option, | ||
| optional_import, | ||
| require_pkg, | ||
| ) | ||
|
|
||
| DEFAULT_FMT = "%(asctime)s %(levelname)s %(filename)s:%(lineno)d - %(message)s" | ||
| EXT_WILDCARD = "*" | ||
| logger = get_logger(module_name=__name__, fmt=DEFAULT_FMT) | ||
|
|
||
| if TYPE_CHECKING: | ||
|
|
@@ -41,7 +43,76 @@ | |
| PILImage, _ = optional_import("PIL.Image") | ||
|
|
||
|
|
||
| __all__ = ["ImageWriter", "ITKWriter", "NibabelWriter", "PILWriter", "logger"] | ||
| __all__ = [ | ||
| "ImageWriter", | ||
| "ITKWriter", | ||
| "NibabelWriter", | ||
| "PILWriter", | ||
| "SUPPORTED_WRITERS", | ||
| "register_writer", | ||
| "resolve_writer", | ||
| "logger", | ||
| ] | ||
|
|
||
| SUPPORTED_WRITERS: Dict = {} | ||
|
|
||
|
|
||
| def register_writer(ext_name, *im_writers): | ||
| """ | ||
| Register ``ImageWriter``, so that writing a file with filename extension ``ext_name`` | ||
| could be resolved to a tuple of potentially appropriate ``ImageWriter``. | ||
| The customised writers could be registered by: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| from monai.data import register_writer | ||
| # `MyWriter` must implement `ImageWriter` interface | ||
| register_writer("nii", MyWriter) | ||
|
|
||
| Args: | ||
| ext_name: the filename extension of the image. | ||
| As an indexing key, it will be converted to a lower case string. | ||
| im_writers: one or multiple ImageWriter classes with high priority ones first. | ||
| """ | ||
| fmt = f"{ext_name}".lower() | ||
| if fmt.startswith("."): | ||
| fmt = fmt[1:] | ||
| existing = look_up_option(fmt, SUPPORTED_WRITERS, default=()) | ||
| all_writers = im_writers + existing | ||
| SUPPORTED_WRITERS[fmt] = all_writers | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @wyli , Here you use a global dictionary to store the supported I am not very sure which way is better, the Thanks in advance.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I am not sure whether it can work fine if calling Thanks. |
||
|
|
||
|
|
||
| def resolve_writer(ext_name, error_if_not_found=True) -> Sequence: | ||
|
wyli marked this conversation as resolved.
|
||
| """ | ||
| Resolves to a tuple of available ``ImageWriter`` in ``SUPPORTED_WRITERS`` | ||
| according to the filename extension key ``ext_name``. | ||
|
|
||
| Args: | ||
| ext_name: the filename extension of the image. | ||
| As an indexing key it will be converted to a lower case string. | ||
| error_if_not_found: whether to raise an error if no suitable image writer is found. | ||
| if True , raise an ``OptionalImportError``, otherwise return an empty tuple. Default is ``True``. | ||
| """ | ||
| if not SUPPORTED_WRITERS: | ||
| init() | ||
| fmt = f"{ext_name}".lower() | ||
| if fmt.startswith("."): | ||
| fmt = fmt[1:] | ||
| avail_writers = [] | ||
| default_writers = SUPPORTED_WRITERS.get(EXT_WILDCARD, ()) | ||
| for _writer in look_up_option(fmt, SUPPORTED_WRITERS, default=default_writers): | ||
| try: | ||
| _writer() # this triggers `monai.utils.module.require_pkg` to check the system availability | ||
| avail_writers.append(_writer) | ||
| except OptionalImportError: | ||
| continue | ||
| except Exception: # other writer init errors indicating it exists | ||
| avail_writers.append(_writer) | ||
| if not avail_writers and error_if_not_found: | ||
| raise OptionalImportError(f"No ImageWriter backend found for {fmt}.") | ||
| writer_tuple = ensure_tuple(avail_writers) | ||
| SUPPORTED_WRITERS[fmt] = writer_tuple | ||
| return writer_tuple | ||
|
|
||
|
|
||
| class ImageWriter: | ||
|
|
@@ -297,7 +368,9 @@ def __init__(self, output_dtype: DtypeLike = np.float32, **kwargs): | |
| """ | ||
| super().__init__(output_dtype=output_dtype, affine=None, channel_dim=0, **kwargs) | ||
|
|
||
| def set_data_array(self, data_array, channel_dim: Optional[int] = 0, squeeze_end_dims: bool = True, **kwargs): | ||
| def set_data_array( | ||
| self, data_array: NdarrayOrTensor, channel_dim: Optional[int] = 0, squeeze_end_dims: bool = True, **kwargs | ||
| ): | ||
| """ | ||
| Convert ``data_array`` into 'channel-last' numpy ndarray. | ||
|
|
||
|
|
@@ -309,14 +382,15 @@ def set_data_array(self, data_array, channel_dim: Optional[int] = 0, squeeze_end | |
| kwargs: keyword arguments passed to ``self.convert_to_channel_last``, | ||
| currently support ``spatial_ndim`` and ``contiguous``, defauting to ``3`` and ``False`` respectively. | ||
| """ | ||
| _r = len(data_array.shape) | ||
| self.data_obj = self.convert_to_channel_last( | ||
| data=data_array, | ||
| channel_dim=channel_dim, | ||
| squeeze_end_dims=squeeze_end_dims, | ||
| spatial_ndim=kwargs.pop("spatial_ndim", 3), | ||
| contiguous=kwargs.pop("contiguous", True), | ||
| ) | ||
| self.channel_dim = channel_dim | ||
| self.channel_dim = channel_dim if len(self.data_obj.shape) >= _r else None # channel dim is at the end | ||
|
|
||
| def set_metadata(self, meta_dict: Optional[Mapping] = None, resample: bool = True, **options): | ||
| """ | ||
|
|
@@ -335,7 +409,7 @@ def set_metadata(self, meta_dict: Optional[Mapping] = None, resample: bool = Tru | |
| data_array=self.data_obj, | ||
| affine=affine, | ||
| target_affine=original_affine if resample else None, | ||
| output_spatial_shape=spatial_shape, | ||
| output_spatial_shape=spatial_shape if resample else None, | ||
| mode=options.pop("mode", GridSampleMode.BILINEAR), | ||
| padding_mode=options.pop("padding_mode", GridSamplePadMode.BORDER), | ||
| align_corners=options.pop("align_corners", False), | ||
|
|
@@ -476,7 +550,7 @@ def set_metadata(self, meta_dict: Optional[Mapping], resample: bool = True, **op | |
| data_array=self.data_obj, | ||
| affine=affine, | ||
| target_affine=original_affine if resample else None, | ||
| output_spatial_shape=spatial_shape, | ||
| output_spatial_shape=spatial_shape if resample else None, | ||
| mode=options.pop("mode", GridSampleMode.BILINEAR), | ||
| padding_mode=options.pop("padding_mode", GridSamplePadMode.BORDER), | ||
| align_corners=options.pop("align_corners", False), | ||
|
|
@@ -716,3 +790,15 @@ def create_backend_obj( | |
| data = np.moveaxis(data, 0, 1) | ||
|
|
||
| return PILImage.fromarray(data, mode=kwargs.pop("image_mode", None)) | ||
|
|
||
|
|
||
| def init(): | ||
| """ | ||
| Initialize the image writer modules according to the filename extension. | ||
| """ | ||
| for ext in ("png", "jpg", "jpeg", "bmp", "tiff", "tif"): | ||
| register_writer(ext, PILWriter) # TODO: test 16-bit | ||
| for ext in ("nii.gz", "nii"): | ||
| register_writer(ext, NibabelWriter, ITKWriter) | ||
| register_writer("nrrd", ITKWriter, NibabelWriter) | ||
| register_writer(EXT_WILDCARD, ITKWriter, NibabelWriter, ITKWriter) | ||
Uh oh!
There was an error while loading. Please reload this page.