def test_shepp_logan(self): '''The much-abused Shepp-Logan.''' ph = shepp_logan(self.N) ph /= np.max(ph.flatten()) phs = ph[..., None] * self.mps kspace = np.fft.ifftshift(np.fft.fft2(np.fft.fftshift(phs, axes=(0, 1)), axes=(0, 1)), axes=(0, 1)) kspace_u = np.array(np.zeros_like(kspace)) # wrap for pylint kspace_u[:, ::2, :] = kspace[:, ::2, :] ctr = int(self.N / 2) pd = 5 calib = kspace[:, ctr - pd:ctr + pd, :].copy() recon = grappa(kspace_u, calib, (5, 5), coil_axis=-1) recon = np.fft.fftshift(np.fft.ifft2(np.fft.ifftshift(recon, axes=(0, 1)), axes=(0, 1)), axes=(0, 1)) recon = np.abs(np.sqrt(np.sum(recon * np.conj(recon), axis=-1))) recon /= np.max(recon.flatten()) # Make sure less than 4% NRMSE # print(compare_nrmse(ph, recon)) self.assertTrue(compare_nrmse(ph, recon) < .04)
def test_shepplogan_init_returns_expected_starting_volume(self): test_obj = NumericalModel(model="shepp-logan") expected_volume = shepp_logan(128) actual_volume = test_obj._starting_volume np.testing.assert_array_equal(actual_volume, expected_volume)
def test_shepplogan_dims_init_returns_expected_starting_volume(self): dims = 256 test_obj = NumericalModel(model="shepp-logan", num_vox=dims) expected_volume = shepp_logan(dims) actual_volume = test_obj._starting_volume np.testing.assert_array_equal(actual_volume, expected_volume)
def shepp_logan2d(M=64, N=64, nc=4, dtype=np.complex128): '''Make 2d phantom.''' ax = (0, 1) imspace = shepp_logan((M, N)) mps = gaussian_csm(M, N, nc) coil_ims = imspace[..., None]*mps coil_ims = coil_ims.astype(dtype) kspace = np.fft.fftshift(np.fft.fft2(np.fft.ifftshift( coil_ims, axes=ax), axes=ax, norm='ortho'), axes=ax) kspace = kspace.astype(dtype) imspace = np.abs(imspace) imspace /= np.max(imspace.flatten()) # normalize return(imspace, coil_ims, kspace, mps)
def _shepp_logan_brain(self, numVox): self._starting_volume = shepp_logan(numVox) self._volume["proton_density"] = self._customize_shepp_logan( self._starting_volume, self.proton_density["WM"], self.proton_density["GM"], self.proton_density["CSF"], ) self._volume["T2_star"] = self._customize_shepp_logan( self._starting_volume, self.T2_star["WM"], self.T2_star["GM"], self.T2_star["CSF"], )
from phantominator import shepp_logan from matplotlib import pyplot as plt import numpy as np import pynufft as pnft #create the shepp_logan phantom ph = shepp_logan(16) for i in range(8): ph[:, i] = [1] * 16 for i in range(8, 16): ph[:, i] = [0.5] * 16 plt.title('phantom'), plt.xticks([]), plt.yticks([]) plt.imshow(ph, cmap='gray') plt.show() #function that draws the k-space under the effect of the non-uniform B0 def non_uniform_kspace(phantom): NufftObj = pnft.NUFFT() Nd = (phantom.shape[0], phantom.shape[1]) Kd = (2 * phantom.shape[0], 2 * phantom.shape[1]) Jd = (6, 6) om = np.random.randn(15120, 3) NufftObj.plan(om, Nd, Kd, Jd) x = NufftObj.forward(phantom) Data = NufftObj.solve(x, solver='cg', maxiter=50) k = np.fft.fft2(Data) k_space = (np.fft.fftshift(k)) / np.sqrt(k)
'''Show basic usage of NL-GRAPPA MATLAB port.''' import numpy as np import matplotlib.pyplot as plt from phantominator import shepp_logan from pygrappa import nlgrappa_matlab from utils import gaussian_csm if __name__ == '__main__': # Generate data N, nc = 128, 8 sens = gaussian_csm(N, N, nc) im = shepp_logan(N) im = im[..., None]*sens sos = np.sqrt(np.sum(np.abs(im)**2, axis=-1)) off = 0 # starting sampling location # The number of ACS lines R = 5 nencode = 42 # The convolution size num_block = 2 num_column = 15 # make smaller to go quick during development # Obtain ACS data and undersampled data sx, sy, nc = im.shape[:] sx2 = int(sx/2)
'''Demonstrate how to use MR Shepp-Logan.''' import matplotlib.pyplot as plt from phantominator import shepp_logan if __name__ == '__main__': # Get proton density, T1, and T2 maps for (L, M, N) sized Shepp- # Logan phantom. L, M, N = 256, 255, 20 M0, T1, T2 = shepp_logan((L, M, N), MR=True, zlims=(-.25, .25)) # Take a look nx, ny = 2, 2 plt.subplot(nx, ny, 1) plt.imshow(M0[..., 0]) plt.title('Proton Density') plt.axis('off') plt.subplot(nx, ny, 2) plt.imshow(T1[..., 0]) plt.title('T1') plt.axis('off') plt.subplot(nx, ny, 3) plt.imshow(T2[..., 0]) plt.title('T2') plt.axis('off') plt.show()
pyfftw.interfaces.cache.enable() #parameters N = 256 floatType = np.complex twoQuads = True p = nt.nearestPrime(N) p = N #------------------------------- #load kspace data #from scipy.io import loadmat #load Cartesian data #Attention: You must ensure the kspace data is correctly centered or not centered. image = shepp_logan(N) kspace = fftpack.fft2(image) print("kSpace Shape:", kspace.shape) kMaxValue = np.max(kspace) kMinValue = np.min(kspace) print("k-Space Max Value:", kMaxValue) print("k-Space Min Value:", kMinValue) print("k-Space Max Magnitude:", np.abs(kMaxValue)) print("k-Space Min Magnitude:", np.abs(kMinValue)) #------------------------------- #compute the Cartesian reconstruction for comparison print("Computing Chaotic Reconstruction...") dftSpace = kspace image = fftpack.ifft2(dftSpace) #the '2' is important image = np.abs(image)
'''Demonstrate usage of iGRAPPA.''' import numpy as np import matplotlib.pyplot as plt from phantominator import shepp_logan from pygrappa import igrappa, cgrappa from utils import gaussian_csm if __name__ == '__main__': # Simple phantom N = 128 ncoil = 5 csm = gaussian_csm(N, N, ncoil) ph = shepp_logan(N)[..., None]*csm # Throw into k-space ax = (0, 1) kspace = np.fft.ifftshift(np.fft.fft2(np.fft.fftshift( ph, axes=ax), axes=ax), axes=ax) ref = kspace.copy() # Small ACS region: 4x4 pad = 2 ctr = int(N/2) calib = kspace[ctr-pad:ctr+pad, ctr-pad:ctr+pad, :].copy() # R=2x2 kspace[::2, 1::2, :] = 0 kspace[1::2, ::2, :] = 0
'''Show basic usage of GS solution.''' import numpy as np import matplotlib.pyplot as plt from phantominator import shepp_logan from ssfp import bssfp, gs_recon if __name__ == '__main__': # Shepp-Logan N = 128 M0 = shepp_logan(N) T1, T2 = M0 * 2, M0 / 2 # Simulate bSSFP acquisition with linear off-resonance TR, alpha = 3e-3, np.deg2rad(30) pcs = np.linspace(0, 2 * np.pi, 4, endpoint=False) df, _ = np.meshgrid(np.linspace(-1 / TR, 1 / TR, N), np.linspace(-1 / TR, 1 / TR, N)) sig = bssfp(T1, T2, TR, alpha, field_map=df, phase_cyc=pcs[None, None, :], M0=M0) # Show the phase-cycled images nx, ny = 2, 2 plt.figure() for ii in range(nx * ny):
'''Basic usage of CG-SENSE implementation.''' import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import make_axes_locatable from phantominator import shepp_logan from pygrappa import cgsense from utils import gaussian_csm if __name__ == '__main__': N, nc = 128, 4 sens = gaussian_csm(N, N, nc) im = shepp_logan(N) im = im[..., None] * sens kspace = np.fft.fftshift(np.fft.fft2(np.fft.ifftshift(im, axes=(0, 1)), axes=(0, 1)), axes=(0, 1)) # Undersample kspace[::2, 1::2, :] = 0 kspace[1::2, ::2, :] = 0 # SOS of the aliased image aliased = np.fft.ifftshift(np.fft.ifft2(np.fft.fftshift(kspace, axes=(0, 1)), axes=(0, 1)), axes=(0, 1)) aliased = np.sqrt(np.sum(np.abs(aliased)**2, axis=-1))
def planet_shepp_logan_example(): # Shepp-Logan N, nslices, npcs = 128, 1, 8 # 2 slices just to show we can M0, T1, T2 = shepp_logan((N, N, nslices), MR=True, zlims=(-.25, 0)) # Simulate bSSFP acquisition with linear off-resonance TR, alpha = 3e-3, np.deg2rad(15) pcs = np.linspace(0, 2 * np.pi, npcs, endpoint=False) df, _ = np.meshgrid(np.linspace(-1 / TR, 1 / TR, N), np.linspace(-1 / TR, 1 / TR, N)) sig = np.empty((npcs, ) + T1.shape, dtype='complex') for sl in range(nslices): sig[..., sl] = bssfp(T1[..., sl], T2[..., sl], TR, alpha, field_map=df, phase_cyc=pcs, M0=M0[..., sl]) # Do T1, T2 mapping for each pixel mask = np.abs(M0) > 1e-8 # Make it noisy np.random.seed(0) sig += 1e-5 * (np.random.normal(0, 1, sig.shape) + 1j * np.random.normal(0, 1, sig.shape)) * mask print(sig.shape, alpha, TR, mask.shape) # Show the phase-cycled images nx, ny = 2, 4 plt.figure() for ii in range(nx * ny): plt.subplot(nx, ny, ii + 1) plt.imshow(np.abs(sig[ii, :, :, 0])) plt.title('%d deg PC' % (ii * (360 / npcs))) plt.show() # Do the thing t0 = perf_counter() Mmap, T1est, T2est, dfest = planet(sig, alpha, TR, mask=mask, pc_axis=0) print('Took %g sec to run PLANET' % (perf_counter() - t0)) print(T1est.shape, T2est.shape, dfest.shape, T1.shape, T2.shape, mask.shape) # Look at a single slice sl = 0 T1est = T1est[..., sl] T2est = T2est[..., sl] dfest = dfest[..., sl] T1 = T1[..., sl] T2 = T2[..., sl] mask = mask[..., sl] # Simple phase unwrapping of off-resonance estimate dfest = unwrap_phase(dfest * 2 * np.pi * TR) / (2 * np.pi * TR) print('t1, mask:', T1.shape, mask.shape) nx, ny = 3, 3 plt.subplot(nx, ny, 1) plt.imshow(T1 * mask) plt.title('T1 Truth') plt.axis('off') plt.subplot(nx, ny, 2) plt.imshow(T1est) plt.title('T1 est') plt.axis('off') plt.subplot(nx, ny, 3) plt.imshow(T1 * mask - T1est) plt.title('NRMSE: %g' % normalized_root_mse(T1, T1est)) plt.axis('off') plt.subplot(nx, ny, 4) plt.imshow(T2 * mask) plt.title('T2 Truth') plt.axis('off') plt.subplot(nx, ny, 5) plt.imshow(T2est) plt.title('T2 est') plt.axis('off') plt.subplot(nx, ny, 6) plt.imshow(T2 * mask - T2est) plt.title('NRMSE: %g' % normalized_root_mse(T2, T2est)) plt.axis('off') plt.subplot(nx, ny, 7) plt.imshow(df * mask) plt.title('df Truth') plt.axis('off') plt.subplot(nx, ny, 8) plt.imshow(dfest) plt.title('df est') plt.axis('off') plt.subplot(nx, ny, 9) plt.imshow(df * mask - dfest) plt.title('NRMSE: %g' % normalized_root_mse(df * mask, dfest)) plt.axis('off') plt.show()
d, info = cg(A, b, x0=x0, maxiter=100) print(info) print(d.shape) return np.moveaxis(np.reshape(d, (nx, ny, nc)), -1, coil_axis) if __name__ == '__main__': import matplotlib.pyplot as plt from phantominator import shepp_logan from utils import gaussian_csm N, nc = 128, 8 ph = shepp_logan(N)[..., None] * gaussian_csm(N, N, nc) kspace = np.fft.ifftshift(np.fft.fft2(np.fft.fftshift(ph, axes=(0, 1)), axes=(0, 1)), axes=(0, 1)) # Get calibration region (20x20) pd = 10 ctr = int(N / 2) calib = kspace[ctr - pd:ctr + pd, ctr - pd:ctr + pd, :].copy() # undersample by a factor of 2 in both kx and ky kspace[::2, 1::2, :] = 0 kspace[1::2, ::2, :] = 0 res = pruno(kspace, calib)
import matplotlib.pyplot as plt from skimage.metrics import normalized_root_mse from phantominator import shepp_logan from ssfp import bssfp, gs_recon, fimtre, planet if __name__ == '__main__': fimtre_results = 1 planet_results = 0 sigma = 4e-5 resid_mult = 10 # Shepp-Logan N, nslices = 256, 1 M0, T1, T2 = shepp_logan((N, N, nslices), MR=True, zlims=(-.25, 0)) M0, T1, T2 = np.squeeze(M0), np.squeeze(T1), np.squeeze(T2) mask = np.abs(M0) > 1e-8 # Simulate bSSFP acquisition with linear off-resonance TR0, TR1 = 3e-3, 6e-3 alpha = np.deg2rad(80) alpha_lo = np.deg2rad(12) pcs8 = np.linspace(0, 2 * np.pi, 8, endpoint=False) pcs6 = np.linspace(0, 2 * np.pi, 6, endpoint=False) pcs4 = np.linspace(0, 2 * np.pi, 4, endpoint=False) pcs2 = np.linspace(0, 2 * np.pi, 2, endpoint=False) df, _ = np.meshgrid(np.linspace(-1 / (2 * TR1), 1 / (2 * TR1), N), np.linspace(-1 / (2 * TR1), 1 / (2 * TR1), N)) df *= mask
'''Example demonstrating how to make a Shepp-Logan phantom.''' import numpy as np import matplotlib.pyplot as plt from phantominator import shepp_logan if __name__ == '__main__': # The original Shepp-Logan ph = shepp_logan(128, modified=False) plt.subplot(1, 2, 1) plt.title('Shepp-Logan') plt.imshow(ph, cmap='gray') # Modified Shepp-Logan for better contrast ph = shepp_logan(128, modified=True) plt.subplot(1, 2, 2) plt.title('Modified Shepp-Logan') plt.imshow(ph, cmap='gray') plt.show() # Generate phantoms with different sizes ph = shepp_logan((128, 256)) plt.title('Strange sizes') plt.imshow(ph, cmap='gray') plt.show() # Get a 3D phantom ph = shepp_logan((128, 128, 20), zlims=(-.5, .5)) # Fancy dancing to nicely show all slices on same plot
'''Basic usage of CG-SENSE implementation.''' import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.axes_grid1 import make_axes_locatable from phantominator import shepp_logan from pygrappa import cgsense from utils import gaussian_csm if __name__ == '__main__': N, nc = 128, 4 sens = gaussian_csm(N, N, nc) im = shepp_logan(N) + np.finfo('float').eps im = im[..., None] * sens kspace = np.fft.fftshift(np.fft.fft2(np.fft.ifftshift(im, axes=(0, 1)), axes=(0, 1)), axes=(0, 1)) # Undersample kspace[::2, 1::2, :] = 0 kspace[1::2, ::2, :] = 0 # SOS of the aliased image aliased = np.fft.ifftshift(np.fft.ifft2(np.fft.fftshift(kspace, axes=(0, 1)), axes=(0, 1)), axes=(0, 1)) aliased = np.sqrt(np.sum(np.abs(aliased)**2, axis=-1))
from phantominator import shepp_logan from skimage.metrics import normalized_root_mse as compare_nrmse # pylint: disable=E0611,E0401 from pygrappa import vcgrappa, grappa from utils import gaussian_csm if __name__ == '__main__': # Simple phantom N = 128 ncoil = 8 _, phi = np.meshgrid( # background phase variation np.linspace(-np.pi, np.pi, N), np.linspace(-np.pi, np.pi, N)) phi = np.exp(1j * phi) csm = gaussian_csm(N, N, ncoil) ph = shepp_logan(N) * phi ph = ph[..., None] * csm # Throw into k-space ax = (0, 1) kspace = np.fft.ifftshift(np.fft.fft2(np.fft.fftshift(ph, axes=ax), axes=ax), axes=ax) # 24 ACS lines pad = 12 ctr = int(N / 2) calib = kspace[ctr - pad:ctr + pad, ...].copy() # R=4 kspace[1::4, ...] = 0
'''Show basic usage of spoiled GRE.''' import numpy as np import matplotlib.pyplot as plt from phantominator import shepp_logan from ssfp import spoiled_gre if __name__ == '__main__': N = 128 M0, T1, T2 = shepp_logan((N, N, 1), MR=True, zlims=(-.25, -.25)) M0, T1, T2 = M0[..., 0], T1[..., 0], T2[..., 0] TR, TE = 0.035, 0.01 alpha = np.deg2rad(70) res = spoiled_gre(T1, T2, TR, TE, alpha=alpha, M0=M0) plt.imshow(res) plt.show()
import numpy as np import matplotlib.pyplot as plt from phantominator import shepp_logan from skimage.measure import compare_nrmse from pygrappa import cgrappa, grappaop from utils import gaussian_csm if __name__ == '__main__': # Make a simple phantom -- note that GRAPPA operator only works # well with pretty well separated coil sensitivities, so using # these simple maps we don't expect GRAPPA operator to work as # well as GRAPPA when trying to do "GRAPPA" things N, nc = 256, 16 ph = shepp_logan(N)[..., None] * gaussian_csm(N, N, nc) # Put into kspace ax = (0, 1) kspace = np.fft.ifftshift(np.fft.fft2(np.fft.fftshift(ph, axes=ax), axes=ax), axes=ax) # 20x20 calibration region ctr = int(N / 2) pad = 10 calib = kspace[ctr - pad:ctr + pad, ctr - pad:ctr + pad, :].copy() # Undersample: R=4 kspace4x1 = kspace.copy() kspace4x1[1::4, ...] = 0
import numpy as np import matplotlib.pyplot as plt from phantominator import shepp_logan from pygrappa import cgsense from utils import gaussian_csm if __name__ == '__main__': # Generate fake sensitivity maps: mps L, M, N = 128, 128, 32 ncoils = 4 mps = gaussian_csm(L, M, ncoils)[..., None, :] # generate 3D phantom ph = shepp_logan((L, M, N), zlims=(-.25, .25)) imspace = ph[..., None] * mps ax = (0, 1, 2) kspace = np.fft.fftshift(np.fft.fftn(np.fft.ifftshift(imspace, axes=ax), axes=ax), axes=ax) # undersample by a factor of 2 in both kx and ky kspace[::2, 1::2, ...] = 0 kspace[1::2, ::2, ...] = 0 # Do the recon t0 = time() res = cgsense(kspace, mps) print('Took %g sec' % (time() - t0))
'''Basic demo of Slice-GRAPPA.''' import numpy as np from phantominator import shepp_logan import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation from pygrappa import slicegrappa from utils import gaussian_csm if __name__ == '__main__': # Get slices of 3D Shepp-Logan phantom N = 128 ns = 2 ph = shepp_logan((N, N, ns), zlims=(-.3, 0)) # Apply some coil sensitivities ncoil = 8 csm = gaussian_csm(N, N, ncoil) ph = ph[..., None, :]*csm[..., None] # Shift one slice FOV/2 (SMS-CAIPI) ph[..., -1] = np.fft.fftshift(ph[..., -1], axes=0) # Put into kspace ax = (0, 1) kspace = np.fft.fftshift(np.fft.fft2(np.fft.ifftshift( ph, axes=ax), axes=ax), axes=ax) # Calibration data is individual slices
if __name__ == '__main__': # Generate fake sensitivity maps: mps N = 128 ncoils = 4 xx = np.linspace(0, 1, N) x, y = np.meshgrid(xx, xx) mps = np.zeros((N, N, ncoils)) mps[..., 0] = x**2 mps[..., 1] = 1 - x**2 mps[..., 2] = y**2 mps[..., 3] = 1 - y**2 # generate 4 coil phantom ph = shepp_logan(N) imspace = ph[..., None] * mps imspace = imspace.astype('complex') # Use NamedTemporaryFiles for kspace and reconstruction results with NTF() as kspace_file, NTF() as res_file: # Make a memmap kspace = np.memmap(kspace_file, mode='w+', shape=(N, N, ncoils), dtype='complex') # Fill the memmap with kspace data (remember the [:]!!!) ax = (0, 1) kspace[:] = 1 / np.sqrt(N**2) * np.fft.fftshift( np.fft.fft2(np.fft.ifftshift(imspace, axes=ax), axes=ax), axes=ax)
from phantominator import shepp_logan from cv2 import imwrite size_list = [256 * 2**i for i in range(3)] if __name__ == '__main__': for size in size_list: sl = shepp_logan(size) sl = (sl * 255).astype(int) imwrite("res/shepp-logan{}.jpg".format(size), sl)