From cdb8391a39beb8b0ca9c8e20f73a2f807f025b74 Mon Sep 17 00:00:00 2001 From: "Wei_Chuan, Chiang" Date: Mon, 23 Dec 2024 14:50:12 +0800 Subject: [PATCH 1/6] Fixes #6840 According to the issue, this PR addresses on the meta dictionary `data['pixdim']` of NIfTI images does not update after applying the `spacing` or `spacingd`. To align with `affine`, we update `data['pixdim']` and keep the original metainfo in `data['original_pixdim']`. Additionally, this PR also update the metainfo `key_{meta_key_postfix}['pixdim']` in NIfTI images, consistent with `spaced_data_dict['image_meta_dict']['pixdim']` in issue #6832. Signed-off-by: Wei_Chuan, Chiang Co-authored-by: einsyang723 Co-authored-by: IamTingTing <6121smile@gmail.com> --- monai/data/image_reader.py | 2 ++ monai/data/meta_tensor.py | 4 ++++ monai/transforms/spatial/array.py | 3 +++ monai/transforms/spatial/dictionary.py | 5 +++++ monai/utils/enums.py | 2 ++ 5 files changed, 16 insertions(+) diff --git a/monai/data/image_reader.py b/monai/data/image_reader.py index 5bc38f69ea..75ea009598 100644 --- a/monai/data/image_reader.py +++ b/monai/data/image_reader.py @@ -989,6 +989,8 @@ def get_data(self, img) -> tuple[np.ndarray, dict]: for i, filename in zip(ensure_tuple(img), self.filenames): header = self._get_meta_dict(i) + if MetaKeys.PIXDIM in header: + header[MetaKeys.ORIGINAL_PIXDIM] = np.array(header[MetaKeys.PIXDIM], copy=True) header[MetaKeys.AFFINE] = self._get_affine(i) header[MetaKeys.ORIGINAL_AFFINE] = self._get_affine(i) header["as_closest_canonical"] = self.as_closest_canonical diff --git a/monai/data/meta_tensor.py b/monai/data/meta_tensor.py index c4c491e1b9..b2ca6860ae 100644 --- a/monai/data/meta_tensor.py +++ b/monai/data/meta_tensor.py @@ -477,6 +477,10 @@ def pixdim(self): return [affine_to_spacing(a) for a in self.affine] return affine_to_spacing(self.affine) + def set_pixdim(self) -> None: + """Update pixdim based on current affine.""" + self.meta[MetaKeys.PIXDIM][1 : 1 + len(self.pixdim)] = affine_to_spacing(self.affine) + def peek_pending_shape(self): """ Get the currently expected spatial shape as if all the pending operations are executed. diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index e4ed196eff..1342953ff5 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -535,6 +535,9 @@ def __call__( dtype=dtype, lazy=lazy_, ) + if isinstance(data_array, MetaTensor) and "pixdim" in data_array.meta: + data_array = data_array.clone() + data_array.set_pixdim() if self.recompute_affine and isinstance(data_array, MetaTensor): if lazy_: raise NotImplementedError("recompute_affine is not supported with lazy evaluation.") diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 2b80034a07..00322abf41 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -24,11 +24,13 @@ import numpy as np import torch +import monai.transforms as transforms from monai.config import DtypeLike, KeysCollection, SequenceStr from monai.config.type_definitions import NdarrayOrTensor from monai.data.box_utils import BoxMode, StandardMode from monai.data.meta_obj import get_track_meta from monai.data.meta_tensor import MetaTensor +from monai.data.utils import is_supported_format from monai.networks.layers.simplelayers import GaussianFilter from monai.transforms.croppad.array import CenterSpatialCrop from monai.transforms.inverse import InvertibleTransform @@ -520,6 +522,9 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = No output_spatial_shape=output_shape_k if should_match else None, lazy=lazy_, ) + if isinstance(d[key], MetaTensor) and "filename_or_obj" in d[key].meta: + if is_supported_format(d[key].meta["filename_or_obj"], ["nii", "nii.gz"]): + d = transforms.sync_meta_info(key, d) if output_shape_k is None: output_shape_k = d[key].peek_pending_shape() if isinstance(d[key], MetaTensor) else d[key].shape[1:] return d diff --git a/monai/utils/enums.py b/monai/utils/enums.py index 1fbf3ffa05..24f723c36a 100644 --- a/monai/utils/enums.py +++ b/monai/utils/enums.py @@ -528,6 +528,8 @@ class MetaKeys(StrEnum): Typical keys for MetaObj.meta """ + PIXDIM = "pixdim" # MetaTensor.pixdim + ORIGINAL_PIXDIM = "original_pixdim" # the pixdim after image loading before any data processing AFFINE = "affine" # MetaTensor.affine ORIGINAL_AFFINE = "original_affine" # the affine after image loading before any data processing SPATIAL_SHAPE = "spatial_shape" # optional key for the length in each spatial dimension From 8cc61754c807d62493651c61869d45641792e820 Mon Sep 17 00:00:00 2001 From: "Wei_Chuan, Chiang" Date: Mon, 23 Dec 2024 17:19:32 +0800 Subject: [PATCH 2/6] fix issue 6840 Signed-off-by: Wei_Chuan, Chiang --- monai/data/meta_tensor.py | 2 +- monai/transforms/spatial/array.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/data/meta_tensor.py b/monai/data/meta_tensor.py index b2ca6860ae..e3d7d71e31 100644 --- a/monai/data/meta_tensor.py +++ b/monai/data/meta_tensor.py @@ -479,7 +479,7 @@ def pixdim(self): def set_pixdim(self) -> None: """Update pixdim based on current affine.""" - self.meta[MetaKeys.PIXDIM][1 : 1 + len(self.pixdim)] = affine_to_spacing(self.affine) + self.meta[MetaKeys.PIXDIM][1:1+len(self.pixdim)] = affine_to_spacing(self.affine) def peek_pending_shape(self): """ diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index 1342953ff5..ecd62e8162 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -536,7 +536,7 @@ def __call__( lazy=lazy_, ) if isinstance(data_array, MetaTensor) and "pixdim" in data_array.meta: - data_array = data_array.clone() + data_array = data_array.clone() # type: MetaTensor data_array.set_pixdim() if self.recompute_affine and isinstance(data_array, MetaTensor): if lazy_: From 501bc45fa3b75e5b453a117a8a1287bd275d628f Mon Sep 17 00:00:00 2001 From: "Wei_Chuan, Chiang" Date: Mon, 23 Dec 2024 17:27:41 +0800 Subject: [PATCH 3/6] fix issue 6840 format Signed-off-by: Wei_Chuan, Chiang --- monai/data/meta_tensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/data/meta_tensor.py b/monai/data/meta_tensor.py index e3d7d71e31..b2ca6860ae 100644 --- a/monai/data/meta_tensor.py +++ b/monai/data/meta_tensor.py @@ -479,7 +479,7 @@ def pixdim(self): def set_pixdim(self) -> None: """Update pixdim based on current affine.""" - self.meta[MetaKeys.PIXDIM][1:1+len(self.pixdim)] = affine_to_spacing(self.affine) + self.meta[MetaKeys.PIXDIM][1 : 1 + len(self.pixdim)] = affine_to_spacing(self.affine) def peek_pending_shape(self): """ From c5ebb0d8333bc11fc0ba34dff14b919b8fc11406 Mon Sep 17 00:00:00 2001 From: "Wei_Chuan, Chiang" Date: Mon, 23 Dec 2024 17:41:02 +0800 Subject: [PATCH 4/6] fix casting Signed-off-by: Wei_Chuan, Chiang --- monai/transforms/spatial/array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monai/transforms/spatial/array.py b/monai/transforms/spatial/array.py index ecd62e8162..f54f76f3bf 100644 --- a/monai/transforms/spatial/array.py +++ b/monai/transforms/spatial/array.py @@ -536,7 +536,7 @@ def __call__( lazy=lazy_, ) if isinstance(data_array, MetaTensor) and "pixdim" in data_array.meta: - data_array = data_array.clone() # type: MetaTensor + data_array = cast(MetaTensor, data_array.clone()) data_array.set_pixdim() if self.recompute_affine and isinstance(data_array, MetaTensor): if lazy_: From 1aabdaec468a04da440c3165a664466faacd9ac6 Mon Sep 17 00:00:00 2001 From: "Wei_Chuan, Chiang" Date: Mon, 23 Dec 2024 19:30:50 +0800 Subject: [PATCH 5/6] fix image_only flag issue Signed-off-by: Wei_Chuan, Chiang --- monai/transforms/spatial/dictionary.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 00322abf41..7e664a508d 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -522,8 +522,8 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = No output_spatial_shape=output_shape_k if should_match else None, lazy=lazy_, ) - if isinstance(d[key], MetaTensor) and "filename_or_obj" in d[key].meta: - if is_supported_format(d[key].meta["filename_or_obj"], ["nii", "nii.gz"]): + if isinstance(d[key], MetaTensor) and f"{key}_meta_dict" in d: + if 'filename_or_obj' in d[key].meta and is_supported_format(d[key].meta['filename_or_obj'], ["nii", "nii.gz"]): d = transforms.sync_meta_info(key, d) if output_shape_k is None: output_shape_k = d[key].peek_pending_shape() if isinstance(d[key], MetaTensor) else d[key].shape[1:] From fe05f591ce9605db5f41adc1e5c2029f6be8ad18 Mon Sep 17 00:00:00 2001 From: "Wei_Chuan, Chiang" Date: Mon, 23 Dec 2024 19:38:38 +0800 Subject: [PATCH 6/6] fix format issue Signed-off-by: Wei_Chuan, Chiang --- monai/transforms/spatial/dictionary.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/monai/transforms/spatial/dictionary.py b/monai/transforms/spatial/dictionary.py index 7e664a508d..3cca368127 100644 --- a/monai/transforms/spatial/dictionary.py +++ b/monai/transforms/spatial/dictionary.py @@ -523,7 +523,9 @@ def __call__(self, data: Mapping[Hashable, torch.Tensor], lazy: bool | None = No lazy=lazy_, ) if isinstance(d[key], MetaTensor) and f"{key}_meta_dict" in d: - if 'filename_or_obj' in d[key].meta and is_supported_format(d[key].meta['filename_or_obj'], ["nii", "nii.gz"]): + if "filename_or_obj" in d[key].meta and is_supported_format( + d[key].meta["filename_or_obj"], ["nii", "nii.gz"] + ): d = transforms.sync_meta_info(key, d) if output_shape_k is None: output_shape_k = d[key].peek_pending_shape() if isinstance(d[key], MetaTensor) else d[key].shape[1:]