Skip to content

Commit

Permalink
change mlc position reporting to be from the CAX instead of image edge.
Browse files Browse the repository at this point in the history
  • Loading branch information
jrkerns committed Jul 29, 2024
1 parent f65e73e commit f6c439b
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 6 deletions.
7 changes: 7 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ Legend
v 3.26.0
--------

Picket Fence
^^^^^^^^^^^^

* :bdg-danger:`Change` The ``mlc_positions_by_leaf`` attribute from the ``results_data`` call has been changed
to be **relative to the CAX**. Previously, the positions were relative to the left/top of the image. This attribute
was requested by French sites and this change was also requested by them.

Gamma
^^^^^

Expand Down
19 changes: 15 additions & 4 deletions pylinac/picketfence.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

from . import Normalization
from .core import image, pdf
from .core.geometry import Line, Point, Rectangle
from .core.geometry import Line, Point, PointSerialized, Rectangle
from .core.io import get_url, retrieve_demo_file
from .core.profile import FWXMProfilePhysical, MultiProfile
from .core.utilities import (
Expand Down Expand Up @@ -187,11 +187,13 @@ class PFResult(ResultBase):
description="The widths of the pickets in mm."
)
mlc_positions_by_leaf: dict[str, list[float]] = Field(
description="A dictionary where the key is the leaf number and the value is a list of positions in mm **from the left or top of the image**."
description="A dictionary where the key is the leaf number and the value is a list of positions in mm **from the CAX**. The distance is from the x-value (or y-value for left-right orientation) of the CAX. "
"Rotation of the MLCs would affect these distances."
)
mlc_errors_by_leaf: dict[str, list[float]] = Field(
description="A dictionary where the key is the leaf number and the value is a list of errors in mm."
)
cax: PointSerialized = Field(description="The position of the CAX in pixels.")


class PFDicomImage(image.LinacDicomImage):
Expand Down Expand Up @@ -248,7 +250,7 @@ def center(self) -> Point:
cax = super().center + cax_shift
# invert the y-axis for plotting purposes/consistency
cax.y = 2 * (self.shape[0] // 2) - cax.y
return cax
return Point(cax.x, cax.y)
else:
return super().center

Expand Down Expand Up @@ -1139,11 +1141,19 @@ def _generate_results_data(self) -> PFResult:
}
errors_by_leaf = {}
positions_by_leaf = {}
cax_position = (
self.image.center.x
if self.orientation == Orientation.UP_DOWN
else self.image.center.y
)
cax_physical_position = cax_position / self.image.dpmm
for _, group_iter in groupby(self.mlc_meas, key=lambda m: m.leaf_num):
leaf_items = list(group_iter) # group_iter is a generator
leaf_names = leaf_items[0].full_leaf_nums
for idx, leaf_name in enumerate(leaf_names):
pos_vals = [m.position_mm[idx] for m in leaf_items]
pos_vals = [
m.position_mm[idx] - cax_physical_position for m in leaf_items
]
error_vals = [m.error[idx] for m in leaf_items]
positions_by_leaf[str(leaf_name)] = pos_vals
errors_by_leaf[str(leaf_name)] = error_vals
Expand All @@ -1170,6 +1180,7 @@ def _generate_results_data(self) -> PFResult:
picket_widths=picket_widths,
mlc_positions_by_leaf=positions_by_leaf,
mlc_errors_by_leaf=errors_by_leaf,
cax=self.image.center,
)

def publish_pdf(
Expand Down
7 changes: 5 additions & 2 deletions tests_basic/test_picketfence.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def test_results_data(self):
) # 36 leaf pairs in the image
# constancy check
self.assertAlmostEqual(
statistics.mean(data.mlc_positions_by_leaf["17"]), 204.63, delta=0.1
statistics.mean(data.mlc_positions_by_leaf["17"]), 7.91, delta=0.1
)
# check max error matches a combination of the leaf values
self.assertEqual(
Expand All @@ -168,7 +168,7 @@ def test_results_data(self):
data_dict = self.pf.results_data(as_dict=True)
self.assertIsInstance(data_dict, dict)
self.assertIn("pylinac_version", data_dict)
self.assertEqual(len(data_dict), 18)
self.assertEqual(len(data_dict), 19)

data_str = self.pf.results_data(as_json=True)
self.assertIsInstance(data_str, str)
Expand Down Expand Up @@ -347,6 +347,9 @@ def test_bb_pf_combo(self):
pf.analyze(separate_leaves=False)
results = pf.results_data()
self.assertAlmostEqual(results.max_error_mm, 0.0, delta=0.005)
self.assertAlmostEqual(results.cax.x, 636.5, delta=0.1)
# bb is 2mm off in bb setup image above
self.assertAlmostEqual(results.mlc_positions_by_leaf["17"][0], -102, delta=0.1)


class LoadingFromMultiple(TestCase):
Expand Down

0 comments on commit f6c439b

Please sign in to comment.