OSEM
OSEM (Ordered Subsets Expectation Maximization) is the main reconstruction algorithm.
Basic Usage
This is an example usage performing the OSEM reconstruction of a list-mode dataset.
import pyyrtpet as yrt
scanner = yrt.Scanner("<MyScannerFile.json>")
lm = yrt.ListModeLUTOwned(scanner, "<MyListMode.lmDat>")
# Create OSEM with scanner
osem = yrt.OSEM(scanner)
# Set reconstruction parameters
osem.num_MLEM_iterations = 10
osem.num_OSEM_subsets = 8
# Set image parameters
img_params = yrt.ImageParams(nx=128, ny=128, nz=64,
length_x=128.0, length_y=128.0, length_z=64.0)
osem.setImageParams(img_params)
# Set data input
osem.setDataInput(lm)
# Set projector
osem.setProjector("Siddon")
# Generate sensitivity images (Will also save the sensitivity image into a NIfTI file)
[sens_image] = osem.generateSensitivityImages("sens_image.nii.gz")
# Set sensitivity image(s)
osem.setSensitivityImage(sens_image)
# Run reconstruction (Will also save the reconstructed image into a NIfTI file)
result = osem.reconstruct("recon_image.nii.gz")
Configuration
Reconstruction Parameters
#<>
osem.num_MLEM_iterations = 20 # Number of iterations
osem.num_OSEM_subsets = 8 # Number of subsets
Data Input
#<>
osem.setDataInput(projection_data)
Projector
#<>
osem.setProjector("DD") # For the distance-driven projector
osem.setProjector("SIDDON") # For the Siddon projector
osem.setNumRays(3) # Multi-ray (Siddon only)
osem.addProjPSF("<proj_psf.csv>") # Adding projection-space PSF (DD only)
# Time-of-flight (TOF width in picoseconds, number of standard deviations to fill
# from the TOF kernel)
osem.addTOF(500.0, 3)
Corrections
Sensitivity correction (sometimes also called normalization correction) can be done by
providing a histogram (which can be of any format that inherits from Histogram,
not necessarily Histogram3D) of the sensitivity coefficient for every detector pair.
Attenuation correction can be done by providing an attenuation image or by providing a histogram of Attenuation Correction Factors (ACFs).
Randoms correction can be performed by either providing a histogram of the randoms
estimate for every detector pair, or by encoding the randoms estimate for every
event (overloaded in the getRandomsEstimate member function)
Scatter correction (Not to be confused with scatter estimation) is perfomed by providing a histogram of the scatter estimate for every detector pair.
#<>
osem.setSensitivityHistogram(sensitivity_his)
osem.setAttenuationImage(att_image) # Attenuation correction (From mu-map)
osem.setACFHistogram(acf_his) # Attenuation correction (From histogram)
osem.setRandomsHistogram(randoms_his) # Randoms correction (From histogram)
osem.setScatterHistogram(scatter_his) # Scatter correction
Point Spread Function (Image-space)
#<>
osem.addImagePSF("<psf.csv>") # Uniform PSF
osem.addImagePSF("<psf_variant.csv>", yrt.ImagePSFMode.VARIANT) # Spatially variant PSF
PSF Modes
yrt.ImagePSFMode.UNIFORM- Same PSF for all voxelsyrt.ImagePSFMode.VARIANT- Spatially variant PSF
Output Options
To save intermediate iteration, you must provide a RangeList object
# Save intermediate iterations
range_list = yrt.RangeList()
range_list.insertSorted(0,1) # Save iterations 0 and 1
range_list.insertSorted(5,8) # Save iterations 5,6,7,8
range_list.insertSorted(10,10) # Save iteration 10
to the OSEM object
#<>
# The filename given will be added a prefix to specify which iteration is was saved
osem.setSaveIterRanges(range_list, "./iterations/recon_image_intermediate.nii.gz")
# Custom initial estimate (instead of a uniform image containing the value 0.1)
osem.setInitialEstimate(initial_image)
# Reconstruction mask (To disable some voxels from the reconstruction)
osem.setMaskImage(mask_image)
Output
The reconstruct() method returns an ImageOwned object, but can also save the reconstructed image to disk if provided a filename.
#<>
result = osem.reconstruct() # Will not save to disk
result = osem.reconstruct("recon_image.nii.gz") # Will save to disk
# Access as numpy array
result_np = np.array(result, copy=False)