def plot_synthetic_banding(self, input, output, results):
        imgs = []
        imgs.append(input[:, :, 0] + 1j * input[:, :, 1])
        imgs.append(input[:, :, 2] + 1j * input[:, :, 3])

        imgs.append(output[:, :, 0] + 1j * output[:, :, 1])
        imgs.append(output[:, :, 2] + 1j * output[:, :, 3])

        # Run the elliptical model on input dataset as ground truth
        from mr_utils.recon.ssfp import gs_recon
        pc0 = input[:, :, 0] + 1j * input[:, :, 1]
        pc90 = output[:, :, 0] + 1j * output[:, :, 1]
        pc180 = input[:, :, 2] + 1j * input[:, :, 3]
        pc270 = output[:, :, 2] + 1j * output[:, :, 3]
        recon_truth = gs_recon(pc0, pc90, pc180, pc270)
        imgs.append(recon_truth)

        imgs.append(results[:, :, 0] + 1j * results[:, :, 1])
        imgs.append(results[:, :, 2] + 1j * results[:, :, 3])

        # Run the elliptical model to verify the reconstruction
        pc0 = input[:, :, 0] + 1j * input[:, :, 1]
        pc90 = results[:, :, 0] + 1j * results[:, :, 1]
        pc180 = input[:, :, 2] + 1j * input[:, :, 3]
        pc270 = results[:, :, 2] + 1j * results[:, :, 3]
        recon_using_prediction = gs_recon(pc0, pc90, pc180, pc270)
        imgs.append(recon_using_prediction)

        count = len(imgs)  # Count of plots
        for i in range(count):
            plt.subplot(1, count, i + 1)
            plt.imshow(np.abs(imgs[i]), cmap='gray')
            plt.axis('off')
        plt.show()
示例#2
0
    def test_simulated_2d(self):


        # # Kind of neat - seeing how phase changes with coil sensitivity...
        # view(np.angle(coil_ims0))

        # Do GS solution to ESM then take SOS
        recon_gs = np.zeros(self.coil_ims0.shape,dtype='complex')
        for ii in range(self.num_coils):
            recon_gs[ii,...] = gs_recon(self.coil_ims0[ii,...],self.coil_ims1[ii,...],self.coil_ims2[ii,...],self.coil_ims3[ii,...])
        # view(np.angle(recon_gs)) # realize this is actually a movie - they all just look the same...
        recon_gs_sos = sos(recon_gs,axes=(0))
        view(recon_gs_sos)

        # Do PCA
        n_components = 4
        pca0 = coil_pca(self.coil_ims0,coil_dim=0,n_components=n_components)
        pca1 = coil_pca(self.coil_ims1,coil_dim=0,n_components=n_components)
        pca2 = coil_pca(self.coil_ims2,coil_dim=0,n_components=n_components)
        pca3,expl_var = coil_pca(self.coil_ims3,coil_dim=0,n_components=n_components,give_explained_var=True)
        # view(expl_var.real)

        # view(np.angle(pca3))

        # Do GS solution to ESM then take SOS, this time using PCA'd data
        recon_pca_gs = np.zeros(pca0.shape,dtype='complex')
        for ii in range(n_components):
            # view(np.concatenate((pca0[ii,...],pca1[ii,...],pca2[ii,...],pca3[ii,...])))
            recon_pca_gs[ii,...] = gs_recon(pca0[ii,...],pca1[ii,...],pca2[ii,...],pca3[ii,...])
        # view(np.angle(recon_pca_gs))
        recon_pca_gs_sos = sos(recon_pca_gs,axes=(0))
示例#3
0
    def test_gs_recon3d(self):
        from mr_utils.recon.ssfp import gs_recon, gs_recon3d

        # Try individually
        I0 = gs_recon(self.I1, self.I2, self.I3, self.I4)
        I0 = np.stack((I0, gs_recon(self.I1, self.I2, self.I3, self.I4)),
                      axis=-1)
        I1 = gs_recon3d(np.stack((self.I1, self.I1), axis=-1),
                        np.stack((self.I2, self.I2), axis=-1),
                        np.stack((self.I3, self.I3), axis=-1),
                        np.stack((self.I4, self.I4), axis=-1))
        self.assertTrue(np.allclose(I0, I1))
