From 71fb8b2b36c0e756580f892e751d9053f459059f Mon Sep 17 00:00:00 2001 From: James Kerns Date: Wed, 13 Nov 2024 15:35:02 -0600 Subject: [PATCH] don't enforce gamma x-domain restrictions. --- docs/source/changelog.rst | 4 ++++ pylinac/core/array_utils.py | 2 +- pylinac/core/gamma.py | 9 -------- tests_basic/core/test_gamma.py | 39 +++++++++++++++++----------------- 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 938a9c11..2f8473e9 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -62,6 +62,10 @@ Gamma were not in ascending order. Coordinates can now be in any order. * :bdg-warning:`Fixed` The geometric gamma function now validates that the x-coordinates are monotonically increasing or decreasing and will error out if not. +* :bdg-warning:`Fixed` The geometric gamma previously enforced that the reference x-domain contained the entire + evaluation x-domain. I.e. your reference scan had to be larger than your evaluation scan. This is no longer the case. + An evaluation profile will be calculated at all points above the threshold. If the reference scan is too small, the gamma + value will be large. Core ^^^^ diff --git a/pylinac/core/array_utils.py b/pylinac/core/array_utils.py index 07cffaf2..2122bdc4 100644 --- a/pylinac/core/array_utils.py +++ b/pylinac/core/array_utils.py @@ -402,7 +402,7 @@ def fill_middle_zeros(array: np.ndarray, cutoff_px: int = 0) -> np.ndarray: return filled_arr -def _rt_image_position(array: np.ndarray, dpmm: float) -> list[float, float]: +def _rt_image_position(array: np.ndarray, dpmm: float) -> list[float]: """Calculate the RT Image Position of the array.""" rows, cols = array.shape pixel_size_mm = 1.0 / dpmm diff --git a/pylinac/core/gamma.py b/pylinac/core/gamma.py index c1e6511b..5d190e5e 100644 --- a/pylinac/core/gamma.py +++ b/pylinac/core/gamma.py @@ -172,15 +172,6 @@ def gamma_geometric( raise ValueError( f"Evaluation and evaluation_x_values must be the same length. Got evaluation: {len(evaluation)} and evaluation_x_values: {len(evaluation_coordinates)}" ) - # we add some padding on the check because resampling SingleProfiles - # can add ~1/2 pixel on each side to retain the same physical size - # when upsampling. - if min(reference_coordinates) - 1 > min(evaluation_coordinates) or max( - reference_coordinates - ) + 1 < max(evaluation_coordinates): - raise ValueError( - "The evaluation x-values must be within the range of the reference x-values" - ) # normalize the dose threshold by the DTA threshold = float(dose_threshold) / float(dose_to_agreement) # convert dose to normalized distance of dose to agreement. I.e. D/delta(D) in Figure 1. diff --git a/tests_basic/core/test_gamma.py b/tests_basic/core/test_gamma.py index a1e394eb..75643237 100644 --- a/tests_basic/core/test_gamma.py +++ b/tests_basic/core/test_gamma.py @@ -292,26 +292,6 @@ def test_eval_x_values_not_same_length_as_eval(self): reference=ref, evaluation=eval, evaluation_coordinates=np.arange(6) ) - def test_min_eval_x_lower_than_min_ref_x(self): - ref = eval = np.ones(5) - with self.assertRaises(ValueError): - gamma_geometric( - reference=ref, - evaluation=eval, - evaluation_coordinates=(np.arange(5) - 2), - reference_coordinates=np.arange(5), - ) - - def test_max_eval_x_higher_than_max_ref_x(self): - ref = eval = np.ones(5) - with self.assertRaises(ValueError): - gamma_geometric( - reference=ref, - evaluation=eval, - evaluation_coordinates=(np.arange(5) + 2), - reference_coordinates=np.arange(5), - ) - def test_same_profile_is_0_gamma(self): ref = eval = np.ones(5) gamma = gamma_geometric(reference=ref, evaluation=eval) @@ -504,6 +484,25 @@ def test_very_far_spacings(self): ) self.assertEqual(np.nanmax(g), 0) + def test_reference_x_domain_smaller_than_eval(self): + """Even if the reference x-domain is too small we can still + evaluate the gamma.""" + vals = [0, 0, 0, 0, 1, 2, 5, 8, 10, 10, 10, 10, 10, 8, 5, 2, 1, 0, 0, 0, 0] + x_vals = np.arange(len(vals)) + ref_vals = vals[3:-3] # we short-change the reference in low-dose areas + x_ref_vals = x_vals[3:-3] + gamma = gamma_geometric( + reference=np.array(ref_vals), + reference_coordinates=np.array(x_ref_vals), + evaluation=np.array(vals), + evaluation_coordinates=np.array(x_vals), + distance_to_agreement=1, + gamma_cap_value=2, + dose_threshold=0, # 0 threshold is important to ensure we calculate the gamma at the edges + ) + self.assertEqual(np.nanmax(gamma), 2) + self.assertAlmostEqual(np.nanmean(gamma), 0.476, places=2) + @parameterized.expand( [ (np.arange(5), np.arange(5), [np.nan, 0, 0, 0, 0]),