[FIX] patch sampling in get_bbox() method to ensure patches are always fully contained inside the volume when oversampling foreground#3033
Open
sifaoso wants to merge 1 commit into
Open
Conversation
…s fully contained inside the volume
ghost
reviewed
May 9, 2026
Contributor
ghost
left a comment
There was a problem hiding this comment.
🔍 nnU-Net Code Review
Summary
This PR fixes a boundary-check bug in get_bbox() where, during foreground oversampling (force_fg=True), the computed bbox_lbs was clamped against the lower bound (lbs) but not the upper bound (ubs). When a selected foreground voxel lies near the end of an axis, the patch could extend beyond the valid image region, causing unexpected zero-padding during training.
Contribution Guidelines
- Bug fix format: The PR describes the root cause clearly and includes a visual (GIF) showing the issue. However,
CONTRIBUTING.mdasks for a minimal reproducible example — a short Python/CLI script that triggers the bug would be more rigorous than a screenshot/GIF alone. This is a minor gap rather than a blocker. - General applicability: The fix is not dataset-specific; the bug exists for any dataset where a foreground voxel coordinate is close to the image edge in any axis. Observed on AMOS but affects all datasets.
- Scope: Minimal, single-line change — no unnecessary complexity introduced.
Key Findings
- Correctness — the fix is correct.
lbs[i]andubs[i]define the valid sampling interval forbbox_lbsin each dimension (nnunetv2/training/dataloading/data_loader.py:93-94). The non-force_fgbranch already samples uniformly within[lbs[i], ubs[i]](line 99). Theforce_fgbranch missed the upper-bound clamp, which the PR correctly adds:# Before bbox_lbs = [max(lbs[i], selected_voxel[i + 1] - self.patch_size[i] // 2) for i in range(dim)] # After bbox_lbs = [max(lbs[i], min(ubs[i], selected_voxel[i + 1] - self.patch_size[i] // 2)) for i in range(dim)]
- Foreground guarantee is preserved. When the clamping triggers (
selected_voxel[i+1] - patch_size[i]//2 > ubs[i]),bbox_lbs[i]is set toubs[i]andbbox_ubs[i] = ubs[i] + patch_size[i]. The selected foreground voxel index satisfiesselected_voxel[i+1] <= data_shape[i] - 1 < bbox_ubs[i]andselected_voxel[i+1] > ubs[i] = bbox_lbs[i], so the foreground voxel is still inside the resulting patch. - The code comment at line 142–143 says "Make sure it is within the bounds of lb and ub", but the original code only enforced
lb. The comment was already misleading before this fix; it is now accurate. No update to the comment is strictly required, but it could be clarified for future readers. - No impact on other paths. The
force_fg=False+has_ignorebranch and the random-fallback branch are untouched.
Minor Suggestions
- Consider adding a short code-level comment (or updating line 143) to make clear that
min(ubs[i], ...)is the fix for the upper-bound case — future readers will wonder why both bounds are needed. - A minimal Python test or assertion demonstrating that
bbox_lbs[i] <= ubs[i]always holds after the fix would strengthen the PR perCONTRIBUTING.md, even if it's just added to the PR description.
Member
|
Thanks for the report. Looks legit! I am in vacation right now and will look into it once I am back |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hey all,
Thanks a lot for all the work that has gone into nnU-Net over the years!
I believe I found a bug in patch sampling when visualizing 3D CT batches (AMOS dataset), where some sampled patches contain slices outside the scan field-of-view (resulting in unexpected padding).
The issue occurs in the
get_bbox()method whenforce_fg=True(foreground oversampling). In this case, a foreground class is first selected, then a random voxel from that class is chosen as the patch center. However, when constructing the patch, the resulting bounding box is only constrained with respect to the lower bounds (lbs) and is not checked against the upper bounds (ubs), which define the valid sampling range to keep the patch fully inside the volume (here:nnUNet/nnunetv2/training/dataloading/data_loader.py
Line 145 in e73bbc7
Note that this issue does not occur when
force_fg=False, sincebbox_lbsis sampled directly within the valid interval[lbs, ubs].The proposed fix explicitly constrains the sampled bounding box to remain within both
lbsandubs, which fixes the issue in my tests.Below is a small GIF illustrating the problem (see the 3rd sample: some slices lie outside the scan field-of-view and are therefore padded unexpectedly).