Skip to content

EnsureChannelFirst(channel_dim="no_channel") leaves spatial_ndim too low, crop drops third-axis affine offset (regression in 1.6.0) #9184

Open

Description

@Spenhouet

Describe the bug

Since 1.6.0, a crop after EnsureChannelFirst(channel_dim="no_channel") on a channel-less 3D MetaTensor only shifts the affine origin on the first two axes. The offset on the third axis is lost, so the cropped image ends up at the wrong position in world space. There is no error or warning.

The cause is the cached MetaTensor.spatial_ndim from #8765. When a (H, W, D) tensor is built with a 4x4 affine, the constructor clamps spatial_ndim to ndim - 1 = 2, because it assumes dim 0 is a channel. EnsureChannelFirst then adds the channel dim but does not update spatial_ndim, so it stays at 2. crop_func uses img.peek_pending_rank() (which returns spatial_ndim) to build the translation, so it only gets a 2D shift. The same happens for 2D input: (H, W) ends up with spatial_ndim == 1.

Images from LoadImage are not affected, since the reader sets original_channel_dim in the metadata. It shows up when the MetaTensor is created directly from an array.

To Reproduce

import numpy as np
from monai.data import MetaTensor
from monai.transforms import EnsureChannelFirstd, SpatialCropd

affine = np.eye(4)
affine[:3, 3] = [10, 20, 30]
d = {"img": MetaTensor(np.zeros((40, 40, 40), dtype=np.float32), affine=affine)}
d = EnsureChannelFirstd("img", channel_dim="no_channel")(d)
print(d["img"].spatial_ndim)
d = SpatialCropd("img", roi_start=[5, 5, 5], roi_end=[20, 20, 20])(d)
print(d["img"].affine[:3, 3])
  • MONAI 1.5.2: translation [15, 25, 35]
  • MONAI 1.6.1 and current dev: spatial_ndim is 2, translation [15, 25, 30]

Expected behavior

After EnsureChannelFirst adds the channel dim, spatial_ndim matches the affine again (3 here), and the crop gives [15, 25, 35] like in 1.5.2.

Environment

MONAI 1.6.1 and dev (6aa4abf), torch 2.x, Python 3.13, Linux.

Additional context

#9166 is a different 1.6 regression around spatial_ndim (the NIfTI writer on 2D channel-less arrays). This one is about EnsureChannelFirst and the crop affine. I have a small fix that recomputes spatial_ndim from the affine in EnsureChannelFirst when it adds the channel dim, and will open a PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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