"""Sub-package containing Fast Fourier transform routines.
FFT routines include:
* centered n-dimensional FFT and iFFT;
* n-dimensional sparse uniform FFT/iFFT with embedded low rank subspace projection;
* n-dimensional NUFFT with embedded low rank subspace projection.
"""
from . import fft as _fft
from . import sparse_fft as _sparse_fft
from . import nufft as _nufft
from .fft import * # noqa
from .nufft import * # noqa
from .sparse_fft import * # noqa
__all__ = _fft.__all__
__all__.extend(
[
"plan_toeplitz_fft",
"apply_sparse_fft_selfadj",
"plan_toeplitz_nufft",
"apply_nufft_selfadj",
]
)
__all__.extend(["sparse_fft", "sparse_ifft", "nufft", "nufft_adj"])
[docs]def sparse_fft(
image,
indexes,
basis_adjoint=None,
device="cpu",
threadsperblock=128,
):
"""
N-dimensional sparse Fast Fourier Transform.
Parameters
----------
image : torch.Tensor
Input image of shape ``(..., ncontrasts, ny, nx)`` (2D)
or ``(..., ncontrasts, nz, ny, nx)`` (3D).
indexes : torch.Tensor
Sampled k-space points indexes of shape ``(ncontrasts, nviews, nsamples, ndims)``.
basis_adjoint : torch.Tensor, optional
Adjoint low rank subspace projection operator
of shape ``(ncoeffs, ncontrasts)``; can be ``None``. The default is ``None``.
device : str, optional
Computational device (``cpu`` or ``cuda:n``, with ``n=0, 1,...nGPUs``).
The default is ``cpu``.
threadsperblock : int
CUDA blocks size (for GPU only). The default is ``128``.
Returns
-------
kspace : torch.Tensor
Output sparse kspace of shape ``(..., ncontrasts, nviews, nsamples)``.
Notes
-----
Sampled points indexes axes ordering is assumed to be ``(x, y)`` for 2D signals
and ``(x, y, z)`` for 3D. Conversely, axes ordering for grid shape is assumed to be ``(z, y, x)``.
Indexes tensor shape is ``(ncontrasts, nviews, nsamples, ndim)``. If there are less dimensions
(e.g., single-shot or single contrast trajectory), assume singleton for the missing ones:
* ``indexes.shape = (nsamples, ndim) -> (1, 1, nsamples, ndim)``
* ``indexes.shape = (nviews, nsamples, ndim) -> (1, nviews, nsamples, ndim)``
"""
# get number of dimensions
ndim = indexes.shape[-1]
# get shape if not provided
shape = image.shape[-ndim:]
# plan interpolator
sampling_mask = _sparse_fft.prepare_sampling(indexes, shape, device)
# perform actual interpolation
return _sparse_fft.apply_sparse_fft(
image, sampling_mask, basis_adjoint, threadsperblock=threadsperblock
)
[docs]def sparse_ifft(
kspace,
indexes,
shape,
basis=None,
device="cpu",
threadsperblock=128,
):
"""
N-dimensional inverse sparse Fast Fourier Transform.
Parameters
----------
kspace : torch.Tensor
Input sparse kspace of shape ``(..., ncontrasts, nviews, nsamples)``.
indexes : torch.Tensor
Sampled k-space points indexes of shape ``(ncontrasts, nviews, nsamples, ndims)``.
shape : int | Iterable[int]
Cartesian grid size of shape ``(ndim,)``.
If scalar, isotropic matrix is assumed.
basis : torch.Tensor, optional
Low rank subspace projection operator
of shape ``(ncontrasts, ncoeffs)``; can be ``None``. The default is ``None``.
device : str, optional
Computational device (``cpu`` or ``cuda:n``, with ``n=0, 1,...nGPUs``).
The default is ``cpu``.
threadsperblock : int
CUDA blocks size (for GPU only). The default is ``128``.
Returns
-------
image : torch.Tensor
Output image of shape ``(..., ncontrasts, ny, nx)`` (2D)
or ``(..., ncontrasts, nz, ny, nx)`` (3D).
Notes
-----
Sampled points indexes axes ordering is assumed to be ``(x, y)`` for 2D signals
and ``(x, y, z)`` for 3D. Conversely, axes ordering for grid shape is assumed to be ``(z, y, x)``.
Sampled points indexes axes ordering is assumed to be ``(x, y)`` for 2D signals
(e.g., single-shot or single contrast trajectory), assume singleton for the missing ones:
* ``indexes.shape = (nsamples, ndim) -> (1, 1, nsamples, ndim)``
* ``indexes.shape = (nviews, nsamples, ndim) -> (1, nviews, nsamples, ndim)``
"""
# plan interpolator
sampling_mask = _sparse_fft.prepare_sampling(indexes, shape, device)
# perform actual interpolation
return _sparse_fft.apply_sparse_ifft(
kspace, sampling_mask, basis, threadsperblock=threadsperblock
)
[docs]def nufft(
image,
coord,
shape=None,
basis_adjoint=None,
device="cpu",
threadsperblock=128,
width=4,
oversamp=1.25,
):
"""
N-dimensional Non-Uniform Fast Fourier Transform.
Parameters
----------
image : torch.Tensor
Input image of shape ``(..., ncontrasts, ny, nx)`` (2D)
or ``(..., ncontrasts, nz, ny, nx)`` (3D).
coord : torch.Tensor
K-space coordinates of shape ``(ncontrasts, nviews, nsamples, ndims)``.
Coordinates must be normalized between ``(-0.5 * shape, 0.5 * shape)``.
shape : int | Iterable[int], optional
Cartesian grid size of shape ``(ndim,)``.
If scalar, isotropic matrix is assumed.
The default is ``None`` (grid size equals to input data size, i.e. ``osf = 1``).
basis_adjoint : torch.Tensor, optional
Adjoint low rank subspace projection operator
of shape ``(ncoeffs, ncontrasts)``; can be ``None``. The default is ``None``.
device : str, optional
Computational device (``cpu`` or ``cuda:n``, with ``n=0, 1,...nGPUs``).
The default is ``cpu``.
threadsperblock : int
CUDA blocks size (for GPU only). The default is ``128``.
width : int | Iterable[int], optional
Interpolation kernel full-width of shape ``(ndim,)``.
If scalar, isotropic kernel is assumed.
The default is ``2``.
oversamp : float | Iterable[float], optional
Grid oversampling factor of shape ``(ndim,)``.
If scalar, isotropic oversampling is assumed.
The default is ``1.125``.
Returns
-------
kspace : torch.Tensor
Output Non-Cartesian kspace of shape ``(..., ncontrasts, nviews, nsamples)``.
Notes
-----
Non-uniform coordinates axes ordering is assumed to be ``(x, y)`` for 2D signals
and ``(x, y, z)`` for 3D. Conversely, axes ordering for grid shape, kernel width
and Kaiser Bessel parameters are assumed to be ``(z, y, x)``.
Coordinates tensor shape is ``(ncontrasts, nviews, nsamples, ndim)``. If there are less dimensions
(e.g., single-shot or single contrast trajectory), assume singleton for the missing ones:
* ``coord.shape = (nsamples, ndim) -> (1, 1, nsamples, ndim)``
* ``coord.shape = (nviews, nsamples, ndim) -> (1, nviews, nsamples, ndim)``
"""
# get number of dimensions
ndim = coord.shape[-1]
# get shape if not provided
if shape is None:
shape = image.shape[-ndim:]
# plan interpolator
nufft_plan = _nufft.plan_nufft(coord, shape, width, oversamp, device)
# perform actual interpolation
return _nufft.apply_nufft(
image, nufft_plan, basis_adjoint, threadsperblock=threadsperblock
)
[docs]def nufft_adj(
kspace,
coord,
shape,
basis=None,
device="cpu",
threadsperblock=128,
width=4,
oversamp=1.25,
):
"""
N-dimensional adjoint Non-Uniform Fast Fourier Transform.
Parameters
----------
kspace : torch.Tensor
Input Non-Cartesian kspace of shape ``(..., ncontrasts, nviews, nsamples)``.
coord : torch.Tensor
K-space coordinates of shape ``(ncontrasts, nviews, nsamples, ndims)``.
Coordinates must be normalized between ``(-0.5 * shape, 0.5 * shape)``.
shape : int | Iterable[int]
Cartesian grid size of shape ``(ndim,)``.
If scalar, isotropic matrix is assumed.
basis : torch.Tensor, optional
Low rank subspace projection operator
of shape ``(ncontrasts, ncoeffs)``; can be ``None``. The default is ``None``.
device : str, optional
Computational device (``cpu`` or ``cuda:n``, with ``n=0, 1,...nGPUs``).
The default is ``cpu``.
threadsperblock : int
CUDA blocks size (for GPU only). The default is ``128``.
width : int | Iterable[int], optional
Interpolation kernel full-width of shape ``(ndim,)``.
If scalar, isotropic kernel is assumed.
The default is ``2``.
oversamp : float | Iterable[float], optional
Grid oversampling factor of shape ``(ndim,)``.
If scalar, isotropic oversampling is assumed.
The default is ``1.125``.
Returns
-------
image : torch.Tensor
Output image of shape ``(..., ncontrasts, ny, nx)`` (2D)
or ``(..., ncontrasts, nz, ny, nx)`` (3D).
Notes
-----
Non-uniform coordinates axes ordering is assumed to be ``(x, y)`` for 2D signals
and ``(x, y, z)`` for 3D. Conversely, axes ordering for grid shape, kernel width
and Kaiser Bessel parameters are assumed to be ``(z, y, x)``.
Coordinates tensor shape is ``(ncontrasts, nviews, nsamples, ndim)``. If there are less dimensions
(e.g., single-shot or single contrast trajectory), assume singleton for the missing ones:
* ``coord.shape = (nsamples, ndim) -> (1, 1, nsamples, ndim)``
* ``coord.shape = (nviews, nsamples, ndim) -> (1, nviews, nsamples, ndim)``
"""
# plan interpolator
nufft_plan = _nufft.plan_nufft(coord, shape, width, oversamp, device)
# perform actual interpolation
return _nufft.apply_nufft_adj(
kspace, nufft_plan, basis, threadsperblock=threadsperblock
)