Skip to content

ImageWriter.convert_to_channel_last implicitly converts from MetaTensor to Numpy Array #8619

@sudomakeinstall

Description

@sudomakeinstall

Describe the bug

The convert_to_channel_last method of the ImageWriter class implicitly converts data from a MetaTensor to an ndarray here:

        data = np.squeeze(data, -1)

This causes downstream issues elsewhere. For example, the documentation for ITKWriter states that by setting affine_lps_to_ras to None, the flag will be determined automatically using the meta["space"] attribute:

        affine_lps_to_ras: whether to convert the affine matrix from "LPS" to "RAS". Defaults to ``True``.
            Set to ``True`` to be consistent with ``NibabelWriter``,
            otherwise the affine matrix is assumed already in the ITK convention.
            Set to ``None`` to use ``data_array.meta[MetaKeys.SPACE]`` to determine the flag.

And the create_backend_obj method does attempt to do this:

    if isinstance(data_array, MetaTensor) and affine_lps_to_ras is None:
        affine_lps_to_ras = (
            data_array.meta.get(MetaKeys.SPACE, SpaceKeys.LPS) != SpaceKeys.LPS
        )  # do the converting from LPS to RAS only if the space type is currently LPS.

However, the check isinstance(data_array, MetaTensor) is always false due to convert_to_channel_last being called earlier (assuming squeeze_end_dims = True, which is the default, and that there are channels to squeeze).

To Reproduce

import numpy as np
import monai as mn

arr = np.random.rand(1, 2, 3, 4, 5)
meta = mn.data.MetaTensor(arr)

writer = mn.data.ITKWriter()
writer.set_data_array(meta)
print(type(writer.data_obj)) # <class 'numpy.ndarray'>

Expected behavior
Type should be <class 'monai.data.meta_tensor.MetaTensor'>

Note that the expected behavior can be achieved by making the following change here:

Old:

data = np.squeeze(data, -1)

New:

data = data.squeeze(-1)

Screenshots

N/A

Environment

python -c "import monai; monai.config.print_debug_info()"
================================
Printing MONAI config...
================================
MONAI version: 1.6.dev2544
Numpy version: 2.3.2
Pytorch version: 2.5.1+cu124
MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False
MONAI rev id: f910be902091ce9efdc2928e1eb473fd607bfeb9
MONAI __file__: /home/<username>/Developer/dv-cardio-seg/.venv/lib/python3.12/site-packages/monai/__init__.py

Optional dependencies:
Pytorch Ignite version: NOT INSTALLED or UNKNOWN VERSION.
ITK version: 5.4.4
Nibabel version: 5.3.2
scikit-image version: NOT INSTALLED or UNKNOWN VERSION.
scipy version: NOT INSTALLED or UNKNOWN VERSION.
Pillow version: 11.0.0
Tensorboard version: NOT INSTALLED or UNKNOWN VERSION.
gdown version: NOT INSTALLED or UNKNOWN VERSION.
TorchVision version: NOT INSTALLED or UNKNOWN VERSION.
tqdm version: 4.66.6
lmdb version: NOT INSTALLED or UNKNOWN VERSION.
psutil version: NOT INSTALLED or UNKNOWN VERSION.
pandas version: 2.2.3
einops version: 0.8.1
transformers version: NOT INSTALLED or UNKNOWN VERSION.
mlflow version: NOT INSTALLED or UNKNOWN VERSION.
pynrrd version: NOT INSTALLED or UNKNOWN VERSION.
clearml version: NOT INSTALLED or UNKNOWN VERSION.

For details about installing the optional dependencies, please visit:
    https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies


================================
Printing system config...
================================
`psutil` required for `print_system_info`

================================
Printing GPU config...
================================
Num GPUs: 1
Has CUDA: True
CUDA version: 12.4
cuDNN enabled: True
NVIDIA_TF32_OVERRIDE: None
TORCH_ALLOW_TF32_CUBLAS_OVERRIDE: None
cuDNN version: 90100
Current device: 0
Library compiled for CUDA architectures: ['sm_50', 'sm_60', 'sm_70', 'sm_75', 'sm_80', 'sm_86', 'sm_90']
GPU 0 Name: NVIDIA GeForce RTX 4090
GPU 0 Is integrated: False
GPU 0 Is multi GPU board: False
GPU 0 Multi processor count: 128
GPU 0 Total memory (GB): 23.6
GPU 0 CUDA capability (maj.min): 8.9

Additional context

N/A

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions