Skip to content

Add AbsoluteVolumeDifferenceMetric to monai.metrics#8945

Open
MDSALMANSHAMS wants to merge 2 commits into
Project-MONAI:devfrom
MDSALMANSHAMS:avd-metric
Open

Add AbsoluteVolumeDifferenceMetric to monai.metrics#8945
MDSALMANSHAMS wants to merge 2 commits into
Project-MONAI:devfrom
MDSALMANSHAMS:avd-metric

Conversation

@MDSALMANSHAMS

Copy link
Copy Markdown

Summary

Closes #4009.

Adds AbsoluteVolumeDifferenceMetric and a standalone compute_absolute_volume_difference() function to monai.metrics.

Why AVD?
Dice score is known to be overly sensitive for small-object segmentation (e.g. retinal fluid sub-types in OCT volumes - SRF, IRF, PED), because small volume differences produce large Dice swings. AVD directly reflects volume size discrepancy, making it the standard evaluation metric in the RETOUCH retinal OCT fluid benchmark (Bogunovic et al., IEEE TMI 2019).

Changes

  • monai/metrics/absolute_volume_difference.py - new file:
    • AbsoluteVolumeDifferenceMetric(CumulativeIterationMetric) - cumulative class matching the DiceMetric / MeanIoU interface. Supports include_background,
      eduction, get_not_nans, ignore_empty.
    • compute_absolute_volume_difference() - standalone function returning shape [B, C].
  • monai/metrics/init.py - exports both new symbols.
  • ** ests/metrics/test_absolute_volume_difference.py** - 14 unit tests.

Test plan

  • Perfect prediction returns zero
  • Known volume difference (hand-verified)
  • ignore_empty=True sets NaN for empty ground-truth channels
  • ignore_empty=False returns raw absolute difference
  • include_background=False strips channel 0
  • 3D spatial volumes (BCDHW)
  • Multi-class output shape
  • Cumulative accumulation across batches
  • Buffer reset
  • Shape mismatch raises ValueError
  • Fewer than 3 dimensions raises ValueError
  • Top-level import from monai.metrics

All 14 tests pass (python -m unittest tests.metrics.test_absolute_volume_difference -v).

This PR was authored with the assistance of an AI coding assistant.

Implements the Absolute Volume Difference (AVD) metric as requested
in Project-MONAI#4009. AVD measures the absolute difference in foreground voxel
counts between prediction and ground truth per class, and is
particularly suited for small-object segmentation tasks (e.g. retinal
fluid in OCT volumes) where Dice score is overly sensitive to volume
size.

Changes:
- monai/metrics/absolute_volume_difference.py: new AbsoluteVolumeDifferenceMetric
  class (CumulativeIterationMetric) and compute_absolute_volume_difference()
  standalone function. Supports include_background, reduction, get_not_nans,
  and ignore_empty. References the RETOUCH benchmark paper.
- monai/metrics/__init__.py: export both new symbols.
- tests/metrics/test_absolute_volume_difference.py: 14 unit tests covering
  perfect prediction, known volume difference, ignore_empty behaviour,
  include_background stripping, 3D volumes, cumulative accumulation,
  and error cases.

This PR was authored with the assistance of an AI coding assistant.

Signed-off-by: MDSALMANSHAMS <salmanshams67@gmail.com>
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 324783b5-3050-416c-85c5-81aec7cb0240

📥 Commits

Reviewing files that changed from the base of the PR and between 74ee065 and df9a173.

📒 Files selected for processing (2)
  • monai/metrics/absolute_volume_difference.py
  • tests/metrics/test_absolute_volume_difference.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/metrics/test_absolute_volume_difference.py
  • monai/metrics/absolute_volume_difference.py

📝 Walkthrough

Walkthrough

A new file monai/metrics/absolute_volume_difference.py adds AbsoluteVolumeDifferenceMetric and compute_absolute_volume_difference. The function sums spatial dimensions to compute per-channel absolute volume differences, and can mask empty ground-truth channels with NaN. The metric wraps this function, applies reduction with do_metric_reduction, and can return not_nans. Both symbols are re-exported from monai/metrics/__init__.py. A new test module covers function behavior, metric accumulation, reset, and import exposure.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: adding AbsoluteVolumeDifferenceMetric to monai.metrics.
Description check ✅ Passed The description covers the feature, rationale, test plan, and linked issue, though it does not follow the template exactly.
Linked Issues check ✅ Passed The PR implements the requested absolute volume difference metric and standalone function for small-fluid-volume segmentation.
Out of Scope Changes check ✅ Passed No unrelated changes are evident beyond the new metric, exports, and tests needed for the feature.
Docstring Coverage ✅ Passed Docstring coverage is 100.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.

@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

🧹 Nitpick comments (2)
monai/metrics/absolute_volume_difference.py (1)

80-121: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Complete method-level Google-style docstrings.

__init__ lacks a method docstring, _compute_tensor/aggregate omit Returns, and aggregate should document its ValueError in Raises.

Suggested docstring patch
     def __init__(
         self,
         include_background: bool = True,
         reduction: MetricReduction | str = MetricReduction.MEAN,
         get_not_nans: bool = False,
         ignore_empty: bool = True,
     ) -> None:
