Main DEBISim Modules


The DEBISim simulators are the central modules that allow implementing the entire DEBISim simulation pipeline to generate X-ray data. The modules organize the operation of each of the four blocks within the implementation pipeline, (i.e., the virtual bag generator, the forward x-ray modelling block, DE decomposition block and the reconstructor) to generate an X-ray data instance. The operation starts from the generation of the virtual bags, then moves on to the creation of X-ray projections, to the decomposition of multi-spectral data to extract X-ray coefficients and finally ends with the 3D reconstruction of the CT images. The data generated therefore includes the reference/ground truth images, the X-ray projections, 3D CT reconstructions and the metadata associated with the objects in the images. The modules can be interactively used for data generation using GUIs or imported into a Python script. The two simulator modules - XrayDataSimulator (for creating X-ray image data for a single baggage image) and XrayDatasetGenerator (for creating a randomized X-ray image dataset) - are explained in detail ahead.

Contents


Data Generation Module, XrayDataSimulator

This is the central module of DEBISim for executing the simulation pipeline - it runs the bag_generator and forward_model blocks in the pipeline to produce single or dual energy CT projection data. The module can be used to generate projection data for randomized data generation, for user-interactive data generation using the DEBISim GUI and from previously saved shape lists for phantoms and saved simulations. This is done by importing the module and initializing an instance of the class XrayDataSimulator(). The class is explicitly used in the dataset generation in the DEBISim scripts for the randomized/user-interactive mode GUI.

Usage:

A DEBISim simulation can be started using this module by initializing an instance of the class XrayDataSimulator() by specifying a working directory for the simulation, a scanner model generated using ScannerTemplate and the X-ray source specifications. When initialized, the class creates the specified simulation directory and creates the following items within the directory:

ground_truth/ This subdirectory is used to hold the reference data / ground truth images - these include gt_label_image, gt_compton_image, gt_pe_image, gt_zeff_image, gt_lac_1_image, gt_lac_2_image, gt_hu_1_image, gt_hu_2_image. (By default, only gt_label_image is saved.) The options for which images to save can be specified while running the simulator. All images are saved as compressed .fits.gz files and can be read using util.read_fits_data() (or any other i/o functions that can process FITS images).
sinograms/ This subdirectory is reserved for saving the polychromatic projection data generated from the forward_model block in the pipeline. The preferred nomenclature for the sinograms is sino_%i.fits.gz, where i = 1, 2, … corresponding to the respective spectra of the projection.
images/ This subdirectory is reserved for saving the reconstructed single or dual energy CT images from the projection data. The reconstructed images are also saved as .fits.gz files. Any post-processing output such as for MAR or Segmentation can be saved in this subdirectory. The preferred nomenclature for the image files is recon_image_%s.fits.gz (where %s - 1,2,… n - for the CT images, c - for compton, p - for PE and z - for effective atomic number.)
sl_metadata.pyc This pickle file contains the Shape List for the simulated ground truth. See ShapeListHandle() for more details.

The reference data for the simulator instance can be created either in a randomized mode or a user specified mode. When in the randomized mode, the simulator calls in the BaggageCreator3D module to spawn a randomized baggage simulation with objects randomly selected, placed and oriented as specified by the baggage creator arguments. In the user specified mode, the objects to be spawned in the baggage simulation are specified by a Shape List provided as input. The parameters within the Shape List can be fed in using a User-Interactive GUI or the Shape List can be manually created by specifying the pose and material properties for each object in the image.

Example - Randomized Mode Operation:

An example for the use of the class in a randomized mode is given below. To create the instance, one must first specify (i) a scanner model generated using the ScannerTemplate module, (ii) a Python dictionary specifying the X-ray source model, i.e., the spectrum pdfs, the dosage and peak tube voltages (see code below) and (iii) the arguments for creating a randomized bag using the BaggageCreator3D module - this involves specifications of the baggage object materials, object dimensions and the number of objects amongst other parameters. The ground truth image can then be generated by calling the self.create_random_simulaiton_instance() method which executes the reference_data_generator block. The projection data is created by calling the self.generate_xray_scanner_projection_data() which executes the forward_model block. Projection data generation also includes options for tuning Poisson noise and Gaussian shot noise models to mimic real CT projections. The code for the example is given ahead - see the comments for intstructions.

