The Fourier transform of a function $g : \mathbb{R} \rightarrow \mathbb{R}$ is given by
$$\widehat{g}(\xi) = \int_{\mathbb{R}} g(x) e^{-2\pi\imath x \xi} \mathrm{d}x.$$The inverse transform is given by
$$g(x) = \int_{\mathbb{R}} \widehat{g}(\xi) e^{2\pi\imath x \xi} \mathrm{d}\xi.$$Similarly, for a function $g : \mathbb{R}^n \rightarrow \mathbb{R}$
$$\widehat{g}(\boldsymbol{\xi}) = \int_{\mathbb{R}^n} g(\mathbf{x}) e^{-2\pi\imath x \cdot \boldsymbol{\xi}} \mathrm{d}\mathbf{x}.$$With inverse
$$g(\mathbf{x}) = \int_{\mathbb{R}^n} \widehat{g}(\boldsymbol{\xi}) e^{2\pi\imath x \cdot \boldsymbol{\xi}} \mathrm{d}\boldsymbol{\xi}.$$The Fourier-Slice Theorem relates the 2D-Fourier transform of $u$ to the 1D-Fourier transform of $f$:
$$\widehat{f}(\theta, \sigma) = \widehat{u}(\sigma \mathbf{n}_\theta),$$with
$$\widehat{f}(\theta,\sigma) = \int_{\mathbb{R}} f(\theta,s) e^{-2\pi\imath \sigma s}\mathrm{d}s,$$$$\widehat{u}(\mathbf{k}) = \int_{\mathbb{R}^2} u(\mathbf{x}) e^{-2\pi\imath \mathbf{k}\cdot \mathbf{x}}\mathrm{d}\mathbf{x},$$$$\mathbf{n}_\theta = (\cos\theta,-\sin\theta).$$We can visualise this as follows
The discrete Fourier transform (DFT) of a sequence $\{g_i\}_{i=0}^n$ is defined as
$$\widehat{g}_i = \sum_{j=0}^{n-1} g_j \exp\left(-\frac{2\pi\imath}{n} ij\right), \quad i = 0, \ldots, n-1.$$When $g_i$ samples $g$ at a grid with spacing $\Delta x$, its Fourier spectrum $\widehat{g}$ is sampled with $\Delta \xi = (n\Delta x)^{-1}$.
We need to be carefull when trying to relate the samples $g_i, \widehat{g}_i$ to the underlying functions $g, \widehat{g}$.
fftshift
and ifftshift
to switch between DFT and normal ordering of the grid.Be aware that some interesting things may happen with the DFT, when subsampling or truncating the signals.
A naive implementation would require $\mathcal{O}(n^2)$ operations to multiply by $F$. With some clever tricks, however, it can be done in $\mathcal{O}(n\log n)$.
To illustrate the concept, consider $n = 2^p$ and express the DFT as
$$\sum_{j=0}^{n-1} g_j \exp\left(-\frac{2\pi\imath}{n} ij\right) = \sum_{j=0}^{n/2-1} g_{2j} \exp\left(-\frac{2\pi\imath}{n} i(2j)\right) + \sum_{j=0}^{n/2-1} g_{2j+1} \exp\left(-\frac{2\pi\imath}{n} i(2j+1)\right)$$$$ = \underbrace{\sum_{j=0}^{n/2-1} g_{2j} \exp\left(-\frac{2\pi\imath}{n/2} ij\right)}_{E_i} + \exp\left(-\frac{2\pi\imath}{n} i\right)\underbrace{\sum_{j=0}^{n/2-1} g_{2j+1} \exp\left(-\frac{2\pi\imath}{n/2} ij\right)}_{O_i}$$We need only compute $E_i$ and $O_i$ for $i = 0, \ldots, n/2-1$ since
$$\widehat{g}_i = E_i + e^{-\frac{2\pi\imath}{n} i} O_i, \quad \widehat{g}_{i+n/2} = E_i - e^{-\frac{2\pi\imath}{n} i} O_i$$This divide-and-conquer technique leads to an overall complexity of $\mathcal{O}(n\log n)$.
Applying the Fourier transform to $f_{ij}$ gives us samples of $\widehat{u}$ at wavenumbers
$$\{(\sigma_i \mathbf{n}_{\theta_j})\},$$with $\sigma_i = i \cdot (n h)^{-1}$ for $i = -(n-1)/2, \ldots, 0, \ldots, (n-1)/2$ (assuming again that $f_{ij} = f(i \cdot h , \theta_j)$ and $n$ odd)
To usefully apply the inverse DFT, we need $\widehat{u}$ at wavenumbers
$$\{(i\cdot (n h)^{-1}, j\cdot (n h )^{-1})\},$$for $i,j = -(n-1)/2, \ldots, 0, \ldots, (n-1)/2$.
In interpolation we aim to approximate the value of a function $g$ at $(x,y)$ from its values at nearby points $g_i = g(x_i,y_i)$.
For linear interpolation, we use a first order polynomial of the form
$$p(x,y) = a \cdot x + b \cdot y + c,$$and set up a system of equations for $(a,b,c)$ by demanding that $p(x_i,y_i) = g_i$.
For bi-linear interpolation, we use a polynomial of the form
$$p(x,y) = a \cdot x \cdot y + b \cdot x + c \cdot y + d,$$and set up a system of equations for $(a,b,c,d)$ by demanding that $p(x_i,y_i) = g_i$.
def lininterp1d(x,g,xs):
"""
Linear interpolation from regular grid.
x - input grid (regularly spaced), 1d array of length n
g - function values, 1d array of length n
xs - values at which to evaluate, 1d array of length ns
"""
n = len(x)
ns = len(xs)
gs = np.zeros(ns)
h = x[1] - x[0]
for i in range(ns):
j = int((xs[i] - x[0]) // h) # find j such that x[j] <= xs[i] < x[j+1]
if 0 <= j < n-1:
gs[i] = ((x[j+1] - xs[i])/h)*g[j] + ((xs[i] - x[j])/h)*g[j+1]
else:
gs[i] = 0
return gs
Implement a function to bi-linearly interpolate data from a polar grid r,theta
to a cartesian grid x,y
. Use the following template
def interpolate(f_hat, theta, ks, kx, ky):
"""
Bi-linear interpolation from radial grid (ks,theta) to cartesian grid (kx,ky),
related via kx = ks*cos(theta), ky = -ks*sin(theta).
The radial coordinates are assumed to include negative radii and theta \in [0,pi).
Input:
f_hat - 2d array of size (ns,ntheta) containing values on the radial grid
theta, ks - 1d arrays of size ns and nt containing the polar gridpoints
kx, ky - 1d arrays of size nx and ny containing the cartesian gridpoints
Output:
u_hat - 2d array of size (nx,nt) containing the interpolated values
"""
Use your interpolation method to reconstruct the following data
# settings
nx = 400
na = 400
theta = np.linspace(0., 180., na)
sigma = 1
# phantom
u = shepp_logan_phantom()
# sinogram
f = radon(u, theta=theta)
f_noisy = f + sigma * np.random.randn(nx,na)
fftshift
and ifftshift
to switch between the regular and DFT ordering of grid.sigma
and angular sampling na
; is Fourier reconstruction more or less sensitive to noise and subsampling than iradon
?