|
| 1 | +""" |
| 2 | +This example compares the fista solver against SIRT and weighted least-squares |
| 3 | +reconstructions (implemented with the PDHG algorithm) of the Shepp-Logan phantom. |
| 4 | +
|
| 5 | +@author: Jérome Lesaint, ESRF - The European Synchrotron, Grenoble, France |
| 6 | +""" |
| 7 | + |
| 8 | +from collections.abc import Sequence |
| 9 | + |
| 10 | +import matplotlib.pyplot as plt |
| 11 | +import numpy as np |
| 12 | +import corrct as cct |
| 13 | + |
| 14 | +try: |
| 15 | + import phantom |
| 16 | +except ImportError: |
| 17 | + cct.testing.download_phantom() |
| 18 | + import phantom |
| 19 | + |
| 20 | + |
| 21 | +def cm2inch(x: Sequence[float]) -> tuple[float, float]: |
| 22 | + """Convert cm to inch. |
| 23 | +
|
| 24 | + Parameters |
| 25 | + ---------- |
| 26 | + x : ArrayLike |
| 27 | + Sizes in cm. |
| 28 | +
|
| 29 | + Returns |
| 30 | + ------- |
| 31 | + tuple[float, float] |
| 32 | + Sizes in inch. |
| 33 | + """ |
| 34 | + return tuple(np.array(x) / 2.54) |
| 35 | + |
| 36 | + |
| 37 | +vol_shape = [256, 256, 3] |
| 38 | +data_type = np.float32 |
| 39 | + |
| 40 | +ph_or = np.squeeze(phantom.modified_shepp_logan(vol_shape).astype(data_type)) |
| 41 | +ph_or = ph_or[:, :, 1] |
| 42 | + |
| 43 | +ph, vol_att_in, vol_att_out = cct.testing.phantom_assign_concentration(ph_or) |
| 44 | +# Create sino with no background noise. |
| 45 | +sino, angles, expected_ph, background_avg = cct.testing.create_sino( |
| 46 | + ph, 30, psf=None, add_poisson=True, dwell_time_s=1e-1, background_avg=1e-5, background_std=1e-6, dtype=np.float32 |
| 47 | +) |
| 48 | + |
| 49 | +num_iterations = 100 |
| 50 | +lower_limit = 0 |
| 51 | +vol_mask = cct.processing.circular_mask(ph_or.shape) |
| 52 | + |
| 53 | +sino_variance = cct.processing.compute_variance_poisson(sino) |
| 54 | +sino_weights = cct.processing.compute_variance_weight(sino_variance, normalized=True) |
| 55 | + |
| 56 | +data_term_ls = cct.data_terms.DataFidelity_l2(background=background_avg) |
| 57 | +data_term_lsw = cct.data_terms.DataFidelity_wl2(sino_weights, background=background_avg) |
| 58 | +data_term_kl = cct.data_terms.DataFidelity_KL(background=background_avg) |
| 59 | + |
| 60 | +with cct.projectors.ProjectorUncorrected(ph.shape, angles) as A: |
| 61 | + reg = cct.regularizers.Regularizer_l1dwl(1e-1, "Haar", 3) |
| 62 | + solver_pdhg = cct.solvers.PDHG(verbose=True, data_term=data_term_kl, regularizer=reg) |
| 63 | + rec_pdhg, _ = solver_pdhg(A, sino, num_iterations, x_mask=vol_mask) |
| 64 | + |
| 65 | + solver_sirt = cct.solvers.SIRT(verbose=True, data_term=data_term_lsw) |
| 66 | + rec_sirt, _ = solver_sirt(A, sino, num_iterations, x_mask=vol_mask) |
| 67 | + |
| 68 | + reg = cct.regularizers.Regularizer_l1dwl(1e-2, "Haar", 3) |
| 69 | + solver_fista = cct.solvers.FISTA(verbose=True, data_term=data_term_lsw, regularizer=reg) |
| 70 | + rec_fista, _ = solver_fista(A, sino, num_iterations, x_mask=vol_mask) |
| 71 | + |
| 72 | + |
| 73 | +vminmax = dict(vmin=expected_ph.min(), vmax=expected_ph.max()) |
| 74 | + |
| 75 | +# Reconstructions |
| 76 | +fig = plt.figure(figsize=cm2inch([36, 24])) |
| 77 | +gs = fig.add_gridspec(8, 3) |
| 78 | +ax_ph = fig.add_subplot(gs[:4, 0]) |
| 79 | +im_ph = ax_ph.imshow(expected_ph, **vminmax) |
| 80 | +ax_ph.set_title("Phantom") |
| 81 | +fig.colorbar(im_ph, ax=ax_ph) |
| 82 | + |
| 83 | +ax_sino_clean = fig.add_subplot(gs[4, 0]) |
| 84 | +with cct.projectors.ProjectorUncorrected(ph_or.shape, angles) as p: |
| 85 | + sino_clean = p.fp(expected_ph) |
| 86 | +im_sino_clean = ax_sino_clean.imshow(sino_clean) |
| 87 | +ax_sino_clean.set_title("Clean sinogram") |
| 88 | + |
| 89 | +ax_sino_noise = fig.add_subplot(gs[5, 0]) |
| 90 | +im_sino_noise = ax_sino_noise.imshow(sino - background_avg) |
| 91 | +ax_sino_noise.set_title("Noisy sinogram") |
| 92 | + |
| 93 | +ax_sino_lines = fig.add_subplot(gs[6:, 0]) |
| 94 | +im_sino_lines = ax_sino_lines.plot(sino[9, :] - background_avg, label="Noisy") |
| 95 | +im_sino_lines = ax_sino_lines.plot(sino_clean[9, :], label="Clean") |
| 96 | +ax_sino_lines.set_title("Sinograms - angle: 10") |
| 97 | +ax_sino_lines.legend() |
| 98 | +ax_sino_lines.grid() |
| 99 | + |
| 100 | +ax_wls_l = fig.add_subplot(gs[:4, 1], sharex=ax_ph, sharey=ax_ph) |
| 101 | +im_wls_l = ax_wls_l.imshow(np.squeeze(rec_pdhg), **vminmax) |
| 102 | +ax_wls_l.set_title(solver_pdhg.info().upper()) |
| 103 | +fig.colorbar(im_wls_l, ax=ax_wls_l) |
| 104 | + |
| 105 | +ax_sirt = fig.add_subplot(gs[4:, 1], sharex=ax_ph, sharey=ax_ph) |
| 106 | +im_sirt = ax_sirt.imshow(np.squeeze(rec_sirt), **vminmax) |
| 107 | +ax_sirt.set_title(solver_sirt.info().upper()) |
| 108 | +fig.colorbar(im_sirt, ax=ax_sirt) |
| 109 | + |
| 110 | +ax_fista = fig.add_subplot(gs[:4, 2], sharex=ax_ph, sharey=ax_ph) |
| 111 | +im_fista = ax_fista.imshow(np.squeeze(rec_fista), **vminmax) |
| 112 | +ax_fista.set_title(solver_fista.info().upper()) |
| 113 | +fig.colorbar(im_fista, ax=ax_fista) |
| 114 | + |
| 115 | +axs = fig.add_subplot(gs[4:, 2]) |
| 116 | +axs.plot(np.squeeze(expected_ph[..., 172]), label="Phantom") |
| 117 | +axs.plot(np.squeeze(rec_pdhg[..., 172]), label=solver_pdhg.info().upper()) |
| 118 | +axs.plot(np.squeeze(rec_sirt[..., 172]), label=solver_sirt.info().upper()) |
| 119 | +axs.plot(np.squeeze(rec_fista[..., 172]), label=solver_fista.info().upper()) |
| 120 | +axs.grid() |
| 121 | +axs.legend() |
| 122 | +fig.tight_layout() |
| 123 | + |
| 124 | +# Comparing FRCs for each reconstruction |
| 125 | +labels = [solver_pdhg.info().upper(), solver_sirt.info().upper(), solver_fista.info().upper()] |
| 126 | +vols = [rec_pdhg, rec_sirt, rec_fista] |
| 127 | +cct.processing.post.plot_frcs([(expected_ph, rec) for rec in vols], labels=labels, snrt=0.4142) |
| 128 | + |
| 129 | +plt.show(block=False) |
0 commit comments