示例#4
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)
    def load_brain(self):
        # Load npy data for each phase cycle
        pc0 = np.load(
            './data/brain/meas_MID23_TRUFI_STW_TE2_5_FID33594.dat_avg_coil_combined.npy'
        )
        pc90 = np.load(
            './data/brain/meas_MID24_TRUFI_STW_TE2_5_dphi_90_FID33595.dat_avg_coil_combined.npy'
        )
        pc180 = np.load(
            './data/brain/meas_MID25_TRUFI_STW_TE2_5_dphi_180_FID33596.dat_avg_coil_combined.npy'
        )
        pc270 = np.load(
            './data/brain/meas_MID26_TRUFI_STW_TE2_5_dphi_270_FID33597.dat_avg_coil_combined.npy'
        )

        # Shape should be (z,x,y,pc)
        imgs = np.stack((pc0, pc90, pc180, pc270)).transpose((3, 1, 2, 0))
        # view(imgs.T)
        # out = np.stack((pc90,pc270)).transpose((3,1,2,0))

        # Output should be solution to ESM for each slice
        out = np.zeros((imgs.shape[0], imgs.shape[1], imgs.shape[2]),
                       dtype='complex')
        for kk in range(imgs.shape[0]):
            out[kk, ...] = gs_recon(pc0[..., kk], pc90[..., kk],
                                    pc180[..., kk], pc270[..., kk])
        # view(out)

        return imgs, out
示例#6
0
    def test_gs_recon(self):
        from mr_utils.recon.ssfp import gs_recon_for_loop, gs_recon

        # Make sure it doesn't matter if we go pixel by pixel or do the whole
        # matrix at once
        I0 = gs_recon_for_loop(self.I1, self.I2, self.I3, self.I4)
        I1 = gs_recon(self.I1, self.I2, self.I3, self.I4)
        self.assertTrue(np.allclose(I0, I1))
示例#7
0
def gs_then_cc(coil_ims, noise_ims):
    '''Do GS recon on each coil then coil combine.'''

    # How many coils?
    nc = coil_ims.shape[0]

    # Now the other way
    gs = np.zeros(coil_ims.shape[:-1], dtype='complex')
    n = np.zeros(noise_ims.shape[:-1], dtype='complex')
    for ii in range(nc):
        gs[ii, ...] = gs_recon(coil_ims[ii, ...], pc_axis=-1)

        # GS recon for the noise, too, to make sure we track what
        # happens to the noise characteristics
        n[ii, ...] = gs_recon(noise_ims[ii, ...], pc_axis=-1)

    csm1 = walsh(gs, n)
    return np.sum(csm1 * np.conj(gs), axis=0)
示例#8
0
    def test_gs(self):
        from mr_utils.sim.ssfp import ssfp
        from mr_utils.recon.ssfp import gs_recon

        pcs = np.zeros((4, self.dim, self.dim), dtype='complex')
        for ii, pc in enumerate([0, np.pi / 2, np.pi, 3 * np.pi / 2]):
            pcs[ii, ...] = ssfp(**self.args, phase_cyc=pc)
        # view(pcs)

        recon = gs_recon(*[x.squeeze() for x in np.split(pcs, 4)])
示例#9
0
def cc_then_gs_no_avg(coil_ims, noise_ims):
    '''Coil combine using individual csm then GS recon.'''

    # How many phase-cycles?
    npcs = coil_ims.shape[-1]

    # Coil combine each phase-cycle separately
    cc1 = np.zeros(coil_ims.shape[1:], dtype='complex')
    for ii in range(npcs):
        csm = walsh(coil_ims[..., ii], noise_ims[..., ii])
        cc1[..., ii] = np.sum(csm * np.conj(coil_ims[..., ii]), axis=0)
    return gs_recon(cc1, pc_axis=-1)
示例#10
0
def cc_then_gs_avg_pc(coil_ims, noise_ims):
    '''Do coil combine averaging correcting for PCs then do GS.'''

    # How many phase-cycles?
    npcs = coil_ims.shape[-1]

    # Average the correlation matrices
    csm0 = walsh_gs(coil_ims, coil_axis=0, pc_axis=-1, avg_method='pc')

    # Apply to each phase-cycle
    cc0 = np.zeros(coil_ims.shape[1:], dtype='complex')
    for ii in range(npcs):
        cc0[..., ii] = np.sum(csm0 * np.conj(coil_ims[..., ii]), axis=0)
    return gs_recon(cc0, pc_axis=-1)
示例#11
0
def gs_field_map(I0, I1, I2, I3, TR, gs_recon_opts=None):
    '''Use the elliptical signal model to estimate the field map.

    Parameters
    ----------
    I0 : array_like
        First of the first phase-cycle pair (0 degrees).
    I2 : array_like
        Second of the first phase-cycle pair (180 degrees).
    I1 : array_like
        First of the second phase-cycle pair (90 degrees).
    I3 : array_like
        Second of the second phase-cycle pair (270 degrees).
    TR : float
        Repetition time of acquisitons in ms.
    gs_recon_opts : dict, optional
        Options to pass to gs_recon.

    Returns
    -------
    gsfm : array_like
        Wrapped field map in hertz.

    Notes
    -----
    I0, I2 and I1, I3 must be phase-cycle pairs, meaning I0, I2 are
    separated by 180 degrees and I1, I3 are separated by 180 degrees.
    It does not matter what the actual phase-cycles are.

    Implements field map estimation given in [1]_.

    References
    ----------
    .. [1] Taylor, Meredith, et al. "MRI Field Mapping using bSSFP
           Elliptical Signal model." Proceedings of the ISMRM Annual
           Conference (2017).
    '''

    if gs_recon_opts is None:
        gs_recon_opts = {}

    # TE = 2*TR
    gs_sol = gs_recon(I0, I1, I2, I3, **gs_recon_opts)
    # gsfm = np.angle(gs_sol)/(2*np.pi*TE)
    gsfm = np.angle(gs_sol) / (np.pi * TR)

    return gsfm
