STIR 6.4.0
ProjMatrixByBin.inl
Go to the documentation of this file.
1//
2//
18/*
19 Copyright (C) 2000 PARAPET partners
20 Copyright (C) 2000- 2013, Hammersmith Imanet Ltd
21 Copyright (C) 2016, University of Hull
22 Copyright (C) 2022 University College London
23 This file is part of STIR.
24
25 SPDX-License-Identifier: Apache-2.0 AND License-ref-PARAPET-license
26
27 See STIR/LICENSE.txt for details
28*/
29#include "stir/Succeeded.h"
32#include "stir/numerics/erf.h"
33
34START_NAMESPACE_STIR
35
36const DataSymmetriesForBins*
38{
39 return symmetries_sptr.get();
40}
41
42const shared_ptr<DataSymmetriesForBins>
44{
45 return symmetries_sptr;
46}
47
48inline void
50{
51 // start_timers(); TODO, can't do this in a const member
52
53 // set to empty
54 probabilities.erase();
55
56 if (cache_stores_only_basic_bins)
57 {
58 // find symmetry operator and basic bin
59 Bin basic_bin = bin;
60 unique_ptr<SymmetryOperation> symm_ptr = symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin);
61
62 probabilities.set_bin(basic_bin);
63 // check if basic bin is in cache
64 if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == Succeeded::no)
65 {
66 // basic bin is not in cache, compute lor probabilities for the basic bin
68#ifndef NDEBUG
69 probabilities.check_state();
70#endif
71 if (proj_data_info_sptr->is_tof_data() && this->tof_enabled)
72 { // Apply TOF kernel to basic bin
73 apply_tof_kernel(probabilities);
74 }
76 }
77
78 // now transform to original bin (inc. TOF)
79 symm_ptr->transform_proj_matrix_elems_for_one_bin(probabilities);
80 }
81 else
82 { // !cache_stores_only_basic_bins
83 probabilities.set_bin(bin);
84 // if bin is in the cache, get the probabilities
85 if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == Succeeded::no)
86 {
87 // bin probabilities not in the cache, check if basic bins are
88 // find basic bin
89 Bin basic_bin = bin;
90 unique_ptr<SymmetryOperation> symm_ptr = symmetries_sptr->find_symmetry_operation_from_basic_bin(basic_bin);
91 probabilities.set_bin(basic_bin);
92
93 // check if basic bin is in cache
94 if (get_cached_proj_matrix_elems_for_one_bin(probabilities) == Succeeded::no)
95 {
96 // basic bin is not in cache, compute lor probabilities for the basic bin
98#ifndef NDEBUG
99 probabilities.check_state();
100#endif
101 if (proj_data_info_sptr->is_tof_data() && this->tof_enabled)
102 { // Apply TOF kernel to basic bin
103 apply_tof_kernel(probabilities);
104 }
105 }
106 // now transform basic bin probabilities into original bin probabilities
107 symm_ptr->transform_proj_matrix_elems_for_one_bin(probabilities);
108 // cache the probabilities for bin
110 }
111 }
112 // stop_timers(); TODO, can't do this in a const member
113}
114
115void
116ProjMatrixByBin::apply_tof_kernel(ProjMatrixElemsForOneBin& probabilities) const
117{
119 proj_data_info_sptr->get_LOR(lor, probabilities.get_bin());
120 const LORAs2Points<float> lor2(lor);
121 const CartesianCoordinate3D<float> point1 = lor2.p1();
122 const CartesianCoordinate3D<float> point2 = lor2.p2();
123
124 // Coordinate system correction: TODO remove in future with ORIGIN shift PR
125 // LOR coordinates have origin at scanner center (z=0 at center of all rings)
126 // Image coordinates have origin at first ring (z=0 at ring 0)
127 // Calculate the offset: distance from first ring to scanner center
128 const float scanner_z_offset = (proj_data_info_sptr->get_scanner_ptr()->get_num_rings() - 1) / 2.0f
129 * proj_data_info_sptr->get_scanner_ptr()->get_ring_spacing();
130 const CartesianCoordinate3D<float> coord_system_offset(scanner_z_offset, 0.0f, 0.0f);
131
132 const CartesianCoordinate3D<float> middle = (point1 + point2) * 0.5f;
133 const CartesianCoordinate3D<float> diff = point2 - middle;
134 const CartesianCoordinate3D<float> diff_unit_vector(diff / static_cast<float>(norm(diff)));
135
136 ProjMatrixElemsForOneBin tof_row(probabilities.get_bin());
137 // Estimate size of TOF row such that we can pre-allocate.
138 std::size_t max_num_elements;
139 {
140 const auto length = norm(point1 - point2);
141 const auto kernel_width = 8 / r_sqrt2_gauss_sigma;
142 const auto tof_bin_width = proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin().timing_pos_num()].high_lim
143 - proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin().timing_pos_num()].low_lim;
144 const auto fraction = (kernel_width + tof_bin_width) / length;
145 // This seems to sometimes over-, sometimes underestimate, but it's probably not very important
146 // as std::vector will grow as necessary.
147 max_num_elements = std::size_t(probabilities.size() * std::min(fraction * 1.2, 1.001));
148 }
149 tof_row.reserve(max_num_elements);
150
151 for (ProjMatrixElemsForOneBin::iterator element_ptr = probabilities.begin(); element_ptr != probabilities.end(); ++element_ptr)
152 {
153 Coordinate3D<int> c(element_ptr->get_coords());
154 // Get voxel physical coordinates (in image coordinate system)
155 const CartesianCoordinate3D<float> voxel_pos_image = image_info_sptr->get_physical_coordinates_for_indices(c);
156
157 // Convert to scanner coordinate system by subtracting the offset
158 const CartesianCoordinate3D<float> voxel_pos_scanner = voxel_pos_image - coord_system_offset;
159
160 // Now compute TOF distance in the same coordinate system as the LOR
161 const float d2 = -inner_product(voxel_pos_scanner - middle, diff_unit_vector);
162
163 const float low_dist
164 = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin().timing_pos_num()].low_lim - d2));
165 const float high_dist
166 = ((proj_data_info_sptr->tof_bin_boundaries_mm[probabilities.get_bin().timing_pos_num()].high_lim - d2));
167
168 const auto tof_kernel_value = get_tof_value(low_dist, high_dist);
169 if (tof_kernel_value > 0)
170 {
171 if (auto non_tof_value = element_ptr->get_value())
172 tof_row.push_back(ProjMatrixElemsForOneBin::value_type(c, non_tof_value * tof_kernel_value));
173 }
174 else
175 {
176 // Optimisation would be to get out of the loop once we're "beyond" the TOF kernel,
177 // but it is tricky to do. It requires that the input is sorted
178 // "along" the LOR, i.e. d2 increases montonically, but that doesn't seem to be true.
179 // if (tof_row.size() > 0)
180 // break;
181 }
182 }
183 probabilities = tof_row;
184 // info("Estimate " + std::to_string(max_num_elements) + ", actual " + std::to_string(tof_row.size()));
185}
186
187float
188ProjMatrixByBin::get_tof_value(const float d1, const float d2) const
189{
190 const float d1_n = d1 * r_sqrt2_gauss_sigma;
191 const float d2_n = d2 * r_sqrt2_gauss_sigma;
192
193 if ((d1_n >= 4.f && d2_n >= 4.f) || (d1_n <= -4.f && d2_n <= -4.f))
194 return 0.F;
195 else
196 return static_cast<float>(0.5 * (erf_interpolation(d2_n) - erf_interpolation(d1_n)));
197}
198
199END_NAMESPACE_STIR
Declaration of class stir::Succeeded.
Declaration of 2 classes: stir::SymmetryOperation and stir::TrivialSymmetryOperation.
A class for storing coordinates and value of a single projection bin.
Definition Bin.h:49
a templated class for 3-dimensional coordinates.
Definition CartesianCoordinate3D.h:53
A class for LORs.
Definition LORCoordinates.h:296
A class for LORs.
Definition LORCoordinates.h:511
const DataSymmetriesForBins * get_symmetries_ptr() const
get a pointer to an object encoding all symmetries that are used by this ProjMatrixByBin
Definition ProjMatrixByBin.inl:37
const shared_ptr< DataSymmetriesForBins > get_symmetries_sptr() const
get a shared_ptr to an object encoding all symmetries that are used by this ProjMatrixByBin
Definition ProjMatrixByBin.inl:43
void cache_proj_matrix_elems_for_one_bin(const ProjMatrixElemsForOneBin &) const
The method to store data in the cache.
Definition ProjMatrixByBin.cxx:202
virtual void calculate_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin &) const =0
This method needs to be implemented in the derived class.
void get_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin &, const Bin &) const
The main method for getting a row of the matrix.
Definition ProjMatrixByBin.inl:49
Succeeded get_cached_proj_matrix_elems_for_one_bin(ProjMatrixElemsForOneBin &) const
The method that tries to get data from the cache.
Definition ProjMatrixByBin.cxx:221
shared_ptr< const ProjDataInfo > proj_data_info_sptr
We need a local copy of the proj_data_info to get the integration boundaries and RayTracing.
Definition ProjMatrixByBin.h:192
This stores the non-zero projection matrix elements for every 'densel' that contributes to a given bi...
Definition ProjMatrixElemsForOneBin.h:69
size_type size() const
number of non-zero elements
Definition ProjMatrixElemsForOneBin.inl:47
const Bin & get_bin() const
get the bin coordinates corresponding to this row
Definition ProjMatrixElemsForOneBin.inl:29
void set_bin(const Bin &)
and set the bin coordinates
Definition ProjMatrixElemsForOneBin.inl:35
iterator begin()
functions for allowing iterator access
Definition ProjMatrixElemsForOneBin.inl:53
Succeeded check_state() const
check if each voxel occurs only once
Definition ProjMatrixElemsForOneBin.cxx:102
void erase()
reset lor to 0 length
Definition ProjMatrixElemsForOneBin.cxx:57
int timing_pos_num() const
get TOF index for const objects
Definition SegmentIndices.inl:44
double norm(const BasicCoordinate< num_dimensions, coordT > &p1)
compute sqrt(inner_product(p1,p1))
Definition BasicCoordinate.inl:426
A few functions to compute distances between lines etc.