Computerized Tomography

We can solve the Computerized Tomography (CT) problem using the routines in adrt. For a detailed mathematical description, see standard references[1] on the topic.

Here we will be using SciPy’s scipy.sparse.linalg.cg() routine as illustrated in the Iterative Inverse Section. We first import requisite modules then define the ADRTNormalOperator and the function iadrt_cg. However, we modify the operator by adding to it a multiple of the identity and name the new operator ADRTRidgeOperator below.

# Using ADRTNormalOperator from the Iterative Inverse example
class ADRTRidgeOperator(ADRTNormalOperator):
   def __init__(self, img_size, dtype=None, ridge_param=1000.0):
      super().__init__(dtype=dtype, img_size=img_size)
      self._ridge_param = ridge_param

   def _matmat(self, x):
      # Use batch dimensions to handle columns of matrix x
      return super()._matmat(x) + self._ridge_param * x

Using this operator with the CG algorithm yields the solution to the ridge regression problem given by \((A^{T}A + \lambda I)x = A^{T}b\) where \(A\) is the matrix representation of adrt.adrt(), \(I\) is the identity matrix, and \(\lambda \ge 0\) is the ridge parameter.

Forward Data

For this demonstration, we will make use of synthetic forward measurement data for the CT problem. We will attempt to recover the Shepp-Logan phantom (data file) from its Radon transform.

phantom = np.load("data/shepp-logan.npz")["phantom"]
n = phantom.shape[0]

We sample the CT data on a uniform Cartesian grid in the \((\theta, t)\) coordinates using the routine provided in skimage.transform.radon().

from skimage.transform import radon

th_array1 = np.unique(adrt.utils.coord_adrt(n).angle)
theta = 90.0 + np.rad2deg(th_array1.squeeze())
sinogram = radon(phantom, theta=theta)

The sampled sinogram is plotted below. Although this sinogram appears similar to that in the sinogram computation example, there are differences: The grid in the \((\theta, t)\) coordinates used here is different, and the approximation used in discretizing the continuous transform is also different.

plt.imshow(sinogram, aspect="auto")
plt.colorbar();
_images/4027e1c66a2c294ad82422d3860660be56eeb27512d74f3665fe0e5b60a60202.png

Then we use scipy.interpolate.RectBivariateSpline to interpolate the sampled forward data at the ADRT coordinates, forming the ADRT data. We plot the interpolated data below.

from scipy import interpolate

t_array = np.linspace(-0.5, 0.5, n)
spline = interpolate.RectBivariateSpline(t_array, th_array1, sinogram)
s_array, th_array = adrt.utils.coord_adrt(n)
adrt_data = spline(s_array, th_array, grid=False)

adrt_stitched = adrt.utils.stitch_adrt(adrt_data)
plt.imshow(adrt_stitched)
plt.colorbar();
_images/76ebb338cc036e5c9ae70e65aabf53a125a1fc7d2894ec2af622f71ce4a6b777.png

Inversion result

We turn to the solution of the ridge regression problem using the CG algorithm. We also show the inverse computed with adrt.iadrt_fmg() included in the package without any regularization for illustration and comparison.

# Using iadrt_cg from the Iterative Inverse example
cg_inv = iadrt_cg(adrt_data, op_cls=ADRTRidgeOperator)
fmg_inv = adrt.iadrt_fmg(adrt_data)

# Display inversion result
fig, axs = plt.subplots(1, 2, sharey=True)
for ax, data, title in zip(
    axs.ravel(),
    [cg_inv, fmg_inv],
    ["CG Ridge Inverse", "FMG Inverse"],
):
    im_plot = ax.imshow(data, cmap="bone", extent=(0, 1, 0, 1))
    fig.colorbar(im_plot, ax=ax, orientation="horizontal", pad=0.08)
    ax.set_title(title)
fig.tight_layout();
_images/2e5c46f7c21be1414ba7bce62c8ae9f444e003cc87637d1555f2fa3ba395766d.png

The inversion result, together with a slice plot in the horizontal direction, is displayed below.

fig, axs = plt.subplots(
    2, 3, sharex=True, sharey="row",
)
vmin = min(map(np.min, [phantom, cg_inv, fmg_inv]))
vmax = max(map(np.max, [phantom, cg_inv, fmg_inv]))
plot_row = n // 5 * 2
plot_x = np.linspace(0.0, 1.0, n)

for ax, data, title in zip(
    axs.T,
    [phantom, cg_inv, fmg_inv],
    ["Original", "CG Ridge Inverse", "FMG Inverse"],
):
    im_ax, plot_ax = ax
    im_ax.imshow(
        data,
        cmap="bone",
        extent=(0, 1, 0, 1),
        vmin=vmin,
        vmax=vmax,
    )
    im_ax.axhline(0.6, color="C0")
    im_ax.set_title(title)
    plot_ax.plot(plot_x, data[plot_row, :], "C0")
    plot_ax.grid(True)
fig.tight_layout();
_images/de135049bdf93ae9e8d8aa0ed6d22a48d34851e4ba27558eba217ec8f3d87c75.png