示例#12
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)
示例#13
0
def cc_then_gs_avg_csm(coil_ims, noise_ims):
    '''Coil combine using averaged csm then GS recon.
    '''

    # How many phase-cycles?
    npcs = coil_ims.shape[-1]

    # Average the coil sensitivities for each phase-cycle
    csm0 = np.zeros(coil_ims.shape, dtype='complex')
    for ii in range(npcs):
        csm0[..., ii] = walsh(coil_ims[..., ii], noise_ims[..., ii])
    csm0 = np.mean(csm0, axis=-1)

    # Apply to each phase-cycle
    cc0 = np.zeros(coil_ims.shape[1:], dtype='complex')
    for ii in range(npcs):
        cc0[..., ii] = np.sum(csm0 * np.conj(coil_ims[..., ii]), axis=0)
    return gs_recon(cc0, pc_axis=-1)
示例#14
0
    def test_noisy_gs_recon(self):
        from mr_utils.recon.ssfp import gs_recon, gs_recon_for_loop

        # Add in gaussian noise on both real,imag channels
        m, std = 0, .08
        n1 = np.random.normal(m, std,
                              size=self.I1.shape) + 1j * np.random.normal(
                                  m, std, size=self.I1.shape)
        n2 = np.random.normal(m, std,
                              size=self.I1.shape) + 1j * np.random.normal(
                                  m, std, size=self.I1.shape)
        n3 = np.random.normal(m, std,
                              size=self.I1.shape) + 1j * np.random.normal(
                                  m, std, size=self.I1.shape)
        n4 = np.random.normal(m, std,
                              size=self.I1.shape) + 1j * np.random.normal(
                                  m, std, size=self.I1.shape)

        I0 = gs_recon(self.I1 + n1, self.I2 + n2, self.I3 + n3, self.I4 + n4)
        I1 = gs_recon_for_loop(self.I1 + n1, self.I2 + n2, self.I3 + n3,
                               self.I4 + n4)
        self.assertTrue(np.allclose(I0, I1))
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
示例#16
0
    def test_simulated_2d_kspace(self):

        # Do PCA on kspace
        n_components = 4
        pca0 = coil_pca(self.kspace_coil_ims0,coil_dim=0,n_components=n_components)
        pca1 = coil_pca(self.kspace_coil_ims1,coil_dim=0,n_components=n_components)
        pca2 = coil_pca(self.kspace_coil_ims2,coil_dim=0,n_components=n_components)
        pca3,expl_var = coil_pca(self.kspace_coil_ims3,coil_dim=0,n_components=n_components,give_explained_var=True)
        # view(expl_var.imag)

        # Put it back in image space
        pca0 = np.fft.ifftshift(np.fft.ifft2(np.fft.ifftshift(pca0,axes=(1,2)),axes=(1,2)),axes=(1,2))
        pca1 = np.fft.ifftshift(np.fft.ifft2(np.fft.ifftshift(pca1,axes=(1,2)),axes=(1,2)),axes=(1,2))
        pca2 = np.fft.ifftshift(np.fft.ifft2(np.fft.ifftshift(pca2,axes=(1,2)),axes=(1,2)),axes=(1,2))
        pca3 = np.fft.ifftshift(np.fft.ifft2(np.fft.ifftshift(pca3,axes=(1,2)),axes=(1,2)),axes=(1,2))

        # Do GS solution to ESM then take SOS, this time using PCA'd data
        recon_pca_gs = np.zeros(pca0.shape,dtype='complex')
        for ii in range(n_components):
            # view(np.concatenate((pca0[ii,...],pca1[ii,...],pca2[ii,...],pca3[ii,...])))
            recon_pca_gs[ii,...] = gs_recon(pca0[ii,...],pca1[ii,...],pca2[ii,...],pca3[ii,...])
        # view(np.angle(recon_pca_gs))
        recon_pca_gs_sos = sos(recon_pca_gs,axes=(0))