from XrayDataSimulator import *

# -------------------------------------------------------------------------
# Step 1: Specify Scanner Model using the class ScannerTemplate - parameters
# such as pitch, slice thickness can be modified in ScannerTemplate initialization
# The default scanner is IDSS DETECT 1000 which is a Spiral CBCT Dual Energy 
# scanner - see bag_generator documentation to specify your own scanner configurations.

scanner_mdl = ScannerTemplate()  
scanner_mdl.set_recon_geometry()

# -------------------------------------------------------------------------
# Step 2: Specify Xray Source Model

# The Xray Source Model is specified by a dictionary with the following key-
# value pairs:
# num_spectra - No of X-ray sources/spectra
# kVp - peak kV voltage for the X-ray source(s)
# spectra - file paths for the each of the X-ray spectra. The spectrum files
#           must contain a N x 2 array with the keV values in the first 
#           column and normalized photon distribution in the 2nd column. See
#           ./include/spectra/ for reference.
# dosage - dosage count for each of the sources

specdir = './include/spectra/'
xray_source_specs = dict(
    num_spectra=2, 
    kVp=160, 
    spectra=[os.path.join(specdir, 'idss_spectrum_unfiltered_160kV.txt'),
             os.path.join(specdir, 'idss_spectrum_filtered_160kV.txt')],
    dosage=[2.2752e7, 8.6648e06]
)

# ----------------------------------------------------------------------------
# Step 3: Specify the arguments the BaggageCreator3D() Arguments - these
# arguments decide the nature of objects that can be spawned in the simulated
# bags.

# The list contains the list of materials that will be assigned to the
# objects in the bag - the material assignment is random but liquids need to
# be specified separately if liquid filled containers are to be spawned.

mlist    = ['Al', 'pyrex', 'acetal', 'acrylic', 'C', 'Si', 'polyethylene']
lqd_list = ['water', 'H2O2']

# See BaggageCreator3D for details about the input arguments.
bag_creator_dict = dict(
    material_list=mlist,
    liquid_list=lqd_list,
    material_pdf=[1/7.]*7,
    liquid_pdf=[0.5,0.5],
    lqd_prob=0.2,
    sheet_prob=0.3,
    min_dim=10,
    max_dim=90,
    number_of_objects=range(20,25),
    spawn_sheets=True,
    spawn_liquids=True,
    sheet_dim_list=range(2, 7),
    custom_objects=None
)

# ----------------------------------------------------------------------------
# Step 4: Initialize the X-ray data simulator with the scanner model and source specs
simulator =  XrayDataSimulator(
    sim_path='./examples/sim_3d_random/',
    scanner_model=scanner_mdl,
    xray_source_model=xray_source_specs,
    zwidth=350
)

# ----------------------------------------------------------------------------
# Step 5: Create a random simulation instance with the baggage creator args
simulator.create_random_simulation_instance(bag_creator_dict)
slide_show(simulator.gt_image_3d.cpu().numpy(), vmin=0, vmax=30)

# ----------------------------------------------------------------------------
# Step 6: Generate the X-ray projections - the resulting data is saved in the projections/ directory.
simulator.generate_xray_scanner_projection_data(
    add_poisson_noise=True,
    add_system_noise=True,
    system_gain=1e2
)

Example - Simulation using a Shape List:

Alternatively, the simulation can be run using the class by reading in a previously saved or a user-specified shape list. (See ShapeListHandle or the User-Interactive Mode GUI for information about shape lists.)

from XrayDataSimulator import *

# -------------------------------------------------------------------------
# Read the pickle file containing the Shape List for ACR phantom -  
# Alternatively, a Shape List can be manually created by specifying objects 
# within the image, see ShapeListHandle for details. 

fpath = './examples/phantom_shape_lists/acr_phantom.pyc'
with open(fpath, 'r') as f:
    data = pickle.load(f)
    f.close()
