elif trajectory == 'spiral': traj = Spiral(FOV=FOV, res=res, oversampling=2, lines_per_shot=2, interleaves=10) # traj.plot_trajectory() # Non-cartesian kspace K = TrajToImage(traj.points, 1000.0 * traj.times, Mxy, r, 50.0) # Non-uniform fft x0 = traj.points[0].flatten().reshape((-1, 1)) x1 = traj.points[1].flatten().reshape((-1, 1)) x0 *= res[0] // 2 / x0.max() x1 *= res[1] // 2 / x1.max() dcf = (x0**2 + x1**2)**0.5 # density compensation function kxky = np.concatenate((x0, x1), axis=1) # flattened kspace coordinates y = K.flatten().reshape((-1, 1)) # flattened kspace measures image = nufft_adjoint(y * dcf, kxky, res) # inverse nufft # Show results fig, axs = plt.subplots(2, 3, figsize=(10, 10)) axs[0, 0].imshow(np.abs(K_c[::2, :])) axs[0, 1].imshow(np.abs(ktoi(K_c[::2, :]))) axs[0, 2].imshow(np.angle(ktoi(K_c[::2, :]))) axs[1, 0].imshow(np.abs(itok(image))) axs[1, 1].imshow(np.abs(image)) axs[1, 2].imshow(np.angle(image)) plt.show()
np.abs(ktoi(D1.k)[..., 1, 0]).max() ]) for n, nlevel in enumerate(noise_levels): # Standard deviation of the noise std = nlevel * I_max_r0 # Generate noise in the image domain noise_1 = np.random.normal(0, std, D1.k.shape) \ + 1j*np.random.normal(0, std, D1.k.shape) noise_2 = np.random.normal(0, std, D2.k.shape) \ + 1j*np.random.normal(0, std, D2.k.shape) # Noise in the fourier domain f_noise_1 = D1.k_msk * itok(noise_1) f_noise_2 = D2.k_msk * itok(noise_2) # Noisy kspaces nH1, nH2 = (H1.k + f_noise_1), (H2.k + f_noise_2) nS1, nS2 = (S1.k + f_noise_1), (S2.k + f_noise_2) nD1, nD2 = (D1.k + f_noise_1), (D2.k + f_noise_2) # kspace to images IH1, IH2 = ktoi(nH1), ktoi(nH2) IS1, IS2 = ktoi(nS1), ktoi(nS2) ID1, ID2 = ktoi(nD1), ktoi(nD2) if n == 1: s = np.std(np.real(noise_1 - noise_2).flatten()) m = np.max(np.abs(ktoi(D1.k - D2.k).flatten()))
def get_exact_image(image, epi, phantom, parameters, debug=False): """ Generate EXACT images """ # Time-steping parameters t = parameters.t dt = parameters.dt n_t = parameters.time_steps # Sequence parameters ke = image.encoding_frequency # encoding frequency M0 = image.M0 # thermal equilibrium magnetization # Regional M0 M0r = np.zeros([phantom.spins.Nb_samples,1]) if isinstance(M0,np.ndarray) or isinstance(M0,list): M0r[phantom.spins.regions[:,0]] = M0[0] # inner M0r[phantom.spins.regions[:,1]] = M0[1] # outer M0r[phantom.spins.regions[:,2]] = M0[2] # ventricle else: M0r[phantom.spins.regions[:,2]] = M0 # ventricle # Determine if the image and phantom geometry are 2D or 3D di = image.type_dim() # image geometric dimension dp = phantom.x.shape[-1] # phantom (fem) geometric dimension dk = np.sum(int(k/k) for k in ke if k!= 0) # Number of encoding directions # Output image size = np.append(image.resolution, [dk, n_t]) mask = np.zeros(np.append(image.resolution, n_t), dtype=np.float32) # Output kspaces k_nsa_1 = kspace(size, image, epi) k_mask = kspace(size, image, epi) # Spins positions x = phantom.x # Check k space bandwidth to avoid folding artifacts res, incr_bw, D = check_kspace_bw(image, x) # Grid, voxel width, image resolution and number of voxels Xf = D['grid'] width = D['voxel_size'] resolution = D['resolution'] nr_voxels = Xf[0].size # Check if the number of slices needs to be increased # for the generation of the connectivity when generating # Slice-Following (SF) images # if image.slice_following: # Xf, SL = check_nb_slices(Xf, x, width, res) # Connectivity (this is done just once) voxel_coords = [X.flatten('F') for X in Xf] (s2p, excited_spins) = getConnectivity3(x, voxel_coords, width) s2p = np.array(s2p) # Spins positions with respect to its containing voxel center # Obs: the option -order='F'- is included to make the grid of the # Z coordinate at the end of the flattened array corners = np.array([Xf[j].flatten('F')[s2p]-0.5*width[j] for j in range(di)]).T x_rel = x[:, 0:dp] - corners # List of spins inside the excited slice # if image.slice_following: # voxel_coords = [X.flatten('F') for X in D['grid']] # reset voxel coords # Magnetization images and spins magnetizations m0_image = np.zeros(np.append(resolution, dk), dtype=np.complex128) m1_image = np.zeros(np.append(resolution, dk), dtype=np.complex128) # Grid to evaluate magnetizations X = D['grid'] # Resolutions and cropping ranges ovrs_fac = image.oversampling_factor r, c, dr, dc = cropping_ranges(image.resolution, resolution, ovrs_fac) # Spins inside the ventricle exc_slice = (x[:,2] < image.center[2] + image.slice_offset + 0.5*image.slice_thickness) exc_slice *= (x[:,2] > image.center[2] + image.slice_offset - 0.5*image.slice_thickness) # Time stepping for time_step in range(n_t): # Update time t += dt # Get displacements in the reference frame and deform mesh u = phantom.displacement(time_step) reshaped_u = u.vector() # Displacement in terms of pixels x_new = x_rel #+ reshaped_u - upre pixel_u = np.floor(np.divide(x_new, width)) subpixel_u = x_new - np.multiply(pixel_u, width) # Change spins connectivity according to the new positions update_s2p(s2p, pixel_u, resolution) # Update pixel-to-spins connectivity # if image.slice_following: # p2s = update_p2s(s2p-SL, excited_spins, nr_voxels) # else: # p2s = update_p2s(s2p, excited_spins, nr_voxels) p2s = update_p2s(s2p, excited_spins, nr_voxels) # Update relative spins positions x_rel[:,:] = subpixel_u # Updated spins positions x_upd = x # Get magnetization on each spin mags = EXACT_magnetizations(M0r, ke[0:dk], reshaped_u[:,0:dk]) for i in range(len(mags)): mags[i][(~exc_slice),:] = 0 mags[-1][~phantom.spins.regions[:,2]] = 0 # # Debug # if MPI_rank==0: # from PyMRStrain.IO import write_vtk # from PyMRStrain.Math import wrap # S2P = Function(u.spins, dim=1) # spins-to-pixel connectivity # EXC = Function(u.spins, dim=1) # excited slice (spins) # rot = Function(u.spins, dim=1) # theta = np.arctan(x[:,1]/x[:,0]) # rot.vector()[:] = wrap(theta.reshape((-1,1)), np.pi/8) # EXC.vector()[exc_slice] = 1 # S2P.vector()[excited_spins] = s2p.reshape((-1,1)) # write_vtk([u,S2P,EXC,rot], path='output/Normal_{:04d}.vtu'.format(time_step), name=['displacement','s2p_connectivity','slice','rot']) # Fill images # Obs: the option -order='F'- is included because the grid was flattened # using this option. Therefore the reshape must be performed accordingly (I, m) = get_images(mags, x_upd, voxel_coords, width, p2s) if debug: MPI_print('Time step {:d}. Average nb. of spins inside a voxel: {:.0f}'.format(time_step, MPI_size*0.5*(m.max()-m.min()))) else: MPI_print('Time step {:d}'.format(time_step)) # Gather results m0_image[...] = gather_image(I[0].reshape(m0_image.shape,order='F')) m1_image[...] = gather_image(I[1].reshape(m1_image.shape,order='F')) # Iterates over slices for slice in range(resolution[2]): # Complex magnetization data for enc_dir in range(dk): # Magnetization expressions tmp0 = m0_image[...,slice,enc_dir] tmp1 = m1_image[...,slice,enc_dir] # Uncorrected kspaces k0 = restore_resolution(itok(tmp0), r, c, dr, dc, enc_dir, image.resolution, ovrs_fac) k1 = restore_resolution(itok(tmp1), r, c, dr, dc, enc_dir, image.resolution, ovrs_fac) # import matplotlib.pyplot as plt # from PyMRStrain.Math import ktoi # fig,ax = plt.subplots(1,2) # ax[0].imshow(np.angle(ktoi(itok(tmp0)[1::ovrs_fac,:]))) # ax[1].imshow(np.angle(ktoi(k0))) # plt.show(block=False) # plt.pause(5) # kspace resizing and epi artifacts generation delta_ph = image.FOV[m_dirs[enc_dir][1]]/image.phase_profiles k_nsa_1.gen_to_acq(k0, delta_ph, m_dirs[enc_dir], slice, enc_dir, time_step) k_mask.gen_to_acq(k1, delta_ph, m_dirs[enc_dir], slice, enc_dir, time_step) return k_nsa_1, k_mask