示例#17
0
    # Get a 2D image, since we can't seem to replicate using a single
    # voxel
    N = 64
    df_noise_std = 0
    radius = .8
    TR = 6e-3
    alpha = np.deg2rad(30)
    npcs = 4
    pcs = np.linspace(0, 2*np.pi, npcs, endpoint=False)
    PD, T1, T2 = cylinder_2d(dims=(N, N), radius=radius)

    df = 1000
    min_df, max_df = -df, df
    fx = np.linspace(min_df, max_df, N)
    fy = np.zeros(N)
    df, _ = np.meshgrid(fx, fy)

    if df_noise_std > 0:
        n = np.random.normal(0, df_noise_std, df.shape)
        df += n

    I = ssfp(T1, T2, TR, alpha, df, pcs, PD, phi_rf=0)


    recon = gs_recon(I, pc_axis=0)
    recon_no_second = gs_recon(I, pc_axis=0, second_pass=False)

    I0 = np.concatenate((
        I, recon[None, ...], recon_no_second[None, ...]), axis=0)
    view(I0, montage_axis=0)
示例#18
0
def comparison_knee():
    '''Coil by coil, Walsh method, and Inati iterative method for knee data.'''

    # Load the knee data
    dir = '/home/nicholas/Documents/rawdata/SSFP_SPECTRA_dphiOffset_08022018/'
    files = [
        'meas_MID362_TRUFI_STW_TE3_FID29379',
        'meas_MID363_TRUFI_STW_TE3_dphi_45_FID29380',
        'meas_MID364_TRUFI_STW_TE3_dphi_90_FID29381',
        'meas_MID365_TRUFI_STW_TE3_dphi_135_FID29382',
        'meas_MID366_TRUFI_STW_TE3_dphi_180_FID29383',
        'meas_MID367_TRUFI_STW_TE3_dphi_225_FID29384',
        'meas_MID368_TRUFI_STW_TE3_dphi_270_FID29385',
        'meas_MID369_TRUFI_STW_TE3_dphi_315_FID29386'
    ]
    pc_vals = [0, 45, 90, 135, 180, 225, 270, 315]
    dims = (512, 256)
    num_coils = 4
    num_avgs = 16

    # # Load in raw once, then save as npy with collapsed avg dimension
    # pcs = np.zeros((len(files),dims[0],dims[1],num_coils),dtype='complex')
    # for ii,file in enumerate(files):
    #     pcs[ii,...] = np.mean(load_raw('%s/%s.dat' % (dir,file),use='s2i'),axis=-1)
    # np.save('%s/te3.npy' % dir,pcs)

    # pcs looks like (pc,x,y,coil)
    pcs = np.load('%s/te3.npy' % dir)
    pcs = np.fft.fftshift(np.fft.fft2(pcs, axes=(1, 2)), axes=(1, 2))
    # print(pcs.shape)
    # view(pcs,fft=True,montage_axis=0,movie_axis=3)

    # Do recon then coil combine
    coils0 = np.zeros((pcs.shape[-1], pcs.shape[1], pcs.shape[2]),
                      dtype='complex')
    coils1 = coils0.copy()
    for ii in range(pcs.shape[-1]):
        # We have two sets: 0,90,180,27 and 45,135,225,315
        idx0 = [0, 2, 4, 6]
        idx1 = [1, 3, 5, 7]
        coils0[ii, ...] = gs_recon(*[x.squeeze() for x in pcs[idx0, :, :, ii]])
        coils1[ii, ...] = gs_recon(*[x.squeeze() for x in pcs[idx1, :, :, ii]])
    # Then do the coil combine
    csm_walsh, _ = calculate_csm_walsh(coils0)
    im_est_recon_then_walsh0 = np.sum(csm_walsh * np.conj(coils0), axis=0)
    # view(im_est_recon_then_walsh0)

    csm_walsh, _ = calculate_csm_walsh(coils1)
    im_est_recon_then_walsh1 = np.sum(csm_walsh * np.conj(coils1), axis=0)
    # view(im_est_recon_then_walsh1)

    rip0 = ripple(im_est_recon_then_walsh0)
    rip1 = ripple(im_est_recon_then_walsh1)
    print('recon then walsh: ', np.mean([rip0, rip1]))

    # Now try inati
    csm_inati, im_est_recon_then_inati0 = calculate_csm_inati_iter(coils0,
                                                                   smoothing=5)
    csm_inati, im_est_recon_then_inati1 = calculate_csm_inati_iter(coils1,
                                                                   smoothing=5)
    rip0 = ripple(im_est_recon_then_inati0)
    rip1 = ripple(im_est_recon_then_inati1)
    print('recon then inati: ', np.mean([rip0, rip1]))

    # Now try sos
    im_est_recon_then_sos0 = sos(coils0, axes=0)
    im_est_recon_then_sos1 = sos(coils1, axes=0)
    rip0 = ripple(im_est_recon_then_sos0)
    rip1 = ripple(im_est_recon_then_sos1)
    print('recon then sos: ', np.mean([rip0, rip1]))
    # view(im_est_recon_then_sos)

    ## Now the other way, combine then recon
    pcs0 = np.zeros((2, pcs.shape[0], pcs.shape[1], pcs.shape[2]),
                    dtype='complex')
    pcs1 = pcs0.copy()
    for ii in range(pcs.shape[0]):
        # Walsh it up
        csm_walsh, _ = calculate_csm_walsh(pcs[ii, ...].transpose((2, 0, 1)))
        pcs0[0, ii, ...] = np.sum(csm_walsh * np.conj(pcs[ii, ...].transpose(
            (2, 0, 1))),
                                  axis=0)
        # view(pcs0[ii,...])

        # Inati it up
        csm_inati, pcs0[1, ii,
                        ...] = calculate_csm_inati_iter(pcs[ii, ...].transpose(
                            (2, 0, 1)),
                                                        smoothing=5)

    ## Now perform gs_recon on each coil combined set
    # Walsh
    im_est_walsh_then_recon0 = gs_recon(
        *[x.squeeze() for x in pcs0[0, idx0, ...]])
    im_est_walsh_then_recon1 = gs_recon(
        *[x.squeeze() for x in pcs0[0, idx1, ...]])
    # Inati
    im_est_inati_then_recon0 = gs_recon(
        *[x.squeeze() for x in pcs0[1, idx0, ...]])
    im_est_inati_then_recon1 = gs_recon(
        *[x.squeeze() for x in pcs0[1, idx1, ...]])

    # view(im_est_walsh_then_recon0)
    # view(im_est_walsh_then_recon1)
    view(im_est_inati_then_recon0)
    view(im_est_inati_then_recon1)

    rip0 = ripple(im_est_walsh_then_recon0)
    rip1 = ripple(im_est_walsh_then_recon1)
    print('walsh then recon: ', np.mean([rip0, rip1]))

    rip0 = ripple(im_est_inati_then_recon0)
    rip1 = ripple(im_est_inati_then_recon1)
    print('inati then recon: ', np.mean([rip0, rip1]))
