# Tutorial In this tutorial, we will first learn the basics of how to reconstruct projection data. We will then introduce attenuation correction, data terms, and regularizers. Finally, we will see the more advanced topics like guided regularization parameter selection. ## Reconstructing projection data ### The data model In `corrct`, the volumes are always organized with coordinates \[Z\]YX, where Z is only present in 3D volumes. The projection data is organized with coordinates \[V\]WU, where: * V is the optional vertical coordinate of the detector images. It is only present for 3D reconstructions. * W is the rotation angle coordinate. W stands for omega, which is the rotation angle. * U is the horizontal coordinate of the detector images. ### The geometry The geometry is supposed to be circular parallel-beam by default, at least in the simplest settings. Attenuation correction is usually meant in X-ray Fluorescence (XRF) settings. Thus, the detector is supposed to be pointed at the sample with an angles, that needs to be specified. The rotation axis is supposed to be in the center of the reconstruction volume. Shifts in the rotation axis position are either intended as or converted to detector shifts with respect to the origin. ### The projectors The projection geometry is specified through the creation of *projectors* from the [`projectors`](corrct.html#module-corrct.projectors) module. The simplest projector is called [`ProjectorUncorrected`](corrct.html#corrct.projectors.ProjectorUncorrected), and it serves as basis for more complex projectors. The projector [`ProjectorAttenuationXRF`](corrct.html#corrct.projectors.ProjectorAttenuationXRF) derives from the [`ProjectorUncorrected`](corrct.html#corrct.projectors.ProjectorUncorrected), and it implements the XRF specific bits, with respect to multi-detector / multi-element handling and attenuation correction. Projectors are usually used through the `with` statement. This takes care of initializing and de-initializing their underlying resources (e.g. GPU usage). To create a simple projector for a `10 x 10` volume and `16 x 10` sinogram (16 angles), we will do: ```python import numpy as np import corrct as cct vol_shape_xy = [10, 10] angles_rad = np.deg2rad(np.linspace(0, 180, 16, endpoint=False)) with cct.projectors.ProjectorUncorrected(vol_shape_xy, angles_rad) as p: prj = p(np.ones((10, 10))) ``` This code also uses the projector to create a sinogram, that is the projection of a volume of all ones. The back-projection can be done in a very similar manner: ```python with cct.projectors.ProjectorUncorrected(vol_shape_xy, angles_rad) as p: vol = p.T(np.ones((16, 10))) ``` Creating attenuation correction projectors is a bit more involved, and we will see it later in the [attenuation correction](#attenuation-correction) section. Projectors can use different backends, depending on the available packages, system resources, and user requests. The included projector backends are based on the `scikit-image` and `astra-toolbox` packages. They can be selected by passing the strings `"astra"` or `"skimage"` to the parameter `backend`. Advanced users can create custom backends, by deriving the base class `ProjectorBackend` from the module `_projector_backends`. ### The solvers Tomographic reconstructions can be achieved using either the included solvers from [`solvers`](corrct.html#module-corrct.solvers) module, or with `scipy`'s solvers. The included solvers are: * Filtered Back-Projection: [**FBP**](corrct.html#corrct.solvers.FBP). * Simultaneous Algebraic Reconstruction Technique [**SART**](corrct.html#corrct.solvers.SART). * Simultaneous Iterative Reconstruction Technique [**SIRT**](corrct.html#corrct.solvers.SIRT). * Primal-Dual Hybrid Gradient [**PDHG**](corrct.html#corrct.solvers.PDHG), from Chambolle and Pock. #### FBP FBP is the only analytical (non iterative) algorithm in the group. It exposes one parameter that is not available for the other methods: `fbp_filter`. This parameter can either be: * a filter name, as available from `scikit-image`. * a custom filter, specified by the user. * an MR data-driven filter, as per [1]. Reconstructing with FBP can be done like the following, assuming that the `16 x 10` sinogram is contained in the variable called `sino`: ```python import numpy as np import corrct as cct vol_shape_xy = [10, 10] angles_rad = np.deg2rad(np.linspace(0, 180, 16, endpoint=False)) solver_fbp = cct.solvers.FBP() with cct.projectors.ProjectorUncorrected(vol_shape_xy, angles_rad) as p: vol, _ = solver_fbp(p, sino) ``` By default, the `"ramp"` filter is selected. Another filter, like the Hann filter can be selected, by passing the `fbp_filter` parameter at initialization: ```python solver_fbp = cct.solvers.FBP(fbp_filter="shepp-logan") ``` #### SIRT, PDHG, and MLEM The SIRT and PDHG algorithms, are algebraic (iterative) methods. They both support regularization, and box constraints on the solution. The PDHG also supports various data fidelity terms. The MLEM algorithm is also an iterative algorithm to find the maximum likelihood estimation of the reconstructed signal. The MLEM does not currently support any regularization. The interface of the iterative methods is the same as for the FBP, with the only difference of requiring an iterations count: ```python import numpy as np import corrct as cct vol_shape_xy = [10, 10] angles_rad = np.deg2rad(np.linspace(0, 180, 16, endpoint=False)) solver_sirt = cct.solvers.SIRT() with cct.projectors.ProjectorUncorrected(vol_shape_xy, angles_rad) as p: vol, _ = solver_sirt(p, sino, iterations=100) ``` It is possible to specify an intial solution or box limits on the solutions like the following: ```python x0 = np.ones(vol_shape_xy) # Initial solution lower_limit = 0.0 # Constraint with cct.projectors.ProjectorUncorrected(vol_shape_xy, angles_rad) as p: vol, _ = solver_sirt(p, sino, iterations=100, x0=x0, lower_limit=lower_limit) ``` The same goes for the parameter `upper_limit`. The MLEM algorithm assumes a `lower_limit` of 0. ## Attenuation correction This package implements the attenuation correction method described in [2]. The correction of the attenuation effects is subject to the knowledge of an attenuation map for the following experimental conditions: * Acquisition geometry (i.e. sample rotation angles, beam size / resolution, detector position, etc) * Excitation beam energy and emission photon energy * Sample morphology and local average composition This is usually achieved in two ways. The simplest way is to provide the projector [`ProjectorAttenuationXRF`](corrct.html#corrct.projectors.ProjectorAttenuationXRF) with the corresponding attenuation maps for the excitation beam and emitted photons. The respective parameters are: `att_in` and `att_out`. This also requires to provide the angle(s) of the detector(s) with respect to the incoming beam direction, through the parameter `angles_detectors_rad`. The values in `att_in` and `att_out` should be in "linear attenuation" per pixel length. The values in `angles_detectors_rad` should be in radians, as suggested by the name of the parameter. The drawback of the simple way is that the computed local attenuation per angle cannot be re-used with other projectors, and the computation / scaling of the maps is delegated entirely to the user. The user can also choose to use the class [`AttenuationVolume`](corrct.html#corrct.attenuation.AttenuationVolume) from the [`attenuation`](corrct.html#module-corrct.attenuation) module. This class is used internally in the projector [`ProjectorAttenuationXRF`](corrct.html#corrct.projectors.ProjectorAttenuationXRF), and it can be used particularly in conjunction with the class [`VolumeMaterial`](corrct.html#corrct.physics.VolumeMaterial) from the [`physics`](corrct.html#module-corrct.physics) module. For a dedicated description of the projection and attenuation correction geometry, the reader can have a look at the dedicated [geometry page](geometry.md). For a in-depth description of the functionality available in the physics module, instead, the reader can have a look at the dedicated [physics page](physics_model.md). ## Data terms and regularization Iterative methods support regularizers, and data fidelity terms. The former can be used to impose prior knowledge on the reconstructed solution, while the latter impose prior knowledge on the weight given to the data points. ### Regularizers Famous regularizers are the TV-min and wavelet l1-min. They can be found in the [`regularizers`](corrct.html#module-corrct.regularizers) module. ### Data fidelity terms The PDHG algorithm supports various data fidelity terms. They can be found in the [`data_terms`](corrct.html#module-corrct.data_terms) module, and they include: * l2 norm - least squares reconstruction - default: [`DataFidelity_l2`](corrct.html#corrct.data_terms.DataFidelity_l2) * weighted l2 norm - when the variance of the sinogram points is known: [`DataFidelity_wl2`](corrct.html#corrct.data_terms.DataFidelity_wl2) * l1 norm - when the sinogram noise is mostly sparse: [`DataFidelity_l1`](corrct.html#corrct.data_terms.DataFidelity_l1) * Kullback-Leibler - when dealing with Poisson noise: [`DataFidelity_KL`](corrct.html#corrct.data_terms.DataFidelity_KL) ## Guided regularization parameter selection Regularizer parameter selection can be performed through either cross-validation, or the elbow method. ## References [1] Pelt, D. M., & Batenburg, K. J. (2014). Improving filtered backprojection reconstruction by data-dependent filtering. Image Processing, IEEE Transactions on, 23(11), 4750-4762. [2] N. Viganò and V. A. Solé, "Physically corrected forward operators for induced emission tomography: a simulation study," Meas. Sci. Technol., no. Advanced X-Ray Tomography, pp. 1–26, Nov. 2017.