Source code for mrinufft.trajectories.projection

"""Functions to fit gradient constraints."""

import numpy as np
import numpy.linalg as nl
from numpy.typing import NDArray
from scipy.interpolate import CubicSpline


[docs] def parameterize_by_arc_length( trajectory: NDArray, order: int | None = None, eps: float = 1e-8 ) -> NDArray: """Adjust the trajectory to have a uniform distribution over the arc-length. The trajectory is parametrized according to its arc length along a cubic-interpolated path and samples are repositioned to minimize the gradients amplitude. This solution is optimal with respect to gradients but can lead to excessive slew rates, and it will change the overall density. .. warning:: - Slew rates are not minimized, and instead likely to increase - The sampling density will not be preserved Parameters ---------- trajectory: NDArray A 2D or 3D trajectory of shape (Nc, Ns, Nd), with Nc the number of shots, Ns the number of samples per shot, and Nd the number of dimensions. order: int | None The order of the norm used to compute arc length, based on the convention from `numpy.linalg.norm`. Defaults to None (Euclidean norm). eps: float Convergence threshold for stopping the iterative refinement. Defaults to 1e-8. Returns ------- NDArray: The reparameterized trajectory with the same shape as the input. """ Nc, Ns, Nd = trajectory.shape new_trajectory = np.copy(trajectory) for i in range(Nc): projection = trajectory[i] # Ignore null gradients gradients = nl.norm(np.diff(projection, axis=0), ord=order, axis=-1) non_zero_ids = np.where(~np.isclose(gradients, 0)) non_zero_time = np.linspace(0, 1, len(non_zero_ids[0])) arc_func = CubicSpline(non_zero_time, projection[non_zero_ids]) # Setup initial conditions time = np.linspace(0, 1, Ns) projection = arc_func(time) old_projection = 0 # Iterate to reduce arc length cubic approximation error while nl.norm(projection - old_projection) / nl.norm(projection) > eps: # Find mapping from arc length back to time arc_length = np.cumsum( nl.norm(np.diff(projection, axis=0), ord=order, axis=-1), axis=0 ) arc_length = np.concatenate([[0], arc_length]) arc_length = arc_length / arc_length[-1] inv_arc_func = CubicSpline(arc_length, time) # Find times such that arc length is uniform time = inv_arc_func(np.linspace(0, 1, Ns)) old_projection = np.copy(projection) projection = arc_func(time) new_trajectory[i] = projection return new_trajectory