The X-ray Forward Modelling block is the second block of the DEBISim pipeline which processes the simulated 3D virtual bags to produce CT projection data as it would be produced for the bag by a physical CT scanner. To do so, it makes use of the knowledge of the scanner geometry, reconstruction setup, the source spectra and the material properties of the objects in the virtual bag. The X-ray Forward Modelling block has two modules for this purpose: (i) the ScannerTemplate module which simulates the scanner operation using the scanner geometry and (ii) the MuDatabaseHandler which handles the material properties of the forward-projected objects while producing the CT projection data. DEBISim contains support to specify a user-configurable scanner geometry for simulating the scanning operation for Circular/Helical Cone-Beam/Fan-Beam CT scanners. Besides, the software also contains pre-defined templates for commercially available scanners (see list below). On the other hand, the MuDatabaseHandler module simulates the X-ray attenuation curves for different materials used in the bag - it maintains a database of elements, compounds and mixtures supported by DEBIsim as well as contains methods for simulating attenuation curves for new materials. Both these modules are explained in detail on this page.
The ScannerTemplate module is used to simulate the scanner operation for generating CT projections from the virtual bags and reconstructing X-ray images from these CT projections. The module allows specifying a user-defined scanner geometry for the forward modelling as well as contains pre-defined templates for commercially available scanners (see list below). The ScannerTemplate can simulate forward models for Spiral/Circular Cone-Beam/Fan-Beam/Parallel-Beam CT scanners. This is done by initializing an instance of the class ScannerTemplate with the scanner geometry specifications (with the argument machine_dict) and the reconstruction parameters (with the argument recon_dict). Once initialized, the class creates an ASTRA object [1][2] representing the scanner and generates class methods for forward projection (ScannerTemplate.run_fwd_projector()) and image reconstruction (ScannerTemplate.reconstruct_data()). An example on how to specify the scanner parameters as well as how to use the scanner is provided below.
[1]. W. van Aarle, W. J. Palenstijn, J. Cant, E. Janssens, F. Bleichrodt, A. Dabravolski, J. De Beenhouwer, K. J. Batenburg, and J. Sijbers, ‘Fast and Flexible X-ray Tomography Using the ASTRA Toolbox’, Optics Express, 24(22), 25129-25147, (2016), http://dx.doi.org/10.1364/OE.24.025129
[2].W. van Aarle, W. J. Palenstijn, J. De Beenhouwer, T. Altantzis, S. Bals, K. J. Batenburg, and J. Sijbers, ‘The ASTRA Toolbox: A platform for advanced algorithm development in electron tomography’, Ultramicroscopy, 157, 35-47, (2015), http://dx.doi.org/10.1016/j.ultramic.2015.05.002
The class ScannerTemplate() can be used to simulate a scanner by initializing it with the following arguments:
{‘cone’ | ‘parallel’ | ‘fan’}
{‘helical’, ‘circular’}
‘fbp’ | ‘sirt’
See the example below for the key-value pairs for recon_dict and machine_dict
# =================================
# Dictionary for Default Machine Geometry - the default machine is a Spiral
# CBCT scanner, Siemens Sensation 32
# Initialize dictionary for Machine Geometry
g = {}
# Annotate a name for the scanner - any log/prm files generated during
# operation will be saved under this name
g['scanner_name'] = 'sensation32'
# Geometric dimensions of the scanner gantry setup - this can be obtained
# from the manufacturer's datasheets
g['gantry_diameter'] = 600.0
g['source_origin'] = 570.0
g['origin_det'] = 470.0
# Dimensions of the detector for the CBCT scanner - the detector plates for
# such scanners are curved rectangular panels
g['sens_spacing_x'], g['sens_spacing_y'] = 1.404, 1.02
# This is the maximum number of views in one turn
g['n_views_per_rot'] = 1160
g['anode_angle'] = 0.1222
# The keys det_row_count, det_col_count corresponds to the number of
# detectors along the fan sweep / helical direction.
g['det_row_count'], g['det_col_count'] = 32, 672
# Choice of the slice thickness is arbitrary for spiral scanners
# should be high enough for stable reconstruction.
g['slice_thickness'] = 1.786
default_machine_geometry = g.copy()
# =============================
# Dictionary for Reconstruction Parameters
default_recon_params = {}
# These values must match with the corresponding values in machine_dict for proper
# reconstruction
default_recon_params['n_views'] = 1160
default_recon_params['pitch'] = 25.10
default_recon_params['slice_thickness'] = 1.5
default_recon_params['recon_params'] = (512, 512, 300)
# =============================
The user can also select from the following pre-defined scanners for simulation (these templates can be found in the ./simlib/forward_model/ directory):
The following example shows how ScannerTemplate() is used to simulate a scanner:
import numpy as np
from util import *
from ScannerTemplate import *
from Template_Siemens_Sensation_32 import siemens_sensation_32
sensation32 = ScannerTemplate(
recon_dict=siemens_sensation_32.recon_params,
machine_dict=siemens_sensation_32.machine_geometry,
geom='cone',
scan='spiral',
recon='fbp')
vol_img = np.zeros(470,470,350) # example volumetric image
vol_img[200:250, 250:280, 100:300] = 0.1 # spawning objects in the volume
vol_img[100:120, 400:450, 50:300] = 0.05
vol_img[300:400, 50:100, 200:250] = 0.4
sensation32.set_recon_geometry()
ct_projn = sensation32.run_fwd_projector(vol_img)
recon_img = sensation32.reconstruct_data(ct_projn)
slide_show(vol_img, vmin=0.0, vmax=1.0)
slide_show(recon_img, vmin=0.0, vmax=1.0)
The module supports GPU operation to accelerate reconstructions for 3D images. For Spiral CBCT scanners specifically, ScannerTemplate uses FreeCT_wFBP [4][5] that can speed up the reconstruction process considerably.
[4]. J. Hoffman, S. Young, F. Noo, M. McNitt-Gray, “Technical Note: FreeCT_wFBP: A robust, efficient, open-source implementation of weighted filtered backprojection for helical, fan-beam CT,” Med. Phys., vol. 43, no. 3, pp. 1411-1420, Feb. 2016.
[5]. K. Stierstorfer, A. Rauscher, J. Boese, H. Bruder, S. Schaller, and T. Flohr, “Weighted FBP—a simple approximate 3D FBP algorithm for multislice spiral CT with good dose usage for arbitrary pitch,” Phys. Med. Biol., vol. 49, no. 11, pp. 2209–2218, Jun. 2004.
To know more about using ScannerTemplate, see Python API Reference: ScannerTemplate
This module generates and manages the attenuation response data for elements, compounds and mixture materials used in the virtual bags. The attenuation curves for these materials are derived from the NIST XCOM Photon Cross Sections Database [1]. The modules performs the following functions: (i) it manages the attenuation response data for all the material supported by DEBISim, (ii) it creates new attenuation data for user-defined materials using the NIST XCOM database and (ii) it calculates the single-/dual-energy X-ray coefficients from the response curves (Compton coefficients, PE coefficients, Effective Atomic Number, Density, linear attenuation coefficient (LAC) and Hounsfield values) for the simulated materials.
[1]. M.J. Berger, J.H. Hubbell, S.M. Seltzer, J. Chang, J.S. Coursey, R. Sukumar, D.S. Zucker, and K. Olsen NIST, PML, Radiation Physics Division
The following code shows the different functions performed by MuDatabaseHandler:
from MuDatabaseHandler import *
mu_handle = MuDatabaseHandler()
print 'Copper:\n', mu_handle.element['Cu']
print '-'*20,'\n'
print 'Water:\n', mu_handle.compound['water']
Output:
Copper:
{'compton': 910.4351012828125,
'density': 8.95,
'mu': array([2.279e+04, 1.797e+04, 1.432e+04, 1.155e+04, 9.436e+03, 7.814e+03,
.
.
.
2.253e+01, 2.233e+01, 2.213e+01, 2.194e+01, 2.175e+01, 2.156e+01,
2.138e+01]),
'pe': 625.6699690326266,
'z': 29}
> ------------------
Water:
{'compton': 0.1636710077950889,
'density': 1.0,
'mu': array([5.33 , 4.026 , 3.126 , 2.487 , 2.021 , 1.672 , 1.408 , 1.203,
.
.
.
0.1518, 0.1515, 0.1512, 0.1508, 0.1505, 0.1502, 0.1499, 0.1496,
0.1493, 0.149 , 0.1487, 0.1484, 0.1481, 0.1478, 0.1475]),
'pe': 5058.233649345908,
'z': 7.420007543374948}
This script shows how attenuation data can be generated for a given compound by specifying its molecular formula and density.
mu_handle.create_compound_mu(compound_formula='NaCl', compound_name='salt',
density=2.16)
print '\nSalt:\n', mu_handle.compound['salt']
Output:
Attenuation Data generated for salt, NaCl.
Salt:
{'compton': 0.2831475714237009,
'density': 2.16,
'mu': array([40.23 , 30.52 , 23.68 , 18.74 , 15.07 , 12.3 , 10.17 ,
.
.
.
0.1348, 0.1344, 0.134 , 0.1336, 0.1333, 0.1329, 0.1325,
0.1321, 0.1318, 0.1314, 0.131 ]),
'pe': 87277.26936840561,
'z': 14.315062719492778}
Once created, the data for a material (element, compound or mixture) is stored as a dictionary with the following keys:
The materials and their properties can be accessed using the method self.material;
print 'Acrylic:\n', mu_handle.material('acrylic')
print '-'*20,'\n'
print 'Acrylic Z:', mu_handle.material('acrylic', 'z')
Output:
Acrylic:
{'compton': 0.16904512642438155,
'density': 1.18,
'mu': array([3.398 , 2.616 , 2.063 , 1.662 , 1.364 , 1.139 , 0.9664, 0.8314,
.
.
.
0.1352, 0.1349, 0.1346, 0.1344, 0.1341, 0.1339, 0.1336, 0.1334,
0.1331, 0.1329, 0.1326, 0.1324, 0.1321]),
'pe': 3804.98491468153,
'z': 6.777477825789594}
> -------------------
Acrylic Z: 6.777477825789594