Пример #1
0
 def test_im2ksp_noncart(self):
     # Image to k-space transformation for non-Cartesian data
     nufft_obj = nufft_init(kt=ktraj, params=acq_params)
     ksp = im2ksp(
         M=ph, cartesian_opt=0, NufftObj=nufft_obj,
         params=acq_params)  # Sample the image along the trajectory
     self.assertEqual(ksp.shape, ktraj.shape)  # Dimensions match
Пример #2
0
 def test_ksp2im_noncart(self):
     # K-space to image transformation for non-Cartesian
     nufft_obj = nufft_init(kt=ktraj, params=acq_params)
     im = ksp2im(ksp=noncart_ksp,
                 cartesian_opt=0,
                 NufftObj=nufft_obj,
                 params=acq_params)  # reconstructed image
     self.assertEqual(im.shape, ph.shape)  # dimensions match
     im = (im - np.min(im)) / (np.max(im) - np.min(im))  # normalize image
     diff = np.round(im - ph)
     diff_percent = np.nonzero(diff)[0].shape[0] / N**2 * 100
     self.assertLess(
         diff_percent, 5
     )  # Less than 5% of voxels should be different between original and reconstructed image
Пример #3
0
def add_or(M, kt, df, nonCart=None, params=None):
    '''Forward model for off-resonance simulation

    Parameters
    ----------
    M : numpy.ndarray
        Image data
    kt : numpy.ndarray
        k-space trajectory
    df : numpy.ndarray
        Field map
    nonCart : int , optional
        Cartesian/Non-Cartesian trajectory option. Default is None.
    params : dict , optional
        Sequence parameters. Default is None.

    Returns
    -------
    M_or : numpy.ndarray
        Off-resonance corrupted image data
    '''
    '''Create a phase matrix - 2*pi*df*t for every df and every t'''
    if nonCart is not None:
        cartesian_opt = 0
        NufftObj = nufft_init(kt, params)

    else:
        cartesian_opt = 1
        NufftObj = None
        params = None

    kspace = im2ksp(M, cartesian_opt, NufftObj, params)
    M_or = np.zeros(M.shape, dtype=complex)

    for x in range(M.shape[0]):
        for y in range(M.shape[1]):
            phi = 2 * pi * df[x, y] * kt
            kspace_orc = kspace * np.exp(-1j * phi)
            M_corr = ksp2im(kspace_orc, cartesian_opt, NufftObj, params)
            M_or[x, y] = M_corr[x, y]

    return M_or
Пример #4
0
 def test_missing_params_N(self):
     # NUFFT for non-cartesian data will fail if a required parameter is missing from the paramters dictionary (e.g. N)
     params = acq_params.copy()
     del params['N']
     with self.assertRaises(ValueError):
         nufft_init(kt=ktraj, params=params)