示例#19
0
    def test_gs_recon_knee(self):
        from mr_utils.recon.ssfp import gs_recon

        I = gs_recon(self.I1, self.I2, self.I3, self.I4)
        self.assertTrue(np.allclose(I, self.I))
示例#20
0
    npcs, ncoils, sx, sy = data.shape[:]
    pcs = np.linspace(0, 2*np.pi, 4, endpoint=False)
    pcs = np.tile(pcs, (sy, sx, 1)).T

    # Estimate the sensitivity maps from coil images
    # Try espirit
    csm_est = np.load(path + 'csm_P.npy')[
        :, :, sl, :].transpose((2, 0, 1))
    # csm_est = np.load(path + 'csm_P_S.npy')[
    #     :, :, sl, :, 0].transpose((2, 0, 1))
    # print(csm_est.shape)
    # view(csm_est*mask)

    M = np.zeros((ncoils, sx, sy), dtype='complex')
    for cc in range(ncoils):
        M[cc, ...] = gs_recon(data[:, cc, ...], pc_axis=0)
        # M[cc, ...] *= np.exp(1j*pcs[0, ...]/2)
        # view(M[cc, ...])
    thresh = threshold_li(np.abs(M))
    mask = np.abs(M) > thresh
    mask0 = mask[0, ...]
    # csm_est, _ = calculate_csm_walsh(M)
    # csm_est, _ = calculate_csm_inati_iter(M)

    # I think what should actually happen is that coil sensitivity
    # map phase information should be applied to each phase-cycle
    # image before GS recon, then 


    # Let's recall the scan parameters
    TR = 6e-3
示例#21
0
    dataset = 1
    t0 = time()
    data0 = np.load('data/20190401_GASP_PHANTOM/set%d_tr6_te3.npy' % dataset)
    data1 = np.load('data/20190401_GASP_PHANTOM/set%d_tr12_te6.npy' % dataset)
    data2 = np.load('data/20190401_GASP_PHANTOM/set%d_tr24_te12.npy' % dataset)
    print(time() - t0)

    print(data0.shape)  # [Height, Width, Coil, Avg, PCs]

    data = np.concatenate(
        (data0[..., None], data1[..., None], data2[..., None]), axis=-1)
    data = np.mean(data, axis=3)  # [Height, Width, Coil, PCs, TRs]

    # Create a mask of the phanton
    band_free = gs_recon(data[:, :, 0, ::4, 0], pc_axis=-1)
    thresh = threshold_li(np.abs(band_free))
    mask = np.abs(band_free) > thresh

    # Apply mask to data
    mask0 = np.tile(mask, (data.shape[2:] + (
        1,
        1,
    ))).transpose((3, 4, 0, 1, 2))
    data = data * mask0

    print(data.shape[:-2])
    data = np.reshape(data, data.shape[:-2] +
                      (-1, ))  # [Height, Width, Coil, PCs x TRs]
    data = np.moveaxis(data, 2, 0)  # [Coil, Height, Width, PCs x TRs]
    data = data.transpose((0, 3, 2, 1))  # [Coil,  PCs x TRs, Width, Height]
