11from __future__ import annotations
22
33import logging
4+ from dataclasses import dataclass
45from typing import List , Sequence , Tuple
56
67import numpy as np
1718 spherical_interpolator_from_mesh ,
1819 vtk_img_from_array_and_affine ,
1920)
21+ from openlifu .util .dict_conversion import DictMixin
22+ from openlifu .util .units import getunitconversion
2023
2124log = logging .getLogger ("VirtualFit" )
2225
2326ras2asl_3x3 = np .array ([[0 ,1 ,0 ],[0 ,0 ,1 ],[- 1 ,0 ,0 ]], dtype = float ) # ASL means Anterior-Superior-Left coordinates
2427asl2ras_3x3 = ras2asl_3x3 .transpose ()
2528
29+ @dataclass
30+ class VirtualFitOptions (DictMixin ):
31+ """Parameters to configure the `virtual_fit` algorithm.
32+
33+ The terms 'pitch' and 'yaw' used here refer to the following target-centric angular coordinates in patient space:
34+ pitch: The angle between the anterior axis through the target and the ray from from the target to the projection of
35+ a given point into the anterior-superior plane.
36+ yaw: The angle between the anterior-superior plane through the target and the ray from the target to a given point.
37+
38+ Another way to describe them in terms of standard spherical coordinates centered at the target in ASL (anterior-superior-left) space:
39+ pitch: The azimuthal spherical coordinate.
40+ yaw: 90 degrees minus the polar spherical coordinate.
41+ """
42+
43+ units :float = "mm"
44+ """The units of length used in the length attributes of this class"""
45+
46+ transducer_steering_center_distance :float = 50.
47+ """Distance from the transducer origin axially to the center of the steering zone in the units `units`"""
48+
49+ steering_limits :Tuple [Tuple [float ,float ],Tuple [float ,float ],Tuple [float ,float ]] = ((- 50 ,50 ),(- 50 ,50 ),(- 50 ,50 ))
50+ """Distance from the transducer origin axially to the center of the steering zone in the units `units`"""
51+
52+ pitch_range : Tuple [float ,float ] = (- 10 ,150 )
53+ """Range of pitches to include in the transducer fitting search grid, in degrees"""
54+
55+ pitch_step : float = 5
56+ """Pitch step size when forming the transducer fitting search grid, in degrees"""
57+
58+ yaw_range : Tuple [float , float ] = (- 65 , 65 )
59+ """Range of yaws to include in the transducer fitting search grid, in degrees"""
60+
61+ yaw_step : float = 5
62+ """Yaw step size when forming the transducer fitting search grid, in degrees"""
63+
64+ planefit_dyaw_extent :float = 15
65+ """Left and right extents of the point grid to be used for plane fitting along the local yaw axes,
66+ in units of `units`. The plane fitting point grid will be twice this size, since this is left
67+ and right extents. (Note that this has units of length, not angle!)"""
68+
69+ planefit_dyaw_step :float = 3
70+ """Local yaw axis step size to use when constructing plane fitting grids. In spatial units of `units`."""
71+
72+ planefit_dpitch_extent :float = 15
73+ """Left and right extents of the point grid to be used for plane fitting along the local pitch axes,
74+ in spatial units of `units`. The plane fitting point grid will be twice this size, since this is left
75+ and right extents."""
76+
77+ planefit_dpitch_step :float = 3
78+ """Local pitch axis step size to use when constructing plane fitting grids. In spatial units of `units`."""
79+
80+ def to_units (self , target_units :str ) -> VirtualFitOptions :
81+ """Do unit conversion and return a version of this VirtualFitOptions that uses
82+ `target_units` as the units for all attributes that have units of length."""
83+ conversion_factor = getunitconversion (from_unit = self .units , to_unit = target_units )
84+ return VirtualFitOptions (
85+ units = target_units ,
86+ transducer_steering_center_distance = conversion_factor * self .transducer_steering_center_distance ,
87+ steering_limits = tuple (map (tuple ,conversion_factor * np .array (self .steering_limits ))),
88+ pitch_range = self .pitch_range ,
89+ pitch_step = self .pitch_step ,
90+ yaw_range = self .yaw_range ,
91+ yaw_step = self .yaw_step ,
92+ planefit_dyaw_extent = conversion_factor * self .planefit_dyaw_extent ,
93+ planefit_dyaw_step = conversion_factor * self .planefit_dyaw_step ,
94+ planefit_dpitch_extent = conversion_factor * self .planefit_dpitch_extent ,
95+ planefit_dpitch_step = conversion_factor * self .planefit_dpitch_step ,
96+ )
97+
2698# (Currently we disable pylint E1121 because it is a temporary issue
2799# which should be resolved by #165 and #166)
28100def virtual_fit ( # pylint: disable=E1121
@@ -49,7 +121,7 @@ def virtual_fit( # pylint: disable=E1121
49121 volume_array: A 3D volume MRI
50122 volume_affine_RAS: A 4x4 affine transform that maps `volume_array` into RAS space with certain units
51123 target_RAS: A 3D point, in the coordinates and units of `volume_affine_RAS`
52- pitch_range: Range of pitches to include in the transducer fitting search grid, in degrees
124+ pitch_range:
53125 pitch_step: Pitch step size when forming the transducer fitting search grid, in degrees
54126 yaw_range: Range of yaws to include in the transducer fitting search grid, in degrees
55127 yaw_step: Yaw step size when forming the transducer fitting search grid, in degrees
@@ -68,16 +140,7 @@ def virtual_fit( # pylint: disable=E1121
68140 In spatial units of `volume_affine_RAS`.
69141
70142 Returns: A list of transducer transform candidates sorted starting from the best-scoring one. The transforms map transducer space
71- into LPS space, and they are in the same units as `volume_affine_RAS`.
72-
73- The terms 'pitch' and 'yaw' used here refer to the following target-centric angular coordinates in patient space:
74- pitch: The angle between the anterior axis through the target and the ray from from the target to the projection of
75- a given point into the anterior-superior plane.
76- yaw: The angle between the anterior-superior plane through the target and the ray from the target to a given point.
77-
78- Another way to describe them in terms of standard spherical coordinates centered at the target in ASL (anterior-superior-left) space:
79- pitch: The azimuthal spherical coordinate.
80- yaw: 90 degrees minus the polar spherical coordinate.
143+ into LPS space, and they are in the same units as the RAS space of `volume_affine_RAS`.
81144 """
82145
83146 log .info ("Computing foreground mask..." )
0 commit comments