Contents

% To begin, add '$(TOOLBOX_PATH)/matlab' to the library path. The
% environment variable TOOLBOX_PATH needs to be set to the base
% directory of the reconstruction tools package.

if isempty(getenv('TOOLBOX_PATH'))
	error('Environment variable TOOLBOX_PATH is not set.');
end

addpath(strcat(getenv('TOOLBOX_PATH'), '/matlab'));

ESPIRiT examples (based on work by Sana Vaziri)

The input and output datasets are each stored in a pair of files: one header (.hdr) and one raw data (.cfl). All the data files used in this demo are in the data folder. The readcfl and writecfl Matlab methods can be found in $(TOOLBOX_PATH)/matlab and can be used to view and process the data and reconstructed images in Matlab.

Example 1: ESPIRiT reconstruction (R = 2x2)

The example uses ESPIRiT to obtain the image from 2x2 undersampled data.

A visualization of k-space data and sampling and zero-filled reconstruction

% sqrt sum-of-squares of k-space
und2x2 = readcfl('data/und2x2');
ksp_rss = bart('rss 8', und2x2);

% zero-filled reconstruction sqrt-sum-of-squares
zf_coils = bart('fft -i 6', und2x2);
zf_rss = bart('rss 8', zf_coils);

ksp_rss = squeeze(ksp_rss);
zf_coils = squeeze(zf_coils);
zf_rss = squeeze(zf_rss);

figure,subplot(1,2,1), imshow(abs(ksp_rss).^0.125, []); title('k-space')
subplot(1,2,2), imshow(abs(zf_rss), []); title('zero-filled recon')

Show singular values of the calibration matrix

calmat = bart('calmat -r 20 -k 6', und2x2);
[U SV VH] = bart('svd', calmat);

figure, plot(SV);
title('Singular Values of the Calibration Matrix');

ESPIRiT calibration (using a maximum calibration region of size 20)

[calib emaps] = bart('ecalib -r 20', und2x2);
Calibration region...  (size: 1x20x20, pos: 0x105x80)
Energy: 0.568 0.282 0.104 0.031 0.008 0.003 0.002 0.001
Build calibration matrix and SVD...
Eigen decomposition... (size: 288)
Using 56/288 kernels (19.44%, last SV: 0.031736).
Zeropad...
FFT (juggling)...
Calculate Gram matrix...
Resize...
Point-wise eigen-decomposition...
Crop maps... (0.80)
Fix phase...
Done.

Extraction of first set of maps (0-th subarray along dimension 4)

sens = bart('slice 4 0', calib);


sens_maps = squeeze(sens);

figure,
subplot(121), imshow3(abs(sens_maps), [],[2,4]);
title('Magnitude ESPIRiT 1st Set of Maps')
subplot(122), imshow3(angle(sens_maps),[],[2,4])
title('Phase ESPIRiT 1st Set of Maps')

For comparison: Produce coil images from fully-sampled data using the fft command. It uses a bitmask as argument to specify the dimensions which are transformed. Here, the bitmask is 6 = 2^1 + 2^2 which corresponds to the dimensions 1 and 2.

full = readcfl('data/full');
coilimgs = bart('fft -i 6', full);

coil_imgs = squeeze(coilimgs);
figure, imshow3(abs(coil_imgs), [],[2,4])
title('Coil images')

Show eigenvalue maps

emaps = squeeze(emaps);
figure, imshow3(emaps, [], [1, 2]);
title('First Two Eigenvalue Maps')

SENSE reconstruction using ESPIRiT maps (using the generalized parallel imaging compressed sensing tool)

reco = bart('pics', und2x2, sens);

sense_recon = squeeze(reco);
figure, imshow(abs(sense_recon), []); title('ESPIRiT Reconstruction')
Size: 41400 Samples: 10650 Acc: 3.89
l2 regularization: 0.000000
conjugate gradients
Total Time: 0.652066

Evaluation of the coil sensitivities.

%
% Computing error from projecting fully sampled error
% onto the sensitivities. This can be done with one
% iteration of POCSENSE.
%
proj = bart('pocsense -r 0. -i 1', full, sens);