示例#22
0
def taylor_method(Is, dphis, alpha, TR, mask=None, chunksize=10,
                  unwrap_fun=None, disp=False):
    '''Parameter mapping for multiple phase-cycled bSSFP.

    Parameters
    ==========
    Is : array_like
        Array of phase-cycled images, (dphi, x, y).
    dphis : array_like
        Phase-cycles (in radians).
    alpha : array_like
        Flip angle map (in rad).
    TR : float
        Repetition time (in sec).
    mask : array_like, optional
        Locations to compute map estimates.
    chunksize : int, optional
        Chunk size to use for parallelized loop.
    unwrap_fun : callable
        Function to do 2d phase unwrapping.  If None, will use
        skimage.restoration.unwrap_phase().
    disp : bool, optional
        Show debugging plots.

    Returns
    =======
    t1map : array_like
        T1 map estimation (in sec)
    t2map : array_like
        T2 map estimation (in sec)
    offresmap : array_like
        Off-resonance map estimation (in Hz)
    m0map : array_like
        Proton density map estimation

    Raises
    ======
    AssertionError
        If number of phase-cycles is not divisible by 4.

    Notes
    =====
    mask=None computes maps for all points.  Note that `Is` must be given as a
    list.
    '''

    # If mask is None, that means we'll do all the points
    if mask is None:
        mask = np.ones(Is[0].shape).astype(bool)

    # If unwrap is None, then use default:
    if unwrap_fun is None:
        from skimage.restoration import unwrap_phase
        unwrap_fun = lambda x: unwrap_phase(x)

    # Calculate the banding-removed image using the algorithm in the elliptical
    # model paper.

    # My addition: average the GS recon over all sets of 4 we have.  This will
    # have to do while I work on a generalization of the GS recon method to
    # handle >=4 phase-cycles at a time.
    assert np.mod(len(Is), 4) == 0, 'We need sets of 4 for GS recon!'
    num_sets = int(len(Is)/4)
    Ms = np.zeros((num_sets,) + Is[0].shape, dtype='complex')
    for ii in range(num_sets):
        Ms[ii, ...] = gs_recon(Is[ii::num_sets, ...])
    M = np.mean(Ms, axis=0)

    # Display elliptical model image.
    if disp:
        plt.imshow(np.abs(M))
        plt.title('Eliptical Model - Banding Removed')
        plt.show()

        # These plots look fishy -- they changed when I moved from Merry's
        # SSFPFit function to my ssfp simulation function.  Need to look at
        # MATLAB output to see who's right and/or what's going on.

        # Choose a row in the mask and show 0, 180 phase cycle lines
        idx = np.argwhere(mask)
        idx = idx[np.random.choice(np.arange(idx.shape[0])), :]
        row, _col = idx[0], idx[1]
        col = np.argwhere(mask)[row, :]
        cols = (np.min(col), np.max(col))

        Is0 = Is[::num_sets, ...]
        plt.suptitle('0 PC')
        plt.subplot(2, 2, 1)
        plt.plot(Is0[0, row, cols[0]:cols[1]].real)
        plt.subplot(2, 2, 2)
        plt.plot(Is0[0, row, cols[0]:cols[1]].imag)
        plt.subplot(2, 2, 3)
        plt.plot(np.abs(Is0[0, row, cols[0]:cols[1]]))
        plt.subplot(2, 2, 4)
        plt.plot(np.angle(Is0[0, row, cols[0]:cols[1]]))
        plt.show()

        plt.suptitle('180 PC')
        plt.subplot(2, 2, 1)
        plt.plot(Is[4, row, cols[0]:cols[1]].real)
        plt.subplot(2, 2, 2)
        plt.plot(Is[4, row, cols[0]:cols[1]].imag)
        plt.subplot(2, 2, 3)
        plt.plot(np.abs(Is[4, row, cols[0]:cols[1]]))
        plt.subplot(2, 2, 4)
        plt.plot(np.angle(Is[4, row, cols[0]:cols[1]]))
        plt.show()
        # compare to Lauzon paper figure 1

    ## Elliptical fit done here
    offres_est = np.angle(M)
    m_offres_est = np.ma.array(offres_est, mask=mask & 0)
    offres_est = unwrap_fun(m_offres_est)*mask
    offres_est /= -1*np.pi*TR # -1 is for sign of phi in ssfp sim
    view(offres_est)

    sh = Is.shape[1:]
    t1map = np.zeros(sh)
    t2map = np.zeros(sh)
    offresmap = np.zeros(sh)
    m0map = np.zeros(sh)

    # Since we have to do it for each pixel and all calculations are
    # independent, we can parallelize this sucker!  Use imap_unordered to to
    # update tqdm progress bar more regularly and use less memory over time.
    tot = np.sum(mask.flatten())
    optim = partial(optim_wrapper, Is=Is, TR=TR, dphis=dphis,
                    offres_est=offres_est, alpha=alpha)
    with Pool() as pool:
        res = list(tqdm(pool.imap_unordered(
            optim, np.argwhere(mask), chunksize=int(chunksize)), total=tot,
                        leave=False, desc='Param mapping'))

    # The answers are then unpacked (not garanteed to be in the right order)
    for ii, jj, xopt in res:
        t1map[ii, jj] = xopt[0]
        t2map[ii, jj] = xopt[1]
        offresmap[ii, jj] = xopt[2]
        m0map[ii, jj] = xopt[3]

    return(t1map, t2map, offresmap, m0map)