Пример #5
0
def MFI(dataIn, dataInType, kt, df, Lx, nonCart=None, params=None):
    '''Off-resonance Correction by Multi-Frequency Interpolation
    Man, L., Pauly, J. M. and Macovski, A. (1997), Multifrequency interpolation for fast off‐resonance correction. Magn. Reson. Med., 37: 785-792. doi:10.1002/mrm.1910370523

    Parameters
    ----------
    dataIn : numpy.ndarray
        k-space raw data or image data
    dataInType : str
        Can be either 'raw' or 'im'
    kt : numpy.ndarray
        k-space trajectory
    df : numpy.ndarray
        Field map
    Lx : int
        L (frequency bins) factor
    nonCart : int
        Non-cartesian trajectory option. Default is None (Cartesian).
    params : dict
        Sequence parameters. Default is None.
    Returns
    -------
    M_hat : numpy.ndarray
        Corrected image data.
    '''

    if nonCart is not None and nonCart == 1:
        check_inputs_noncartesian(dataIn.shape, dataInType, kt.shape, df.shape,
                                  params)
        cartesian_opt = 0
        NufftObj = nufft_init(kt, params)
        t_vector = params['t_vector']
        T = np.tile(params['t_vector'], (1, kt.shape[1]))
        t_ro = T[-1, 0] - T[0, 0]  # T[end] - TE
        N = params['N']

    elif nonCart == 'EPI':
        # check_inputs_cartesian(dataIn.shape, dataInType, kt.shape, df.shape)
        cartesian_opt = 1
        NufftObj = None
        T = np.flipud(params['t_vector']).reshape(params['N'], params['N'])
        T[1:params['N']:2, :] = np.fliplr(T[1:params['N']:2, :])
        N = dataIn.shape[0]
        t_ro = params['t_readout']
        t_vector = params['t_vector']

    else:
        check_inputs_cartesian(dataIn.shape, dataInType, kt.shape, df.shape)
        cartesian_opt = 1
        NufftObj = None
        N = dataIn.shape[0]

        t_vector = kt[0].reshape(kt.shape[1], 1)
        T = kt
        t_ro = T[0, -1] - T[0, 0]

    if dataInType == 'im':
        rawData = im2ksp(dataIn, cartesian_opt, NufftObj, params)
    elif dataInType == 'raw':
        rawData = dataIn

    df = np.round(df, 1)
    idx, idy = np.where(df == -0.0)
    df[idx, idy] = 0.0

    # Number of frequency segments
    df_max = max(np.abs([df.max(), df.min()]))  # Hz
    df_range = (df.min(), df.max())
    L = ceil(df_max * 2 * pi * t_ro / pi)
    L = L * Lx
    if len(np.unique(df)) == 1:
        L = 1
    f_L = np.linspace(df.min(), df.max(), L + 1)  # Hz

    #T = np.tile(params['t_vector'], (1, kt.shape[1]))

    # reconstruct the L basic images
    M_MFI = np.zeros((N, N, L + 1), dtype=complex)
    for l in range(L + 1):
        phi = 2 * pi * f_L[l] * T
        kspace_L = rawData * np.exp(1j * phi)
        M_MFI[:, :, l] = ksp2im(kspace_L, cartesian_opt, NufftObj, params)

    # calculate MFI coefficients
    coeffs_LUT = coeffs_MFI_lsq(kt, f_L, df_range, t_vector)

    # final image reconstruction
    M_hat = np.zeros((N, N), dtype=complex)
    for i in range(M_hat.shape[0]):
        for j in range(M_hat.shape[1]):
            fieldmap_val = df[i, j]
            val_coeffs = coeffs_LUT[str(fieldmap_val)]
            M_hat[i, j] = sum(val_coeffs * M_MFI[i, j, :])

    return M_hat
Пример #6
0
def fs_CPR(dataIn, dataInType, kt, df, Lx, nonCart=None, params=None):
    '''Off-resonance Correction by frequency-segmented Conjugate Phase Reconstruction
    Noll, D. C., Pauly, J. M., Meyer, C. H., Nishimura, D. G. and Macovskj, A. (1992), Deblurring for non‐2D fourier transform magnetic resonance imaging. Magn. Reson. Med., 25: 319-333. doi:10.1002/mrm.1910250210

    Parameters
    ----------
    dataIn : numpy.ndarray
        k-space raw data or image data
    dataInType : str
        Can be either 'raw' or 'im'
    kt : numpy.ndarray
        k-space trajectory
    df : numpy.ndarray
        Field map
    Lx : int
        L (frequency bins) factor
    nonCart : int
        Non-cartesian trajectory option. Default is None (Cartesian).
    params : dict
        Sequence parameters. Default is None (Cartesian).

    Returns
    -------
    M_hat : numpy.ndarray
        Corrected image data.
    '''
    if nonCart is not None and nonCart == 1:
        check_inputs_noncartesian(dataIn.shape, dataInType, kt.shape, df.shape,
                                  params)

        cartesian_opt = 0
        NufftObj = nufft_init(kt, params)
        T = np.tile(params['t_vector'], (1, kt.shape[1]))

        N = params['N']
        t_ro = T[-1, 0] - T[0, 0]  # T[end] - TE

    elif nonCart == 'EPI':
        # check_inputs_cartesian(dataIn.shape, dataInType, kt.shape, df.shape)
        cartesian_opt = 1
        NufftObj = None
        T = np.flipud(params['t_vector']).reshape(params['N'], params['N'])
        T[1:params['N']:2, :] = np.fliplr(T[1:params['N']:2, :])
        N = dataIn.shape[0]
        t_ro = params['t_readout']

    else:
        check_inputs_cartesian(dataIn.shape, dataInType, kt.shape, df.shape)
        cartesian_opt = 1
        NufftObj = None
        N = dataIn.shape[0]

        t_vector = kt[0].reshape(kt.shape[1], 1)
        T = kt
        t_ro = T[0, -1] - T[0, 0]

    if dataInType == 'im':
        rawData = im2ksp(dataIn, cartesian_opt, NufftObj, params)
    elif dataInType == 'raw':
        rawData = dataIn

    # Number of frequency segments
    df_max = max(np.abs([df.max(), df.min()]))  # Hz
    L = ceil(4 * df_max * 2 * pi * t_ro / pi)
    L = L * Lx
    if len(np.unique(df)) == 1:
        L = 1
    f_L = np.linspace(df.min(), df.max(), L + 1)  # Hz

    # T = np.tile(params['t_vector'], (1, kt.shape[1]))

    # reconstruct the L basic images
    M_fsCPR = np.zeros((N, N, L + 1), dtype=complex)
    for l in range(L + 1):
        phi = 2 * pi * f_L[l] * T
        kspace_L = rawData * np.exp(1j * phi)
        M_fsCPR[:, :, l] = ksp2im(kspace_L, cartesian_opt, NufftObj, params)

    # final image reconstruction
    M_hat = np.zeros((N, N), dtype=complex)
    for i in range(M_hat.shape[0]):
        for j in range(M_hat.shape[1]):
            fieldmap_val = df[i, j]
            closest_fL_idx = find_nearest(f_L, fieldmap_val)

            if fieldmap_val == f_L[closest_fL_idx]:
                pixel_val = M_fsCPR[i, j, closest_fL_idx]
            else:
                if fieldmap_val < f_L[closest_fL_idx]:
                    f_vals = [f_L[closest_fL_idx - 1], f_L[closest_fL_idx]]
                    im_vals = [
                        M_fsCPR[i, j, closest_fL_idx - 1],
                        M_fsCPR[i, j, closest_fL_idx]
                    ]
                else:
                    f_vals = [f_L[closest_fL_idx], f_L[closest_fL_idx + 1]]
                    im_vals = [
                        M_fsCPR[i, j, closest_fL_idx],
                        M_fsCPR[i, j, closest_fL_idx + 1]
                    ]

                pixel_val = np.interp(fieldmap_val, f_vals, im_vals)

            M_hat[i, j] = pixel_val

    return M_hat
