-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #35 from KrisThielemans/DetectionEfficiencies
add DetectionEfficiencies (aka "normalisation")
- Loading branch information
Showing
9 changed files
with
497 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/* | ||
Copyright (C) 2024 University College London | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include "generated/types.h" | ||
|
||
namespace petsird_helpers | ||
{ | ||
|
||
using namespace petsird; | ||
|
||
inline std::size_t | ||
get_num_det_els(const ScannerGeometry& scanner_geometry) | ||
{ | ||
std::size_t num_det_els = 0; | ||
for (const auto& rep_module : scanner_geometry.replicated_modules) | ||
{ | ||
const auto& det_els = rep_module.object.detecting_elements; | ||
for (const auto& rep_volume : det_els) | ||
{ | ||
num_det_els += rep_volume.transforms.size() * rep_module.transforms.size(); | ||
} | ||
} | ||
return num_det_els; | ||
} | ||
|
||
struct ModuleAndElement | ||
{ | ||
int module; | ||
int el; | ||
}; | ||
|
||
template <class T> | ||
inline std::vector<ModuleAndElement> | ||
get_module_and_element(const ScannerGeometry& scanner_geometry, const T& scanner_det_ids) | ||
{ | ||
assert(scanner_geometry.replicated_modules.size() == 1); | ||
const auto& rep_module = scanner_geometry.replicated_modules[0]; | ||
assert(rep_module.object.detecting_elements.size() == 1); | ||
int num_modules = rep_module.transforms.size(); | ||
|
||
std::vector<ModuleAndElement> result; | ||
for (int det : scanner_det_ids) | ||
{ | ||
result.push_back({ det % num_modules, det / num_modules }); | ||
} | ||
return result; | ||
} | ||
|
||
inline float | ||
get_detection_efficiency(const ScannerInformation& scanner, const CoincidenceEvent& event) | ||
{ | ||
float eff = 1.0F; | ||
const auto& det_el_efficiencies = scanner.detection_efficiencies.det_el_efficiencies; | ||
if (!det_el_efficiencies) | ||
{ | ||
eff *= ((*det_el_efficiencies)(event.detector_ids[0], event.energy_indices[0]) | ||
* (*det_el_efficiencies)(event.detector_ids[1], event.energy_indices[1])); | ||
} | ||
const auto& module_pair_efficiencies_vector = scanner.detection_efficiencies.module_pair_efficiencies_vector; | ||
if (!module_pair_efficiencies_vector) | ||
{ | ||
const auto& module_pair_SGID_LUT = scanner.detection_efficiencies.module_pair_sgidlut; | ||
assert(!module_pair_SGID_LUT); | ||
|
||
const auto mod_and_els = get_module_and_element(scanner.scanner_geometry, event.detector_ids); | ||
assert(scanner.scanner_geometry.replicated_modules.size() == 1); | ||
const int SGID = (*module_pair_SGID_LUT)(mod_and_els[0].module, mod_and_els[1].module); | ||
assert(SGID >= 0); | ||
|
||
const auto& module_pair_efficiencies = (*module_pair_efficiencies_vector)[SGID]; | ||
assert(module_pair_efficiencies.sgid == static_cast<unsigned>(SGID)); | ||
eff *= module_pair_efficiencies.values(mod_and_els[0].el, event.energy_indices[0], mod_and_els[1].el, | ||
event.energy_indices[1]); | ||
} | ||
return eff; | ||
} | ||
|
||
} // namespace petsird_helpers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Detection efficiencies for every detecting element and energy window | ||
# If size along energyBinIdx is 1, the effiencies are assumed to be the same for every energy bin. | ||
# Constraint: size(DetElEfficiencies, "energyBinIdx") == scannerInformation.numberOfEnergyBins or 1 | ||
DetElEfficiencies: float[detElIdx, energyBinIdx] | ||
|
||
# Efficiency for two detecting elements (det_els) in a pair of modules. | ||
# This is one component (often called "geometric") of the detection efficiency model. | ||
# If size along energyBinIdx is 1, the effiencies are assumed to be the same for every energy bin. | ||
ModulePairEfficiencies: !record | ||
fields: | ||
# Detection efficiency for a pair of detecting elements | ||
# detElIdx1 and detElIdx2 run from 0 to the number of det_els in each module | ||
values: float[detElIdx1, energyBinIdx1, detElIdx2, energyBinIdx2] | ||
# Symmetry Group Identifier (SGID) | ||
# This should be a number between 0 and numberOfSGIDs-1 | ||
sgid: uint | ||
|
||
|
||
# Lookup table for SGIDs | ||
# For every module pair, give the SGID. If -1, the module-pair is not in coincidence. | ||
# Values run from -1 ... (numberOfSGIDs-1) | ||
ModulePairSGIDLUT: int[moduleIdx1, moduleIdx2] | ||
|
||
ModulePairEfficienciesVector: ModulePairEfficiencies* | ||
|
||
# Component-based information on detection efficiencies | ||
# This encodes a simple model for the detection efficiency of (true) coincidences | ||
# consisting of the product of the efficiency of the two detecting elements (det_els) | ||
# and a (geometric) component determined by their location in the two modules. | ||
# The former are stored in detElEfficiencies, and the latter in modulePairEfficienciesVector | ||
# (a list of ModulePairEfficiencies, each entry corresponding to a module pair). | ||
# | ||
# Most PET scanners have some kind of geometric symmetry, e.g. rotation over a full | ||
# module, or translation along the axis of the scanner. Module-pairs that are related | ||
# by such a symmetry often have the same geometric detection efficiencies. PETSIRD | ||
# calls this a "symmetry group" (SG). Each SG had an identifier (SGID). To save memory, | ||
# the modulePairEfficienciesVector needs to contain only one of element for each SGID. | ||
# The SGID for a module-pair can be found in modulePairSGIDLUT. | ||
# | ||
# Finding the total detection efficiency therefore follows these steps: | ||
# (the following pseudo-code ignores energy bins for simplicity) | ||
# 1. find modules for each det_el | ||
# 2. find det_el indices "inside" each module | ||
# 3. SGID = modulePairSGIDLUT[mod1, mod1] | ||
# 4. if (SGID < 0) return 0 | ||
# 5. module_pair_efficiencies = modulePairEfficienciesVector[SGID] | ||
# 6. return detElEfficiencies[det_el1] * detElEfficiencies[det_el2] * module_pair_efficiencies[det_el_in_mod1, det_el_in_mod2] | ||
# | ||
# If either of the components is not present, its value is considered to be 1. | ||
DetectionEfficiencies: !record | ||
fields: | ||
# Detection efficiencies for every detecting element | ||
detElEfficiencies: DetElEfficiencies? | ||
# Lookup table for SGIDs. | ||
# Also indicates if coincidences between a module-pair are recorded. | ||
modulePairSGIDLUT: ModulePairSGIDLUT? | ||
# Vector of all modulePairEfficiencies (one for each SGID) | ||
# Constraint: size(modulePairEfficienciesVector) == max(modulePairSGIDLUT) + 1 | ||
modulePairEfficienciesVector: ModulePairEfficienciesVector? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.