% Compute error and transform it into image domain and combine into a single map.
errimgs = bart('fft -i 6', (full - proj));
errsos_espirit = bart('rss 8', errimgs);

%
% For comparison: compute sensitivities directly from the center.
sens_direct = bart('caldir 20', und2x2);

% Compute error map.
proj = bart('pocsense -r 0. -i 1', full, sens_direct);
errimgs = bart('fft -i 6', (full - proj));
errsos_direct = bart('rss 8', errimgs);


errsos_espirit = squeeze(errsos_espirit);
errsos_direct = squeeze(errsos_direct);

figure,
imshow(abs([errsos_direct errsos_espirit]), []); title('Projection Error (direct calibration vs ESPIRiT)');
Reconstruction...
Done
Done.
Calibration region 1x20x20
Done.
Reconstruction...
Done
Done.

Example 2: Reconstruction of undersampled data with small FOV.

This example uses a undersampled data set with a small FOV. The image reconstructed using ESPIRiT is compared to an image reconstructed with SENSE. By using two sets of maps, ESPIRiT can avoid the central artifact which appears in the SENSE reconstruction.

% Zero padding to make square voxels since resolution in x-y for this
% data set is lower in phase-encode than readout
smallfov = readcfl('data/smallfov');
smallfov = bart('resize -c 2 252', smallfov);

% Direct calibration of the sensitivities from k-space center for SENSE
sensemaps = bart('caldir 20', smallfov);

% SENSE reconstruction
sensereco = bart('pics -r0.01', smallfov, sensemaps);

% ESPIRiT calibration with 2 maps to mitigate with aliasing in the calibration
espiritmaps = bart('ecalib -r 20 -m 2', smallfov);

% ESPIRiT reconstruction with 2 sets of maps
espiritreco = bart('pics -r0.01', smallfov, espiritmaps);

% Combination of the two ESPIRiT images using root of sum of squares
espiritreco_rss = bart('rss 16', espiritreco);

espirit_maps = squeeze(espiritmaps);
figure, imshow3(abs(espirit_maps), [],[2,8])
title('The two sets of ESPIRiT maps')
Calibration region 1x20x20
Done.
Size: 80640 Samples: 27167 Acc: 2.97
l2 regularization: 0.010000
conjugate gradients
Total Time: 0.630694
Calibration region...  (size: 1x20x20, pos: 0x150x116)
Energy: 0.515 0.306 0.115 0.045 0.013 0.004 0.002 0.001
Build calibration matrix and SVD...
Eigen decomposition... (size: 288)
Using 73/288 kernels (25.35%, last SV: 0.034163).
Zeropad...
FFT (juggling)...
Calculate Gram matrix...
Resize...
Point-wise eigen-decomposition...
Crop maps... (0.80)
Fix phase...
Done.
2 maps.
ESPIRiT reconstruction.
Size: 80640 Samples: 27167 Acc: 2.97
l2 regularization: 0.010000
conjugate gradients
Total Time: 0.682624
Warning: Image is too big to fit on screen; displaying at 50% 
% SENSE image:

reco1 = squeeze(sensereco);
figure,
subplot(1,2,1), imshow(abs(reco1), [])
title('SENSE Reconstruction')


% ESPIRiT image:

reco2 = squeeze(espiritreco_rss);
subplot(1,2,2), imshow(abs(reco2), [])
title('ESPIRiT Reconstruction from 2 maps')

Example 3: Compressed Sensing and Parallel Imaging

This example demonstrates L1-ESPIRiT reconstruction of a human knee. Data has been acquired with variable-density poisson-disc sampling.

% A visualization of k-space data

knee = readcfl('data/knee');
ksp_rss = bart('rss 8', knee);

ksp_rss = squeeze(ksp_rss);
figure, imshow(abs(ksp_rss).^0.125, []); title('k-space')


% Root-of-sum-of-squares image

knee_imgs = bart('fft -i 6', knee);
knee_rss = bart('rss 8', knee_imgs);


