Skip to content

Add Fréchet Radiomics Distance (FRD) to MONAI metrics (#8643)#8769

Open
AymanL wants to merge 3 commits intoProject-MONAI:devfrom
AymanL:implement_frd_metric
Open

Add Fréchet Radiomics Distance (FRD) to MONAI metrics (#8643)#8769
AymanL wants to merge 3 commits intoProject-MONAI:devfrom
AymanL:implement_frd_metric

Conversation

@AymanL
Copy link

@AymanL AymanL commented Mar 7, 2026

Fixes #8643

Description

Implements FRD as in Konz et al. (arXiv:2412.01496): same Fréchet distance formula as FID, applied to pre-extracted radiomic features.
Supports 2D/3D and optional mask conditioning via external feature extraction (e.g. PyRadiomics).

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.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

📝 Walkthrough

Walkthrough

Adds Fréchet Radiomics Distance (FRD) support. New module monai/metrics/frd.py implements a FrechetRadiomicsDistance Metric class and a get_frd_score(y_pred, y) function that compute the Fréchet distance by applying FID logic to radiomic feature vectors (expects 2D tensors (N, F); raises on ndim > 2). monai/metrics/init.py now exports FrechetRadiomicsDistance and get_frd_score. A new test file tests/metrics/test_compute_frd_metric.py validates FRD outputs, equality with FID for identical features, and input-shape error handling (tests skip if SciPy is unavailable).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.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 Title accurately and concisely describes the main change: adding Fréchet Radiomics Distance (FRD) support to MONAI metrics.
Description check ✅ Passed Description follows template with all required sections completed: includes issue reference, clear implementation details, appropriate type checkboxes marked, and testing confirmations.
Linked Issues check ✅ Passed PR implements all core objectives: FrechetRadiomicsDistance metric class [#8643], radiomic feature-based distance computation [#8643], 2D/3D support [#8643], and Fréchet distance calculation [#8643].
Out of Scope Changes check ✅ Passed All changes directly support FRD implementation: new frd.py module, metric exports, and test coverage. No unrelated modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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

52-65: Add Raises section to docstring.

get_fid_score raises ValueError when inputs have >2 dimensions. Document this for callers.

📝 Suggested docstring addition
     Args:
         y_pred: Feature vectors for the first distribution, shape (N, F).
         y: Feature vectors for the second distribution, shape (N, F).

     Returns:
         Scalar tensor containing the Fréchet Radiomics Distance.
+
+    Raises:
+        ValueError: If inputs have more than 2 dimensions.
     """
     return get_fid_score(y_pred, y)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/metrics/frd.py` around lines 52 - 65, Add a Raises section to the
get_frd_score docstring documenting that get_fid_score may raise a ValueError
when inputs have more than 2 dimensions; update the get_frd_score docstring (the
function named get_frd_score calling get_fid_score) to include a "Raises" entry
that explicitly states ValueError is raised for input tensors with rank > 2 so
callers are aware of this precondition.
tests/metrics/test_compute_frd_metric.py (1)

41-43: Test name and setup are slightly misleading.

The ValueError is raised because ndimension() > 2, not due to spatial dimension mismatch (144 vs 145). Consider renaming to test_rejects_high_dimensional_input and using identical tensors.

♻️ Suggested clarification
-    def test_input_dimensions(self):
+    def test_rejects_high_dimensional_input(self):
+        """Inputs with more than 2 dimensions should raise ValueError."""
         with self.assertRaises(ValueError):
-            FrechetRadiomicsDistance()(torch.ones([3, 3, 144, 144]), torch.ones([3, 3, 145, 145]))
+            FrechetRadiomicsDistance()(torch.ones([3, 3, 144, 144]), torch.ones([3, 3, 144, 144]))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/metrics/test_compute_frd_metric.py` around lines 41 - 43, Rename the
test to reflect the actual rejection reason and make the input tensors identical
so the failure is due to ndimension()>2: change the test name from
test_input_dimensions to test_rejects_high_dimensional_input and call
FrechetRadiomicsDistance() with two tensors of the same shape (e.g.,
torch.ones([3, 3, 144, 144]) for both) so the ValueError arises from the high
number of dimensions rather than mismatched spatial sizes; update any test
docstring or comment accordingly to reference FrechetRadiomicsDistance and
ndimension.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@monai/metrics/frd.py`:
- Around line 52-65: Add a Raises section to the get_frd_score docstring
documenting that get_fid_score may raise a ValueError when inputs have more than
2 dimensions; update the get_frd_score docstring (the function named
get_frd_score calling get_fid_score) to include a "Raises" entry that explicitly
states ValueError is raised for input tensors with rank > 2 so callers are aware
of this precondition.

In `@tests/metrics/test_compute_frd_metric.py`:
- Around line 41-43: Rename the test to reflect the actual rejection reason and
make the input tensors identical so the failure is due to ndimension()>2: change
the test name from test_input_dimensions to test_rejects_high_dimensional_input
and call FrechetRadiomicsDistance() with two tensors of the same shape (e.g.,
torch.ones([3, 3, 144, 144]) for both) so the ValueError arises from the high
number of dimensions rather than mismatched spatial sizes; update any test
docstring or comment accordingly to reference FrechetRadiomicsDistance and
ndimension.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e025c2d5-a892-4a84-a2a7-8546a1f5e587

📥 Commits

Reviewing files that changed from the base of the PR and between daaedaa and be31e64.

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

Signed-off-by: AymanL <40838419+AymanL@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (1)
monai/metrics/frd.py (1)

24-36: Tighten the API docstring.

This callable only accepts pre-extracted (N, F) feature tensors. Saying it “works for 2D and 3D images” and supports mask conditioning reads like image or mask tensors are accepted here, which this API does not expose. Please state that 2D/3D images and masks are handled during external feature extraction, not by this metric itself.

As per coding guidelines, "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 the current code and only fix it if needed.

In `@monai/metrics/frd.py` around lines 24 - 36, Update the frd module/class
docstring to explicitly state the callable accepts only pre-extracted feature
tensors of shape (N, F) and does not accept raw 2D/3D images or masks; clarify
that any handling of 2D/3D images and mask-based conditioning must be performed
during external feature extraction (e.g., via PyRadiomics) prior to calling this
metric. Tighten wording to remove implications that images or masks are accepted
directly, and add Google-style docstring sections for Args (describe the
features tensor shape and dtype), Returns (what the function returns), and
Raises (any errors for invalid shapes or inputs), referencing the callable name
in the docstring so reviewers can locate it easily.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@monai/metrics/frd.py`:
- Around line 52-69: Add a symmetric input-dimension validation in
get_frd_score: check y_pred.ndimension() > 2 and raise the same ValueError used
by monai.metrics.fid.get_fid_score when inputs have >2 dims, before calling
get_fid_score(y_pred, y); also add a unit test that passes a 3D y_pred and a
valid 2D y to exercise the error path. This ensures get_frd_score mirrors
get_fid_score's shape guard and the y_pred-only high-dimensional case is
covered.

---

Nitpick comments:
In `@monai/metrics/frd.py`:
- Around line 24-36: Update the frd module/class docstring to explicitly state
the callable accepts only pre-extracted feature tensors of shape (N, F) and does
not accept raw 2D/3D images or masks; clarify that any handling of 2D/3D images
and mask-based conditioning must be performed during external feature extraction
(e.g., via PyRadiomics) prior to calling this metric. Tighten wording to remove
implications that images or masks are accepted directly, and add Google-style
docstring sections for Args (describe the features tensor shape and dtype),
Returns (what the function returns), and Raises (any errors for invalid shapes
or inputs), referencing the callable name in the docstring so reviewers can
locate it easily.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ab4187ab-ad3b-4657-921a-6464fefb2d3c

📥 Commits

Reviewing files that changed from the base of the PR and between be31e64 and e56a919.

📒 Files selected for processing (2)
  • monai/metrics/frd.py
  • tests/metrics/test_compute_frd_metric.py

…ub.com>

I, AymanL <40838419+AymanL@users.noreply.github.com>, hereby add my Signed-off-by to this commit: be31e64

Signed-off-by: AymanL <40838419+AymanL@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (1)
monai/metrics/frd.py (1)

22-49: Clarify the callable API in the docstrings.

FrechetRadiomicsDistance() takes no constructor args, but the class docstring has an Args:/Returns: block for __call__, and __call__ itself is undocumented. Move the parameter/exception docs onto __call__ (or keep them only on get_frd_score) and phrase the 2D/3D image + mask support as upstream feature-extraction context, not direct inputs to this class.

As per coding guidelines, "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 all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@monai/metrics/frd.py`:
- Around line 52-71: The get_frd_score function currently only rejects tensors
with ndimension() > 2 but should enforce a strict (N, F) contract and reject
non-2D inputs and single-sample batches before delegating to get_fid_score;
modify get_frd_score to validate that both y_pred.ndimension() == 2 and
y.ndimension() == 2 and that y_pred.size(0) >= 2 and y.size(0) >= 2, raising a
clear ValueError if any check fails (mentioning expected shape and minimum batch
size), then keep calling get_fid_score when checks pass, and add unit tests that
assert ValueError is raised for 0D/1D inputs and for N < 2 for both y_pred and
y.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a736e1b6-45fd-4ce7-81bf-045e2e26f5f1

📥 Commits

Reviewing files that changed from the base of the PR and between e56a919 and 12ee177.

📒 Files selected for processing (1)
  • monai/metrics/frd.py

Comment on lines +52 to +71
def get_frd_score(y_pred: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
"""Computes the FRD score from two batches of radiomic feature vectors.

The implementation reuses the same Fréchet distance as FID; only the
semantics (radiomic features vs. deep features) differ.

Args:
y_pred: Feature vectors for the first distribution, shape (N, F).
y: Feature vectors for the second distribution, shape (N, F).

Returns:
Scalar tensor containing the Fréchet Radiomics Distance.

Raises:
ValueError: When either tensor has more than 2 dimensions. Inputs must have
shape (number of samples, number of features).
"""
if y_pred.ndimension() > 2 or y.ndimension() > 2:
raise ValueError("Inputs should have (number images, number of features) shape.")
return get_fid_score(y_pred, y)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Enforce the full (N, F) contract here.

ndimension() > 2 still lets 0D/1D tensors and N < 2 batches fall through into get_fid_score, where covariance estimation is not well-defined and failures get opaque. Please reject non-2D inputs and single-sample batches here, then add tests for both cases.

🩹 Proposed fix
 def get_frd_score(y_pred: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
@@
-    if y_pred.ndimension() > 2 or y.ndimension() > 2:
+    if y_pred.ndimension() != 2 or y.ndimension() != 2:
         raise ValueError("Inputs should have (number images, number of features) shape.")
+    if y_pred.shape[0] < 2 or y.shape[0] < 2:
+        raise ValueError("At least 2 samples are required to estimate covariance.")
     return get_fid_score(y_pred, y)
As per coding guidelines, "Examine code for logical error or inconsistencies, and suggest what may be changed to addressed these. Ensure new or modified definitions will be covered by existing or new unit tests."
🧰 Tools
🪛 Ruff (0.15.4)

[warning] 70-70: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/metrics/frd.py` around lines 52 - 71, The get_frd_score function
currently only rejects tensors with ndimension() > 2 but should enforce a strict
(N, F) contract and reject non-2D inputs and single-sample batches before
delegating to get_fid_score; modify get_frd_score to validate that both
y_pred.ndimension() == 2 and y.ndimension() == 2 and that y_pred.size(0) >= 2
and y.size(0) >= 2, raising a clear ValueError if any check fails (mentioning
expected shape and minimum batch size), then keep calling get_fid_score when
checks pass, and add unit tests that assert ValueError is raised for 0D/1D
inputs and for N < 2 for both y_pred and y.

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.

Add Fréchet Radiomics Distance (FRD-Score) to MONAI Metrics

1 participant