Пример #7
0
def CPR(dataIn, dataInType, kt, df, nonCart=None, params=None):
    '''Off-resonance Correction by Conjugate Phase Reconstruction
    Maeda, A., Sano, K. and Yokoyama, T. (1988), Reconstruction by weighted correlation for MRI with time-varying gradients. IEEE Transactions on Medical Imaging, 7(1): 26-31. doi: 10.1109/42.3926

    Parameters
    ----------
    dataIn : numpy.ndarray
        k-space raw data or image data
    dataInType : str
        Can be either 'raw' or 'im'
    kt : numpy.ndarray
        k-space trajectory.
    df : numpy.ndarray
        Field map
    nonCart : int
        Non-cartesian trajectory option. Default is None (Cartesian).
    params : dict
        Sequence parameters. Default is None (Cartesian).

    Returns
    -------
    M_hat : numpy.ndarray
        Corrected image data.
    '''

    if nonCart is not None and nonCart == 1:
        check_inputs_noncartesian(dataIn.shape, dataInType, kt.shape, df.shape,
                                  params)
        cartesian_opt = 0
        NufftObj = nufft_init(kt, params)
        T = np.tile(params['t_vector'], (1, kt.shape[1]))
        N = params['N']

    elif nonCart == 'EPI':
        # check_inputs_cartesian(dataIn.shape, dataInType, kt.shape, df.shape)
        cartesian_opt = 1
        NufftObj = None
        T = np.flipud(params['t_vector']).reshape(params['N'], params['N'])
        T[1:params['N']:2, :] = np.fliplr(T[1:params['N']:2, :])

        N = dataIn.shape[0]

    else:
        check_inputs_cartesian(dataIn.shape, dataInType, kt.shape, df.shape)
        cartesian_opt = 1
        NufftObj = None
        T = kt
        N = dataIn.shape[0]

    if dataInType == 'im':
        rawData = im2ksp(dataIn, cartesian_opt, NufftObj, params)
    elif dataInType == 'raw':
        rawData = dataIn

    df_values = np.unique(df)
    M_CPR = np.zeros((N, N, len(df_values)), dtype=complex)
    for i in range(len(df_values)):
        phi = 2 * pi * df_values[i] * T
        kspace_orc = rawData * np.exp(1j * phi)
        M_corr = ksp2im(kspace_orc, cartesian_opt, NufftObj, params)
        M_CPR[:, :, i] = M_corr

    M_hat = np.zeros((N, N), dtype=complex)
    for x in range(df.shape[0]):
        for y in range(df.shape[1]):
            fieldmap_val = df[x, y]
            idx = np.where(df_values == fieldmap_val)
            M_hat[x, y] = M_CPR[x, y, idx]

    return M_hat