sf_file = [data[x] for x in data.keys() if data[x]['shape']!='T'] 

# -------------------------------------------------------------------------
# Step 1: Specify Scanner Model using the class ScannerTemplate - parameters
# such as pitch, slice thickness can be modified in ScannerTemplate initialization
# The default scanner is IDSS DETECT 1000 which is a Spiral CBCT Dual Energy 
# scanner - see bag_generator documentation to specify your own scanner configurations.

scanner_mdl = ScannerTemplate()
scanner_mdl.set_recon_geometry()

# -------------------------------------------------------------------------
# Step 2: Specify Xray Source Model

# The Xray Source Model is specified by a dictionary with the following key-
# value pairs:
# num_spectra - No of X-ray sources/spectra
# kVp - peak kV voltage for the X-ray source(s)
# spectra - file paths for the each of the X-ray spectra. The spectrum files
#           must contain a N x 2 array with the keV values in the first 
#           column and normalized photon distribution in the 2nd column. See
#           /include/spectra/ for reference.
# dosage - dosage count for each of the sources

specdir = './include/spectra/'
xray_source_specs = dict(
    num_spectra=2,
    kVp=160,
    spectra=[os.path.join(specdir, 'idss_spectrum_unfiltered_160kV.txt'),
             os.path.join(specdir, 'idss_spectrum_filtered_160kV.txt')],
    dosage=[2.2752e7, 8.6648e06]
)

t0 = time.time()

# ----------------------------------------------------------------------------
# Step 3: Initialize the X-ray data simulator with the scanner model and source specs
simulator =  XrayDataSimulator(
    sim_path='/home/ubuntu/acr_phantom_simulation/',
    scanner_model=id1,
    xray_source_model=xray_source_specs,
    zwidth=350
)

# ----------------------------------------------------------------------------
# Step 4: Create the simulation from the Shape List.
simulator.create_simulation_from_sl_file(sf_file)
slide_show(simulator.gt_image_3d.cpu().numpy(), vmin=0, vmax=30)

# ----------------------------------------------------------------------------
# Step 5: Generate the X-ray projections.
simulator.generate_xray_scanner_projection_data(
    add_poisson_noise=True,
    add_system_noise=True,
    system_gain=1e2
)

torch.cuda.empty_cache()

print "Total Time:", time.time()-t0

The code can be extended to run the entire simulation pipeline by calling functions to execute the decomposer and reconstructor blocks. The scripts for implementing the entire pipeline can be found in Examples. Alternatively, the pipeline can be implemented using GUIs, which can also be found in Examples. See the Python API Reference for a detailed description of the module and its methods.

Dataset Generation Module, XrayDatasetGenerator

The XrayDatasetGenerator module is implemented to allow for convenient generation of randomized X-ray datasets using the XrayDataSimulaor. The module iteratively executes the entire DebiSim simulation pipeline to generate a randomized baggage dataset. The operation can be run by simply calling the XrayDatasetGenerator.run_xray_dataset_generator() and feeding specifications for each of the four blocks of the simulation pipeline as input arguments. Running the functions executes each of the pipeline blocks in sequence:

  1. bag_generator
  2. forward_model
  3. decomposer
  4. reconstructor

The structure of the simulation directory and its contents is shown in XrayDataSimulator. The bag_generator and forward_model blocks are run implicitly within XrayDataSimulator(). This is followed by the decomposer block which extracts dual energy coefficient data from the projection data pairs. This step is optional and is executed only if a decomposer is provided as input. If no decomposer is provided, the code proceeds to the reconstructor block. The decomposition method(s) (CDM or SIRZ) can be determined by providing the decomposer and its corresponding arguments (Both types of reconstructors can be provided to the function simultaneously as a list). Once decomposition is performed, the reconstructor block reconstructs the volumetric images for the single/dual energy projections and saves them. The procedure for creating datasets from this module is explained in the next section.

Usage:

The following code illustrates how the function can be used to create a randomized dataset. The details of how each of the input arguments are defined are given in the comments:

from XrayDatasetGenerator import *

# -------------------------------------------------------------------------
# Step 1: Specify dataset parameters:

