Skip to content

Fix Pad.inverse channel-dimension back-crop#8900

Open
aymuos15 wants to merge 2 commits into
Project-MONAI:devfrom
aymuos15:fix/pad-inverse-channel-crop
Open

Fix Pad.inverse channel-dimension back-crop#8900
aymuos15 wants to merge 2 commits into
Project-MONAI:devfrom
aymuos15:fix/pad-inverse-channel-crop

Conversation

@aymuos15

@aymuos15 aymuos15 commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Description

Pad.inverse computed the channel back-crop as min(max(padded[0][1], s + 1), len(data)), where s is the front pad. The max(..., s + 1) term forced the back-crop to at least front_pad + 1 even when the recorded back pad was smaller or zero, so inverting a Pad with a non-zero channel pad cropped the wrong number of channels (often emptying the channel dim) and failed to round-trip.

For example, inverting a channel pad of (2, 0) on a 3-channel image produced s = 2, e = min(max(0, 3), 5) = 3, i.e. data[2 : len - 3], an empty slice.

The back-crop is now computed directly from the recorded back pad: e = min(padded[0][1], len(data) - s).

Note this only affects the NumPy padding backend (e.g. mode="symmetric"), which pads the channel dimension; the PyTorch backend ignores channel padding, so the added test uses mode="symmetric" to exercise the channel path.

Types of changes

  • Non-breaking change (fix or new feature that would not break existing functionality).
  • 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.

aymuos15 added 2 commits June 7, 2026 03:10
Pad.inverse computed the channel-dimension back-crop as
min(max(padded[0][1], s + 1), len(data)), where s is the front pad.
The max(..., s + 1) term forced the crop to be at least front_pad + 1
even when the recorded back pad was smaller (or zero), so inverting a
transform with a non-zero channel pad sliced away the wrong number of
channels (often emptying the channel dim) and failed to round-trip.

Compute the back-crop directly from the recorded back pad instead:
e = min(padded[0][1], len(data) - s).

Signed-off-by: Soumya Snigdha Kundu <soumya_snigdha.kundu@kcl.ac.uk>
Covers round-tripping a non-zero channel pad (front-only, back-only,
symmetric, and combined channel+spatial) through Pad/Pad.inverse using
the NumPy backend, which pads the channel dimension. These cases failed
before the back-crop fix.

Signed-off-by: Soumya Snigdha Kundu <soumya_snigdha.kundu@kcl.ac.uk>
@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The PR fixes a boundary-condition bug in the Pad.inverse method where the channel-dimension crop end index calculation used incorrect clamping logic. The fix replaces min(max(padded[0][1], s + 1), len(data)) with min(padded[0][1], len(data) - s) to correctly compute the slice boundary. A new test module validates the fix by parameterizing multiple channel-padding scenarios, padding MetaTensors with symmetric mode, and confirming inverse operations restore original shapes and values exactly.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main fix: correcting the channel-dimension back-crop logic in Pad.inverse.
Description check ✅ Passed The description covers the problem, the solution, and test additions. The author checked relevant boxes. Detailed technical explanation of the bug and fix is provided.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

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

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/transforms/croppad/test_pad_inverse_channel.py`:
- Around line 32-40: Add a Google-style docstring to the test method
test_inverse_roundtrip_channel_pad describing the test's purpose and its
parameter to_pad (e.g., what padding configuration is being tested and the
expected round-trip behavior), placed immediately below the def line; ensure it
follows Google style with a short description and an Args section for to_pad.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 305f534c-ba43-49b2-9512-d937a43c10e1

📥 Commits

Reviewing files that changed from the base of the PR and between 2a7d0cf and 7fccf52.

📒 Files selected for processing (2)
  • monai/transforms/croppad/array.py
  • tests/transforms/croppad/test_pad_inverse_channel.py

Comment on lines +32 to +40
def test_inverse_roundtrip_channel_pad(self, to_pad):
set_track_meta(True)
img = MetaTensor(torch.arange(3 * 4 * 4).reshape(3, 4, 4).float())
pad = Pad(to_pad=to_pad, mode="symmetric")
padded = pad(img.clone())
self.assertEqual(padded.shape[0], img.shape[0] + to_pad[0][0] + to_pad[0][1])
inv = pad.inverse(padded)
self.assertEqual(inv.shape, img.shape)
self.assertTrue(torch.equal(inv.as_tensor(), img.as_tensor()))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add docstring to test method.

Per coding guidelines, all definitions require Google-style docstrings describing parameters and purpose.

📝 Suggested docstring
     `@parameterized.expand`(CHANNEL_PAD_CASES)
     def test_inverse_roundtrip_channel_pad(self, to_pad):
+        """Test Pad.inverse correctly restores original tensor after channel-dimension padding.
+
+        Args:
+            to_pad: Padding specification [(ch_pad), (h_pad), (w_pad)].
+        """
         set_track_meta(True)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_inverse_roundtrip_channel_pad(self, to_pad):
set_track_meta(True)
img = MetaTensor(torch.arange(3 * 4 * 4).reshape(3, 4, 4).float())
pad = Pad(to_pad=to_pad, mode="symmetric")
padded = pad(img.clone())
self.assertEqual(padded.shape[0], img.shape[0] + to_pad[0][0] + to_pad[0][1])
inv = pad.inverse(padded)
self.assertEqual(inv.shape, img.shape)
self.assertTrue(torch.equal(inv.as_tensor(), img.as_tensor()))
def test_inverse_roundtrip_channel_pad(self, to_pad):
"""Test Pad.inverse correctly restores original tensor after channel-dimension padding.
Args:
to_pad: Padding specification [(ch_pad), (h_pad), (w_pad)].
"""
set_track_meta(True)
img = MetaTensor(torch.arange(3 * 4 * 4).reshape(3, 4, 4).float())
pad = Pad(to_pad=to_pad, mode="symmetric")
padded = pad(img.clone())
self.assertEqual(padded.shape[0], img.shape[0] + to_pad[0][0] + to_pad[0][1])
inv = pad.inverse(padded)
self.assertEqual(inv.shape, img.shape)
self.assertTrue(torch.equal(inv.as_tensor(), img.as_tensor()))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/transforms/croppad/test_pad_inverse_channel.py` around lines 32 - 40,
Add a Google-style docstring to the test method
test_inverse_roundtrip_channel_pad describing the test's purpose and its
parameter to_pad (e.g., what padding configuration is being tested and the
expected round-trip behavior), placed immediately below the def line; ensure it
follows Google style with a short description and an Args section for to_pad.

Source: Coding guidelines

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.

1 participant