CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
iProjectorIncrementalSiddonMulti.cc
Go to the documentation of this file.
1 /*
2 This file is part of CASToR.
3 
4  CASToR is free software: you can redistribute it and/or modify it under the
5  terms of the GNU General Public License as published by the Free Software
6  Foundation, either version 3 of the License, or (at your option) any later
7  version.
8 
9  CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  details.
13 
14  You should have received a copy of the GNU General Public License along with
15  CASToR (in file GNU_GPL.TXT). If not, see <http://www.gnu.org/licenses/>.
16 
17 Copyright 2017-2019 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF
20 
21 This is CASToR version 3.0.
22 */
23 
31 #include "sOutputManager.hh"
32 #ifdef _WIN32
33 // Avoid compilation errors due to mix up between std::min()/max() and
34 // min max macros
35 #undef min
36 #undef max
37 #endif
38 
39 // =====================================================================
40 // ---------------------------------------------------------------------
41 // ---------------------------------------------------------------------
42 // =====================================================================
43 
45 {
46  m_nbSensLines = 0;
47  m_nbRecoLines = 0;
48  // This projector is not compatible with SPECT attenuation correction because
49  // the voxels contributing to the line are not strictly ordered with respect to
50  // their distance to point 2 (due to the use of multiple lines that are
51  // stack one after the other)
53  // This projector is not compatible with compression as it works only
54  // with the detection element indices
56 }
57 
58 // =====================================================================
59 // ---------------------------------------------------------------------
60 // ---------------------------------------------------------------------
61 // =====================================================================
62 
64 {
65 }
66 
67 // =====================================================================
68 // ---------------------------------------------------------------------
69 // ---------------------------------------------------------------------
70 // =====================================================================
71 
72 int iProjectorIncrementalSiddonMulti::ReadConfigurationFile(const string& a_configurationFile)
73 {
74  // Read the transaxial FWHM option
75  string key_word = "number of lines sensitivity";
76  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_nbSensLines, 1, KEYWORD_OPTIONAL))
77  {
78  Cerr("***** iProjectorIncrementalSiddonMulti::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
79  return 1;
80  }
81 
82  key_word = "number of lines reconstruction";
83  if (ReadDataASCIIFile(a_configurationFile, key_word, &m_nbRecoLines, 1, KEYWORD_MANDATORY))
84  {
85  Cerr("***** iProjectorIncrementalSiddonMulti::ReadConfigurationFile() -> Failed to get the '" << key_word << "' keyword !" << endl);
86  return 1;
87  }
88 
89  // If not initialized, set the number of line used for sens image generation
90  // to the number of lines used for reconstruction
92 
93  // Normal end
94  return 0;
95 }
96 
97 // =====================================================================
98 // ---------------------------------------------------------------------
99 // ---------------------------------------------------------------------
100 // =====================================================================
101 
103 {
104  // Read them
105  uint16_t option[2];
106  if (ReadStringOption(a_optionsList, option, 2, ",", "Multi-Siddon configuration"))
107  {
108  Cerr("***** iProjectorIncrementalSiddonMulti::ReadConfigurationFile() -> Failed to correctly read the list of options !" << endl);
109  return 1;
110  }
111 
112  m_nbSensLines = option[0];
113  m_nbRecoLines = option[1];
114 
115  // Normal end
116  return 0;
117 }
118 
119 // =====================================================================
120 // ---------------------------------------------------------------------
121 // ---------------------------------------------------------------------
122 // =====================================================================
123 
125 {
126  cout << "This projector uses multiple ray-tracing for a single event in order to estimate the solid angle contribution." << endl;
127  cout << "For each line of an event, the end points of the line are randomly chosen inside the detector element." << endl;
128  cout << "For PET systems, if a mean depth of interaction is given, the distribution of the random points will be centered on the provided value" << endl << endl;
129  cout << "The ray-tracing is performed with the incremental Siddon algorithm (see incrementalSiddon projector)." << endl;
130  cout << "!! IMPORTANT: This implementation has not been assessed for systems using detectors with non-zero axial angle yet." << endl;
131  cout << "There are 2 parameters for this projector:" << endl;
132  cout << " number of lines sensitivity: the number of lines used per event for the sensitivity image generation (list-mode only)." << endl;
133  cout << " this parameter is ignored for histogram format." << endl;
134  cout << " number of lines reconstruction: the number of lines used per event for the reconstruction (and histogram sensitivity image generation)" << endl << endl;
135  cout << "This projector can be initialized with a configuration file containing the 'number of lines sensitivity' and 'number of lines reconstruction' keywords and values" << endl;
136  cout << "This projector can be initialized with command line options (1st option: number of lines sensitivity, 2nd option: number of lines reconstruction" << endl;
137 }
138 
139 // =====================================================================
140 // ---------------------------------------------------------------------
141 // ---------------------------------------------------------------------
142 // =====================================================================
143 
145 {
146  // Send an error if less than 1 line
147  if (m_nbSensLines<1 || m_nbRecoLines<1)
148  {
149  Cerr("***** iProjectorIncrementalSiddonMulti::CheckSpecificParameters() -> The provided number of lines is less than 1 !" << endl);
150  return 1;
151  }
152  // Normal end
153  return 0;
154 }
155 
156 // =====================================================================
157 // ---------------------------------------------------------------------
158 // ---------------------------------------------------------------------
159 // =====================================================================
160 
162 {
163  // Verbose
164  if (m_verbose>=2) Cout("iProjectorIncrementalSiddonMulti::Initialize() -> Use incremental Siddon projector with " << m_nbRecoLines << " lines per event in reconstruction" << endl);
165  // Normal end
166  return 0;
167 }
168 
169 // =====================================================================
170 // ---------------------------------------------------------------------
171 // ---------------------------------------------------------------------
172 // =====================================================================
173 
175 {
176  // Find the maximum number of voxels along a given dimension
177  INTNB max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxX();
178  if (mp_ImageDimensionsAndQuantification->GetNbVoxY()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxY();
179  if (mp_ImageDimensionsAndQuantification->GetNbVoxZ()>max_nb_voxels_in_dimension) max_nb_voxels_in_dimension = mp_ImageDimensionsAndQuantification->GetNbVoxZ();
180  // We should have at most 4 voxels in a given plane, so multiply by 4
181  // (note: this is not true however it ensures no overflow and is already quite optimized for RAM usage !)
182  max_nb_voxels_in_dimension *= 4;
183  // Finally multiply by the number of lines
184  max_nb_voxels_in_dimension *= (INTNB)(std::max)( m_nbSensLines, m_nbRecoLines );
185  // Return the value
186  return max_nb_voxels_in_dimension;
187 }
188 
189 // =====================================================================
190 // ---------------------------------------------------------------------
191 // ---------------------------------------------------------------------
192 // =====================================================================
193 
195 {
196  #ifdef CASTOR_DEBUG
197  if (!m_initialized)
198  {
199  Cerr("***** iProjectorIncrementalSiddonMulti::ProjectWithoutTOF() -> Called while not initialized !" << endl);
200  Exit(EXIT_DEBUG);
201  }
202  #endif
203 
204  #ifdef CASTOR_VERBOSE
205  if (m_verbose>=10)
206  {
207  string direction = "";
208  if (a_direction==FORWARD) direction = "forward";
209  else direction = "backward";
210  Cout("iProjectorIncrementalSiddonMulti::ProjectWithoutTOF() -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
211  }
212  #endif
213 
214  // For list-mode, the number of lines to use depends on the processing stage (sensitivity generation or reconstruction)
215  uint16_t nb_lines = (m_sensitivityMode == true) ?
216  m_nbSensLines :
217  m_nbRecoLines ;
218 
219  // Loop on the number of lines
220  for (uint16_t line=0; line<nb_lines; line++)
221  {
222  // Get random positions from the scanner (mean depth of interaction intrinsicaly taken into account), taking POI into account if any
223  if (mp_Scanner->GetRdmPositionsAndOrientations( ap_ProjectionLine->GetIndex1(), ap_ProjectionLine->GetIndex2(),
224  ap_ProjectionLine->GetPosition1(), ap_ProjectionLine->GetPosition2(),
225  ap_ProjectionLine->GetOrientation1(), ap_ProjectionLine->GetOrientation2() ))
226  {
227  Cerr("***** iProjectorIncrementalSiddonMulti::ProjectWithoutTOF() -> A problem occurred while getting positions and orientations from scanner !" << endl);
228  return 1;
229  }
230 
231  // We must called here ApplyOffset() and ComputeLineLength() which
232  // are supposed to be called in vProjector::Project()
233  // This is a temporary solution as everyting related to multi-lines
234  // management should be performed in vProjector
235 
236  // Apply global image offset
237  ap_ProjectionLine->ApplyOffset();
238 
239  // Apply bed position offset
240  ap_ProjectionLine->ApplyBedOffset();
241 
242  // Get end points position
243  FLTNB* event1Float = ap_ProjectionLine->GetPosition1();
244  FLTNB* event2Float = ap_ProjectionLine->GetPosition2();
245  HPFLTNB event1[3] = { event1Float[0], event1Float[1], event1Float[2] };
246  HPFLTNB event2[3] = { event2Float[0], event2Float[1], event2Float[2] };
247 
248  // **************************************
249  // STEP 1: LOR length calculation
250  // **************************************
251  // Must recompute LOR length for each new line of response
252  ap_ProjectionLine->ComputeLineLength();
253  HPFLTNB length_LOR = ap_ProjectionLine->GetLength();
254 
255  // **************************************
256  // STEP 2: Compute entrance voxel indexes
257  // **************************************
258  HPFLTNB alphaFirst[] = { 0.0, 0.0, 0.0 };
259  HPFLTNB alphaLast[] = { 0.0, 0.0, 0.0 };
260 
261  HPFLTNB alphaMin = 0.0f, alphaMax = 1.0f;
262  HPFLTNB delta_pos[] = {
263  event2[ 0 ] - event1[ 0 ],
264  event2[ 1 ] - event1[ 1 ],
265  event2[ 2 ] - event1[ 2 ]
266  };
267 
268  // Computation of alphaMin et alphaMax values (entrance and exit point of the ray)
269  for( int i = 0; i < 3; ++i )
270  {
271  if( delta_pos[ i ] != 0.0 )
272  {
273  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
274  alphaLast[i] = ( mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
275  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
276  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
277  }
278  }
279 
280  // if alphaMax is less than or equal to alphaMin no intersection
281  // and return an empty buffer
282  if( alphaMax <= alphaMin ) return 0;
283 
284  // Now we have to find the indices of the particular plane
285  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
286  int iMin = 0, iMax = 0;
287  int jMin = 0, jMax = 0;
288  int kMin = 0, kMax = 0;
289 
290  // For the X-axis
291  if( delta_pos[ 0 ] > 0.0f)
292  {
293  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
294  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
295  }
296  else if( delta_pos[ 0 ] < 0.0 )
297  {
298  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
299  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
300  }
301  if( delta_pos[ 0 ] == 0 )
302  {
303  iMin = 1, iMax = 0;
304  }
305 
306  // For the Y-axis
307  if( delta_pos[ 1 ] > 0 )
308  {
309  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
310  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
311  }
312  else if( delta_pos[ 1 ] < 0 )
313  {
314  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
315  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
316  }
317  else if( delta_pos[ 1 ] == 0 )
318  {
319  jMin = 1, jMax = 0;
320  }
321 
322  // For the Z-axis
323  if( delta_pos[ 2 ] > 0 )
324  {
325  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
326  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
327  }
328  else if( delta_pos[ 2 ] < 0 )
329  {
330  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
331  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
332  }
333  else if( delta_pos[ 2 ] == 0 )
334  {
335  kMin = 1, kMax = 0;
336  }
337 
338  // Computing the last term n number of intersection
339  INTNB n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
340  + ( kMax - kMin + 1 );
341 
342  // Array storing the first alpha in X, Y and Z
343  HPFLTNB alpha_XYZ[ 3 ] = { 1.0, 1.0, 1.0 };
344 
345  // Computing the first alpha in X
346  if( delta_pos[ 0 ] > 0 )
347  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMin - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
348  else if( delta_pos[ 0 ] < 0 )
349  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMax - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
350 
351  // Computing the first alpha in Y
352  if( delta_pos[ 1 ] > 0 )
353  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMin - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
354  else if( delta_pos[ 1 ] < 0 )
355  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMax - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
356 
357  // Computing the first alpha in Z
358  if( delta_pos[ 2 ] > 0 )
359  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMin - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
360  else if( delta_pos[ 2 ] < 0 )
361  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMax - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
362 
363  // Computing the alpha updating
364  HPFLTNB alpha_u[ 3 ] = {
365  mp_sizeVox[0] / std::fabs( delta_pos[ 0 ] ),
366  mp_sizeVox[1] / std::fabs( delta_pos[ 1 ] ),
367  mp_sizeVox[2] / std::fabs( delta_pos[ 2 ] )
368  };
369 
370  // Computing the index updating
371  INTNB index_u[ 3 ] = {
372  (delta_pos[ 0 ] < 0) ? -1 : 1,
373  (delta_pos[ 1 ] < 0) ? -1 : 1,
374  (delta_pos[ 2 ] < 0) ? -1 : 1
375  };
376 
377  // Check which alpha is the min/max and increment
378  if( alpha_XYZ[ 0 ] == alphaMin ) alpha_XYZ[ 0 ] += alpha_u[ 0 ];
379  if( alpha_XYZ[ 1 ] == alphaMin ) alpha_XYZ[ 1 ] += alpha_u[ 1 ];
380  if( alpha_XYZ[ 2 ] == alphaMin ) alpha_XYZ[ 2 ] += alpha_u[ 2 ];
381 
382  // Computing the minimum value in the alpha_XYZ buffer
383  HPFLTNB const min_alpha_XYZ = (std::min)( alpha_XYZ[ 0 ],
384  (std::min)( alpha_XYZ[ 1 ], alpha_XYZ[ 2 ] ) );
385 
386  // Computing the first index of intersection
387  HPFLTNB const alphaMid = ( min_alpha_XYZ + alphaMin ) / 2.0f;
388  INTNB index_ijk[ 3 ] = {
389  1 + (int)( ( event1[ 0 ] + alphaMid * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] ),
390  1 + (int)( ( event1[ 1 ] + alphaMid * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] ),
391  1 + (int)( ( event1[ 2 ] + alphaMid * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] )
392  };
393 
394  INTNB const w = mp_nbVox[ 0 ];
395  INTNB const wh = w * mp_nbVox[ 1 ];
396 
397  // Loop over the number of plane to cross
398  HPFLTNB alpha_c = alphaMin;
399  FLTNB coeff = 0.0f;
400  INTNB numVox = 0;
401 
402  for( int nP = 0; nP < n - 1; ++nP )
403  {
404  if( ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 1 ] )
405  && ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 2 ] ) )
406  {
407  // Storing values
408  if( ( alpha_XYZ[ 0 ] >= alphaMin )
409  && ( index_ijk[ 0 ] - 1 >= 0 )
410  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
411  && ( index_ijk[ 1 ] - 1 >= 0 )
412  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
413  && ( index_ijk[ 2 ] - 1 >= 0 )
414  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
415  {
416  coeff = ( alpha_XYZ[ 0 ] - alpha_c ) * length_LOR;
417  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
418  // if this voxel is masked, do nothing
419  if (!m_hasMask || mp_mask[numVox]) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff/((FLTNB)nb_lines) );
420  }
421  // Increment values
422  alpha_c = alpha_XYZ[ 0 ];
423  alpha_XYZ[ 0 ] += alpha_u[ 0 ];
424  index_ijk[ 0 ] += index_u[ 0 ];
425  }
426  else if( ( alpha_XYZ[ 1 ] < alpha_XYZ[ 0 ] )
427  && ( alpha_XYZ[ 1 ] <= alpha_XYZ[ 2 ] ) )
428  {
429  // Storing values
430  if( ( alpha_XYZ[ 1 ] >= alphaMin )
431  && ( index_ijk[ 0 ] - 1 >= 0 )
432  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
433  && ( index_ijk[ 1 ] - 1 >= 0 )
434  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
435  && ( index_ijk[ 2 ] - 1 >= 0 )
436  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
437  {
438  coeff = ( alpha_XYZ[ 1 ] - alpha_c ) * length_LOR;
439  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
440  // if this voxel is masked, do nothing
441  if (!m_hasMask || mp_mask[numVox]) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff/((FLTNB)nb_lines) );
442  }
443  // Increment values
444  alpha_c = alpha_XYZ[ 1 ];
445  alpha_XYZ[ 1 ] += alpha_u[ 1 ];
446  index_ijk[ 1 ] += index_u[ 1 ];
447  }
448  else if( ( alpha_XYZ[ 2 ] < alpha_XYZ[ 0 ] )
449  && ( alpha_XYZ[ 2 ] < alpha_XYZ[ 1 ] ) )
450  {
451  // Storing values
452  if( ( alpha_XYZ[ 2 ] >= alphaMin )
453  && ( index_ijk[ 0 ] - 1 >= 0 )
454  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
455  && ( index_ijk[ 1 ] - 1 >= 0 )
456  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
457  && ( index_ijk[ 2 ] - 1 >= 0 )
458  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
459  {
460  coeff = ( alpha_XYZ[ 2 ] - alpha_c ) * length_LOR;
461  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
462  // if this voxel is masked, do nothing
463  if (!m_hasMask || mp_mask[numVox]) ap_ProjectionLine->AddVoxel( a_direction, numVox, coeff/((FLTNB)nb_lines) );
464  }
465 
466  // Increment values
467  alpha_c = alpha_XYZ[ 2 ];
468  alpha_XYZ[ 2 ] += alpha_u[ 2 ];
469  index_ijk[ 2 ] += index_u[ 2 ];
470  }
471  }
472  }
473 
474  return 0;
475 }
476 
477 // =====================================================================
478 // ---------------------------------------------------------------------
479 // ---------------------------------------------------------------------
480 // =====================================================================
481 
483 {
484  #ifdef CASTOR_DEBUG
485  if (!m_initialized)
486  {
487  Cerr("***** iProjectorIncrementalSiddonMulti::ProjectTOFListmode() -> Called while not initialized !" << endl);
488  Exit(EXIT_DEBUG);
489  }
490  #endif
491 
492  #ifdef CASTOR_VERBOSE
493  if (m_verbose>=10)
494  {
495  string direction = "";
496  if (a_direction==FORWARD) direction = "forward";
497  else direction = "backward";
498  Cout("iProjectorIncrementalSiddonMulti::ProjectTOFListmode() -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
499  }
500  #endif
501 
502  // For list-mode, the number of lines to use depends on the processing stage (sensitivity generation or reconstruction)
503  uint16_t nb_lines = (m_sensitivityMode == true) ?
504  m_nbSensLines :
505  m_nbRecoLines ;
506 
507  // Loop on the number of lines
508  for (uint16_t line=0; line<nb_lines; line++)
509  {
510  // Get random positions from the scanner (mean depth of interaction intrinsicaly taken into account), taking POI into account if any
511  if (mp_Scanner->GetRdmPositionsAndOrientations( ap_ProjectionLine->GetIndex1(), ap_ProjectionLine->GetIndex2(),
512  ap_ProjectionLine->GetPosition1(), ap_ProjectionLine->GetPosition2(),
513  ap_ProjectionLine->GetOrientation1(), ap_ProjectionLine->GetOrientation2() ))
514  {
515  Cerr("***** iProjectorIncrementalSiddonMulti::ProjectTOFListmode() -> A problem occurred while getting positions and orientations from scanner !" << endl);
516  return 1;
517  }
518 
519  // We must called here ApplyOffset() and ComputeLineLength() which
520  // are supposed to be called in vProjector::Project()
521  // This is a temporary solution as everyting related to multi-lines
522  // management should be performed in vProjector
523 
524  // Apply global image offset
525  ap_ProjectionLine->ApplyOffset();
526 
527  // Apply bed position offset
528  ap_ProjectionLine->ApplyBedOffset();
529 
530  // Get end points position
531  FLTNB* event1Float = ap_ProjectionLine->GetPosition1();
532  FLTNB* event2Float = ap_ProjectionLine->GetPosition2();
533  HPFLTNB event1[3] = { event1Float[0], event1Float[1], event1Float[2] };
534  HPFLTNB event2[3] = { event2Float[0], event2Float[1], event2Float[2] };
535 
536  // **************************************
537  // STEP 1: LOR length calculation
538  // **************************************
539  // Must recompute LOR length for each new line of response
540  ap_ProjectionLine->ComputeLineLength();
541  HPFLTNB length_LOR = ap_ProjectionLine->GetLength();
542 
543  // **************************************
544  // STEP 2: Compute entrance voxel indexes
545  // **************************************
546  HPFLTNB alphaFirst[] = { 0.0, 0.0, 0.0 };
547  HPFLTNB alphaLast[] = { 0.0, 0.0, 0.0 };
548 
549  HPFLTNB alphaMin = 0.0f, alphaMax = 1.0f;
550  HPFLTNB delta_pos[] = {
551  event2[ 0 ] - event1[ 0 ],
552  event2[ 1 ] - event1[ 1 ],
553  event2[ 2 ] - event1[ 2 ]
554  };
555 
556  // Get TOF info
557  HPFLTNB tof_measurement = ap_ProjectionLine->GetTOFMeasurementInPs();
558 
559  // convert delta time into delta length
560  HPFLTNB tof_delta = tof_measurement * SPEED_OF_LIGHT_IN_MM_PER_PS * 0.5;
561 
562  // TOF Gaussian standard deviation and truncation
564  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
565  HPFLTNB tof_half_span = 0.;
566 
568  else if (m_TOFBinProperProcessingFlag) tof_half_span = tof_sigma * m_TOFNbSigmas + 0.5 * m_TOFBinSizeInMm;
569  else tof_half_span = tof_sigma * m_TOFNbSigmas;
570 
571  // distance between the first event1 and the TOF measurement
572  HPFLTNB lor_tof_center = length_LOR * 0.5 + tof_delta;
573 
574  // index along each axis of the first voxel falling inside the truncated TOF distribution centered at the TOF measurement
575  HPFLTNB tof_edge_low[] = {0,0,0};
576  // index along each axis of the last voxel falling inside the truncated TOF distribution centered at the TOF measurement
577  HPFLTNB tof_edge_high[] = {0,0,0};
578  HPFLTNB tof_center;
579  INTNB tof_index_low[] = {0,0,0};
580  INTNB tof_index_high[] = {0,0,0};
581 
582  // low/high voxel edges (in absolute coordinates) for truncated TOF
583  for (int ax=0;ax<3;ax++)
584  {
585  // absolute coordinate along each axis of the center of the TOF distribution
586  tof_center = event1[ax] + lor_tof_center * delta_pos[ax] / length_LOR;
587 
588  // absolute coordinate along each axis of the lowest voxel edge spanned by the TOF distribution, limited by the lowest FOV edge
589  tof_edge_low[ax] = tof_center - tof_half_span * fabs(delta_pos[ax]) / length_LOR;
590  tof_index_low[ax] = max( (INTNB)::floor( (tof_edge_low[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), (INTNB)0);
591  // if low TOF edge above the highest FOV edge, return empty line
592  if (tof_index_low[ax]>mp_nbVox[ax]-1) return 0;
593  tof_edge_low[ax] = (HPFLTNB)tof_index_low[ax] * mp_sizeVox[ax] - mp_halfFOV[ax];
594 
595  // absolute coordinate along each axis of the highest voxel edge spanned by the TOF distribution, limited by the highest FOV edge
596  tof_edge_high[ax] = tof_center + tof_half_span * fabs(delta_pos[ax]) / length_LOR;
597  tof_index_high[ax] = min( (INTNB)::floor( (tof_edge_high[ax] - (-mp_halfFOV[ax])) / mp_sizeVox[ax] ), mp_nbVox[ax]-1);
598  // if high TOF edge below the lowest FOV edge, return empty line
599  if (tof_index_high[ax]<0) return 0;
600  tof_edge_high[ax] = (HPFLTNB)(tof_index_high[ax]+1) * mp_sizeVox[ax] - mp_halfFOV[ax];
601  }
602 
603  // Computation of alphaMin et alphaMax values entrance and exit point of the ray at voxel grid (edges),
604  // with respect to the truncated TOF distribution centered at the TOF measurement,
605  // limited by the FOV, absolute normalized distance from event1
606  for( int i = 0; i < 3; ++i )
607  {
608  if( delta_pos[ i ] != 0.0 )
609  {
610  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
611  alphaLast[i] = ( mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
612  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
613  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
614  }
615  }
616 
617  // if alphaMax is less than or equal to alphaMin no intersection
618  // and return an empty buffer
619  if( alphaMax <= alphaMin ) return 0;
620 
621  // Now we have to find the indices of the particular plane
622  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
623  int iMin = 0, iMax = 0;
624  int jMin = 0, jMax = 0;
625  int kMin = 0, kMax = 0;
626 
627  // For the X-axis
628  if( delta_pos[ 0 ] > 0.0f )
629  {
630  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
631  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
632  }
633  else if( delta_pos[ 0 ] < 0.0f )
634  {
635  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
636  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
637  }
638  if( delta_pos[ 0 ] == 0 )
639  {
640  iMin = 1, iMax = 0;
641  }
642 
643  // For the Y-axis
644  if( delta_pos[ 1 ] > 0 )
645  {
646  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
647  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
648  }
649  else if( delta_pos[ 1 ] < 0 )
650  {
651  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
652  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
653  }
654  else if( delta_pos[ 1 ] == 0 )
655  {
656  jMin = 1, jMax = 0;
657  }
658 
659  // For the Z-axis
660  if( delta_pos[ 2 ] > 0 )
661  {
662  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
663  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
664  }
665  else if( delta_pos[ 2 ] < 0 )
666  {
667  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
668  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
669  }
670  else if( delta_pos[ 2 ] == 0 )
671  {
672  kMin = 1, kMax = 0;
673  }
674 
675  // Computing the last term n number of intersection
676  INTNB n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
677  + ( kMax - kMin + 1 );
678 
679  // Array storing the first alpha in X, Y and Z
680  HPFLTNB alpha_XYZ[ 3 ] = { 1.0f, 1.0f, 1.0f };
681 
682  // Computing the first alpha in X
683  if( delta_pos[ 0 ] > 0 )
684  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMin - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
685  else if( delta_pos[ 0 ] < 0 )
686  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMax - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
687 
688  // Computing the first alpha in Y
689  if( delta_pos[ 1 ] > 0 )
690  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMin - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
691  else if( delta_pos[ 1 ] < 0 )
692  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMax - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
693 
694  // Computing the first alpha in Z
695  if( delta_pos[ 2 ] > 0 )
696  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMin - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
697  else if( delta_pos[ 2 ] < 0 )
698  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMax - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
699 
700  // Computing the alpha updating
701  HPFLTNB alpha_u[ 3 ] = {
702  mp_sizeVox[0] / std::fabs( delta_pos[ 0 ] ),
703  mp_sizeVox[1] / std::fabs( delta_pos[ 1 ] ),
704  mp_sizeVox[2] / std::fabs( delta_pos[ 2 ] )
705  };
706 
707  // Computing the index updating
708  INTNB index_u[ 3 ] = {
709  (delta_pos[ 0 ] < 0) ? -1 : 1,
710  (delta_pos[ 1 ] < 0) ? -1 : 1,
711  (delta_pos[ 2 ] < 0) ? -1 : 1
712  };
713 
714  // Check which alpha is the min/max and increment
715  if( alpha_XYZ[ 0 ] == alphaMin ) alpha_XYZ[ 0 ] += alpha_u[ 0 ];
716  if( alpha_XYZ[ 1 ] == alphaMin ) alpha_XYZ[ 1 ] += alpha_u[ 1 ];
717  if( alpha_XYZ[ 2 ] == alphaMin ) alpha_XYZ[ 2 ] += alpha_u[ 2 ];
718 
719  // Computing the minimum value in the alpha_XYZ buffer
720  HPFLTNB const min_alpha_XYZ = (std::min)( alpha_XYZ[ 0 ], (std::min)( alpha_XYZ[ 1 ], alpha_XYZ[ 2 ] ) );
721 
722  // Computing the first index of intersection
723  HPFLTNB alphaMid = ( min_alpha_XYZ + alphaMin ) / 2.0f;
724  INTNB index_ijk[ 3 ] = {
725  1 + (INTNB)( ( event1[ 0 ] + alphaMid * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] ),
726  1 + (INTNB)( ( event1[ 1 ] + alphaMid * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] ),
727  1 + (INTNB)( ( event1[ 2 ] + alphaMid * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] )
728  };
729 
730  INTNB const w = mp_nbVox[ 0 ];
731  INTNB const wh = w * mp_nbVox[ 1 ];
732 
734  //HPFLTNB previous_edge_erf = 0.;
735  //HPFLTNB next_edge_erf = 0.;
736  //bool previous_edge_erf_initialized = false;
737 
738  // Loop over the number of plane to cross
739  HPFLTNB alpha_c = alphaMin;
740  FLTNB coeff = 0.0f;
741  INTNB numVox = 0;
742  for( int nP = 0; nP < n - 1; ++nP )
743  {
744  if( ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 1 ] )
745  && ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 2 ] ) )
746  {
747  // Storing values
748  if( ( alpha_XYZ[ 0 ] >= alphaMin )
749  && ( index_ijk[ 0 ] - 1 >= tof_index_low[0] )
750  && ( index_ijk[ 0 ] - 1 <= tof_index_high[0] )
751  && ( index_ijk[ 1 ] - 1 >= tof_index_low[1] )
752  && ( index_ijk[ 1 ] - 1 <= tof_index_high[1] )
753  && ( index_ijk[ 2 ] - 1 >= tof_index_low[2] )
754  && ( index_ijk[ 2 ] - 1 <= tof_index_high[2] ) )
755  {
757  //if (!previous_edge_erf_initialized)
758  //{
759  //previous_edge_erf = erf( (length_LOR *alpha_c - lor_tof_center) / tof_sigma_sqrt2 );
760  //previous_edge_erf_initialized = true;
761  //}
762 
763  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
764 
765  // voxel center
766  alphaMid = ( alpha_XYZ[ 0 ] + alpha_c ) * 0.5;
767 
768  // non TOF projection coefficient
769  coeff = ( alpha_XYZ[ 0 ] - alpha_c ) * length_LOR;
770 
772  {
773  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
774  // nearest to the projection of the voxel center onto the LOR
775  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
776  // multiply the non TOF projection coefficient with the TOF weight
777  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) coeff *= mp_TOFWeightingFcn[temp];
778  else coeff = 0.;
779  }
780  else
781  {
783  {
784  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
785  // of the Gaussian centered at the projection of the voxel center onto the LOR
786  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
787  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
788  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
789  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
790  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
791  // multiply the non TOF projection coefficient with the TOF weight
792  coeff *= 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
793  }
794  else
795  {
797  //next_edge_erf = erf( (length_LOR * alpha_XYZ[ 0 ] - lor_tof_center) / tof_sigma_sqrt2 );
799  //coeff = 0.5 * std::fabs(previous_edge_erf - next_edge_erf) ;
801  //previous_edge_erf = next_edge_erf;
802 
803  // TOF weight = value of the normalized Gaussian (centered at the TOF measurement)
804  // at the projection of the voxel center onto the LOR
805  HPFLTNB temp = (alphaMid * length_LOR - lor_tof_center) / tof_sigma;
806  // multiply the non TOF projection coefficient with the TOF weight
807  coeff *= exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef;
808  }
809  }
810 
811  if (!m_hasMask || mp_mask[numVox]) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
812  }
813 
814  // Increment values
815  alpha_c = alpha_XYZ[ 0 ];
816  alpha_XYZ[ 0 ] += alpha_u[ 0 ];
817  index_ijk[ 0 ] += index_u[ 0 ];
818  }
819  else if( ( alpha_XYZ[ 1 ] < alpha_XYZ[ 0 ] )
820  && ( alpha_XYZ[ 1 ] <= alpha_XYZ[ 2 ] ) )
821  {
822  // Storing values
823  if( ( alpha_XYZ[ 1 ] >= alphaMin )
824  && ( index_ijk[ 0 ] - 1 >= tof_index_low[0] )
825  && ( index_ijk[ 0 ] - 1 <= tof_index_high[0] )
826  && ( index_ijk[ 1 ] - 1 >= tof_index_low[1] )
827  && ( index_ijk[ 1 ] - 1 <= tof_index_high[1] )
828  && ( index_ijk[ 2 ] - 1 >= tof_index_low[2] )
829  && ( index_ijk[ 2 ] - 1 <= tof_index_high[2] ) )
830  {
832  //if (!previous_edge_erf_initialized)
833  //{
834  //previous_edge_erf = erf( (length_LOR *alpha_c - lor_tof_center) / tof_sigma_sqrt2 );
835  //previous_edge_erf_initialized = true;
836  //}
837 
838  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
839 
840  alphaMid = ( alpha_XYZ[ 1 ] + alpha_c ) / 2.;
841 
842  // non TOF projection coefficient
843  coeff = ( alpha_XYZ[ 1 ] - alpha_c ) * length_LOR;
844 
846  {
847  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
848  // nearest to the projection of the voxel center onto the LOR
849  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
850  // multiply the non TOF projection coefficient with the TOF weight
851  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) coeff *= mp_TOFWeightingFcn[temp];
852  else coeff=0.;
853  }
854  else
855  {
857  {
858  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
859  // of the Gaussian centered at the projection of the voxel center onto the LOR
860  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
861  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
862  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
863  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
864  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
865  // multiply the non TOF projection coefficient with the TOF weight
866  coeff *= 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
867  }
868  else
869  {
871  //next_edge_erf = erf( (length_LOR * alpha_XYZ[ 1 ] - lor_tof_center) / tof_sigma_sqrt2 );
873  //coeff = 0.5 * std::fabs(previous_edge_erf - next_edge_erf) ;
875  //previous_edge_erf = next_edge_erf;
876 
877  // TOF weight = value of the normalized Gaussian (centered at the TOF measurement)
878  // at the projection of the voxel center onto the LOR
879  HPFLTNB temp = (alphaMid * length_LOR - lor_tof_center) / tof_sigma;
880  // multiply the non TOF projection coefficient with the TOF weight
881  coeff *= exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef;
882  }
883  }
884 
885  // if the voxel is not masked
886  if (!m_hasMask || mp_mask[numVox]) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
887  }
888 
889  // Increment values
890  alpha_c = alpha_XYZ[ 1 ];
891  alpha_XYZ[ 1 ] += alpha_u[ 1 ];
892  index_ijk[ 1 ] += index_u[ 1 ];
893  }
894  else if( ( alpha_XYZ[ 2 ] < alpha_XYZ[ 0 ] )
895  && ( alpha_XYZ[ 2 ] < alpha_XYZ[ 1 ] ) )
896  {
897  // Storing values
898  if( ( alpha_XYZ[ 2 ] >= alphaMin )
899  && ( index_ijk[ 0 ] - 1 >= tof_index_low[0] )
900  && ( index_ijk[ 0 ] - 1 <= tof_index_high[0] )
901  && ( index_ijk[ 1 ] - 1 >= tof_index_low[1] )
902  && ( index_ijk[ 1 ] - 1 <= tof_index_high[1] )
903  && ( index_ijk[ 2 ] - 1 >= tof_index_low[2] )
904  && ( index_ijk[ 2 ] - 1 <= tof_index_high[2] ) )
905  {
907  //if (!previous_edge_erf_initialized)
908  //{
909  //previous_edge_erf = erf( (length_LOR *alpha_c - lor_tof_center) / tof_sigma_sqrt2 );
910  //previous_edge_erf_initialized = true;
911  //}
912 
913  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
914 
915  alphaMid = ( alpha_XYZ[ 2 ] + alpha_c ) / 2.;
916 
917  // non TOF projection coefficient
918  coeff = ( alpha_XYZ[ 2 ] - alpha_c ) * length_LOR;
919 
921  {
922  // fetch the value of the precomputed TOF weighting function (centered at the TOF measurement)
923  // nearest to the projection of the voxel center onto the LOR
924  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( (alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
925  // multiply the non TOF projection coefficient with the TOF weight
926  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) coeff *= mp_TOFWeightingFcn[temp];
927  else coeff = 0.;
928  }
929  else
930  {
932  {
933  // integration between the edges of the current quantization TOF bin (centered at the TOF measurement)
934  // of the Gaussian centered at the projection of the voxel center onto the LOR
935  // normalized by the size of the bin so that the integral of the final TOF weighting function remains 1
936  HPFLTNB temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
937  HPFLTNB prev_erf = erf(temp_erf/tof_sigma_sqrt2);
938  temp_erf = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
939  HPFLTNB new_erf = erf(temp_erf/tof_sigma_sqrt2);
940  // multiply the non TOF projection coefficient with the TOF weight
941  coeff *= 0.5 * fabs(new_erf - prev_erf) / m_TOFBinSizeInMm;
942  }
943  else
944  {
946  //next_edge_erf = erf( (length_LOR * alpha_XYZ[ 2 ] - lor_tof_center) / tof_sigma_sqrt2 );
948  //coeff = 0.5 * std::fabs(previous_edge_erf - next_edge_erf) ;
950  //previous_edge_erf = next_edge_erf;
951 
952  // TOF weight = value of the normalized Gaussian (centered at the TOF measurement)
953  // at the projection of the voxel center onto the LOR
954  HPFLTNB temp = (alphaMid * length_LOR - lor_tof_center) / tof_sigma;
955  // multiply the non TOF projection coefficient with the TOF weight
956  coeff *= exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef;
957  }
958  }
959 
960  // if the voxel is not masked
961  if (!m_hasMask || mp_mask[numVox]) ap_ProjectionLine->AddVoxel(a_direction, numVox, coeff);
962  }
963 
964  // Increment values
965  alpha_c = alpha_XYZ[ 2 ];
966  alpha_XYZ[ 2 ] += alpha_u[ 2 ];
967  index_ijk[ 2 ] += index_u[ 2 ];
968  }
969  }
970  }
971 
972  return 0;
973 }
974 
975 // =====================================================================
976 // ---------------------------------------------------------------------
977 // ---------------------------------------------------------------------
978 // =====================================================================
979 
981 {
982  #ifdef CASTOR_DEBUG
983  if (!m_initialized)
984  {
985  Cerr("***** iProjectorIncrementalSiddonMulti::ProjectTOFHistogram() -> Called while not initialized !" << endl);
986  Exit(EXIT_DEBUG);
987  }
988  #endif
989 
990  #ifdef CASTOR_VERBOSE
991  if (m_verbose>=10)
992  {
993  string direction = "";
994  if (a_direction==FORWARD) direction = "forward";
995  else direction = "backward";
996  Cout("iProjectorIncrementalSiddonMulti::ProjectTOFHistogram() -> Project line '" << ap_ProjectionLine << "' in " << direction << " direction" << endl);
997  }
998  #endif
999 
1000  // For list-mode, the number of lines to use depends on the processing stage (sensitivity generation or reconstruction)
1001  uint16_t nb_lines = (m_sensitivityMode == true) ?
1002  m_nbSensLines :
1003  m_nbRecoLines ;
1004 
1005  // Loop on the number of lines
1006  for (uint16_t line=0; line<nb_lines; line++)
1007  {
1008  // Get random positions from the scanner (mean depth of interaction intrinsicaly taken into account), taking POI into account if any
1009  if (mp_Scanner->GetRdmPositionsAndOrientations( ap_ProjectionLine->GetIndex1(), ap_ProjectionLine->GetIndex2(),
1010  ap_ProjectionLine->GetPosition1(), ap_ProjectionLine->GetPosition2(),
1011  ap_ProjectionLine->GetOrientation1(), ap_ProjectionLine->GetOrientation2() ))
1012  {
1013  Cerr("***** iProjectorIncrementalSiddonMulti::ProjectTOFHistogram() -> A problem occurred while getting positions and orientations from scanner !" << endl);
1014  return 1;
1015  }
1016 
1017  // We must called here ApplyOffset() and ComputeLineLength() which
1018  // are supposed to be called in vProjector::Project()
1019  // This is a temporary solution as everyting related to multi-lines
1020  // management should be performed in vProjector
1021 
1022  // Apply global image offset
1023  ap_ProjectionLine->ApplyOffset();
1024 
1025  // Apply bed position offset
1026  ap_ProjectionLine->ApplyBedOffset();
1027 
1028  // Get end points position
1029  FLTNB* event1Float = ap_ProjectionLine->GetPosition1();
1030  FLTNB* event2Float = ap_ProjectionLine->GetPosition2();
1031  HPFLTNB event1[3] = { event1Float[0], event1Float[1], event1Float[2] };
1032  HPFLTNB event2[3] = { event2Float[0], event2Float[1], event2Float[2] };
1033 
1034  // **************************************
1035  // STEP 1: LOR length calculation
1036  // **************************************
1037  // Must recompute LOR length for each new line of response
1038  ap_ProjectionLine->ComputeLineLength();
1039  HPFLTNB length_LOR = ap_ProjectionLine->GetLength();
1040  HPFLTNB length_LOR_half = length_LOR * 0.5;
1041 
1042  // **************************************
1043  // STEP 2: Compute entrance voxel indexes
1044  // **************************************
1045  HPFLTNB alphaFirst[] = { 0.0, 0.0, 0.0 };
1046  HPFLTNB alphaLast[] = { 0.0, 0.0, 0.0 };
1047 
1048  HPFLTNB alphaMin = 0.0f, alphaMax = 1.0f;
1049  HPFLTNB delta_pos[] = {
1050  event2[ 0 ] - event1[ 0 ],
1051  event2[ 1 ] - event1[ 1 ],
1052  event2[ 2 ] - event1[ 2 ]
1053  };
1054 
1055  // Get TOF info
1056  INTNB tof_nb_bins = ap_ProjectionLine->GetNbTOFBins();
1057  INTNB tof_half_nb_bins = tof_nb_bins/2;
1058 
1059  // TOF Gaussian standard deviation and truncation
1061  HPFLTNB tof_sigma_sqrt2 = sqrt(2.)*tof_sigma;
1062  HPFLTNB tof_half_span = 0.;
1064  else tof_half_span = tof_sigma * m_TOFNbSigmas;
1065 
1066  // if integration of the Gaussian over the TOF bin, need for help variables to save calls to erf
1067  HPFLTNB prev_erf = 0., new_erf = 0.;
1068 
1069  // minimum and maximum TOF bins
1070  INTNB tof_bin_last = tof_half_nb_bins;
1071  INTNB tof_bin_first = -tof_half_nb_bins;
1072 
1073  // distance between the first event1 and the center of a TOF bin along the LOR
1074  HPFLTNB lor_tof_center = 0.;
1075  // the sum of all TOF bin weights for a voxel
1076  //HPFLTNB tof_norm_coef = 0.;
1077 
1078  // the first and the last relevant TOF bins for a voxel
1079  INTNB tof_bin_first_for_voxel = 0, tof_bin_last_for_voxel = 0;
1080 
1081  // Computation of alphaMin et alphaMax values (entrance and exit point of the ray)
1082  for( int i = 0; i < 3; ++i )
1083  {
1084  if( delta_pos[ i ] != 0.0 )
1085  {
1086  alphaFirst[i] = (-mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
1087  alphaLast[i] = ( mp_halfFOV[i] - event1[i]) / (delta_pos[ i ]);
1088  alphaMin = (std::max)(alphaMin,(std::min)(alphaFirst[i],alphaLast[i]));
1089  alphaMax = (std::min)(alphaMax,(std::max)(alphaFirst[i],alphaLast[i]));
1090  }
1091  }
1092 
1093  // if alphaMax is less than or equal to alphaMin no intersection
1094  // and return an empty buffer
1095  if( alphaMax <= alphaMin ) return 0;
1096 
1097  // temporary storage for TOF bin weights
1098  // allocation after potential returns
1099  HPFLTNB* tof_weights_temp = new HPFLTNB[tof_nb_bins];
1100  for (INTNB tof_bin=0; tof_bin<tof_nb_bins; tof_bin++) tof_weights_temp[tof_bin] = 0.;
1101 
1102  // Now we have to find the indices of the particular plane
1103  // (iMin,iMax), (jMin,jMax), (kMin,kMax)
1104  int iMin = 0, iMax = 0;
1105  int jMin = 0, jMax = 0;
1106  int kMin = 0, kMax = 0;
1107 
1108  // For the X-axis
1109  if( delta_pos[ 0 ] > 0.0f )
1110  {
1111  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMin * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
1112  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMax * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
1113  }
1114  else if( delta_pos[ 0 ] < 0.0f )
1115  {
1116  iMin = ::ceil( ( mp_nbVox[ 0 ] + 1 ) - ( mp_halfFOV[ 0 ] - alphaMax * delta_pos[ 0 ] - event1[ 0 ] ) / mp_sizeVox[0] );
1117  iMax = ::floor( 1 + ( event1[ 0 ] + alphaMin * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] );
1118  }
1119  if( delta_pos[ 0 ] == 0 )
1120  {
1121  iMin = 1, iMax = 0;
1122  }
1123 
1124  // For the Y-axis
1125  if( delta_pos[ 1 ] > 0 )
1126  {
1127  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMin * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
1128  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMax * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
1129  }
1130  else if( delta_pos[ 1 ] < 0 )
1131  {
1132  jMin = ::ceil( ( mp_nbVox[ 1 ] + 1 ) - ( mp_halfFOV[ 1 ] - alphaMax * delta_pos[ 1 ] - event1[ 1 ] ) / mp_sizeVox[1] );
1133  jMax = ::floor( 1 + ( event1[ 1 ] + alphaMin * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] );
1134  }
1135  else if( delta_pos[ 1 ] == 0 )
1136  {
1137  jMin = 1, jMax = 0;
1138  }
1139 
1140  // For the Z-axis
1141  if( delta_pos[ 2 ] > 0 )
1142  {
1143  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMin * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
1144  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMax * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
1145  }
1146  else if( delta_pos[ 2 ] < 0 )
1147  {
1148  kMin = ::ceil( ( mp_nbVox[ 2 ] + 1 ) - ( mp_halfFOV[ 2 ] - alphaMax * delta_pos[ 2 ] - event1[ 2 ] ) / mp_sizeVox[2] );
1149  kMax = ::floor( 1 + ( event1[ 2 ] + alphaMin * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] );
1150  }
1151  else if( delta_pos[ 2 ] == 0 )
1152  {
1153  kMin = 1, kMax = 0;
1154  }
1155 
1156  // Computing the last term n number of intersection
1157  INTNB n = ( iMax - iMin + 1 ) + ( jMax - jMin + 1 )
1158  + ( kMax - kMin + 1 );
1159 
1160  // Array storing the first alpha in X, Y and Z
1161  HPFLTNB alpha_XYZ[ 3 ] = { 1.0f, 1.0f, 1.0f };
1162 
1163  // Computing the first alpha in X
1164  if( delta_pos[ 0 ] > 0 )
1165  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMin - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
1166  else if( delta_pos[ 0 ] < 0 )
1167  alpha_XYZ[ 0 ] = ( ( (-mp_halfFOV[ 0 ]) + ( iMax - 1 ) * mp_sizeVox[0] ) - event1[ 0 ] ) / delta_pos[ 0 ];
1168 
1169  // Computing the first alpha in Y
1170  if( delta_pos[ 1 ] > 0 )
1171  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMin - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
1172  else if( delta_pos[ 1 ] < 0 )
1173  alpha_XYZ[ 1 ] = ( ( (-mp_halfFOV[ 1 ]) + ( jMax - 1 ) * mp_sizeVox[1] ) - event1[ 1 ] ) / delta_pos[ 1 ];
1174 
1175  // Computing the first alpha in Z
1176  if( delta_pos[ 2 ] > 0 )
1177  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMin - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
1178  else if( delta_pos[ 2 ] < 0 )
1179  alpha_XYZ[ 2 ] = ( ( (-mp_halfFOV[ 2 ]) + ( kMax - 1 ) * mp_sizeVox[2] ) - event1[ 2 ] ) / delta_pos[ 2 ];
1180 
1181  // Computing the alpha updating
1182  HPFLTNB alpha_u[ 3 ] = {
1183  mp_sizeVox[0] / std::fabs( delta_pos[ 0 ] ),
1184  mp_sizeVox[1] / std::fabs( delta_pos[ 1 ] ),
1185  mp_sizeVox[2] / std::fabs( delta_pos[ 2 ] )
1186  };
1187 
1188  // Computing the index updating
1189  INTNB index_u[ 3 ] = {
1190  (delta_pos[ 0 ] < 0) ? -1 : 1,
1191  (delta_pos[ 1 ] < 0) ? -1 : 1,
1192  (delta_pos[ 2 ] < 0) ? -1 : 1
1193  };
1194 
1195  // Check which alpha is the min/max and increment
1196  if( alpha_XYZ[ 0 ] == alphaMin ) alpha_XYZ[ 0 ] += alpha_u[ 0 ];
1197  if( alpha_XYZ[ 1 ] == alphaMin ) alpha_XYZ[ 1 ] += alpha_u[ 1 ];
1198  if( alpha_XYZ[ 2 ] == alphaMin ) alpha_XYZ[ 2 ] += alpha_u[ 2 ];
1199 
1200  // Computing the minimum value in the alpha_XYZ buffer
1201  HPFLTNB const min_alpha_XYZ = (std::min)( alpha_XYZ[ 0 ], (std::min)( alpha_XYZ[ 1 ], alpha_XYZ[ 2 ] ) );
1202 
1203  // Computing the first index of intersection
1204  HPFLTNB alphaMid = ( min_alpha_XYZ + alphaMin ) / 2.0f;
1205  INTNB index_ijk[ 3 ] = {
1206  1 + (INTNB)( ( event1[ 0 ] + alphaMid * delta_pos[ 0 ] - (-mp_halfFOV[ 0 ]) ) / mp_sizeVox[0] ),
1207  1 + (INTNB)( ( event1[ 1 ] + alphaMid * delta_pos[ 1 ] - (-mp_halfFOV[ 1 ]) ) / mp_sizeVox[1] ),
1208  1 + (INTNB)( ( event1[ 2 ] + alphaMid * delta_pos[ 2 ] - (-mp_halfFOV[ 2 ]) ) / mp_sizeVox[2] )
1209  };
1210 
1211  INTNB const w = mp_nbVox[ 0 ];
1212  INTNB const wh = w * mp_nbVox[ 1 ];
1213 
1214  // Loop over the number of plane to cross
1215  HPFLTNB alpha_c = alphaMin;
1216  FLTNB coeff = 0.0f;
1217  INTNB numVox = 0;
1218 
1219  for( int nP = 0; nP < n - 1; ++nP )
1220  {
1221  if( ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 1 ] )
1222  && ( alpha_XYZ[ 0 ] <= alpha_XYZ[ 2 ] ) )
1223  {
1224  // Storing values
1225  if( ( alpha_XYZ[ 0 ] >= alphaMin )
1226  && ( index_ijk[ 0 ] - 1 >= 0 )
1227  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
1228  && ( index_ijk[ 1 ] - 1 >= 0 )
1229  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
1230  && ( index_ijk[ 2 ] - 1 >= 0 )
1231  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
1232  {
1233  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
1234 
1235  // if this voxel is masked, do nothing
1236  if (!m_hasMask || mp_mask[numVox])
1237  {
1238  coeff = ( alpha_XYZ[ 0 ] - alpha_c ) * length_LOR;
1239 
1240  alphaMid = ( alpha_XYZ[ 0 ] + alpha_c ) * 0.5;
1241 
1242  // compute the first and the last relevant TOF bin for this voxel
1244  {
1245  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1246  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (alphaMid * length_LOR - tof_half_span - length_LOR_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1247  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (alphaMid * length_LOR + tof_half_span - length_LOR_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1248  }
1249  else
1250  {
1251  // taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
1252  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (alphaMid * length_LOR - tof_half_span - length_LOR_half ) / m_TOFBinSizeInMm )));
1253  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (alphaMid * length_LOR + tof_half_span - length_LOR_half) / m_TOFBinSizeInMm )));
1254  }
1255 
1256  lor_tof_center = length_LOR_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1257 
1258  // shift tof bin indices from -/+ to 0:nbBins range
1259  tof_bin_first_for_voxel += tof_half_nb_bins;
1260  tof_bin_last_for_voxel += tof_half_nb_bins;
1261 
1262  // initialization of help variables for reducing calls to erf
1264  {
1265  // bound the integration to the Gaussian truncation
1266  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1267  prev_erf = erf(temp/tof_sigma_sqrt2);
1268  }
1269 
1270  // compute TOF bin weights for the current voxel for all relevant TOF bins
1271  //tof_norm_coef = 0.;
1272  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1273  {
1275  {
1276  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1277  // nearest to the projection of the voxel center onto the LOR
1278  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor);
1279  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = mp_TOFWeightingFcn[temp];
1280  // add the weight to the sum for normalization
1281  //tof_norm_coef += tof_weights_temp[tof_bin];
1282  // update TOF bin center along the LOR for the next TOF bin
1283  lor_tof_center += m_TOFBinSizeInMm;
1284  }
1285  else
1286  {
1288  {
1289  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1290  // reuse of integration from the previous bin to save calls to erf
1291  // bound the integration to the Gaussian truncation
1292  HPFLTNB temp = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1293  new_erf = erf(temp/tof_sigma_sqrt2);
1294  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1295  // add the weight to the sum
1296  // tof_norm_coef += tof_weights_temp[tof_bin];
1297  prev_erf = new_erf;
1298  // update TOF bin center along the LOR for the next TOF bin
1299  lor_tof_center += m_TOFBinSizeInMm;
1300  }
1301  else
1302  {
1303  // TOF weight = TOF bin size * value of the normalized Gaussian (centered at the TOF bin center) at the projection of the voxel center onto the LOR
1304  HPFLTNB temp = ( alphaMid * length_LOR - lor_tof_center) / tof_sigma;
1305  // save the weight temporarily
1306  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef * m_TOFBinSizeInMm;
1307  // add the weight to the sum
1308  //tof_norm_coef += tof_weights_temp[tof_bin];
1309  // update TOF center along the LOR for the next TOF bin
1310  lor_tof_center += m_TOFBinSizeInMm;
1311  }
1312  }
1313  }
1314  /*
1315  if (tof_norm_coef>0.)
1316  {
1317  // first normalize TOF bin weights so that they sum to 1
1318  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++) tof_weights_temp[tof_bin] /= tof_norm_coef;
1319  }
1320  */
1321  // write the final TOF bin weighted projection coefficients for the current voxel
1322  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, numVox, coeff, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1323 
1324  }
1325  }
1326  // Increment values
1327  alpha_c = alpha_XYZ[ 0 ];
1328  alpha_XYZ[ 0 ] += alpha_u[ 0 ];
1329  index_ijk[ 0 ] += index_u[ 0 ];
1330  }
1331  else if( ( alpha_XYZ[ 1 ] < alpha_XYZ[ 0 ] )
1332  && ( alpha_XYZ[ 1 ] <= alpha_XYZ[ 2 ] ) )
1333  {
1334  // Storing values
1335  if( ( alpha_XYZ[ 1 ] >= alphaMin )
1336  && ( index_ijk[ 0 ] - 1 >= 0 )
1337  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
1338  && ( index_ijk[ 1 ] - 1 >= 0 )
1339  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
1340  && ( index_ijk[ 2 ] - 1 >= 0 )
1341  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
1342  {
1343  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
1344 
1345  // if this voxel is masked, do nothing
1346  if (!m_hasMask || mp_mask[numVox])
1347  {
1348  coeff = ( alpha_XYZ[ 1 ] - alpha_c ) * length_LOR;
1349 
1350  alphaMid = ( alpha_XYZ[ 1 ] + alpha_c ) * 0.5;
1351 
1352  // compute the first and the last relevant TOF bin for this voxel
1354  {
1355  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1356  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (alphaMid * length_LOR - tof_half_span - length_LOR_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1357  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (alphaMid * length_LOR + tof_half_span - length_LOR_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1358  }
1359  else
1360  {
1361  // taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
1362  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (alphaMid * length_LOR - tof_half_span - length_LOR_half ) / m_TOFBinSizeInMm )));
1363  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (alphaMid * length_LOR + tof_half_span - length_LOR_half) / m_TOFBinSizeInMm )));
1364  }
1365 
1366  lor_tof_center = length_LOR_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1367 
1368  // shift tof bin indices from -/+ to 0:nbBins range
1369  tof_bin_first_for_voxel += tof_half_nb_bins;
1370  tof_bin_last_for_voxel += tof_half_nb_bins;
1371 
1372  // initialization of help variables for reducing calls to erf
1374  {
1375  // bound the integration to the Gaussian truncation
1376  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1377  prev_erf = erf(temp/tof_sigma_sqrt2);
1378  }
1379 
1380  // compute TOF bin weights for the current voxel for all relevant TOF bins
1381  //tof_norm_coef = 0.;
1382  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1383  {
1385  {
1386  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1387  // nearest to the projection of the voxel center onto the LOR
1388  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
1389  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = mp_TOFWeightingFcn[temp];
1390  // add the weight to the sum
1391  //tof_norm_coef += tof_weights_temp[tof_bin];
1392  // update TOF center along the LOR for the next TOF bin
1393  lor_tof_center += m_TOFBinSizeInMm;
1394  }
1395  else
1396  {
1398  {
1399  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1400  // reuse of integration from the previous bin to save calls to erf
1401  // bound the integration to the Gaussian truncation
1402  HPFLTNB temp = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1403  new_erf = erf(temp/tof_sigma_sqrt2);
1404  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1405  // add the weight to the sum for normalization
1406  //tof_norm_coef += tof_weights_temp[tof_bin];
1407  prev_erf = new_erf;
1408  // update TOF center along the LOR for the next TOF bin
1409  lor_tof_center += m_TOFBinSizeInMm;
1410  }
1411  else
1412  {
1413  // TOF weight = TOF bin size * value of the normalized Gaussian (centered at the TOF bin center) at the projection of the voxel center onto the LOR
1414  HPFLTNB temp = ( alphaMid * length_LOR - lor_tof_center) / tof_sigma;
1415  // save the weight temporarily
1416  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef * m_TOFBinSizeInMm;
1417  // add the weight to the sum for normalization
1418  //tof_norm_coef += tof_weights_temp[tof_bin];
1419  // update TOF center along the LOR for the next TOF bin
1420  lor_tof_center += m_TOFBinSizeInMm;
1421  }
1422  }
1423  }
1424  /*
1425  if (tof_norm_coef>0.)
1426  {
1427  // first normalize TOF bin weights so that they sum to 1
1428  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++) tof_weights_temp[tof_bin] /= tof_norm_coef;
1429  }
1430  */
1431  // write the final TOF bin weighted projection coefficients for the current voxel
1432  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, numVox, coeff, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1433  }
1434  }
1435 
1436  // Increment values
1437  alpha_c = alpha_XYZ[ 1 ];
1438  alpha_XYZ[ 1 ] += alpha_u[ 1 ];
1439  index_ijk[ 1 ] += index_u[ 1 ];
1440  }
1441  else if( ( alpha_XYZ[ 2 ] < alpha_XYZ[ 0 ] )
1442  && ( alpha_XYZ[ 2 ] < alpha_XYZ[ 1 ] ) )
1443  {
1444  // Storing values
1445  if( ( alpha_XYZ[ 2 ] >= alphaMin )
1446  && ( index_ijk[ 0 ] - 1 >= 0 )
1447  && ( index_ijk[ 0 ] - 1 <= mp_nbVox[ 0 ] - 1 )
1448  && ( index_ijk[ 1 ] - 1 >= 0 )
1449  && ( index_ijk[ 1 ] - 1 <= mp_nbVox[ 1 ] - 1 )
1450  && ( index_ijk[ 2 ] - 1 >= 0 )
1451  && ( index_ijk[ 2 ] - 1 <= mp_nbVox[ 2 ] - 1 ) )
1452  {
1453  numVox = ( index_ijk[ 0 ] - 1 ) + ( ( index_ijk[ 1 ] - 1 ) ) * w + ( ( index_ijk[ 2 ] - 1 ) ) * wh;
1454 
1455  // if this voxel is masked, do nothing
1456  if (!m_hasMask || mp_mask[numVox])
1457  {
1458  coeff = ( alpha_XYZ[ 2 ] - alpha_c ) * length_LOR;
1459 
1460  alphaMid = ( alpha_XYZ[ 2 ] + alpha_c ) * 0.5;
1461 
1462  // compute the first and the last relevant TOF bin for this voxel
1464  {
1465  // taking the TOF bins reached by the truncated Gaussian centered at the voxel center projection
1466  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(floor( (alphaMid * length_LOR - tof_half_span - length_LOR_half - m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1467  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(ceil( (alphaMid * length_LOR + tof_half_span - length_LOR_half + m_TOFBinSizeInMm/2.) / m_TOFBinSizeInMm )));
1468  }
1469  else
1470  {
1471  // taking the TOF bins whose TOF weighting function, centered at the bin center, reaches the voxel center projection
1472  tof_bin_first_for_voxel = max(tof_bin_first , (INTNB)(ceil( (alphaMid * length_LOR - tof_half_span - length_LOR_half ) / m_TOFBinSizeInMm )));
1473  tof_bin_last_for_voxel = min(tof_bin_last , (INTNB)(floor( (alphaMid * length_LOR + tof_half_span - length_LOR_half) / m_TOFBinSizeInMm )));
1474  }
1475 
1476  lor_tof_center = length_LOR_half + tof_bin_first_for_voxel * m_TOFBinSizeInMm;
1477 
1478  // shift tof bin indices from -/+ to 0:nbBins range
1479  tof_bin_first_for_voxel += tof_half_nb_bins;
1480  tof_bin_last_for_voxel += tof_half_nb_bins;
1481 
1482 
1483  // initialization of help variables for reducing calls to erf
1485  {
1486  // bound the integration to the Gaussian truncation
1487  HPFLTNB temp = std::min(tof_half_span,std::max(-tof_half_span, lor_tof_center - m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1488  prev_erf = erf(temp/tof_sigma_sqrt2);
1489  }
1490 
1491  // compute TOF bin weights for the current voxel for all relevant TOF bins
1492  //tof_norm_coef = 0.;
1493  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++)
1494  {
1496  {
1497  // fetch the value of the precomputed TOF weighting function (centered at the TOF bin center)
1498  // nearest to the projection of the voxel center onto the LOR
1499  INTNB temp = m_TOFWeightingFcnNbSamples/2 + (INTNB)round( ( alphaMid * length_LOR - lor_tof_center) * m_TOFPrecomputedSamplingFactor );
1500  if (temp>=0 && temp<m_TOFWeightingFcnNbSamples) tof_weights_temp[tof_bin] = mp_TOFWeightingFcn[temp];
1501  // add the weight to the sum
1502  //tof_norm_coef += tof_weights_temp[tof_bin];
1503  // update TOF bin center along the LOR for the next TOF bin
1504  lor_tof_center += m_TOFBinSizeInMm;
1505  }
1506  else
1507  {
1509  {
1510  // integration between the edges of the current TOF bin of the Gaussian centered at the projection of the voxel center onto the LOR
1511  // reuse of integration from the previous bin to save calls to erf
1512  // bound the integration to the Gaussian truncation
1513  HPFLTNB temp = std::min(tof_half_span, std::max(-tof_half_span, lor_tof_center + m_TOFBinSizeInMm/2. - alphaMid * length_LOR));
1514  new_erf = erf(temp/tof_sigma_sqrt2);
1515  tof_weights_temp[tof_bin] = 0.5 * fabs(new_erf - prev_erf);
1516  // add the weight to the sum
1517  //tof_norm_coef += tof_weights_temp[tof_bin];
1518  prev_erf = new_erf;
1519  // update TOF center along the LOR for the next TOF bin
1520  lor_tof_center += m_TOFBinSizeInMm;
1521  }
1522  else
1523  {
1524  // TOF weight = TOF bin size * value of the normalized Gaussian (centered at the TOF bin center) at the projection of the voxel center onto the LOR
1525  HPFLTNB temp = ( alphaMid * length_LOR - lor_tof_center) / tof_sigma;
1526  tof_weights_temp[tof_bin] = exp(- 0.5 * temp * temp ) * m_TOFGaussianNormCoef * m_TOFBinSizeInMm;
1527  // add the weight to the sum
1528  //tof_norm_coef += tof_weights_temp[tof_bin];
1529  // update TOF center along the LOR for the next TOF bin
1530  lor_tof_center += m_TOFBinSizeInMm;
1531  }
1532  }
1533  }
1534  /*
1535  if (tof_norm_coef>0.)
1536  {
1537  // first normalize TOF bin weights so that they sum to 1
1538  for (INTNB tof_bin=tof_bin_first_for_voxel; tof_bin<=tof_bin_last_for_voxel; tof_bin++) tof_weights_temp[tof_bin] /= tof_norm_coef;
1539  }
1540  */
1541  // write the final TOF bin weighted projection coefficients for the current voxel
1542  ap_ProjectionLine->AddVoxelAllTOFBins(a_direction, numVox, coeff, tof_weights_temp, tof_bin_first_for_voxel, tof_bin_last_for_voxel);
1543  }
1544  }
1545 
1546  // Increment values
1547  alpha_c = alpha_XYZ[ 2 ];
1548  alpha_XYZ[ 2 ] += alpha_u[ 2 ];
1549  index_ijk[ 2 ] += index_u[ 2 ];
1550  }
1551  }
1552  delete[] tof_weights_temp;
1553  }
1554 
1555  return 0;
1556 }
1557 
1558 // =====================================================================
1559 // ---------------------------------------------------------------------
1560 // ---------------------------------------------------------------------
1561 // =====================================================================
void ShowHelpSpecific()
A function used to show help about the child module.
bool m_compatibleWithSPECTAttenuationCorrection
Definition: vProjector.hh:407
FLTNB * GetOrientation1()
This function is used to get the pointer to the mp_orientation1 (3-values tab).
FLTNB mp_sizeVox[3]
Definition: vProjector.hh:378
FLTNB m_TOFNbSigmas
Definition: vProjector.hh:389
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.
INTNB mp_nbVox[3]
Definition: vProjector.hh:379
#define FLTNB
Definition: gVariables.hh:81
#define SPEED_OF_LIGHT_IN_MM_PER_PS
Definition: gVariables.hh:106
FLTNB m_TOFGaussianNormCoef
Definition: vProjector.hh:397
void AddVoxelAllTOFBins(int a_direction, INTNB a_voxelIndex, FLTNB a_voxelWeight, HPFLTNB *a_tofWeights, INTNB a_tofBinFirst, INTNB a_tofBinLast)
Add a voxel contribution to the line for all relevant TOF bins.
#define TWO_SQRT_TWO_LN_2
Definition: gVariables.hh:100
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
Definition: vProjector.hh:384
#define HPFLTNB
Definition: gVariables.hh:83
FLTNB GetTOFMeasurementInPs()
This function is used to get the TOF measurement in ps.
This class is designed to generically described any on-the-fly projector.
Definition: vProjector.hh:75
bool m_TOFWeightingFcnPrecomputedFlag
Definition: vProjector.hh:391
int InitializeSpecific()
This function is used to initialize specific stuff to the child projector.
bool m_TOFBinProperProcessingFlag
Definition: vProjector.hh:392
HPFLTNB * mp_TOFWeightingFcn
Definition: vProjector.hh:393
FLTNB GetLength()
This function is used to get the length of the line.
FLTNB * GetOrientation2()
This function is used to get the pointer to the mp_orientation2 (3-values tab).
void Exit(int code)
void ComputeLineLength()
Simply compute and update the m_length using the associated mp_position1 and mp_position2.
#define Cerr(MESSAGE)
int GetIndex2()
This function is used to get the index associated to point 2.
void ApplyOffset()
Apply the offset of oImageDimensionsAndQuantification to the mp_position1 and mp_position2.
FLTNB * GetPosition1()
This function is used to get the pointer to the mp_position1 (3-values tab).
bool m_initialized
Definition: vProjector.hh:416
bool m_sensitivityMode
Definition: vProjector.hh:405
bool * mp_mask
Definition: vProjector.hh:400
INTNB EstimateMaxNumberOfVoxelsPerLine()
This function is used to compute and provide an estimate of the maximum number of voxels that could c...
void AddVoxel(int a_direction, INTNB a_voxelIndice, FLTNB a_voxelWeight)
This function is used to add a voxel contribution to the line, assuming TOF bin 0 (i...
int ReadDataASCIIFile(const string &a_file, const string &a_keyword, T *ap_return, int a_nbElts, bool a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_file" file matching the "a_keyword" string passed as parameter a...
Definition: gOptions.cc:129
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child projector.
FLTNB m_TOFResolutionInMm
Definition: vProjector.hh:396
Declaration of class iProjectorIncrementalSiddonMulti.
#define KEYWORD_MANDATORY
Definition: gOptions.hh:47
#define KEYWORD_OPTIONAL
Definition: gOptions.hh:49
bool m_hasMask
Definition: vProjector.hh:401
virtual int GetRdmPositionsAndOrientations(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3], FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3])=0
This is a pure virtual method that must be implemented by children. Get random positions of the sca...
~iProjectorIncrementalSiddonMulti()
The destructor of iProjectorIncrementalSiddonMulti.
#define INTNB
Definition: gVariables.hh:92
This class is designed to manage and store system matrix elements associated to a vEvent...
FLTNB m_TOFPrecomputedSamplingFactor
Definition: vProjector.hh:398
int ProjectTOFListmode(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project with TOF continuous information.
FLTNB m_TOFBinSizeInMm
Definition: vProjector.hh:394
Declaration of class sOutputManager.
int GetNbTOFBins()
This function is used to get the number of TOF bins in use.
iProjectorIncrementalSiddonMulti()
The constructor of iProjectorIncrementalSiddonMulti.
FLTNB mp_halfFOV[3]
Definition: vProjector.hh:381
#define EXIT_DEBUG
Definition: gVariables.hh:97
FLTNB * GetPosition2()
This function is used to get the pointer to the mp_position2 (3-values tab).
INTNB GetNbVoxX()
Get the number of voxels along the X axis.
INTNB GetNbVoxZ()
Get the number of voxels along the Z axis.
#define FORWARD
#define Cout(MESSAGE)
int ReadOptionsList(const string &a_optionsList)
A function used to read options from a list of options.
bool m_compatibleWithCompression
Definition: vProjector.hh:410
int ProjectTOFHistogram(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project with TOF binned information.
int GetIndex1()
This function is used to get the index associated to point 1.
int ReadStringOption(const string &a_input, T *ap_return, int a_nbElts, const string &sep, const string &a_option)
Parse the &#39;a_input&#39; string corresponding to the &#39;a_option&#39; into &#39;a_nbElts&#39; elements, using the &#39;sep&#39; separator. The results are returned in the templated &#39;ap_return&#39; dynamic templated array. Call "ConvertFromString()" to perform the correct conversion depending on the type of the data to convert.
Definition: gOptions.cc:50
void ApplyBedOffset()
Apply the bed offset of m_bedOffset to the mp_position1 and mp_position2.
vScanner * mp_Scanner
Definition: vProjector.hh:386
int ProjectWithoutTOF(int a_direction, oProjectionLine *ap_ProjectionLine)
A function to project without TOF.
INTNB GetNbVoxY()
Get the number of voxels along the Y axis.
INTNB m_TOFWeightingFcnNbSamples
Definition: vProjector.hh:390