bags_to_create = range(5)         # Number of bags to create
sim_dir = '/home/ubuntu/DEBISimDataset_01/' # simulation directory

# -------------------------------------------------------------------------
# Step 2: Specify Scanner Model using ScannerTemplate.py - parameters such as pitch, slice thickness can be modified in ScannerTemplate initialization

scanner_mdl = ScannerTemplate() # Uses default scanner (IDSS Detector 1000)
scanner_mdl.set_recon_geometry()

# -------------------------------------------------------------------------
# Step 3: Specify Xray Source Model

# The Xray Source Model is specified by a dictionary with the following key-
# value pairs:
# num_spectra - No of X-ray sources/spectra
# kVp - peak kV voltage for the X-ray source(s)
# spectra - file paths for the each of the X-ray spectra. The spectrum files
#           must contain a N x 2 array with the keV values in the first 
#           column and normalized photon distribution in the 2nd column. See
#           /include/spectra/ for reference.
# dosage - dosage count for each of the sources

specdir = './include/spectra/'
xray_source_specs = dict(
    num_spectra=2,
    kVp=160,
    spectra=[os.path.join(specdir, 'idss_spectrum_unfiltered_160kV.txt'),
             os.path.join(specdir, 'idss_spectrum_filtered_160kV.txt')],
    dosage=[2.2752e6, 8.6648e5]
)

# ----------------------------------------------------------------------------
# Step 4: Specify the arguments the BaggageCreator3D() Arguments - these
# arguments decide the nature of objects that can be spawned in the simulated
# bags.

# The list contains the list of materials that will be assigned to the
# objects in the bag - the material assignment is random but liquids need to
# be specified separately if liquid filled containers are to be spawned.

mlist    = ['Al', 'pyrex', 'acetal', 'acrylic', 'C', 'Si', 'polyethylene']
lqd_list = ['water', 'H2O2']

bag_creator_args = dict(
    material_list=mlist,
    liquid_list=lqd_list,
    material_pdf=[1/7.]*7,
    liquid_pdf=[0.5,0.5],
    lqd_prob=0.2,
    sheet_prob=0.3,
    min_dim=10,
    max_dim=90,
    number_of_objects=range(10,15),
    spawn_sheets=True,
    spawn_liquids=True,
    sheet_dim_list=range(2, 7),     # range of sheet thicknesses
    custom_objects=None             # if any custom object shapes to be
                                    # specified
)

# ----------------------------------------------------------------------------
# Step 5: Specify the decomposer and its arguments - if using Dual-/Multi-
#         energy Spectra, a decomposer module can be specified to extract the
#         coefficient integrals. In this case, it is the CDM Dual Energy
#         Decomposer that is specifed, if nothing is specified, the
#         simulation only generates the single energy CT reconstructions.

ntheta = scanner_mdl.machine_geometry['n_views_per_rot'] *scanner_mdl.machine_geometry['n_turns']
ndet   = scanner_mdl.machine_geometry['n_sens_x']

# Specify the parameters for the CDMDecomposer for DE decomposition
cdm_args = dict(
cdm_solver='gpu',
cdm_type='cpd',
projector='cpu',
init_val=(1, 1)
)

# Initialize the CDM Decomposer
sim_specs = dict(
    spctr_h_fname=xray_source_specs['spectra'][0],
    spctr_l_fname=xray_source_specs['spectra'][1],
    photon_count_high=xray_source_specs['dosage'][0],
    photon_count_low=xray_source_specs['dosage'][1],
    nangs=ntheta,
    nbins=ndet,
    projector='cpu'
)

cdm_sim = CDMDecomposer(**sim_specs)

# ----------------------------------------------------------------------------
# Step 6: Run the XrayDatasetGenerator function with the setup arguments
run_xray_dataset_generator(bags_to_create,
                           sim_dir,
                           scanner=scanner_mdl,
                           xray_src_mdl=xray_source_specs,
                           bag_creator_args=bag_creator_args,
                           decomposer=cdm_sim,
                           decomposer_args=cdm_args
                           )

See the Python API Reference for a detailed description of the module and its methods.