示例#23
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)
示例#24
0
    # Let's do one slice for now
    sl = 8
    im = im[:, :, sl, ...].squeeze()
    csm = csm[:, :, sl, ..., 0].squeeze()
    # view(csm)
    print(im.shape, im.shape)

    # Adjust for coil sensitivities
    im0 = np.zeros((im.shape[:3]), dtype='complex')
    print(im0.shape)
    for ii in range(4):
        im0[..., ii] = np.sum(im[..., :, ii]*csm.conj(), axis=-1)

    # Now do GS recon
    TR = 6e-3
    M = gs_recon(im0, pc_axis=-1)
    m = int(M.shape[0]/4)
    M = M[m:-m, :]
    thresh = threshold_li(np.abs(M))
    mask = np.abs(M) > thresh
    df_est0 = mask*np.angle(M)
    # df_est = unwrap_phase(
    #     np.ma.array(df_est, mask=np.abs(mask - 1)))
    # df_est = np.unwrap(df_est, axis=0)*mask
    # df_est = np.unwrap(df_est, axis=1)*mask

    # win = np.hamming(M.shape[1])
    # win = np.outer(win, win)
    # df_est = np.fft.fft2(df_est)*win
    # df_est = np.fft.ifft2(df_est)
    df_est0 /= np.pi*TR
示例#25
0
                                                                   axes=(1,
                                                                         2)),
                                                   axes=(1, 2)),
                                       axes=(1, 2))
    kspace_coil_ims3 = np.fft.fftshift(np.fft.fft2(np.fft.fftshift(coil_ims3,
                                                                   axes=(1,
                                                                         2)),
                                                   axes=(1, 2)),
                                       axes=(1, 2))

    view(np.angle(coil_ims0))

    # Do GS solution to ESM then take SOS
    recon_gs = np.zeros(coil_ims0.shape, dtype='complex')
    for ii in range(num_coils):
        recon_gs[ii, ...] = gs_recon(coil_ims0[ii, ...], coil_ims1[ii, ...],
                                     coil_ims2[ii, ...], coil_ims3[ii, ...])
    recon_gs_sos = sos(recon_gs, axes=(0))
    view(recon_gs_sos)

    # Do PCA
    n_components = 4
    pca0 = coil_pca(coil_ims0, coil_dim=0, n_components=n_components)
    pca1 = coil_pca(coil_ims1, coil_dim=0, n_components=n_components)
    pca2 = coil_pca(coil_ims2, coil_dim=0, n_components=n_components)
    pca3, expl_var = coil_pca(coil_ims3,
                              coil_dim=0,
                              n_components=n_components,
                              give_explained_var=True)
    view(expl_var.real)
    view(np.angle(pca3))
示例#26
0
if __name__ == '__main__':

    # Load in phantom data, shape: (512, 256, 4, 16)
    path = 'mr_utils/test_data/examples/coils/'
    imspace, kspace = load_test_data(path, ['imspace', 'kspace'])
    print(imspace.shape)
    nx, ny, nc, npcs = imspace.shape[:]
    trim = int(nx / 4)
    pcs = np.linspace(0, 2 * np.pi, npcs, endpoint=False)

    # Get mask
    ngs = int(npcs / 4)
    tmp = np.zeros((nx, ny, nc, ngs), dtype='complex')
    for coil in range(nc):
        for ii in range(ngs):
            tmp[..., coil, ii] = gs_recon(imspace[..., coil, ii::4],
                                          pc_axis=-1)
    im_true = np.mean(tmp, axis=-1)
    im_true = sos(im_true, axes=-1)
    thresh = threshold_li(im_true)
    mask = im_true > thresh
    # view(im_true)
    # view(mask)

    # Define coil combination functions
    ccs, cc_list = get_coil_combine_funs(np.min([nx, ny]), v='')

    # Do coil combine then ESM
    res = np.zeros((len(ccs), nx, ny), dtype='complex')
    err_cc_then_gs = np.zeros(len(ccs))
    for fun, cc in enumerate(ccs):