Пример #8
0
def add_or_CPR(M, kt, df, nonCart=None, params=None):
    '''Forward model for off-resonance simulation. The number of fourier transforms = number of unique values in the field map.

    Parameters
    ----------
    M : numpy.ndarray
        Image data
    kt : numpy.ndarray
        k-space trajectory
    df : numpy.ndarray
        Field map
    nonCart : int , optional
        Cartesian/Non-Cartesian trajectory option. Default is None.
    params : dict , optional
        Sequence parameters. Default is None.

    Returns
    -------
    M_or : numpy.ndarray
        Off-resonance corrupted image data
    '''
    # Create a phase matrix - 2*pi*df*t for every df and every t
    if nonCart is not None and nonCart == 1:
        cartesian_opt = 0
        NufftObj = nufft_init(kt, params)
        T = np.tile(params['t_vector'], (1, kt.shape[1]))

    elif nonCart == 'EPI':
        cartesian_opt = 1
        NufftObj = None
        T = np.flipud(params['t_vector']).reshape(params['N'], params['N'])
        T[1:params['N']:2, :] = np.fliplr(T[1:params['N']:2, :])

    else:
        cartesian_opt = 1
        NufftObj = None
        params = None
        T = kt

    kspace = im2ksp(M, cartesian_opt, NufftObj, params)

    df_values = np.unique(df)

    M_or_CPR = np.zeros((M.shape[0], M.shape[1], len(df_values)),
                        dtype=complex)
    kspsave = np.zeros((kspace.shape[0], kspace.shape[1], len(df_values)),
                       dtype=complex)
    for i in range(len(df_values)):
        phi = -2 * pi * df_values[i] * T
        kspace_or = kspace * np.exp(1j * phi)
        kspsave[:, :, i] = kspace_or
        M_corr = ksp2im(kspace_or, cartesian_opt, NufftObj, params)
        M_or_CPR[:, :, i] = M_corr

    M_or = np.zeros(M.shape, dtype=complex)
    for x in range(M.shape[0]):
        for y in range(M.shape[1]):
            fieldmap_val = df[x, y]
            idx = np.where(df_values == fieldmap_val)
            M_or[x, y] = M_or_CPR[x, y, idx]
    '''plt.imshow(abs(M_or))
    plt.show()'''
    return M_or, kspsave
Пример #9
0
def spiral_recon(data, ktraj, N, plot=0, save=0, dst_folder=None):
    '''
    Spiral image reconstruction from raw data

    Parameters
    ----------
    data : str or np.ndarray
        Path containing the raw data file of array containing the raw data
    ktraj : np.ndarray
        k-space trajectory coordinates with dimensions [Npoints, Nshots]
    N : int
        Matrix size of the reconstructed image
    plot : bool, Optional
        Plotting a slice of the reconstructed image option. Default is 0 (not plot).
    save : bool, Optional
        Saving the data in a .npy file option. Default is 0 (not save).
    dst_folder : str, Optional
        Path to the folder where the reconstructed image is saved. Default is None.
    '''

    ##
    # Load the raw data
    ##
    if isinstance(data, str):
        dat = get_data_from_file(data)
    elif isinstance(data, np.ndarray):
        dat = data
    ##
    # Acq parameters
    ##
    Npoints = ktraj.shape[0]
    Nshots = ktraj.shape[1]
    Nchannels = dat.shape[-1]

    if len(dat.shape) < 4:
        Nslices = 1
        dat = dat.reshape(Npoints, Nshots, 1, Nchannels)
    else:
        Nslices = dat.shape[-2]

    if dat.shape[0] != ktraj.shape[0] or dat.shape[1] != ktraj.shape[1]:
        raise ValueError('Raw data and k-space trajectory do not match!')

    ##
    # Recon
    ##
    NUFFT_object = nufft_init(ktraj, {
        'N': N,
        'Npoints': Npoints,
        'Nshots': Nshots
    })
    im = np.zeros((N, N, Nslices, Nchannels), dtype=complex)
    for ch in range(Nchannels):
        for sl in range(Nslices):
            im[:, :, sl, ch] = ksp2im(
                dat[:, :, sl, ch], 0, NUFFT_object, {
                    'N': N,
                    'Npoints': Npoints,
                    'Nshots': Nshots
                }
            )  #NufftObj.solve(dat[:,:,sl,ch].flatten(), solver='cg', maxiter=50)

    #sos = np.sum(np.abs(im), -1)
    sos = np.sqrt(np.sum(np.abs(im)**2, -1))
    sos = np.divide(sos, np.max(sos))

    if plot:
        #plt.imshow(np.rot90(np.abs(sos[:,:,0]),-1), cmap='gray')
        plt.imshow(sos[:, :, 0], cmap='gray')
        plt.axis('off')
        plt.title('Uncorrected Image')
        plt.show()

    if save:
        if dst_folder is None:
            raise ValueError('Please specify destination folder')
        np.save(dst_folder + 'uncorrected_spiral.npy', sos)
    return sos