bartorch.linop.CartesianSense#
- bartorch.linop.CartesianSense(sensitivities, image_shape, pattern=None, *, basis=None, toeplitz=True, **kwargs)#
Coils, a Fourier transform, and the samples that were taken.
What
picsencodes on a grid: sensitivities, the centred unitary transform, and a pattern that keeps the samples the sequence acquired.The transform is
NoncartesianSenseover BART’s own FFT instead of a NUFFT – the same operator, coil batching and all – withSamplingchained onto it. Without a pattern and without a basis it is that operator, returned unchanged, because there is nothing to add.With a basis it is what
grecon/model.cchains: the encoding over the coefficient images, the basis contracting them into frames, the pattern keeping the samples. The image is then(coeffs, 1, 1, 1, *spatial)and the samples(1, frames, 1, coils, *spatial).- Parameters:
sensitivities (tensor) – Coil sensitivities,
(coils, *spatial), or their k-space kernels withkernels=True.image_shape (tuple of int) – Coil-image shape,
(coils, *spatial). A two-dimensional problem may leave BART’s third spatial axis out.pattern (tensor, optional) – Ones where a sample was taken and zeros where it was not, broadcast over the axes it has one of – so
(1, 1, y, 1)undersamples a phase encode across every coil and slice. With a basis it is read against the sample shape, so its second axis is the frames.basis (tensor, optional) – Temporal subspace basis
(coeffs, frames, 1, ...), contracting the image’s coefficients into the frames that were acquired. This is T2 shuffling and whatpics -Btakes.toeplitz (bool) – With a basis, apply the normal as one coefficient-by-coefficient kernel rather than as the two applications. See the notes.
**kwargs – Passed to
NoncartesianSense:coil_batch,kernels,modulated,deviceand the rest.modulatedasks for BART’s own sample convention – the onepicsworks in – instead of the centred one, and does so whatever the slab.
Notes
Without a basis the normal is the two applications: a mask does not commute with the transform, so there is no shortcut, and on a grid there is nothing to gain from one anyway.
With a basis there is a great deal to gain, and it is the same shortcut the non-Cartesian encoding calls Toeplitz – the product in closed form rather than the two applications:
(A^H A x)[k'] = sum_k ( sum_t P[t] conj(B[k',t]) B[k,t] ) x[k]
The sum over the frames is done once, when the operator is built, so an iteration never makes the frames at all: sixty-four echoes over four coefficients is sixteen times less k-space in the middle of every step. Nothing is convolved and no grid is doubled – the pattern already lies on the grid the transform is circular over – which is the one way this differs from the non-Cartesian normal. It costs a kernel of
coeffs x coeffsover the axes the pattern varies on, so a pattern that is flat along the readout keeps it flat too.Examples
>>> A = CartesianSense(maps, (coils, y, x), pattern=mask) >>> x = bartorch.optim.CG(maxiter=30)(kspace, A)
>>> A = CartesianSense(maps, (coils, y, x), pattern=mask, basis=phi) >>> A.ishape, A.oshape ((4, 1, 1, 1, 1, y, x), (1, 64, 1, coils, 1, y, x))