openlifu.seg.skinseg.spherical_interpolator_from_mesh

openlifu.seg.skinseg.spherical_interpolator_from_mesh(surface_mesh: vtk.vtkPolyData, origin: Tuple[float, float, float] = (0.0, 0.0, 0.0), xyz_direction_columns: np.ndarray | None = None, dist_tolerance: float = 0.0001) Callable[[float, float], float][source]

Create a spherical interpolator from a vtkPolyData.

Here a “spherical interpolator” is a function that maps angles from a spherical coordinate system to r values (radial spherical coordinate values) by interpolating over a set of known values. It’s essentially a “spherical plotter.”

Parameters:
  • surface_mesh – The mesh containing the points to be interpolated over

  • origin – The origin of the spherical coordinate system

  • xyz_direction_columns – A matrix of shape (3,3) the columns of which are unit vectors that describe the cartesian x,y,z axis directions on which to base the spherical coordinate system. For example the spherical azimuthal angle is the polar angle of the projection of the point into the x-y-plane, etc. See the documentation on spherical_to_cartesian and cartesian_to_spherical for a complete description of how the spherical angles relate to the x, y, and z axes. If not provided, the xyz_direction_columns will be an identity matrix, which means that the coordinates in which surface_mesh is given will directly be interpreted as the x,y,z upon which a spherical coordinate system will be based.

  • dist_tolerance – A vertex of the surface_mesh will only be included if it is the furthest point from the origin that is on the mesh along the ray emanating from the origin and passing through the vertex. The dist_tolerance is the threshold for determining whether an intersection of the ray with the mesh counts as being a distinct further out point from the vertex.

Returns:

A spherical interpolator, which is a callable that maps (theta,phi) pairs of spherical coordinates (phi being azimuthal) to r values (radial spherical coordinate values). The angles are in radians.

Summary of the algorithm:
  • Transform the input mesh based on the desired origin and orientation of the spherical coordinate system.

  • We will gather some points into a set $S$. For each point $P$ on the mesh consider the ray $vec{OP}$ from the origin through $P$ and look at all the intersections of this ray $vec{OP}$ with the mesh. If none of those intersections are further out from the origin than $P$ is, then we put $P$ into our set $S$.

  • Using the spherical coordinates of the points in $S$, build a scipy.interpolate.LinearNDInterpolator that interpolates spherical $r$ values from the spherical $(theta,phi)$ values.

  • Problem: All the gathered $(theta,phi)$ values are likely strictly inside the square $[0,pi]times[-pi,pi]$, and LinearNDInterpolator does not _extrapolate_, and so angles close to the “seams” of the spherical coordinate system (the boundaries of that square) generate NaNs through the interpolator. The solution used here is to first clone the gathered points with appropriate angular shifts so as to cover those seams, and then give that larger set of points to the interpolator.

  • Return the interpolator.