v5.0.0
This version is 95% backwards compatible with STIR 4.0 for the user (see below). Developers might need to make code changes as detailed below.
Overall summary
- At least C++-11 is now required. We are not aware of any problems with more recent versions of C++.
- Initial support for PET scanners with block detectors or even generic location of detectors (less tested feature), taking into account appropriate geometry. This is currently kept independent of the cylindrical scanner modelling used normally, but this will be changed in a future version. See PR #577. This work is described in
P. Khateri, J. Fischer, W. Lustermann, C. Tsoumpas, and G. Dissertori,
Implementation of cylindrical PET scanners with block detector geometry in STIR,
EJNMMI Physics, vol. 6, no. 1, p. 15, Jul. 2019, doi: 10.1186/s40658-019-0248-9.
V. Dao, E. Mikhaylova, M. L. Ahnen, J. Fischer , K. Thielemans, C. Tsoumpas,
Image Reconstruction for PET Scanners With Polygonal Prism Geometry Using STIR, proc. IEEE MIC 2021
This code was initially developed by Parisa Khateri and Michael Roethlisberger (ETH), and further improved and tested by Viet Dao (Leeds), Daniel Deidda (NPL) and Kris Thielemans (UCL and ASC) with funding by the University of Leeds and Positrigo AG. - View Offset Support enabled for the PET scanners, contributed by Palak Wadhwa (Leeds Univ), allowing fixing rotation of reconstructed images, see PR 181.
- Maximum Likelihood estimation of normalisation factors now includes estimation of geometric factors in 3D, see PR 619. This code was mostly contributed by Tahereh Niknejad (work completed at University of Lisbao, Portugal and PETsys Electronics, together with Kris Thielemans, UCL), as well as capabilities to work with virtual crystals, see PR 833 and PR 949. See the following proceedings for the initial work
Tahereh Niknejad, Stefaan Tavernier, Joao Varela, and Kris Thielemans, Validation of 3D Model-Based Maximum-Likelihood Estimation of Normalisation Factors for Partial Ring Positron Emission Tomography In 2016 IEEE Nuclear Science Symposium, Medical Imaging Conference and Room-Temperature Semiconductor Detector Workshop (NSS/MIC/RTSD), 1–5. DOI: 10.1109/NSSMIC.2016.8069577. - A new
ProjDataInfoSubsetByView
class has been implemented which allows for subsets of projection data. Previously, subset projections could only be stored in fully sampled ProjData. This is still possible, the implementation is backward compatible, and subset reconstructions such as OSMAPSOL have not been changed. However, there is now an option to create subsets withProjData::get_subset
which only store a subset of views and so are more memory efficient when subsets might be handled separately.ProjDataInfoSubsetByView
currently does not support file I/O. ROOT
files produced byGATE
can now be interpreted using "virtual crystals", see PR #617.- A calibration factor can be applied in the normalisation with a new
BinNormalisation
derived classBinNormalisationWithCalibration
; Interfile now reads/writes isotope name and calibration factor. See PR 672. This code was contributed by Daniel Deidda (NPL) and Kris Thielemans (UCL) - Radionuclide information is saved into a JSON configuration file for a number of popular radionuclide for PET and SPECT. This can make operations like decay correction easier and accurate. To be able to use the database we need to provide the isotope name used in the acquisition. For SPECT this is extracted from the Dicom header and saved in the interfile. For PET ways to read this information need to be implemented for each scanner/vendor. The new classes are new
Radionuclide
andRadionuclideDB
; This code was contributed by Daniel Deidda (NPL) and Kris Thielemans (UCL) - KOSMAPOSL (HKEM) allows now to freeze the iterative part of the kernel at a specific subiteration. Code contributed by Daniel Deidda (NPL), Kris Thielemans (UCL) and Ashley Gillman (CSIRO)
- It is now possibly to call the Georg's Schramm parallelproj projectors (using either OpenMP or CUDA), see PR 817, contributed by Kris Thielemans (UCL) with Georg Schramm (KUL).
- OpenMP loop scheduling changed to use
dynamic
instead ofruntime
, resulting in faster performance.
Of course, there is also the usual code-cleanup and improvements to the documentation.
This release contains mainly code written by Kris Thielemans (UCL), Richard Brown (UCL), Parisa Khateri and Michael Roethlisberger (ETHZ), Robert Twyman (UCL), Daniel Deidda (NPL), Tahereh Nikjenad (PETsys), Palak Wadhwa (Univ of Leeds), Viet Ahn Dao (Univ of Leeds), Ashley Gillman (CSIRO), Georg Schramm (KUL), Markus Jehl (Positrigo), Gefei Chen (Univ of Macao)
Summary for end users (also to be read by developers)
Changes breaking backwards compatibility from a user-perspective
-
View Offset Support enabled for the PET scanners.
- For the scanners that have non-zero intrinsic azimuthal tilt angle, reconstructed images will now get rotated.
- If you use view mashing, in previous STIR versions images were rotated according to half the number of mashed views. This is now corrected.
WARNING This means that reconstructed PET images will not be identical when either the scanner has non-zero view-offset, or view-mashing is on.
To reflect this change,Scanner::get_default_intrinsic_tilt()
has been renamed toget_intrinsic_azimuthal_tilt()
.
Note: start angle was already supported for SPECT
Backward compatibility for reconstructed images can be achieved by setting theSTIR_LEGACY_IGNORE_VIEW_OFFSET
CMake option toON
. (However, copied sinogram data will then always have the offset set to 0, in contrast to earlier versions of STIR). -
PoissonLogLikelihood Hessian methods have been corrected to reflect the concavity of the function. Hessian vector products now return non-positive voxels, if the vector (input) is non-negative. STIR usages of these methods (OSSPS and SqrtHessianRowSum) have been updated to see no effective change in functionality. However, calling the Hessian vector product methods, via python etc., will result in a change in functionality as the sign of the output voxel values has changed.
-
Python/MATLAB wrappers no longer have
ProjDataInfo::ProjDataInfoCTI
, useProjDataInfo::construct_proj_data_info
instead. However, this now returns an object of the appropriate (derived) type as opposed to justProjDataInfo
. This should be transparent, except apparently for testing equality of objects.
Bug fixes
- There was an inconsistency between log-likelihood function and its gradient when
use_subset_sensitivities
was false andnum_subsets
greater than 1. Fixing this isssue means that images reconstructed withOSSPS
are different from previous versions of STIR when the above conditions are met. See Issue #873 and associated PR #893. - Parametric image reconstruction with
POSMAPOSL
could lead to zeroes being introduced gradually during reconstruction. See Issue #906 and associated PR #978.
New functionality
-
Capability to model block and generic PET detectors was added. This is currently limited to span=1, no view mashing and the ray-tracing matrix (single LOR). It is enabled by specifying appropriate keywords in the Interfile header of the projection data.
Scanner parameters:= normal parameters... Scanner geometry (BlocksOnCylindrical/Cylindrical/Generic) := BlocksOnCylindrical Distance between crystals in axial direction (cm) := 0.22 Distance between crystals in transaxial direction (cm) := 0.22 Distance between blocks in axial direction (cm) := 0.22 Distance between blocks in transaxial direction (cm) := 3.3 end scanner parameters:=
Scatter and normalisation code are still pending changes.
-
Georg's Schramm parallelproj is an implementation of the Joseph algorithm for projection. If it has been installed in your system, and you tell CMake where to find it (`parallelproj_DIR=/wherever/lib/cmake`), the STIR user is now able to select an additional projector, called
Parallelproj
. This will use the CUDA version if available, otherwise will fall-back to the OpenMP version. Check the new sample files inexamples/samples
and the section in the User's Guide. -
The (still preliminary) code for Maximum Likelihood estimation of normalisation factors now includes estimation of geometric factors in 3D as well
-
The (also preliminary)
ProjDataInfoSubsetByView
is backwards compatible and allows a new, memory efficient method for subset projections. Subset projections are created with withProjData::get_subset
, which is the only addition to theProjData
class (additions are mostly in the aforementioned new Info class).ProjDataInfoSubsetByView
can be used to set up projectors, which will project only the subset.ProjDataInfoSubsetByView
currently does not support file I/O. Therefore, the interface is not yet accessible through parameter files, only through C++ interface.
PR #969 -
calculate_attenuation_coefficients
utility now accepts an optional forward projection parameter file in the same format as theforward_project
utility. Example usage has been added tosrc/recon_test_pack/simulate_data.sh
. -
ROOT
files produced byGATE
can now be interpreted using ``virtual crystals", i.e. by inserting ``dummy" crystals before converting to cylindrical geometry (as is done on many Siemens scanners).LmToProjData
and list mode reconstructions will then put the LORs more accurately (at least when the ``virtual crystals" roughly correspond to gaps between blocks). See the update.hroot
file inexamples/samples
.
Warning: if you use theoriginating system
to specify the scanner, this will be automatically enabled. (If you do not want this, set it toUser_defined_scanner
and specify all values).
PR #617 -
Moved most code from the
ctac_to_mu_values.cxx
utility to a newHUToMuImageProcessor
class (derived fromDataProcessor
. This allows combining it with filtering etc, also from in Python. It means that thectac_to_mu_values.cxx
utility itself is now obsolete, but it still exists in this version. (Note that this functionality depends on an external JSON library.) -
Addition of a new utility
create_multi_header
which can be used to create a single header pointing to several files (e.g. one image per time frame). The header uses the (STIR-specific)multi
format. -
Addition of a new utility
extract_single_images_from_parametric_image
to get each parametric image in a single file. -
generate_image
parameter files now support theoriginating system
keyword. -
list_image_info
now works for dynamic images, with a new--per-volume
option to list min/max/sum for every volume. -
SSRB
now has the option of taking a template sinogram. -
KOSMAPOSL
(HKEM) allows now to freeze the iterative part of the kernel at a specific subiteration. This can be set in the parameter file through the keywordfreeze_iterative_kernel_at_subiter_num
-
BinNormalisationWithCalibration
is a new class derived fromBinNormalisation
. This class allows to apply calibration factor from the scanner and save the information into the interfile header. To use this, a specific BinNormalisation class should be derived from BinNormalisationWithCalibration, the information about the calibration factor read, and the function get_uncalibrated_bin_efficiencies() needs to be overwritten. Note that also the isotope name and branching ratio can be set here (the latter will need to be set according to the isotope by reading into a radionuclide database, see below). BinNormalisationSPECT already reads Calibration factor and isotope name and apply the calibration factor read from interfile. Since not all SPECT scanner do quantitative reconstruction an option of setting the calibration factor from the parameter file is added. Factors for BinNormalisationECA8/ECAT7 and GEHDF5 are set to one at the moment as we need to double check on the meaning of cross calibration factor for ECAT and find out how to read them for GEHDF5. Documentation for this is pending and will be added in the following PR. -
Radionuclide
is a new class which contains radionuclide information such as halflife, branching ratio, energy etc. This class allows to propagate infrmation trough the reconstruction as it is now a member in theExamInfo
. It is populated from the information extracted fromRadionuclideDB
which allows to create a Radionuclide object from the information stored in theradionuclide_info.json
in the STIR configuration directory. Different vendors, as we saw with SPECT, may have different standards for the isotope name. Three different ones were observed, therefore a lookup table has been added to allow the use of the data base for any different isotope name format. The lookup table is a JSON file stored inradionuclide_names.json
. -
Logcosh Prior.
-
SAFIR input file format now supports a Gaussian LOR randomization, which is applied on the endpoints of each LOR when sorting the listmode events into virtual scanner projection data.
-
Scanner
has now a newstatic
memberget_names_of_predefined_scanners
returning a list of names. -
Exposed
compute_total_ROI_values
andROIValues
to Python/MATLAB via swig. Added python exampleROI_demo.py
to demonstrate usage. -
Additional C++ demo demonstrating the use the objective function and gradient ascent optimisation, see
examples/src/demo4_obj_fun.cxx
. -
A few functions related to TOF were added to
ProjDataInfo
andScanner
for future compatibility. However, this release still does not support TOF. (Check the GitHub site for the relevant PR). -
Exposed
BinNormalisation
,ListModeData
,LmToProjData
,ProjectorByBinPair
,ScatterSimulation
,ScatterEstimation
and a couple of support classes and functions to Python/MATLAB via swig. Added python examplelistmode_demo.py
to demonstrate usage. -
More setter functions on ScatterEstimation.
Changed functionality
- OpenMP loop scheduling changed to use
dynamic
instead ofruntime
. On some machines, this was causing slower projection operations due to astatic
scheduler being selected by default. See Issue #935 for more details. - Many operations with
ProjDataInMemory
are now much faster (it now uses an underlying 1D array). ParametricDiscretisedDensity
objects can now have anExamInfo
object with multiple time frames (corresponding to the time frames of the data where the parametric image is derived from). In some cases, there could only be a single time frame (start to end of the study).find_ML_normfactors3D
andapply_ML_normfactors3D
can be used for scanner with virtual crystal (Only verified on mCT). Contributed by Gefei Chen, see PR 833.- If there are multiple buckets specified in the interfile header, we increase the symmetry size to a bucket in the
find_ML_normfactors3D
. Otherwise, we use a block. The use of an argument--for-symmetry-per-block
in thefind_ML_normfactors3D
utility will use the symmetry size of a block. apply_patlak_to_images
no longer uses an existing file as a template for the dynamic image but will overwrite it.ROOT
file I/O improvements. An entire entry's tree is no longer loaded by default and instead individual branches are loaded as needed. ROOT file event processing is now up to 4x faster. In addition, there is now an option tocheck energy window information
(defaulting to on). Futhermore, added functionality to exclude true and unscattered event types from list mode processing.
Build system
- We now require CMake at least version 3.1, although we highly recommended to use a very recent version of CMake to avoid problems with libraries or compilers which are more recent than your CMake version.
- At least C++-11 is now required. We are not aware of any problems with most recent versions of C++. When building, you can change the C++ version by setting
CMAKE_CXX_STANDARD
, see the CMake documentation for supported values.
When importing STIR'sSTIRConfig.cmake
viafind_package(STIR)
, your compiler will be set to use at least C++-11 (via CMake'starget_compile_features
).
Note that some external libraries that STIR depends on (such as ROOT) might increase the required C++ version, depending on how they were built. - CERN's
ROOT
library is now preferentially found by searching for its own exportedROOTConfig.cmake
. Set the CMake variableROOT_DIR
accordingly. Older behaviour relying onROOTSYS
androot-config
will be deprecated in a future version. - If you have CMake 3.14 or more recent, Python is found using the FindPython module, as opposed to the deprecated FindPythonLibs. FindPythonInterp modules. You can set the
Python_EXECUTABLE
variable to a particular version (see the CMake doc for other hints you can provide).( If the oldPYTHON_EXECUTABLE
is set, we use it to initialisePython_EXECUTABLE
).
Known problems
Minor bug fixes
- Removed boolean state from SAFIR ListMode factory, to enable reading more than one listmode file in the same runtime environment.
Documentation changes
- Added documentation on new features
- Also check the wiki in addition to the provided PDFs.
recon_test_pack changes
- updated version number and added some clarification to the README.txt
- moved the
src/recon_test/test_modelling.sh
and associated input files to therecon_test_pack
. It is also modified to be independent of ECAT7 and now runs again.
Other changes to tests
- expanded
test_proj_data_in_memory
to also testProjDataInterfile
so renamed the test totest_proj_data
.
What's new for developers (aside from what should be obvious from the above):
Major bugs fixed
- see above
Backward incompatibities
- Classes that use
InterfilePDFSHeader
now contain ashared_ptr<ProjDataInfo>
instead of a raw pointer, removing a memory leak. - Changes improving safety of use of
shared_ptr
:
In the previous version of STIR, the use ofshared_ptr
allowed unsafe access to the objects (although this never happened in distributed STIR code). We now prevent this with changes to the class interface (although there is still work to do):- Where possible, classes that internally contained a
shared_ptr<ProjDataInfo>
now contain ashared_ptr<const ProjDataInfo>
, and similar forDiscretisedDensity
get_proj_data_info_sptr
used to returnshared_ptr<ProjDataInfo>
, but now returnsshared_ptr<const ProjDataInfo>
, similar forExamInfo
.- Corresponding constructors and some functions, including
set_up
, that acceptshared_ptr
now take ashared_ptr
to aconst
object.
- Where possible, classes that internally contained a
ProjData*::copy_to
andfill_from
now return the updated iterator (as opposed to the size). This is likestd::copy
, and more convenient for reusing it.ModelMatrix::set_if_uncalibrated
andModelMatrix::get_if_uncalibrated
renamed toModelMatrix::set_is_uncalibrated
andModelMatrix::get_is_uncalibrated
PlasmaData::set_if_decay_corrected
andPlasmaData::get_if_decay_corrected
renamed toPlasmaData::set_is_decay_corrected
andPlasmaData::get_is_decay_corrected
- Remove overloaded
PatlakPlot::get_model_matrix
, now it can only be called using class members BinNormalisation::set_up()
now need exam_info_sptr as input.BinNormalisation::apply()
andundo
now no longer accept start_time and end_time as they are taken from exam_info. To allow this to happen, classBin
has now an extra membertime_frame()
. Note that it defaults to 1. If this is incorrect, it has to be initialised explicitly (not via a constructor).- Poisson log-likelihood hierarchy has changed for gradient methods to now use
actual_compute_subset_gradient_without_penalty
in the derived classes (which has an extra argumentadd_sensitivity
). Therefore developers that created their own derived class will need to adjust their class accordingly. Thepublic
class interface is identical though. DetectorCoordinateMapFromfile
(used by SAFIR listmode files) has been renamed toDetectorCoordinateMap
and moved fromlistmode
tobuildblock
.Scanner
now has aset_up()
function. This needs to be called after using any of theset_*()
functions (exceptset_params
). For the blocks/generic scanners, this will fill in the detector maps etc. You will get a run-time exception if you forgot to do this.
New functionality
-
New templated functions
stir::copy_to
andstir::fill_from
instir/copy_fill.h
which can be used to fill most STIR objects from an iterator range (or copy to). The functions normally usestir_object.begin_all()
but resort tostir_object::fill_from
orcopy_to
for a few cases where no iterators exist. We use some specialisations to try and find the fastest version. -
Introduced
ProjData::standard_segment_sequence
function returning0,+1,-1,...
, as used bycopy_to
andfill_from
-
stir::read_from_file
can now be called with a "node", e.g.auto uptr = read_from_file<VoxelsOnCartesianGrid<float> >(filename);
; To do this, we now need a typedef
hierarchy_base_type
at the top level, as otherwise we would need multiple registries. However, this was already required by write_to_file
anyway.
GeneralisedPrior
andQuadraticPrior
have a new methodaccumulate_Hessian_times_input(output, current_estimate, input)
. This computes the hessian of the prior at thecurrent_image_estimate
and multiplies this byinput
. Before this, theadd_multiplication_with_approximate_Hessian(output, input)
method was used that assumed a quadratic function and thereforehessian = 1
.Exam_info
,TimeFrameDefinition
andPatientPosition
have now a==
operator- Overloaded
compute_total_ROI_values
andcompute_ROI_values_per_plane
functions to allow the passing of aVoxelsOnCartesianGrid
object instead of aShape3D
as the ROI shape. This allows the ROI volume to be constructed during preprocessing for multiple image analysis. KeyParser::parameter_info()
now outputs vectorised keys as wellDetectionPosition
now has all comparison operators.LOR
classes now no longer require phi at input to be between 0 and pi, nor psi to be between 0 and 2 pi. They will standardise the input automatically.
Other code changes
- store data in
ProjDataInMemory
in the same order as what is used bycopy_to
andfill_from
. This enabled using straight-forward copy. (This change should not affect anyone, except if you relied on a specific order in the buffer before.)