https://github.com/jyhmiinlin/pynufft
Tip revision: 4f6d6637a032111295abc3b878c9b670f234395a authored by Your Name on 24 December 2016, 10:52:16 UTC
version 0.3.1.8
version 0.3.1.8
Tip revision: 4f6d663
pynufft.py
'''
@package docstring
The MIT License (MIT)
Copyright (c) 2013 - 2016 pynufft team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''
import numpy
import scipy.sparse
import numpy.fft
import scipy.signal
import scipy.linalg
import scipy.special
def dirichlet(x):
return numpy.sinc(x)
def outer_sum(xx, yy):
return numpy.add.outer(xx,yy)
# nx = numpy.size(xx)
# ny = numpy.size(yy)
#
# arg1 = numpy.tile(xx, (ny, 1)).T
# arg2 = numpy.tile(yy, (nx, 1))
# return arg1 + arg2
def nufft_offset(om, J, K):
'''
For every om points(outside regular grids), find the nearest
central grid (from Kd dimension)
'''
gam = 2.0 * numpy.pi / (K * 1.0)
k0 = numpy.floor(1.0 * om / gam - 1.0 * J / 2.0) # new way
return k0
def nufft_alpha_kb_fit(N, J, K, dtype):
'''
find out parameters alpha and beta
of scaling factor st['sn']
Note, when J = 1 , alpha is hardwired as [1,0,0...]
(uniform scaling factor)
'''
beta = 1
Nmid = (N - 1.0) / 2.0
if N > 40:
L = 13
else:
L = numpy.ceil(N / 3)
nlist = numpy.arange(0, N) * 1.0 - Nmid
(kb_a, kb_m) = kaiser_bessel('string', J, 'best', 0, K / N)
if J > 1:
sn_kaiser = 1 / kaiser_bessel_ft(nlist / K, J, kb_a, kb_m, 1.0)
elif J == 1: # The case when samples are on regular grids
sn_kaiser = numpy.ones((1, N), dtype=dtype)
gam = 2 * numpy.pi / K
X_ant = beta * gam * nlist.reshape((N, 1), order='F')
X_post = numpy.arange(0, L + 1)
X_post = X_post.reshape((1, L + 1), order='F')
X = numpy.dot(X_ant, X_post) # [N,L]
X = numpy.cos(X)
sn_kaiser = sn_kaiser.reshape((N, 1), order='F').conj()
X = numpy.array(X, dtype=dtype)
sn_kaiser = numpy.array(sn_kaiser, dtype=dtype)
coef = numpy.linalg.lstsq(X, sn_kaiser)[0] # (X \ sn_kaiser.H);
alphas = coef
if J > 1:
alphas[0] = alphas[0]
alphas[1:] = alphas[1:] / 2.0
elif J == 1: # cases on grids
alphas[0] = 1.0
alphas[1:] = 0.0
alphas = numpy.real(alphas)
return (alphas, beta)
def kaiser_bessel(x, J, alpha, kb_m, K_N):
if K_N != 2:
kb_m = 0
alpha = 2.34 * J
else:
kb_m = 0
# Parameters in Fessler's code
# because it was experimentally determined to be the best!
# input: number of interpolation points
# output: Kaiser_bessel parameter
jlist_bestzn = {2: 2.5,
3: 2.27,
4: 2.31,
5: 2.34,
6: 2.32,
7: 2.32,
8: 2.35,
9: 2.34,
10: 2.34,
11: 2.35,
12: 2.34,
13: 2.35,
14: 2.35,
15: 2.35,
16: 2.33}
if J in jlist_bestzn:
alpha = J * jlist_bestzn[J]
else:
tmp_key = (jlist_bestzn.keys())
min_ind = numpy.argmin(abs(tmp_key - J * numpy.ones(len(tmp_key))))
p_J = tmp_key[min_ind]
alpha = J * jlist_bestzn[p_J]
kb_a = alpha
return (kb_a, kb_m)
def kaiser_bessel_ft(u, J, alpha, kb_m, d):
'''
interpolation weight for given J/alpha/kb-m
'''
u = u * (1.0 + 0.0j)
import scipy.special
z = numpy.sqrt((2 * numpy.pi * (J / 2) * u) ** 2.0 - alpha ** 2.0)
nu = d / 2 + kb_m
y = ((2 * numpy.pi) ** (d / 2)) * ((J / 2) ** d) * (alpha ** kb_m) / \
scipy.special.iv(kb_m, alpha) * scipy.special.jv(nu, z) / (z ** nu)
y = numpy.real(y)
return y
def nufft_scale1(N, K, alpha, beta, Nmid):
'''
calculate image space scaling factor
'''
# import types
# if alpha is types.ComplexType:
alpha = numpy.real(alpha)
# print('complex alpha may not work, but I just let it as')
L = len(alpha) - 1
if L > 0:
sn = numpy.zeros((N, 1))
n = numpy.arange(0, N).reshape((N, 1), order='F')
i_gam_n_n0 = 1j * (2 * numpy.pi / K) * (n - Nmid) * beta
for l1 in range(-L, L + 1):
alf = alpha[abs(l1)]
if l1 < 0:
alf = numpy.conj(alf)
sn = sn + alf * numpy.exp(i_gam_n_n0 * l1)
else:
sn = numpy.dot(alpha, numpy.ones((N, 1)))
return sn
def nufft_scale(Nd, Kd, alpha, beta):
dd = numpy.size(Nd)
Nmid = (Nd - 1) / 2.0
if dd == 1:
sn = nufft_scale1(Nd, Kd, alpha, beta, Nmid)
else:
sn = 1
for dimid in numpy.arange(0, dd):
tmp = nufft_scale1(Nd[dimid], Kd[dimid], alpha[dimid],
beta[dimid], Nmid[dimid])
sn = numpy.dot(list(sn), tmp.H)
return sn
def mat_inv(A):
# I = numpy.eye(A.shape[0], A.shape[1])
B = scipy.linalg.pinv2(A)
return B
def nufft_T(N, J, K, alpha, beta):
'''
equation (29) and (26)Fessler's paper
create the overlapping matrix CSSC (diagonal dominent matrix)
of J points
and then find out the pseudo-inverse of CSSC '''
# import scipy.linalg
L = numpy.size(alpha) - 1
# print('L = ', L, 'J = ',J, 'a b', alpha,beta )
cssc = numpy.zeros((J, J))
[j1, j2] = numpy.mgrid[1:J + 1, 1:J + 1]
overlapping_mat = j2 - j1
for l1 in range(-L, L + 1):
for l2 in range(-L, L + 1):
alf1 = alpha[abs(l1)]
# if l1 < 0: alf1 = numpy.conj(alf1)
alf2 = alpha[abs(l2)]
# if l2 < 0: alf2 = numpy.conj(alf2)
tmp = overlapping_mat + beta * (l1 - l2)
tmp = dirichlet(1.0 * tmp / (1.0 * K / N))
cssc = cssc + alf1 * alf2 * tmp
return mat_inv(cssc)
def iterate_sum(rr, alf, r1):
rr = rr + alf * r1
return rr
def iterate_l1(L, alpha, arg, beta, K, N, rr):
oversample_ratio = (1.0 * K / N)
import time
t0=time.time()
for l1 in range(-L, L + 1):
alf = alpha[abs(l1)] * 1.0
# if l1 < 0:
# alf = numpy.conj(alf)
# r1 = numpy.sinc(1.0*(arg+1.0*l1*beta)/(1.0*K/N))
input_array = (arg + 1.0 * l1 * beta) / oversample_ratio
r1 = dirichlet(input_array)
rr = iterate_sum(rr, alf, r1)
return rr
def nufft_r(om, N, J, K, alpha, beta):
'''
equation (30) of Fessler's paper
'''
M = numpy.size(om) # 1D size
gam = 2.0 * numpy.pi / (K * 1.0)
nufft_offset0 = nufft_offset(om, J, K) # om/gam - nufft_offset , [M,1]
dk = 1.0 * om / gam - nufft_offset0 # om/gam - nufft_offset , [M,1]
arg = outer_sum(-numpy.arange(1, J + 1) * 1.0, dk)
L = numpy.size(alpha) - 1
# print('alpha',alpha)
rr = numpy.zeros((J, M), dtype=numpy.float32)
rr = iterate_l1(L, alpha, arg, beta, K, N, rr)
return (rr, arg)
def block_outer_prod(x1, x2):
'''
multiply the amplitudes along different axes
'''
(J1, M) = x1.shape
(J2, M) = x2.shape
# print(J1,J2,M)
xx1 = x1.reshape((J1, 1, M), order='F') # [J1 1 M] from [J1 M]
xx1 = numpy.tile(xx1, (1, J2, 1)) # [J1 J2 M], emulating ndgrid
xx2 = x2.reshape((1, J2, M), order='F') # [1 J2 M] from [J2 M]
xx2 = numpy.tile(xx2, (J1, 1, 1)) # [J1 J2 M], emulating ndgrid
y = xx1 * xx2
return y # [J1 J2 M]
def block_outer_sum(x1, x2):
'''
update the new index after adding a new axis
'''
(J1, M) = x1.shape
(J2, M) = x2.shape
xx1 = x1.reshape((J1, 1, M), order='F') # [J1 1 M] from [J1 M]
xx1 = numpy.tile(xx1, (1, J2, 1)) # [J1 J2 M], emulating ndgrid
xx2 = x2.reshape((1, J2, M), order='F') # [1 J2 M] from [J2 M]
xx2 = numpy.tile(xx2, (J1, 1, 1)) # [J1 J2 M], emulating ndgrid
y = xx1 + xx2
return y # [J1 J2 M]
def crop_slice_ind(Nd):
'''
Return the "slice" of Nd size.
In Python language, "slice" means the index of a matrix.
Slice can provide a smaller "view" of a larger matrix.
'''
return [slice(0, Nd[ss]) for ss in range(0, len(Nd))]
class pynufft:
"""
The class pynufft computes Non-Uniform Fast Fourier Transform (NUFFT).
Using Fessler and Sutton's min-max interpolator algorithm.
"Fessler JA, Sutton BP. Nonuniform fast Fourier transforms using min-max interpolation. IEEE Trans Signal Process 2003;51(2):560-574."
Methods
----------
__init__() : constructor
Input:
None
Return:
pynufft instance
Example: MyNufft = pynufft.pynufft()
plan(om, Nd, Kd, Jd) : to plan the pynufft object
Input:
om: M * ndims array: The locations of M non-uniform points in the ndims dimension. Normalized between [-pi, pi]
Nd: tuple with ndims elements. Image matrix size. Example: (256,256)
Kd: tuple with ndims elements. Oversampling k-space matrix size. Example: (512,512)
Jd: tuple with ndims elements. The number of adjacent points in the interpolator. Example: (6,6)
Return:
None
forward(x) : perform NUFFT
Input:
x: numpy.array. The input image on the regular grid. The size must be Nd.
Output:
y: M array.The output M points array.
adjoint(y): adjoint NUFFT (Hermitian transpose (a.k.a. conjugate transpose) of NUFFT)
Note: adjoint is not the inverse of forward NUFFT,
because Non-uniform coordinates cause uneven density,
which must be compensated by "density compensation (DC)"
See inverse_DC() method
Input:
y: M array.The input M points array.
Output:
x: numpy.array. The output image on the regular grid.
inverse_DC(y): inverse NUFFT using Pipe's sampling density compensation (James Pipe, Magn. Res. Med., 1999)
Note: adjoint is not the inverse of forward NUFFT,
because Non-uniform coordinates cause uneven density,
which must be compensated by "density compensation (DC)"
Note: A more accurate inverse NUFFT requires iterative reconstruction,
such as conjugate gradient method (CG) or other optimization methods.
Input:
y: M array.The input M points array.
Output:
x: numpy.array. The output image on the regular grid.
"""
def __init__(self):
'''
Construct the pynufft instance
'''
self.dtype = numpy.complex64
def plan(self, om, Nd, Kd, Jd):
'''
Plan pyNufft
Start from here
om: M * ndims array: The locations of M non-uniform points in the ndims dimension. Normalized between [-pi, pi]
Nd: tuple with ndims elements. Image matrix size. Example: (256,256)
Kd: tuple with ndims elements. Oversampling k-space matrix size. Example: (512,512)
Jd: tuple with ndims elements. The number of adjacent points in the interpolator. Example: (6,6)
'''
self.debug = 0 # debug
if type(Nd) != tuple:
raise TypeError('Nd must be tuple, e.g. (256, 256)')
if type(Kd) != tuple:
raise TypeError('Kd must be tuple, e.g. (512, 512)')
if type(Jd) != tuple:
raise TypeError('Jd must be tuple, e.g. (6, 6)')
if (len(Nd) != len(Kd)) | (len(Nd) != len(Jd)) | len(Kd) != len(Jd):
raise KeyError('Nd, Kd, Jd must be in the same length, e.g. Nd=(256,256),Kd=(512,512),Jd=(6,6)')
# dimensionality of input space (usually 2 or 3)
dd = numpy.size(Nd)
###############################################################
# check input errors
###############################################################
st = {}
ud = {}
kd = {}
n_shift = tuple(0*x for x in Nd)
###############################################################
# First, get alpha and beta: the weighting and freq
# of formula (28) in Fessler's paper
# in order to create slow-varying image space scaling
###############################################################
for dimid in range(0, dd):
(tmp_alpha, tmp_beta) = nufft_alpha_kb_fit(
Nd[dimid], Jd[dimid], Kd[dimid],
self.dtype)
st.setdefault('alpha', []).append(tmp_alpha)
st.setdefault('beta', []).append(tmp_beta)
st['tol'] = 0
st['Jd'] = Jd
st['Nd'] = Nd
st['Kd'] = Kd
M = om.shape[0]
st['M'] = M
st['om'] = om
st['sn'] = numpy.array(1.0 + 0.0j)
dimid_cnt = 1
###############################################################
# create scaling factors st['sn'] given alpha/beta
# higher dimension implementation
###############################################################
for dimid in range(0, dd):
tmp = nufft_scale(
Nd[dimid],
Kd[dimid],
st['alpha'][dimid],
st['beta'][dimid])
dimid_cnt = Nd[dimid] * dimid_cnt
###############################################################
# higher dimension implementation: multiply over all dimension
###############################################################
st['sn'] = numpy.dot(st['sn'], tmp.T)
st['sn'] = numpy.reshape(st['sn'], (dimid_cnt, 1), order='F')
# JML do not apply scaling
# order = 'F' is for fortran order
st['sn'] = st['sn'].reshape(Nd, order='F') # [(Nd)]
###############################################################
# else:
# st['sn'] = numpy.array(st['sn'],order='F')
###############################################################
st['sn'] = numpy.real(st['sn']) # only real scaling is relevant
# [J? M] interpolation coefficient vectors.
# Iterate over all dimensions and
# multiply the coefficients of all dimensions
for dimid in range(0, dd): # loop over dimensions
N = Nd[dimid]
J = Jd[dimid]
K = Kd[dimid]
alpha = st['alpha'][dimid]
beta = st['beta'][dimid]
###############################################################
# formula 29 , 26 of Fessler's paper
###############################################################
# pseudo-inverse of CSSC using large N approx [J? J?]
T = nufft_T(N, J, K, alpha, beta)
###############################################################
# formula 30 of Fessler's paper
###############################################################
(r, arg) = nufft_r(om[:, dimid], N, J,
K, alpha, beta) # large N approx [J? M]
###############################################################
# formula 25 of Fessler's paper
###############################################################
c = numpy.dot(T, r)
###############################################################
# grid intervals in radius
###############################################################
gam = 2.0 * numpy.pi / (K * 1.0)
phase_scale = 1.0j * gam * (N - 1.0) / 2.0
phase = numpy.exp(phase_scale * arg) # [J? M] linear phase
ud[dimid] = phase * c
# indices into oversampled FFT components
# FORMULA 7
koff = nufft_offset(om[:, dimid], J, K)
# FORMULA 9, find the indexes on Kd grids, of each M point
kd[dimid] = numpy.mod(
outer_sum(
numpy.arange(
1,
J + 1) * 1.0,
koff),
K)
if dimid > 0: # trick: pre-convert these indices into offsets!
# ('trick: pre-convert these indices into offsets!')
kd[dimid] = kd[dimid] * numpy.prod(Kd[0:dimid]) - 1
kk = kd[0] # [J1 M] # pointers to indices
uu = ud[0] # [J1 M]
Jprod = Jd[0]
Kprod = Kd[0]
for dimid in range(1, dd):
Jprod = numpy.prod(Jd[:dimid + 1])
Kprod = numpy.prod(Kd[:dimid + 1])
kk = block_outer_sum(kk, kd[dimid]) + 1 # outer sum of indices
kk = kk.reshape((Jprod, M), order='F')
# outer product of coefficients
uu = block_outer_prod(uu, ud[dimid])
uu = uu.reshape((Jprod, M), order='F')
# now kk and uu are [*Jd M]
# now kk and uu are [*Jd M]
# *numpy.tile(phase,[numpy.prod(Jd),1]) # product(Jd)xM
uu = uu.conj()
mm = numpy.arange(0, M) # indices from 0 to M-1
mm = numpy.tile(mm, [numpy.prod(Jd), 1]) # product(Jd)xM
# Now build sparse matrix from uu, mm, kk
# convert array to list
csrdata = numpy.reshape(uu, (Jprod * M, ), order='F')
# row indices, from 1 to M, convert array to list
rowindx = numpy.reshape(mm, (Jprod * M, ), order='F')
# colume indices, from 1 to prod(Kd), convert array to list
colindx = numpy.reshape(kk, (Jprod * M, ), order='F')
# The shape of sparse matrix
csrshape = (M, numpy.prod(Kd))
# Build sparse matrix (interpolator)
st['p'] = scipy.sparse.csr_matrix((csrdata, (rowindx, colindx)),
shape=csrshape)#.tocsr()
# Note: the sparse matrix requires the following linear phase,
# which moves the image to the center of the image
self.st = st
self.Nd = self.st['Nd'] # backup
self.sn = self.st['sn'] # backup
self.ndims=len(self.st['Nd']) # dimension
self.linear_phase(n_shift)
# calculate the linear phase thing
self.st['W'] = self.pipe_density()
def pipe_density(self):
'''
Create the density function by iterative solution
Generate pHp matrix
'''
# W = pipe_density(self.st['p'])
# sampling density function
W = numpy.ones((self.st['M'],),dtype=self.dtype)
V1= self.st['p'].getH()
# VVH = V.dot(V.getH())
for pp in range(0,1):
# E = self.st['p'].dot(V1.dot(W))
E = self.forward(self.adjoint(W))
W = W/E
# pHp = V1.dot(self.st['p'])
# Precomputing the regridding matrix * interpolation matrix
# W_diag = scipy.sparse.diags(W,0, dtype=dtype,format="csr")
# pHWp = V1.dot(W_diag.dot(self.st['p']))
return W
# density of the k-space, reduced size
def linear_phase(self, n_shift):
'''
This method shifts the interpolation matrix self.st['p0']
and create a shifted interpolation matrix self.st['p']
Parameters
----------
n_shift : tuple
Input shifts. integers
Raises
------
ValueError
If `s` and `axes` have different length.
IndexError
If an element of `axes` is larger than than the number of axes of `a`.
'''
om = self.st['om']
M = self.st['M']
final_shifts = tuple(
numpy.array(n_shift) +
numpy.array(
self.st['Nd']) /
2)
phase = numpy.exp(
1.0j *
numpy.sum(
om *
numpy.tile(
final_shifts,
(M,
1)),
1))
# add-up all the linear phasees in all axes,
self.st['p'] = scipy.sparse.diags(phase, 0).dot(self.st['p'])
return 0 # shifted sparse matrix
def forward(self, x):
'''
This method computes the Non-Uniform Fast Fourier Transform.
input:
x: Nd array
output:
y: (M,) array
'''
y = self.k2y(self.xx2k(self.x2xx(x)))
return y
def adjoint(self, y):
'''
adjoint method (not inverse, see inverse_DC() method) computes the adjoint transform
(conjugate transpose, or Hermitian transpose) of forward
Non-Uniform Fast Fourier Transform.
input:
y: non-uniform data, (M,) array
output:
x: adjoint image, Nd array
'''
x = self.xx2x(self.k2xx(self.y2k(y)))
return x
def forwardadjoint(self, x):
x2 = self.adjoint(self.forward(x))
# x2 = self.xx2x(self.k2xx(self.k2k(self.xx2k(self.x2xx(x)))))
return x2
def forward_modulate_adjoint(self, x):
x2 = self.adjoint(self.st['W']*self.forward(x))
# x2 = self.xx2x(self.k2xx(self.k2k(self.xx2k(self.x2xx(x)))))
return x2
def inverse_DC(self,y):
'''
reconstruction of non-uniform data y into image
using density compensation method
input:
y: (M,) array
output:
x2: Nd array
'''
x2 = self.adjoint(self.st['W']*y)
return x2
def x2xx(self, x):
'''
scaling of the image, generate Nd array
Scaling of image is equivalent to convolution in k-space.
Thus, scaling improves the accuracy of k-space interpolation.
input:
x: 2D image
output:
xx: scaled 2D image
'''
xx = x * self.st['sn']
return xx
def y2k_DC(self,y):
'''
Density compensated, adjoint transform of the non-uniform data (y: (M,) array) to k-spectrum (Kd array)
Note: adjoint is not the inverse of forward NUFFT,
because Non-uniform coordinates cause uneven density,
which must be compensated by "density compensation (DC)"
k-spectrum requires another numpy.fft.fftshift to move the k-space center.
input:
y: (M,) array
output
k: Kd array
'''
k = self.y2k(y*self.st['W'])
return k
def xx2k(self, xx):
'''
fft of the image
input:
xx: scaled 2D image
output:
k: k-space grid
'''
dd = numpy.size(self.st['Kd'])
output_x = numpy.zeros(self.st['Kd'], dtype=self.dtype)
output_x[crop_slice_ind(xx.shape)] = xx
k = numpy.fft.fftn(output_x, self.st['Kd'], range(0, dd))
return k
def k2vec(self,k):
k_vec = numpy.reshape(k, (numpy.prod(self.st['Kd']), ), order='F')
return k_vec
def vec2y(self,k_vec):
'''
gridding:
'''
y = self.st['p'].dot(k_vec)
return y
def k2y(self, k):
'''
2D k-space grid to 1D array
input:
k: k-space grid,
output:
y: non-Cartesian data
'''
y = self.vec2y(self.k2vec(k)) #numpy.reshape(self.st['p'].dot(Xk), (self.st['M'], ), order='F')
return y
def y2vec(self, y):
'''
regridding non-uniform data, (unsorted vector)
'''
k_vec = self.st['p'].getH().dot(y)
return k_vec
def vec2k(self, k_vec):
'''
Sorting the vector to k-spectrum Kd array
'''
k = numpy.reshape(k_vec, self.st['Kd'], order='F')
return k
def y2k(self, y):
'''
Conjugate transpose of min-max interpolator
input:
y: non-uniform data, (M,) array
output:
k: k-spectrum on the Kd grid (Kd array)
'''
k_vec = self.y2vec(y)
k = self.vec2k(k_vec)
return k
def k2xx(self, k):
'''
Transform regular k-space (Kd array) to scaled image xx (Nd array)
'''
dd = numpy.size(self.st['Kd'])
xx = numpy.fft.ifftn(k, self.st['Kd'], range(0, dd))
xx = xx[crop_slice_ind(self.st['Nd'])]
return xx
def xx2x(self, xx):
'''
Rescaled image xx (Nd array) to x (Nd array)
Thus, rescaling improves the accuracy of k-space interpolation.
'''
x = self.x2xx(xx)
return x
def k2k(self, k):
'''
gridding and regridding of k-spectrum
input
k: Kd array,
output
k: Kd array
'''
Xk = numpy.reshape(k, (numpy.prod(self.st['Kd']), ), order='F')
# y = numpy.reshape(self.st['p'].dot(Xk), (self.st['M'], ), order='F')
k = self.st['pHp'].dot(Xk)
k = numpy.reshape(k, self.st['Kd'], order='F')
return k
def test_2D():
import pkg_resources
DATA_PATH = pkg_resources.resource_filename('pynufft', 'data/')
PHANTOM_FILE = pkg_resources.resource_filename('pynufft', 'data/phantom_256_256.txt')
import numpy
import matplotlib.pyplot
# load example image
image = numpy.loadtxt(DATA_PATH +'phantom_256_256.txt')
#numpy.save('phantom_256_256',image)
print('loading image...')
matplotlib.pyplot.imshow(image.real, cmap=matplotlib.cm.gray)
matplotlib.pyplot.show()
Nd = (256, 256) # image size
print('setting image dimension Nd...', Nd)
Kd = (512, 512) # k-space size
print('setting spectrum dimension Kd...', Kd)
Jd = (6, 6) # interpolation size
print('setting interpolation size Jd...', Jd)
# load k-space points
om = numpy.loadtxt(DATA_PATH+'om.txt')
print('setting non-uniform coordinates...')
matplotlib.pyplot.plot(om[::10,0],om[::10,1],'o')
matplotlib.pyplot.title('non-uniform coordinates')
matplotlib.pyplot.xlabel('axis 0')
matplotlib.pyplot.ylabel('axis 1')
matplotlib.pyplot.show()
NufftObj = pynufft()
NufftObj.plan(om, Nd, Kd, Jd)
y = NufftObj.forward(image)
print('setting non-uniform data')
print('y is an (M,) list',type(y), y.shape)
kspectrum = NufftObj.y2k_DC(y)
shifted_kspectrum = numpy.fft.fftshift(kspectrum, axes=(0,1))
print('getting the k-space spectrum, shape =',kspectrum.shape)
print('Showing the shifted k-space spectrum')
matplotlib.pyplot.imshow( shifted_kspectrum.real, cmap = matplotlib.cm.gray, norm=matplotlib.colors.Normalize(vmin=-100, vmax=100))
matplotlib.pyplot.title('shifted k-space spectrum')
matplotlib.pyplot.show()
image2 = NufftObj.adjoint(y * NufftObj.st['W'])
matplotlib.pyplot.imshow(image2.real, cmap=matplotlib.cm.gray, norm=matplotlib.colors.Normalize(vmin=0.0, vmax=1))
matplotlib.pyplot.show()
if __name__ == '__main__':
'''
Test the module pynufft
'''
test_2D()