+        """Initialize AbsoluteVolumeDifferenceMetric.
+
+        Args:
+            include_background: Whether to include channel 0.
+            reduction: Reduction mode used in :meth:`aggregate`.
+            get_not_nans: Whether :meth:`aggregate` returns `(metric, not_nans)`.
+            ignore_empty: Whether empty ground-truth channels are set to NaN.
+        """
         super().__init__()
         self.include_background = include_background
         self.reduction = reduction
         self.get_not_nans = get_not_nans
         self.ignore_empty = ignore_empty
@@
     def _compute_tensor(self, y_pred: torch.Tensor, y: torch.Tensor) -> torch.Tensor:  # type: ignore[override]
         """
         Args:
             y_pred: binarized prediction tensor, shape BCHW[D].
             y: binarized ground-truth tensor, shape BCHW[D].
 
+        Returns:
+            Per-batch per-class AVD tensor with shape [B, C].
+
         Raises:
             ValueError: when ``y_pred`` has fewer than three dimensions.
         """
@@
     def aggregate(
         self, reduction: MetricReduction | str | None = None
     ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
         """
         Execute reduction logic for the accumulated AVD values.
 
         Args:
             reduction: optional override for the reduction mode set at construction.
+
+        Returns:
+            Reduced metric tensor, or ``(metric, not_nans)`` when ``get_not_nans=True``.
+
+        Raises:
+            ValueError: when internal buffered data is not a tensor.
         """

As per path instructions, "Docstrings should be present for all definition which describe each variable, return value, and raised exception in the appropriate section of the Google-style of docstrings."

🤖 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/metrics/absolute_volume_difference.py` around lines 80 - 121, Add
complete Google-style docstrings for the AVD metric methods in
AbsoluteVolumeDifference: the __init__ method needs a short summary plus Args
entries for include_background, reduction, get_not_nans, and ignore_empty;
_compute_tensor should include a Returns section describing the computed tensor
and keep its ValueError in Raises; aggregate should add a Returns section for
the reduced output and document the ValueError it can raise in Raises. Use the
existing method names to update each docstring so every parameter, return value,
and exception is covered.

Source: Path instructions

tests/metrics/test_absolute_volume_difference.py (1)

24-157: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add Google-style docstrings to test methods.

Method definitions from Line 24 through Line 156 are missing per-method docstrings, so this file does not meet the docstring requirement for Python definitions.

As per path instructions, "Docstrings should be present for all definition which describe each variable, return value, and raised exception in the appropriate section of the Google-style of docstrings."

🤖 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/metrics/test_absolute_volume_difference.py` around lines 24 - 157, Add
Google-style docstrings to each test method in TestAbsoluteVolumeDifference and
TestAbsoluteVolumeDifferenceMetric so every definition is documented as
required. Update the individual test_* methods to describe the purpose,
arguments/fixtures used, expected return behavior, and any raised exceptions in
Google-style format, keeping the docstrings aligned with the method names and
assertions in this file.

Source: Path instructions

🤖 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/metrics/test_absolute_volume_difference.py`:
- Around line 149-150: The reset/empty-buffer path in aggregate() is being
tested with a too-broad exception check, which can hide unrelated failures.
Update the test around AbsoluteVolumeDifference.aggregate() to assert the
specific ValueError expected after reset instead of Exception, using the
metric.aggregate call in the existing test case so the failure mode is explicit.

---

Nitpick comments:
In `@monai/metrics/absolute_volume_difference.py`:
- Around line 80-121: Add complete Google-style docstrings for the AVD metric
methods in AbsoluteVolumeDifference: the __init__ method needs a short summary
plus Args entries for include_background, reduction, get_not_nans, and
ignore_empty; _compute_tensor should include a Returns section describing the
computed tensor and keep its ValueError in Raises; aggregate should add a
Returns section for the reduced output and document the ValueError it can raise
in Raises. Use the existing method names to update each docstring so every
parameter, return value, and exception is covered.

In `@tests/metrics/test_absolute_volume_difference.py`:
- Around line 24-157: Add Google-style docstrings to each test method in
TestAbsoluteVolumeDifference and TestAbsoluteVolumeDifferenceMetric so every
definition is documented as required. Update the individual test_* methods to
describe the purpose, arguments/fixtures used, expected return behavior, and any
raised exceptions in Google-style format, keeping the docstrings aligned with
the method names and assertions in this file.
🪄 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: 4cd5d4f5-95d5-4ac0-966f-e7e70094a110

📥 Commits

Reviewing files that changed from the base of the PR and between 557ffaa and 74ee065.

📒 Files selected for processing (3)
  • monai/metrics/__init__.py
  • monai/metrics/absolute_volume_difference.py
  • tests/metrics/test_absolute_volume_difference.py

Comment thread tests/metrics/test_absolute_volume_difference.py
Add one-liner docstring to __init__ and brief docstrings to all
fourteen test methods so CodeRabbit's docstring-coverage pre-merge
check passes (was 16.67%, threshold is 80%).

Signed-off-by: MDSALMANSHAMS <salmanshams67@gmail.com>
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.

Requesting new metric to calculate volume difference for small fluid volume --> Absolute Volume Difference (AVD)

1 participant