Skip to content

Add BoundaryLoss#8916

Merged
ericspod merged 3 commits into
Project-MONAI:devfrom
txmed82:boundary-loss
Jul 17, 2026
Merged

Add BoundaryLoss#8916
ericspod merged 3 commits into
Project-MONAI:devfrom
txmed82:boundary-loss

Conversation

@txmed82

@txmed82 txmed82 commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Added BoundaryLoss. - Handles 2D/3D seg - Supports sigmoid, softmax, other_act, include_background, batch - Does empty masks without producing arbitrary distance ramps - Added tests for shape handling, reductions, gradient flow, single channel warnings, degenerative mask & channel free targets Verified with tests including run tests.sh

Closes #8884

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR introduces BoundaryLoss, a new loss function for boundary-aware segmentation. It computes a signed distance map for each target class using scipy's distance_transform_edt, weights predicted probabilities by these distance maps to emphasize boundary regions, and normalizes by pixel count. The implementation supports configurable activation (sigmoid/softmax/custom), one-hot target conversion, background exclusion, and standard reduction modes. Full package integration includes __init__ re-export and documentation entry. Tests cover parameterized configurations, shape validation, edge cases, error handling, and gradient propagation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description covers main functionality and linked issue, but lacks proper template structure with explicit section headings and missing checkbox completions. Use the repository's template structure with clear sections (Description, Types of changes) and mark relevant checkboxes to clarify testing status and documentation updates.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title "Add BoundaryLoss" is concise and directly describes the main change: introducing a new loss function.
Linked Issues check ✅ Passed The PR implements all core objectives from #8884: BoundaryLoss class with 2D/3D support, activation options, distance-map weighting, and comprehensive tests.
Out of Scope Changes check ✅ Passed All changes directly support BoundaryLoss implementation: new loss class, module imports, documentation, and tests. No extraneous modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 95.00% which is sufficient. The required threshold is 80.00%.

✏️ 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/losses/test_boundary_loss.py`:
- Around line 151-154: Add a Google-style docstring to the helper function
_describe_test_case that documents the parameters and return value: describe the
arguments (test_func, test_number, params) and that params.args unpacks to
(input_param, input_data, _), explain the expected structure of input_data (a
dict with 'input' Tensor including shape and device), and state that the
function returns a formatted string describing the params, shape, and device;
place the docstring immediately under the def line in Google style with Args:
and Returns: sections.
🪄 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: 8cc5f9a0-8eb4-430a-bb9e-4ca7e4e8938d

📥 Commits

Reviewing files that changed from the base of the PR and between eccefc5 and 9c26ae6.

📒 Files selected for processing (4)
  • docs/source/losses.rst
  • monai/losses/__init__.py
  • monai/losses/boundary_loss.py
  • tests/losses/test_boundary_loss.py

Comment thread tests/losses/test_boundary_loss.py
Signed-off-by: Colin Son <txmed82@users.noreply.github.com>

@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.

🧹 Nitpick comments (2)
monai/losses/boundary_loss.py (2)

109-124: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Docstring missing Raises section.

Method raises ValueError at line 124 but this isn't documented.

Proposed fix
         Args:
             target: target tensor of shape BNHW[D], with values in {0, 1} (one-hot encoded).
 
         Returns:
             Signed distance map of the same shape as target.
+
+        Raises:
+            ValueError: If target is not 4D (BNHW) or 5D (BNHWD).
         """
         if target.dim() not in (4, 5):
             raise ValueError("Only 2D (BNHW) and 3D (BNHWD) supported")
🤖 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 `@monai/losses/boundary_loss.py` around lines 109 - 124, The
compute_distance_map method raises a ValueError when the target tensor
dimensions are not 4 or 5, but this exception is not documented in the
docstring. Add a Raises section to the docstring of compute_distance_map that
documents the ValueError exception along with a description of when it is raised
(specifically when target.dim() is not in (4, 5)).

Source: Coding guidelines


147-168: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Docstring missing Returns section.

Per coding guidelines, document the return value.

Proposed fix
         Raises:
             ValueError: If the input is not 2D (BNHW) or 3D (BNHWD).
             AssertionError: When input and target (after one hot transform if set)
                 have different shapes.
             ValueError: When ``self.reduction`` is not one of ["mean", "sum", "none"].
+
+        Returns:
+            torch.Tensor: Computed boundary loss. Shape depends on ``reduction``:
+                scalar if "mean" or "sum", shape (B, C') if "none" (or (C',) if batch=True).
 
         Example:
🤖 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 `@monai/losses/boundary_loss.py` around lines 147 - 168, The forward method in
the BoundaryLoss class is missing a Returns section in its docstring. Add a
Returns section after the Raises section that documents the return type as
torch.Tensor and describes what it represents, such as the computed boundary
loss value. This should follow the same format as the other documented
parameters and exceptions in the docstring.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@monai/losses/boundary_loss.py`:
- Around line 109-124: The compute_distance_map method raises a ValueError when
the target tensor dimensions are not 4 or 5, but this exception is not
documented in the docstring. Add a Raises section to the docstring of
compute_distance_map that documents the ValueError exception along with a
description of when it is raised (specifically when target.dim() is not in (4,
5)).
- Around line 147-168: The forward method in the BoundaryLoss class is missing a
Returns section in its docstring. Add a Returns section after the Raises section
that documents the return type as torch.Tensor and describes what it represents,
such as the computed boundary loss value. This should follow the same format as
the other documented parameters and exceptions in the docstring.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8e42e9d2-b109-4af8-96f3-3f541d99bb04

📥 Commits

Reviewing files that changed from the base of the PR and between 9c26ae6 and a473d06.

📒 Files selected for processing (4)
  • docs/source/losses.rst
  • monai/losses/__init__.py
  • monai/losses/boundary_loss.py
  • tests/losses/test_boundary_loss.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/source/losses.rst
  • monai/losses/init.py
  • tests/losses/test_boundary_loss.py

@aymuos15

Copy link
Copy Markdown
Contributor

#4068

This should close as well I think if/when this is merged.

@ericspod ericspod left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hi @txmed82 thanks for this added loss. I don't feel anything needs changing so we can go ahead with merging now.

@ericspod
ericspod enabled auto-merge (squash) July 17, 2026 21:30
@ericspod
ericspod merged commit 5da2472 into Project-MONAI:dev Jul 17, 2026
23 checks passed
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.

[Feature Request] Add BoundaryLoss for highly imbalanced segmentation

3 participants