Skip to content

Fix spatial_ndim after EnsureChannelFirst adds a channel dim - #9185

Open
Spenhouet wants to merge 2 commits into
Project-MONAI:devfrom
Spenhouet:fix-ensure-channel-first-spatial-ndim
Open

Spenhouet wants to merge 2 commits into
Project-MONAI:devfrom
Spenhouet:fix-ensure-channel-first-spatial-ndim

Conversation

@Spenhouet

Copy link
Copy Markdown
Contributor

Fixes #9184.

Description

EnsureChannelFirst with channel_dim="no_channel" adds a channel dim, but the cached MetaTensor.spatial_ndim was computed before that and clamped to ndim - 1. For a (H, W, D) input with a 4x4 affine it stayed at 2. Crops then built a 2D translation from peek_pending_rank() and the offset on the third axis was lost.

After adding the channel dim, I now recompute spatial_ndim from the affine (only for the no-channel case and a non-batched affine). Inputs where an existing channel dim is moved are not touched.

I added tests to test_ensure_channel_firstd.py that check spatial_ndim for 3D and 2D channel-less input, and that SpatialCropd gets the full origin shift afterwards. They fail on dev without the change.

Types of changes

  • Non-breaking change (fix or new feature that would not break existing functionality).
  • Breaking change (fix or new feature that would cause existing functionality to change).
  • New tests added to cover the changes.
  • Integration tests passed locally by running ./runtests.sh -f -u --net --coverage.
  • Quick tests passed locally by running ./runtests.sh --quick --unittests --disttests.
  • In-line docstrings updated.
  • Documentation updated, tested make html command in the docs/ folder.

I ran the meta tensor, EnsureChannelFirst, crop, spacing, load image and utility transform tests locally, plus black, isort and ruff on the changed files.

…-MONAI#9184)

When the input has no channel dim, the cached spatial_ndim was clamped
to ndim - 1 at construction and was not updated after the channel dim
was added. Crops then built a translation with too few dims and lost
the offset on the last spatial axis. Recompute spatial_ndim from the
affine once the channel dim exists.

Signed-off-by: Sebastian Penhouet <7819068+Spenhouet@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: Project-MONAI/MONAI/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 33e404a4-8260-4b1d-9731-b3f5f311ae16
📥 Commits

Reviewing files that changed from the base of the PR and between dd306b5 and 8159218.

📒 Files selected for processing: 2
  • monai/transforms/utility/array.py
  • tests/transforms/test_ensure_channel_firstd.py
🚧 Files skipped from review as they are similar to previous changes: 2
  • monai/transforms/utility/array.py
  • tests/transforms/test_ensure_channel_firstd.py

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 6 remain after this review.

📝 Walkthrough

EnsureChannelFirst recalculates spatial_ndim after conversion when a channel-less input produces a MetaTensor with a 2D affine. Tests cover channel insertion and spatial dimensionality for 2D and 3D inputs. A crop test checks the resulting affine translation.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~8 minutes

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 81592

This fixes the spatial dimensionality for channel-less inputs after a channel dimension is added, so crops keep the full origin shift. No merge-blocking risk is evident from the supplied change.

Pre-merge checks: Passed 5
Check name Status Explanation
Title check Passed The title clearly and concisely identifies the main fix: recalculating spatial_ndim after EnsureChannelFirst adds a channel dimension.
Description check Passed The description explains the bug, implementation scope, tests added, and validation performed. It includes the required sections and correctly leaves unrun integration and quick test commands unchecke…
Linked Issues check Passed Issue [#9184] requires spatial_ndim correction after EnsureChannelFirst(channel_dim="no_channel") adds a channel to a direct MetaTensor. EnsureChannelFirst.__call__ now recalculates `spatial_n…
Out of Scope Changes check Passed The changes are limited to the EnsureChannelFirst correction, its focused docstring, and tests in test_ensure_channel_firstd.py. These changes directly support issue [#9184].
Docstring Coverage Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 2 files.
Full details: Description check

Explanation

The description explains the bug, implementation scope, tests added, and validation performed. It includes the required sections and correctly leaves unrun integration and quick test commands unchecked.

Full details: Linked Issues check

Explanation

Issue [#9184] requires spatial_ndim correction after EnsureChannelFirst(channel_dim="no_channel") adds a channel to a direct MetaTensor. EnsureChannelFirst.__call__ now recalculates spatial_ndim from a 2D affine for the resulting MetaTensor. The branch does not affect existing-channel moves. Tests cover 2D and 3D spatial_ndim and verify the full three-axis SpatialCropd affine shift.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Signed-off-by: Sebastian Penhouet <7819068+Spenhouet@users.noreply.github.com>

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant