Beispiel #1
0
def get_true_im_numerical_phantom():

    # Load in params for  simulation
    params = get_numerical_phantom_params(SNR=None)
    dim = params['dim']
    pc_vals = params['pc_vals']

    # Find true im by using no noise gs_recon averaged over several
    # different phase-cycles to remove residual banding
    # true_im = bssfp_2d_cylinder(dims=(dim,dim),phase_cyc=0)
    true_im = np.zeros((dim, dim), dtype='complex')
    avgs = [0, np.pi / 6, np.pi / 3, np.pi / 4]
    # avgs = [ 0 ]
    for ii, extra in enumerate(avgs):
        pc0 = bssfp_2d_cylinder(dims=(dim, dim),
                                phase_cyc=(pc_vals[0] + extra))
        pc1 = bssfp_2d_cylinder(dims=(dim, dim),
                                phase_cyc=(pc_vals[1] + extra))
        pc2 = bssfp_2d_cylinder(dims=(dim, dim),
                                phase_cyc=(pc_vals[2] + extra))
        pc3 = bssfp_2d_cylinder(dims=(dim, dim),
                                phase_cyc=(pc_vals[3] + extra))
        true_im += gs_recon(pc0, pc1, pc2, pc3)
    true_im /= len(avgs)
    true_im += 1j * true_im
    # view(np.concatenate((true_im,pc0)))
    return (true_im)
Beispiel #2
0
def get_ims(N, npcs, nc, radius, SNR=np.inf):
    '''Generate coil images and truth image.'''

    pcs = np.linspace(0, 2 * np.pi, npcs, endpoint=False)
    coil_sens = gbs(N, number_of_coils=nc)

    # Now get phase-cycles
    coil_ims = np.zeros((nc, N, N, len(pcs)), dtype='complex')
    for ii, pc in enumerate(pcs):
        ims = bssfp_2d_cylinder(dims=(N, N), phase_cyc=pc, radius=radius)

        # Apply coil sensitivities to coil images
        coil_ims[:, ..., ii] = ims * coil_sens

    # Add noise
    if SNR != np.inf:
        im_r = np.mean(np.abs(coil_ims.real)[np.nonzero(coil_ims.real)])
        im_i = np.mean(np.abs(coil_ims.imag)[np.nonzero(coil_ims.imag)])

        noise_std_r = im_r / SNR
        noise_std_i = im_i / SNR
        nr = np.random.normal(0, noise_std_r, coil_ims.shape)
        ni = np.random.normal(0, noise_std_i, coil_ims.shape)
        coil_ims += nr + 1j * ni

        nr = np.random.normal(0, noise_std_r, coil_ims.shape)
        ni = np.random.normal(0, noise_std_i, coil_ims.shape)
        noise_ims = nr + 1j * ni
    else:
        noise_ims = np.zeros(coil_ims.shape)
    # view(coil_ims)

    # Get truth image
    pcs_true = np.linspace(0, 2 * np.pi, 16, endpoint=False)
    ngs = int(pcs_true.size / 4)
    im_true = np.zeros((N, N, ngs), dtype='complex')
    tmp = np.zeros((N, N, pcs_true.size), dtype='complex')
    for ii, pc in enumerate(pcs_true):
        tmp[..., ii] = bssfp_2d_cylinder(dims=(N, N),
                                         phase_cyc=pc,
                                         radius=radius)
    for ii in range(ngs):
        im_true[..., ii] = gs_recon(tmp[..., ii::ngs], pc_axis=-1)
    im_true = np.mean(im_true, axis=-1)

    return (coil_ims, noise_ims, im_true)
Beispiel #3
0
    def setUp(self):

        # Simple numerical phantom
        self.im0 = bssfp_2d_cylinder(phase_cyc=0)
        self.im1 = bssfp_2d_cylinder(phase_cyc=np.pi/2)
        self.im2 = bssfp_2d_cylinder(phase_cyc=np.pi)
        self.im3 = bssfp_2d_cylinder(phase_cyc=3*np.pi/2)

        # Get coil images
        self.num_coils = 64
        self.coil_sens = generate_birdcage_sensitivities(self.im0.shape[0],number_of_coils=self.num_coils)
        self.coil_ims0 = self.im0*self.coil_sens
        self.coil_ims1 = self.im1*self.coil_sens
        self.coil_ims2 = self.im2*self.coil_sens
        self.coil_ims3 = self.im3*self.coil_sens

        # Get kspace coil images
        self.kspace_coil_ims0 = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(self.coil_ims0,axes=(1,2)),axes=(1,2)),axes=(1,2))
        self.kspace_coil_ims1 = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(self.coil_ims1,axes=(1,2)),axes=(1,2)),axes=(1,2))
        self.kspace_coil_ims2 = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(self.coil_ims2,axes=(1,2)),axes=(1,2)),axes=(1,2))
        self.kspace_coil_ims3 = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(self.coil_ims3,axes=(1,2)),axes=(1,2)),axes=(1,2))
def get_true_im_numerical_phantom():
    '''Get reference bSSFP simulated phantom.

    As the geometric solution to the elliptical signal model still has some
    residual banding, do it a few times at a bunch of different phase cycles
    to remove virually all banding.  This ensures that the contrast will be
    comparable to the banded phantoms.

    Returns
    -------
    true_im : array_like
        Banding free reference image with true bSSFP contrast.
    '''

    # Load in params for  simulation
    params = get_numerical_phantom_params(SNR=None)
    dim = params['dim']
    pc_vals = params['pc_vals']

    # Find true im by using no noise gs_recon averaged over several
    # different phase-cycles to remove residual banding
    # true_im = bssfp_2d_cylinder(dims=(dim,dim),phase_cyc=0)
    true_im = np.zeros((dim, dim), dtype='complex')
    avgs = [0, np.pi / 6, np.pi / 3, np.pi / 4]
    # avgs = [ 0 ]
    for extra in avgs:
        pc0 = bssfp_2d_cylinder(dims=(dim, dim),
                                phase_cyc=(pc_vals[0] + extra))
        pc1 = bssfp_2d_cylinder(dims=(dim, dim),
                                phase_cyc=(pc_vals[1] + extra))
        pc2 = bssfp_2d_cylinder(dims=(dim, dim),
                                phase_cyc=(pc_vals[2] + extra))
        pc3 = bssfp_2d_cylinder(dims=(dim, dim),
                                phase_cyc=(pc_vals[3] + extra))
        true_im += gs_recon(pc0, pc1, pc2, pc3)
    true_im /= len(avgs)
    true_im += 1j * true_im
    # view(np.concatenate((true_im,pc0)))
    return true_im
Beispiel #5
0
'''Kind of neat - seeing how phase changes with coil sensitivity...'''

import numpy as np
from ismrmrdtools.simulation import generate_birdcage_sensitivities

from mr_utils.coils.coil_combine import coil_pca
from mr_utils.test_data.phantom import bssfp_2d_cylinder
from mr_utils.recon.ssfp import gs_recon
from mr_utils.utils import sos
from mr_utils import view

