Skip to content

Accelerated MRI with SENSE

julia script jupyter notebook launch binder

Parallel imaging reduces scan time by acquiring fewer phase-encoding lines. The missing data make a conventional image fold over, but an array of receive coils provides additional spatial information that can separate the overlapping pixels. We will compare fully sampled and three-fold accelerated EPI acquisitions of the same brain_phantom2D.

Undersample k-space

The fully sampled EPI trajectory visits every line. The accelerated sequence visits every third line, so its acceleration factor is  . Both sequences reach the centre of k-space at the same echo time. The plot_kspace view below makes the missing lines explicit.

See the aliasing with sum-of-squares

Attach a 16-channel BirdcageCoilSens receiver. The sequence and phantom do not change; each channel simply measures the same transverse magnetization through a different complex spatial sensitivity.

julia
birdcage = BirdcageCoilSens(; ncoils=16);
system = Scanner(; receiver=birdcage);
raw_coils_full = simulate(obj, full_seq, system; verbose=false);
raw_coils_acc = simulate(obj, acc_seq, system; verbose=false);

Sum-of-squares (SoS) combines the reconstructed channel magnitudes. It produces a robust fully sampled image, but it cannot separate pixels that overlap after undersampling:

julia
sos_full = sqrt.(sum(abs2, coil_images_full; dims=5));
sos_acc = sqrt.(sum(abs2, coil_images_acc; dims=5));

Inspect the coil sensitivities

SENSE needs one complex sensitivity per reconstruction pixel and receive channel. Use get_sens to evaluate the receiver on the reconstruction grid—not on the phantom's irregular spin positions—and get_n_coils to obtain its channel count:

julia
x_axis = range(-FOV / 2, FOV / 2; length=recon_size[1])
y_axis = range(-FOV / 2, FOV / 2; length=recon_size[2])
x = vec([x for x in x_axis, _ in y_axis])
y = vec([y for _ in x_axis, y in y_axis])
z = zeros(length(x))
sensitivity_maps = reshape(
    get_sens(birdcage, x, y, z),
    recon_size..., 1, get_n_coils(birdcage),
);

BirdcageCoilSens evaluates an analytic model. Measured maps can instead be supplied with ArbitraryCoilSens; its get_sens method interpolates those samples onto the same reconstruction grid.

Four channels distributed around the array illustrate why it contains spatial information. The top row shows sensitivity magnitude and the bottom row shows the corresponding fully sampled coil image.

Recover the accelerated image with SENSE

SENSE instead combines the unknown image with the complex sensitivity maps before sampling each coil's k-space trajectory. The inverse problem is solved jointly across all 16 channels:

julia
sense_params = Dict{Symbol,Any}(
    :reco => "multiCoil",
    :reconSize => recon_size,
    :senseMaps => sensitivity_maps,
    :iterations => 20,
    :densityWeighting => false,
    :toeplitz => false,
)
sense_acc = reconstruction(acq_coils_acc, sense_params);

The   SoS image still folds because channel combination alone cannot separate overlapping pixels. SENSE uses the distinct complex channel sensitivities to remove that aliasing while preserving the shorter acquisition.


This page was generated using Literate.jl.