% ESPIRiT calibration (one map)

knee_maps = bart('ecalib -c0. -m1', knee);



% l1-regularized reconstruction (wavelet basis)

knee_l1 = bart('pics -l1 -r0.01', knee, knee_maps);



% Results

knee_rss = knee_rss / 1.5E9;

image = [ squeeze(knee_rss) squeeze(knee_l1) ];
figure, imshow(abs(image), [])
title('Zero-filled and Compressed Sensing/Parallel Imaging')
Calibration region...  (size: 1x24x24, pos: 0x132x106)
Energy: 0.560 0.266 0.116 0.037 0.010 0.006 0.004 0.001
Build calibration matrix and SVD...
Eigen decomposition... (size: 288)
Using 61/288 kernels (21.18%, last SV: 0.032264).
Zeropad...
FFT (juggling)...
Calculate Gram matrix...
Resize...
Point-wise eigen-decomposition...
Crop maps... (0.00)
Fix phase...
Done.
Size: 67968 Samples: 8082 Acc: 8.41
l1-wavelet regularization: 0.010000
FISTA
Total Time: 5.277991

Example 4: Basic Tools

Various tools are demonstrated by manipulating an image.

knee_l1 = readcfl('data/knee_l1');

% Zero pad

knee2 = bart('resize -c 1 300 2 300', knee_l1);

% Switch dimensions 1 and 2

tmp = bart('transpose 1 2', knee2);

% Scale by a factor of 0.5

tmp2 = bart('scale 0.5', tmp);

% Join original and the transposed and scaled version along dimension 2.

joined = bart('join 2', knee2, tmp2);

% Flip 1st and 2nd dimension (2^1 + 2^2 = 6)

tmp = bart('flip 6', joined);

% Join flipped and original along dimension 1.

big = bart('join 1', joined, tmp);

% Extract sub-array

tmp = bart('extract 1 150 449', big);
small = bart('extract 2 150 449', tmp);

% Circular shift by 115 pixels

tmp = bart('circshift 1 150', small);
shift = bart('circshift 2 150', tmp);

% Show the final result.

figure, imshow(abs(squeeze(shift)), []);

non-Cartesian MRI using BART

% Generate k-space trajectory with 64 radial spokes
traj_rad = bart('traj -r -x512 -y64');

% 2x oversampling
traj_rad2 = bart('scale 0.5', traj_rad);

% simulate eight-channel k-space data
ksp_sim = bart('phantom -k -s8 -t', traj_rad2);

% increase the reconstructed FOV a bit
traj_rad2 = bart('scale 0.6', traj_rad);


% inverse gridding
igrid = bart('nufft -i -t', traj_rad2, ksp_sim);

% channel combination
reco1 = bart('rss 8', igrid);


% reconstruct low-resolution image and transform back to k-space
lowres_img = bart('nufft -i -d24:24:1 -t', traj_rad2, ksp_sim);
lowres_ksp = bart('fft -u 7', lowres_img);

% zeropad to full size
ksp_zerop = bart('resize -c 0 308 1 308', lowres_ksp);

% ESPIRiT calibration
sens = bart('ecalib -m1', ksp_zerop);

% non-Cartesian parallel imging
reco2 = bart('pics -S -r0.001 -t', traj_rad2, ksp_sim, sens);



figure, imshow(abs(squeeze([reco1, reco2])), []);
title('Inverse Gridding vs Parallel Imaging')
Est. image size: 308 308 1
Done.
Done.
Calibration region...  (size: 24x24x1, pos: 142x142x0)
Energy: 0.591 0.287 0.102 0.015 0.004 0.001 0.000 0.000
Build calibration matrix and SVD...
Eigen decomposition... (size: 288)
Using 38/288 kernels (13.19%, last SV: 0.037886).
Zeropad...
FFT (juggling)...
Calculate Gram matrix...
Resize...
Point-wise eigen-decomposition...
Crop maps... (0.80)
Fix phase...
Done.
l2 regularization: 0.001000
conjugate gradients
Total Time: 1.684765