if __name__ == '__main__':

    # Simple numerical phantom
    im0 = bssfp_2d_cylinder(phase_cyc=0)
    im1 = bssfp_2d_cylinder(phase_cyc=np.pi / 2)
    im2 = bssfp_2d_cylinder(phase_cyc=np.pi)
    im3 = bssfp_2d_cylinder(phase_cyc=3 * np.pi / 2)

    # Get coil images
    num_coils = 64
    coil_sens = generate_birdcage_sensitivities(im0.shape[0],
                                                number_of_coils=num_coils)
    coil_ims0 = im0 * coil_sens
    coil_ims1 = im1 * coil_sens
    coil_ims2 = im2 * coil_sens
    coil_ims3 = im3 * coil_sens

    # Get kspace coil images
    kspace_coil_ims0 = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(coil_ims0,
                                                                   axes=(1,
Beispiel #6
0
def comparison_numerical_phantom(SNR=None):
    '''Compare coil by coil, Walsh method, and Inati iterative method.'''

    true_im = get_true_im_numerical_phantom()
    csms = get_coil_sensitivity_maps()
    params = get_numerical_phantom_params(SNR=SNR)
    pc_vals = params['pc_vals']
    dim = params['dim']
    noise_std = params['noise_std']
    coil_nums = params['coil_nums']

    # We want to solve gs_recon for each coil we have in the pc set
    err = np.zeros((5, len(csms)))
    rip = err.copy()
    for ii, csm in enumerate(csms):

        # I have coil sensitivities, now I need images to apply them to.
        # coil_ims: (pc,coil,x,y)
        coil_ims = np.zeros((len(pc_vals), csm.shape[0], dim, dim),
                            dtype='complex')
        for jj, pc in enumerate(pc_vals):
            im = bssfp_2d_cylinder(dims=(dim, dim), phase_cyc=pc)
            im += 1j * im
            coil_ims[jj, ...] = im * csm
            coil_ims[jj, ...] += np.random.normal(0, noise_std, coil_ims[
                jj, ...].shape) / 2 + 1j * np.random.normal(
                    0, noise_std, coil_ims[jj, ...].shape) / 2

        # Solve the gs_recon coil by coil
        coil_ims_gs = np.zeros((csm.shape[0], dim, dim), dtype='complex')
        for kk in range(csm.shape[0]):
            coil_ims_gs[kk, ...] = gs_recon(*[
                x.squeeze()
                for x in np.split(coil_ims[:, kk, ...], len(pc_vals))
            ])
        coil_ims_gs[np.isnan(coil_ims_gs)] = 0

        # Easy way out: combine all the coils using sos
        im_est_sos = sos(coil_ims_gs)
        # view(im_est_sos)

        # Take coil by coil solution and do Walsh on it to collapse coil dim
        # walsh
        csm_walsh, _ = calculate_csm_walsh(coil_ims_gs)
        im_est_recon_then_walsh = np.sum(csm_walsh * np.conj(coil_ims_gs),
                                         axis=0)
        im_est_recon_then_walsh[np.isnan(im_est_recon_then_walsh)] = 0
        # view(im_est_recon_then_walsh)

        # inati
        csm_inati, im_est_recon_then_inati = calculate_csm_inati_iter(
            coil_ims_gs)

        # Collapse the coil dimension of each phase-cycle using Walsh,Inati
        pc_est_walsh = np.zeros((len(pc_vals), dim, dim), dtype='complex')
        pc_est_inati = np.zeros((len(pc_vals), dim, dim), dtype='complex')
        for jj in range(len(pc_vals)):
            ## Walsh
            csm_walsh, _ = calculate_csm_walsh(coil_ims[jj, ...])
            pc_est_walsh[jj,
                         ...] = np.sum(csm_walsh * np.conj(coil_ims[jj, ...]),
                                       axis=0)
            # view(csm_walsh)
            # view(pc_est_walsh)

            ## Inati
            csm_inati, pc_est_inati[jj, ...] = calculate_csm_inati_iter(
                coil_ims[jj, ...], smoothing=1)
            # pc_est_inati[jj,...] = np.sum(csm_inati*np.conj(coil_ims[jj,...]),axis=0)
            # view(csm_inati)

        # Now solve the gs_recon using collapsed coils
        im_est_walsh = gs_recon(
            *[x.squeeze() for x in np.split(pc_est_walsh, len(pc_vals))])
        im_est_inati = gs_recon(
            *[x.squeeze() for x in np.split(pc_est_inati, len(pc_vals))])

        # view(im_est_walsh)
        # view(im_est_recon_then_walsh)

        # Compute error metrics
        err[0, ii] = compare_nrmse(im_est_sos, true_im)
        err[1, ii] = compare_nrmse(im_est_recon_then_walsh, true_im)
        err[2, ii] = compare_nrmse(im_est_recon_then_inati, true_im)
        err[3, ii] = compare_nrmse(im_est_walsh, true_im)
        err[4, ii] = compare_nrmse(im_est_inati, true_im)

        im_est_sos[np.isnan(im_est_sos)] = 0
        im_est_recon_then_walsh[np.isnan(im_est_recon_then_walsh)] = 0
        im_est_recon_then_inati[np.isnan(im_est_recon_then_inati)] = 0
        im_est_walsh[np.isnan(im_est_walsh)] = 0
        im_est_inati[np.isnan(im_est_inati)] = 0

        rip[0, ii] = ripple_normal(im_est_sos)
        rip[1, ii] = ripple_normal(im_est_recon_then_walsh)
        rip[2, ii] = ripple_normal(im_est_recon_then_inati)
        rip[3, ii] = ripple_normal(im_est_walsh)
        rip[4, ii] = ripple_normal(im_est_inati)

        # view(im_est_inati)

        # # SOS of the gs solution on each individual coil gives us low periodic
        # # ripple accross the phantom, similar to Walsh method:
        # plt.plot(np.abs(true_im[int(dim/2),:]),'--',label='True Im')
        # plt.plot(np.abs(im_est_sos[int(dim/2),:]),'-.',label='SOS')
        # plt.plot(np.abs(im_est_recon_then_walsh[int(dim/2),:]),label='Recon then Walsh')
        # plt.plot(np.abs(im_est_walsh[int(dim/2),:]),label='Walsh then Recon')
        # # plt.plot(np.abs(im_est_inati[int(dim/2),:]),label='Inati')
        # plt.legend()
        # plt.show()

    # # Let's show some stuff
    # plt.plot(coil_nums,err[0,:],'*-',label='SOS')
    # plt.plot(coil_nums,err[1,:],label='Recon then Walsh')
    # plt.plot(coil_nums,err[2,:],label='Walsh then Recon')
    # # plt.plot(coil_nums,err[3,:],label='Inati')
    # plt.legend()
    # plt.show()

    print('SOS RMSE:', np.mean(err[0, :]))
    print('recon then walsh RMSE:', np.mean(err[1, :]))
    print('recon then inati RMSE:', np.mean(err[2, :]))
    print('walsh then recon RMSE:', np.mean(err[3, :]))
    print('inati then recon RMSE:', np.mean(err[4, :]))

    print('SOS ripple:', np.mean(err[0, :]))
    print('recon then walsh ripple:', np.mean(rip[1, :]))
    print('recon then inati ripple:', np.mean(rip[2, :]))
    print('walsh then recon ripple:', np.mean(rip[3, :]))
    print('inati then recon ripple:', np.mean(rip[4, :]))

    view(im_est_recon_then_walsh[int(dim / 2), :])
    view(im_est_recon_then_inati[int(dim / 2), :])
    view(im_est_walsh[int(dim / 2), :])
    view(im_est_inati[int(dim / 2), :])
    # view(im_est_inati)

    # view(np.stack((im_est_recon_then_walsh,im_est_recon_then_inati,im_est_walsh,im_est_inati)))

    return (err)
Beispiel #7
0
    def test_simulated_phantom_2d(self):

        # Create the target field map
        dim = 64
        min_df, max_df = -500, 500
        fx = np.linspace(min_df, max_df, dim)
        fy = np.zeros(dim)
        field_map, _ = np.meshgrid(fx, fy)
        # field_map = 100*np.random.normal(0,1,(dim,dim))
        # view(field_map)

        # Simulate phase-cycled bSSFP acquisitons
        # Seems to be better with higher flip angle!
        # For some reason worse with higher TR
        args = {
            'TR': 5e-3,
            'alpha': np.pi / 3,
            'dims': (dim, dim),
            'FOV': ((-1, 1), (-1, 1)),
            'radius': .75,
            'field_map': field_map
        }
        pc_vals = [0, np.pi / 2, np.pi, 3 * np.pi / 2]
        pcs = np.zeros((len(pc_vals), dim, dim), dtype='complex')
        for ii, pc in enumerate(pc_vals):
            pcs[ii, ...] = bssfp_2d_cylinder(**args, phase_cyc=pc)
        # view(pcs)

        # Estimate field map using GS to ESM
        gsfm = gs_field_map(
            *[x.squeeze() for x in np.split(pcs, len(pc_vals))],
            TR=args['TR'],
            gs_recon_opts={'second_pass': False})
        # TODO: Interestingly, the second pass solution fails... Why is this?

        # Now do sims for GRE for a sanity check
        TE1 = args['TR'] / 2
        TE2 = TE1 - args['TR'] / 2
        PD, T1s, T2s = cylinder_2d(dims=args['dims'],
                                   FOV=args['FOV'],
                                   radius=args['radius'])
        m1 = gre_sim(T1s,
                     T2s,
                     TR=args['TR'],
                     TE=TE1,
                     alpha=args['alpha'],
                     field_map=args['field_map'],
                     dphi=0,
                     M0=PD,
                     tol=1e-4)
        m2 = gre_sim(T1s,
                     T2s,
                     TR=args['TR'],
                     TE=TE2,
                     alpha=args['alpha'],
                     field_map=args['field_map'],
                     dphi=0,
                     M0=PD,
                     tol=1e-4)
        grefm = dual_echo_gre(m1, m2, TE1, TE2)

        # Phase wrap the real field map so we prove equivalence up to phase
        # unwrapping...
        dTE = np.abs(TE1 - TE2)
        field_map_pw = np.mod(field_map - 1 /
                              (2 * dTE), 1 / dTE) - 1 / (2 * dTE)

        # Make sure we're getting the same thing when wrapped
        idx = np.where(np.abs(grefm) > 0)
        self.assertTrue(np.allclose(grefm[idx], field_map_pw[idx]))

        idx = np.where(np.abs(gsfm) > 0)
        self.assertTrue(np.allclose(gsfm[idx], field_map_pw[idx]))

        # Just for fun, try phase unwrapping...
        mask = np.zeros(gsfm.shape).astype(bool)
        mask[idx] = True

        # scale between [-pi,pi], do unwrap, scale back up
        scale_fac = np.pi / np.max(np.abs(gsfm)) * np.sign(np.max(gsfm))
        val = unwrap_phase(mask * gsfm * scale_fac) / scale_fac

        # For some reason the edges are off by about pi, so let's clip it...
        val = val[:, 19:-19]
        field_map_clipped = field_map[:, 19:-19]
        idx = np.where(np.abs(val) > 0)
        self.assertTrue(np.allclose(field_map_clipped[idx], val[idx]))
    PSNR = compare_psnr(abs_coil_imspace, abs_recon_flipped)
    return (MSE, SSIM, PSNR)


if __name__ == '__main__':

    # Make a dynamic phantom
    mask = load_mat(
        'mr_utils/recon/reordering/temporal_tv/mask_k_space_sparse.mat')
    nt, nx, ny = mask.shape[:]

    circ = np.zeros(mask.shape, dtype='complex')
    radius = np.abs(np.sin(np.linspace(0, np.pi, nt)) * .7 + .1)
    for ii in range(nt):
        circ[ii, ...] = bssfp_2d_cylinder(dims=(nx, ny),
                                          radius=radius[ii],
                                          kspace=True)
    circ = np.fft.fftshift(circ, axes=(1, 2)).transpose((2, 1, 0))
    mask = mask.transpose(2, 1, 0)
    # view(mask*circ,fft=True)

    # Run Ganesh's temporal recon
    weight_fidelity = 1.0
    weight_temporal = 0.01
    beta_sqrd = 1e-7
    noi = 200
    # data = run_ganesh_tcr(circ,mask,weight_fidelity,weight_temporal,beta_sqrd,noi)
    #
    # # Unpack, FFT, apply masks to reference, recon, and prior image estimates
    # coil_imspace,recon_flipped,prior = preprocess(data)
    #
Beispiel #9
0
    # Simulate phase-cycled bSSFP acquisitons
    # Seems to be better with higher flip angle!
    # For some reason worse with higher TR
    args = {
        'TR': 5e-3,
        'alpha': np.pi / 3,
        'dims': (dim, dim),
        'FOV': ((-1, 1), (-1, 1)),
        'radius': .75,
        'field_map': field_map
    }
    pc_vals = [0, np.pi / 2, np.pi, 3 * np.pi / 2]
    pcs = np.zeros((len(pc_vals), dim, dim), dtype='complex')
    for ii, pc in enumerate(pc_vals):
        pcs[ii, ...] = bssfp_2d_cylinder(**args, phase_cyc=pc)
    view(pcs)

    # Estimate field map using GS to ESM
    gsfm = gs_field_map(*[x.squeeze() for x in np.split(
        pcs, len(pc_vals))], \
        TR=args['TR'], gs_recon_opts={'second_pass': False})
    # TODO: Interestingly, the second pass solution fails... Why is this?
    view(gsfm)

    # Now do sims for GRE for a sanity check
    TE1 = args['TR'] / 2
    TE2 = TE1 - args['TR'] / 2
    PD, T1s, T2s = cylinder_2d(dims=args['dims'],
                               FOV=args['FOV'],
                               radius=args['radius'])
Beispiel #10
0
    N = np.max([X, Y])
    M = np.min([X, Y])
    radius = M/N
    pcs = [0, np.pi/2, np.pi, 3*np.pi/2]
    npcs = len(pcs)
    ims = dict()
    coil_ims = dict()
    for nc in num_coils:
        # Generate coil sensitivities for this number of coils
        coil_sens = gbs(N, number_of_coils=nc)

        # Now get phase-cycles
        ims[nc] = np.zeros((N, N, npcs), dtype='complex')
        coil_ims[nc] = np.zeros((nc, N, N, len(pcs)), dtype='complex')
        for ii, pc in enumerate(pcs):
            ims[nc][..., ii] = bssfp_2d_cylinder(
                dims=(N, N), phase_cyc=pc, radius=radius)

            # Apply coil sensitivities to coil images
            coil_ims[nc][:, ..., ii] = (
                ims[nc][..., ii]*coil_sens)

        # Trim down to correct size
        if X < Y:
            trim = int((Y - X)/2)
            coil_ims[nc] = coil_ims[nc][:, trim:-trim, ...]
        elif X > Y:
            trim = int((X - Y)/2)
            coil_ims[nc] = coil_ims[nc][:, :, trim:-trim, ...]
        # view(coil_ims[nc])

    # # See coil images for ESM block diagram