示例#27
0
                          PD,
                          phi_rf=-phi_rf[cc, ...])

        # DON'T DO THIS, STUPID!
        # # phase-cycle correction
        # Imag = np.abs(I[cc, ...])
        # Iphase = np.angle(I[cc, ...]) - np.tile(pcs/2, (N, N, 1)).T
        # I[cc, ...] = Imag*np.exp(1j*Iphase)

    I *= csm_mag
    # view(I.transpose((2, 3, 0, 1)))

    # Estimate the sensitivity maps from coil images
    recons = np.zeros((ncoils, N, N), dtype='complex')
    for cc in range(ncoils):
        recons[cc, ...] = gs_recon(I[cc, ...], pc_axis=0)
    thresh = threshold_li(np.abs(recons))
    mask = np.abs(recons) > thresh
    csm_est, _ = calculate_csm_walsh(recons)

    # This doesn't work as well, still alright, but we knew this about
    # inati
    # csm_est, _ = calculate_csm_inati_iter(recons)

    # # Do the other way: estimate coil sensitivities from phase-cycle
    # csms = np.zeros((npcs, ncoils, N, N))
    # for ii in range(npcs):
    #     csms[ii, ...], _ = calculate_csm_walsh(I[:, ii, ...])
    # csm_est = np.mean(csms, axis=0)

    # # Look at residual phase
示例#28
0
                phi_rf=phi_rf[cc])

        # Do PLANET rotation to get vertical ellipse
        xr, yr, _C0, phis[cc] = do_planet_rotation(I[cc, :])
        I0[cc, :] = xr + 1j*yr

        # plt.plot(I[cc, :].real, I[cc, :].imag)
        # plt.plot(I0[cc, :].real, I0[cc, :].imag)
        # plt.axis('square')
        # plt.show()

    # Get coil sensitivity map estimates
    from ismrmrdtools.coils import calculate_csm_walsh
    recons = np.zeros(ncoils, dtype='complex')
    for cc in range(ncoils):
        view(gs_recon(I[cc, :]))
        # recons[cc] = gs_recon(I[cc, :][:, None, None], pc_axis=-1)
    # csm_est = calculate_csm_walsh(recons[:, None, None])
    # view(csm_est)

    # # Can we find the null?
    # fig, ax1 = plt.subplots()
    # ax2 = ax1.twinx()
    # dfs = np.linspace(-1/TR, 1/TR, lpcs)
    # print(np.rad2deg(phi_rf))
    # print(np.rad2deg(phis))
    # for cc in range(ncoils):
    #     im0 = np.atleast_2d(I[cc, ::2])
    #     im1 = np.atleast_2d(I[cc, 1::2])
    #     recon0 = gs_recon(im0, pc_axis=-1)
    #     recon1 = gs_recon(im1, pc_axis=-1)
示例#29
0
    right_zeros = 30
    slide = 0
    box[left_zeros:-right_zeros] = 1
    box = np.roll(box, int(slide))
    plt.plot(box)
    plt.show()

    # Do the thing!
    I0 = gasp(I, box)
    print(I.shape, I0.shape)

    # Find the 0, 90, 180, 270 phase-cycles to do GS recon on
    idx = [
        ii for ii, val in enumerate(np.rad2deg(pcs).astype(int))
        if val in [0, 90, 180, 270]
    ]

    # Show me da apples!
    plt.imshow(np.abs(gs_recon(I[idx, ...], pc_axis=0)))
    plt.show()
    plt.imshow(np.abs(I0))
    plt.show()

    # Take a slice through the water phantom to compare with desired
    # spectral profile
    mid = int(I0.shape[0] / 4)
    plt.plot(np.abs(I0[mid, :]))
    p = int((I0.shape[1] - box.size) / 2)
    plt.plot(np.concatenate((np.zeros(p), box, np.zeros(p))), '--')
    plt.show()
示例#30
0
    #     view(recon, fft_axes=(1, 2), is_imspace=True,
    #          coil_combine_axis=0)
    # else:
    #     view(recon)
    #     view(gs_recon(recon, pc_axis=0))

    # 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)

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

    # Get a mask so we only look at phantom for comparisons
    thresh = threshold_li(np.abs(im_true))
    mask = np.abs(im_true) > thresh
    # view(mask)