CASToR  1.1
Tomographic Reconstruction (PET/SPECT)
 All Classes Files Functions Variables Typedefs Macros Groups Pages
iScannerPET.cc
Go to the documentation of this file.
1 
2 /*
3  Implementation of class iScannerPET
4 
5  - separators: X
6  - doxygen: X
7  - default initialization: X
8  - CASTOR_DEBUG: none
9  - CASTOR_VERBOSE: X
10 */
11 
18 #include "iScannerPET.hh"
19 #include "sOutputManager.hh"
20 #include "sScannerManager.hh"
21 
22 
23 // =====================================================================
24 // ---------------------------------------------------------------------
25 // ---------------------------------------------------------------------
26 // =====================================================================
27 
29 {
30  // Set member variables to default values
32  m_nbLayers = -1;
33  m_nbCrystals = -1;
35  mp_nbCrystalsInLayer = NULL;
36  mp_nbRings = NULL;
44  mp_sizeCrystalTrans = NULL;
45  mp_sizeCrystalAxial = NULL;
46  mp_sizeCrystalDepth = NULL;
48  mp_positionMatrix_ref = NULL;
49  mp_positionMatrix_out = NULL;
50  mp_rotationMatrix = NULL;
51  // Variables depending on the acquisition
52  m_maxRingDiff = -1;
53 }
54 
55 // =====================================================================
56 // ---------------------------------------------------------------------
57 // ---------------------------------------------------------------------
58 // =====================================================================
59 
61 {
62  if (mp_sizeCrystalTrans != NULL) delete mp_sizeCrystalTrans;
63  if (mp_sizeCrystalAxial != NULL) delete mp_sizeCrystalAxial;
64  if (mp_sizeCrystalDepth != NULL) delete mp_sizeCrystalDepth;
72  if (mp_nbCrystalsInLayer != NULL) delete mp_nbCrystalsInLayer;
73  if (mp_positionMatrix_ref != NULL) delete mp_positionMatrix_ref;
74  if (mp_positionMatrix_out != NULL) delete mp_positionMatrix_out;
75  if (mp_rotationMatrix != NULL) delete mp_rotationMatrix;
76 }
77 
78 // =====================================================================
79 // ---------------------------------------------------------------------
80 // ---------------------------------------------------------------------
81 // =====================================================================
82 
83 int iScannerPET::Instantiate(bool a_scannerFileIsLUT)
84 {
86 
87  // Verbose
88  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerPET::Instantiate() -> Create scanner structure and read parameters from configuration file"<< endl);
89 
90  // Get scanner manager
91  sScannerManager* p_scannerManager;
92  p_scannerManager = sScannerManager::GetInstance();
93 
94  // Get the number of layers in the scanner
95  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of layers", &m_nbLayers, 1, KEYWORD_MANDATORY))
96  {
97  Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read the number of layers in the scanner header file !" << endl);
98  return 1;
99  }
100  // Check the correct initialization of the number of layers
101  if (m_nbLayers<=0)
102  {
103  Cerr("***** iScannerPET::Instantiate() -> Incorrect value for the number of layer (should be >0) !" << endl);
104  return 1;
105  }
106  // Get the number of elements in the system
107  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of elements", &m_nbCrystals, 1, KEYWORD_MANDATORY))
108  {
109  Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read the number of elements in the scanner header file !" << endl);
110  return 1;
111  }
112 
113  // Instanciation of layer dependent member variables
114  mp_nbCrystalsInLayer = new int[m_nbLayers];
115  mp_nbRings = new int[m_nbLayers];
120 
121  // Instanciation of number of crystals dependent member variables
128 
129  // Initialize layer size with default value (=0);
130  for (int l=0 ; l<m_nbLayers ; l++)
131  {
132  mp_sizeCrystalTrans[l] = 0.;
133  mp_sizeCrystalAxial[l] = 0.;
134  mp_sizeCrystalDepth[l] = 0.;
135  }
136 
137  // Size of crystals
138  if (a_scannerFileIsLUT)
139  {
140  // For the moment, only the depth is mandatory as the others are not yet implemented
141  if ( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size trans", mp_sizeCrystalTrans, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
142  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size axial", mp_sizeCrystalAxial, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
143  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size depth", mp_sizeCrystalDepth, m_nbLayers, KEYWORD_MANDATORY) )
144  {
145  Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read the crystals size in the scanner header file !" << endl);
146  return 1;
147  }
148  }
149  else
150  {
151  // Mandatory parameter
152  if ( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size trans", mp_sizeCrystalTrans, m_nbLayers, KEYWORD_MANDATORY) ||
153  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size axial", mp_sizeCrystalAxial, m_nbLayers, KEYWORD_MANDATORY) ||
154  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystals size depth", mp_sizeCrystalDepth, m_nbLayers, KEYWORD_MANDATORY) )
155  {
156  Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read the crystals size in the scanner header file !" << endl);
157  return 1;
158  }
159  }
160 
161  // ----------------------------------------------
162  // Optional parameters
163  // ----------------------------------------------
164 
165  // Initialize with default values
166  for (int l=0 ; l<m_nbLayers ; l++) mp_meanDepthOfInteraction[l] = -1;
169 
170  // Check if value is provided in the scanner conf file
171  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "mean depth of interaction", mp_meanDepthOfInteraction, m_nbLayers, KEYWORD_OPTIONAL) == 1)
172  {
173  Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read the mean depth of interaction in the scanner header file !" << endl);
174  return 1;
175  }
176  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "min angle difference", &m_minAngleDifference, 1, KEYWORD_OPTIONAL) == 1)
177  {
178  Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read the min angle difference in the scanner header file !" << endl);
179  return 1;
180  }
181  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "multiple bed displacement", &m_multiBedDisplacementInMm, 1, KEYWORD_OPTIONAL) == 1)
182  {
183  Cerr("***** iScannerPET::Instantiate() -> An error occurred while trying to read the multiple bed displacement in the scanner header file !" << endl);
184  return 1;
185  }
186 
187  // Instanciation of objects for matrix calculation during reconstruction
188  mp_positionMatrix_ref = new oMatrix(3,1);
189  mp_positionMatrix_out = new oMatrix(3,1);
190  mp_rotationMatrix = new oMatrix(3,3);
191 
192  // End
193  return 0;
194 }
195 
196 // =====================================================================
197 // ---------------------------------------------------------------------
198 // ---------------------------------------------------------------------
199 // =====================================================================
200 /*
201  \fn BuildLUT
202  \param a_scannerFileIsLUT : boolean indicating if the file describing
203  the SPECT camera is a generic file (0) or custom Look-up-table (1)
204  \brief Call the functions to generate the LUT or read the user-made LUT depending on the user choice
205  \return 0 if success, positive value otherwise
206  \todo default values to max ring diff
207 */
208 int iScannerPET::BuildLUT(bool a_scannerFileIsLUT)
209 {
211 
212  // Verbose
213  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerPET::BuildLUT() -> Build LUT for scanner elements coordinates and orientations"<< endl);
214 
215  // Either generate the LUT from a generic file, or load the user precomputed LUT
216  if (!a_scannerFileIsLUT)
217  {
218  if (ComputeLUT())
219  {
220  Cerr("***** iScannerPET::BuildLUT() -> A problem occured while generating scanner LUT !" << endl);
221  return 1;
222  }
223  }
224  else
225  {
226  if (LoadLUT())
227  {
228  Cerr("***** iScannerPET::BuildLUT() -> A problem occured while loading scanner LUT !" << endl);
229  return 1;
230  }
231  }
232 
233  // Set default values to parameters
234  if(m_maxRingDiff < 0) // Not initialized
235  m_maxRingDiff = mp_nbRings[0]; //TODO // How to do with scanner with several layers ? For now setmaximal ring difference number to the number of rings in the first layer by default
236 
237  // End
238  return 0;
239 }
240 
241 // =====================================================================
242 // ---------------------------------------------------------------------
243 // ---------------------------------------------------------------------
244 // =====================================================================
245 /*
246  \fn CheckParameters
247  \brief Check if all parameters have been correctly initialized
248  \return 0 if success, positive value otherwise
249 */
251 {
253 
254  // Check if all parameters have been correctly initialized. Return error otherwise
255  if (m_scannerType == -1)
256  {
257  Cerr("***** iScannerPET::CheckParameters() -> Scanner type not initialized !" << endl);
258  return 1;
259  }
260  if (m_verbose == -1)
261  {
262  Cerr("***** iScannerPET::CheckParameters() -> Verbosity not initialized !" << endl);
263  return 1;
264  }
265  if (m_nbLayers <= 0)
266  {
267  Cerr("***** iScannerPET::CheckParameters() -> Incorrect value for the number of layer (should be >0) !" << endl);
268  return 1;
269  }
270  if (m_nbCrystals <0)
271  {
272  Cerr("***** iScannerPET::CheckParameters() -> Number of crystals not initialized !" << endl);
273  return 1;
274  }
275  if (mp_nbRings == NULL)
276  {
277  Cerr("***** iScannerPET::CheckParameters() -> Number of rings not initialized !" << endl);
278  return 1;
279  }
280  if (mp_nbCrystalsInLayer == NULL)
281  {
282  Cerr("***** iScannerPET::CheckParameters() -> Number of crystals in layer(s) not initialized !" << endl);
283  return 1;
284  }
286  {
287  Cerr("***** iScannerPET::CheckParameters() -> Crystals central positions not initialized !" << endl);
288  return 1;
289  }
291  {
292  Cerr("***** iScannerPET::CheckParameters() -> Crystals orientations not initialized !" << endl);
293  return 1;
294  }
295  if (mp_sizeCrystalTrans==NULL || mp_sizeCrystalAxial==NULL || mp_sizeCrystalDepth==NULL)
296  {
297  Cerr("***** iScannerPET::CheckParameters() -> Crystals dimensions not initialized !" << endl);
298  return 1;
299  }
300  if (mp_meanDepthOfInteraction == NULL)
301  {
302  Cout("***** iScannerPET::CheckParameters() -> Mean depth of interaction not initialized !" << endl);
303  return 1;
304  }
305  if (m_minAngleDifference < 0)
306  {
307  Cerr("***** iScannerPET::CheckParameters() -> Minimum angle difference not initialized !" << endl);
308  return 1;
309  }
310  if (m_maxRingDiff < 0)
311  {
312  Cerr("***** iScannerPET::CheckParameters() -> Maximal ring difference not initialized !" << endl);
313  return 1;
314  }
315  if (m_scannerType == -1)
316  {
317  Cerr("***** iScannerPET::CheckParameters() -> Scanner type not initialized !" << endl);
318  return 1;
319  }
320  if (m_scannerType == -1)
321  {
322  Cerr("***** iScannerPET::CheckParameters() -> Scanner type not initialized !" << endl);
323  return 1;
324  }
325  // End
326  m_allParametersChecked = true;
327  return 0;
328 }
329 
330 // =====================================================================
331 // ---------------------------------------------------------------------
332 // ---------------------------------------------------------------------
333 // =====================================================================
334 /*
335  \fn Initialize
336  \brief Check general initialization and set several parameters to their default value
337  \return 0 if success, positive value otherwise
338 */
340 {
342 
343  // Verbose
344  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerPET::Initialize() -> Initialize remaining stuff for scanner to be ready"<< endl);
345 
346  // Parameters checked ?
348  {
349  Cerr("***** iScannerPET::Initialize() -> Parameters have not been checked !" << endl);
350  return 1;
351  }
352 
353  // Set mean depth of interaction to the center of crystal if not initialized by default.
354  for(int l=0 ; l<m_nbLayers ; l++)
356 
357  // Conversion of the minimal angle difference into radians
358  m_minAngleDifference *= M_PI/180;
359 
360  return 0;
361 }
362 
363 // =====================================================================
364 // ---------------------------------------------------------------------
365 // ---------------------------------------------------------------------
366 // =====================================================================
367 /*
368  \fn LoadLUT
369  \brief Load a precomputed scanner LUT
370  \details Read mandatory data from the header of the LUT.
371  Then load the LUT elements for each crystal
372  \return 0 if success, positive value otherwise
373 */
375 {
377 
378  // Verbose
379  if (m_verbose>=VERBOSE_DETAIL) Cout("iScannerPET::LoadLUT() -> Start loading LUT from user-provided file" << endl);
380 
381  // Get scanner manager
382  sScannerManager* p_scannerManager;
383  p_scannerManager = sScannerManager::GetInstance();
384 
385  if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of crystals in layer", mp_nbCrystalsInLayer, m_nbLayers, 1) ||
386  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of rings in scanner", mp_nbRings, m_nbLayers, 1))
387  {
388  Cerr("***** iScannerPET::LoadLUT() -> An error occurred while trying to read a mandatory parameter in the scanner header file !" << endl);
389  return 1;
390  }
391 
392  // Open the scanner LUT file
393  string scanner_lut_file = p_scannerManager->GetPathToScannerFile();
394  //todo : maybe more practical to directly read the path from the header.
395  // Do not put restrictions on the file extension (.lut)
396  scanner_lut_file = scanner_lut_file.substr(0, scanner_lut_file.find_last_of(".")).append(".lut");
397 
398  // Open file
399  FILE* LUT_file = fopen(scanner_lut_file.c_str(), "rb");
400  if (LUT_file==NULL)
401  {
402  Cerr("***** iScannerPET::LoadLUT() -> Input LUT file '" << scanner_lut_file << "' is missing or corrupted !" << endl);
403  return 1;
404  }
405 
406  // Read data for each index
407  int nb_data_read = 0;
408  for (int i=0; i<m_nbCrystals; i++)
409  {
410  FLTNBLUT buffer;
411  // Read central crystal position X, then Y and Z
412  nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
413  mp_crystalCentralPositionX[i] = ((FLTNB)buffer);
414  nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
415  mp_crystalCentralPositionY[i] = ((FLTNB)buffer);
416  nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
417  mp_crystalCentralPositionZ[i] = ((FLTNB)buffer);
418  // Read crystal orientation X, then Y and Z
419  nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
420  mp_crystalOrientationX[i] = ((FLTNB)buffer);
421  nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
422  mp_crystalOrientationY[i] = ((FLTNB)buffer);
423  nb_data_read += fread(&buffer,sizeof(FLTNBLUT),1,LUT_file);
424  mp_crystalOrientationZ[i] = ((FLTNB)buffer);
425  }
426 
427  // Close file
428  fclose(LUT_file);
429 
430  // Check reading
431  if (nb_data_read!=m_nbCrystals*6)
432  {
433  Cerr("***** iScannerPET::LoadLUT() -> Failed to read all data in input LUT file '" << scanner_lut_file << "' !" << endl);
434  return 1;
435  }
436  // Verbose
437  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> LUT successfully loaded" << endl);
438  // End
439  return 0;
440 }
441 
442 // =====================================================================
443 // ---------------------------------------------------------------------
444 // ---------------------------------------------------------------------
445 // =====================================================================
446 /*
447  \fn ComputeLUT
448  \brief Compute the LUT of the scanner from a generic (.geom) file
449  \details Read mandatory data from the geom file. Then compute the LUT elements for each crystal from the geometry described in the file
450  \details Compute the look-up-tables of the system containing the locations of the scanner elements center in space and their orientations
451  \todo Add option to inverse rsector if transaxial orientation is counter-clockwise.?
452  \todo rotation for non-perfectly cylindric scanner (angles along the Z axis)
453  \return 0 if success, positive value otherwise
454 */
456 {
458 
459  if (m_verbose>=VERBOSE_DETAIL) Cout("iScannerPET::ComputeLUT() -> Start LUT generation" << endl);
460 
461  // ============================================================================================================
462  // Init local variables to recover data from scanner file
463  // ============================================================================================================
464 
465  // Layer dependent variables
466  int *nb_rsectors_lyr,
467  *nb_trans_mod_lyr,
468  *nb_axial_mod_lyr,
469  *nb_trans_submod_lyr,
470  *nb_axial_submod_lyr,
471  *nb_trans_crystal_lyr,
472  *nb_axial_crystal_lyr;
473 
474  FLTNBLUT *radius_lyr,
475  *rsector_first_angle_lyr,
476  *rsector_angular_span_lyr,
477  *gap_trans_mod_lyr,
478  *gap_axial_mod_lyr,
479  *gap_trans_submod_lyr,
480  *gap_axial_submod_lyr,
481  *gap_trans_crystal_lyr,
482  *gap_axial_crystal_lyr;
483 
484  nb_rsectors_lyr = new int[m_nbLayers];
485  nb_trans_mod_lyr = new int[m_nbLayers];
486  nb_axial_mod_lyr = new int[m_nbLayers];
487  nb_trans_submod_lyr = new int[m_nbLayers];
488  nb_axial_submod_lyr = new int[m_nbLayers];
489  nb_trans_crystal_lyr = new int[m_nbLayers];
490  nb_axial_crystal_lyr = new int[m_nbLayers];
491 
492  radius_lyr = new FLTNBLUT[m_nbLayers];
493  gap_trans_mod_lyr = new FLTNBLUT[m_nbLayers];
494  gap_axial_mod_lyr = new FLTNBLUT[m_nbLayers];
495  gap_trans_submod_lyr = new FLTNBLUT[m_nbLayers];
496  gap_axial_submod_lyr = new FLTNBLUT[m_nbLayers];
497  gap_trans_crystal_lyr = new FLTNBLUT[m_nbLayers];
498  gap_axial_crystal_lyr = new FLTNBLUT[m_nbLayers];
499  rsector_first_angle_lyr = new FLTNBLUT[m_nbLayers];
500  rsector_angular_span_lyr = new FLTNBLUT[m_nbLayers];
501 
502  string rotation_direction = "";
503 
504  // -------------------------------------------------------------------
505  // Recover mandatory data from scanner file
506  sScannerManager* p_scannerManager;
507  p_scannerManager = sScannerManager::GetInstance();
508 
509  if( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of rsectors", nb_rsectors_lyr, m_nbLayers, KEYWORD_MANDATORY) ||
510  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of crystals transaxial", nb_trans_crystal_lyr, m_nbLayers, KEYWORD_MANDATORY) ||
511  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of crystals axial", nb_axial_crystal_lyr, m_nbLayers, KEYWORD_MANDATORY) ||
512  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "scanner radius", radius_lyr, m_nbLayers, KEYWORD_MANDATORY) )
513  {
514  Cerr("***** iScannerPET::ComputeLUT() -> An error occurred while trying to read a mandatory parameter for scanner LUT generation in the scanner header file !" << endl);
515  return 1;
516  }
517 
518  // -------------------------------------------------------------------
519  // Recover optionnal data from scanner file
520 
521  // Initialize defaulf values
522  for(int l=0 ; l<m_nbLayers ; l++)
523  {
524  nb_trans_mod_lyr[ l ] =1;
525  nb_axial_mod_lyr[ l ] =1;
526  nb_trans_submod_lyr[ l ] = 1;
527  nb_axial_submod_lyr[ l ] = 1;
528  gap_trans_mod_lyr[ l ] = 0.;
529  gap_axial_mod_lyr[ l ] = 0.;
530  gap_trans_submod_lyr[ l ] = 0.;
531  gap_axial_submod_lyr[ l ] = 0.;
532  gap_trans_crystal_lyr[ l ] = 0.;
533  gap_axial_crystal_lyr[ l ] = 0.;
534  rsector_first_angle_lyr[ l ] = 0;
535  rsector_angular_span_lyr[ l ] = 360.*(FLTNBLUT)(nb_rsectors_lyr[l]-1)/(nb_rsectors_lyr[l]);
536  }
537 
538  if( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of modules transaxial", nb_trans_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
539  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of modules axial", nb_axial_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
540  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of submodules transaxial", nb_trans_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
541  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of submodules axial", nb_axial_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
542  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "module gap transaxial", gap_trans_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
543  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "module gap axial", gap_axial_mod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
544  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "submodule gap transaxial", gap_trans_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
545  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "submodule gap axial", gap_axial_submod_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
546  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystal gap transaxial", gap_trans_crystal_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
547  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "crystal gap axial", gap_axial_crystal_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
548  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors first angle", rsector_first_angle_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
549  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors angular span", rsector_angular_span_lyr, m_nbLayers, KEYWORD_OPTIONAL) == 1 ||
550  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rotation direction", &rotation_direction, 1, KEYWORD_OPTIONAL) == 1)
551  {
552  Cerr("***** iScannerPET::ComputeLUT() -> An error occurred while trying to read an optionnal parameter for scanner LUT generation in the scanner header file !" << endl);
553  return 1;
554  }
555 
556  // Compute nb rings per layer
557  for(int lyr=0 ; lyr<m_nbLayers ; lyr++)
558  mp_nbRings[lyr] = nb_axial_mod_lyr[ lyr ]
559  * nb_axial_submod_lyr[ lyr ]
560  * nb_axial_crystal_lyr[ lyr ];
561 
562  // Set rotation direction for geometry generation
563  if (SetRotDirection(rotation_direction) )
564  {
565  Cerr("***** iScannerPET::ComputeLUT() -> Error occured while trying to initialize head rotation orientation " << endl);
566  return 1;
567  }
568 
569  // Z-SHIFT MANAGEMENT
570 
571  // Variables
572  int rsector_nb_zshift;
573  FLTNBLUT *zshift;
574 
575  int rvalue = ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors nbZShift", &rsector_nb_zshift, 1, KEYWORD_OPTIONAL);
576 
577  // reading error
578  if( rvalue == 1 )
579  {
580  Cerr("***** iScannerPET::ComputeLUT() -> Error occurred when trying to read z-shift nb !" << endl);
581  return 1;
582  }
583  // z-shift not found, or == 0
584  else if( rvalue > 1 || rsector_nb_zshift==0)
585  {
586  rsector_nb_zshift = 1; // set to default value (=1)
587  zshift = new FLTNBLUT[rsector_nb_zshift];
588  zshift[0] = 0;
589  }
590  // (==0, zhift value provided)
591  else
592  {
593  zshift = new FLTNBLUT[rsector_nb_zshift];
594 
595  if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "rsectors ZShift", zshift, rsector_nb_zshift, KEYWORD_MANDATORY) )
596  {
597  Cerr("***** iScannerPET::ComputeLUT() -> No data found about modules z-shift in the scanner header file, whereas z-shift is enabled !" << endl);
598  return 1;
599  }
600  }
601 
602  // keep track of the number of crystals already indexed between layer
603  int nb_crystals_cur = 0;
604 
605  // Loop on layer, crystals indexed according to the layer they belong to
606  for(int lyr=0 ; lyr<m_nbLayers ; lyr++)
607  {
608  if (lyr>0)
609  nb_crystals_cur+=mp_nbCrystalsInLayer[lyr-1];
610 
611  int nb_rsectors = nb_rsectors_lyr[lyr],
612  nb_trans_mod = nb_trans_mod_lyr[lyr],
613  nb_axial_mod = nb_axial_mod_lyr[lyr],
614  nb_trans_submod = nb_trans_submod_lyr[lyr],
615  nb_axial_submod = nb_axial_submod_lyr[lyr],
616  nb_trans_crystal = nb_trans_crystal_lyr[lyr],
617  nb_axial_crystal = nb_axial_crystal_lyr[lyr];
618 
619  FLTNBLUT radius = radius_lyr[lyr],
620  rsector_first_angle = rsector_first_angle_lyr[lyr],
621  angular_span = rsector_angular_span_lyr[lyr],
622  gap_trans_mod = gap_trans_mod_lyr[lyr],
623  gap_axial_mod = gap_axial_mod_lyr[lyr],
624  gap_trans_submod = gap_trans_submod_lyr[lyr],
625  gap_axial_submod = gap_axial_submod_lyr[lyr],
626  gap_trans_crystal = gap_trans_crystal_lyr[lyr],
627  gap_axial_crystal = gap_axial_crystal_lyr[lyr];
628 
629  FLTNBLUT size_trans_submod = nb_trans_crystal*mp_sizeCrystalTrans[lyr] + (nb_trans_crystal-1)*gap_trans_crystal;
630  FLTNBLUT size_axial_submod = nb_axial_crystal*mp_sizeCrystalAxial[lyr] + (nb_axial_crystal-1)*gap_axial_crystal;
631  FLTNBLUT size_trans_mod = nb_trans_submod*size_trans_submod + (nb_trans_submod-1)*gap_trans_submod;
632  FLTNBLUT size_axial_mod = nb_axial_submod*size_axial_submod + (nb_axial_submod-1)*gap_axial_submod;
633 
634  int nb_mod = nb_axial_mod * nb_trans_mod;
635  int nb_submod = nb_axial_submod * nb_trans_submod;
636  int nb_crystal = nb_axial_crystal * nb_trans_crystal;
637 
638  // Check the number of crystals < nb elements provided by the scanner configuration file
639  int nb_crystals = nb_rsectors*nb_mod*nb_submod*nb_crystal + nb_crystals_cur;
640  if(m_nbCrystals<nb_crystals)
641  {
642  Cerr("***** iScannerPET::ComputeLUT() -> Computed number of crystals computed from the geom file ("<< nb_crystals
643  <<") > not consistent with the total number of crystals (including potential layers) provided in the geom file ("<< m_nbCrystals <<") !" << endl);
644  return 1;
645  }
646 
647  mp_nbCrystalsInLayer[lyr] = nb_rsectors*nb_mod*nb_submod*nb_crystal;
648  int number_crystals_in_ring = mp_nbCrystalsInLayer[lyr]/mp_nbRings[lyr];
649 
650  // ============================================================================================================
651  // Main part of the function: Generate the LUT
652  // ============================================================================================================
653 
654  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Generate positions for each crystal"<< endl);
655 
656  // loop to nb_rsectors+1. crystal_center[0] will be used to gather position of the reference rsector (directly above isocenter)
657  oMatrix *****crystal_center = new oMatrix ****[nb_rsectors+1];
658 
659  for(int i = 0; i < nb_rsectors+1 ; i++)
660  {
661  crystal_center[i] = new oMatrix ***[nb_axial_mod * nb_trans_mod];
662 
663  for (int j = 0; j<nb_axial_mod*nb_trans_mod ; j++)
664  {
665  crystal_center[i][j] = new oMatrix **[nb_axial_submod * nb_trans_submod];
666 
667  for (int k = 0; k<nb_axial_submod*nb_trans_submod; k++)
668  {
669  crystal_center[i][j][k] = new oMatrix*[nb_axial_crystal * nb_trans_crystal];
670 
671  for (int l = 0; l<nb_axial_crystal*nb_trans_crystal; l++)
672  {
673  crystal_center[i][j][k][l] = new oMatrix(3,1);
674  }
675  }
676  }
677  }
678 
679  // Generation of the rotation matrix allowing to compute the position of all the rsectors.
680  oMatrix** rotation_mtx = new oMatrix*[nb_rsectors];
681 
682  for(int i=0; i<nb_rsectors; i++)
683  rotation_mtx[i] = new oMatrix(3,3);
684 
685  // convert first angle and angular span in radians
686  FLTNBLUT rsector_first_angle_rad = rsector_first_angle*M_PI/180.;
687  FLTNBLUT angular_span_rad = angular_span*M_PI/180.;
688 
689  int dir = (m_rotDirection == GEO_ROT_CCW) ? -1 : 1;
690  angular_span = 360.;
691 
692  for (int i = 0; i<nb_rsectors; i++)
693  {
694  FLTNBLUT angle = remainderf(rsector_first_angle_rad + ((FLTNBLUT)i)*angular_span_rad/((FLTNBLUT)(nb_rsectors-1)), 2.*M_PI);
695  rotation_mtx[i]->SetMatriceElt(0,0,cos(angle) );
696  rotation_mtx[i]->SetMatriceElt(1,0,-dir*sin(angle) );
697  rotation_mtx[i]->SetMatriceElt(2,0,0);
698  rotation_mtx[i]->SetMatriceElt(0,1,dir*sin(angle) );
699  rotation_mtx[i]->SetMatriceElt(1,1,cos(angle) );
700  rotation_mtx[i]->SetMatriceElt(2,1,0);
701  rotation_mtx[i]->SetMatriceElt(0,2,0);
702  rotation_mtx[i]->SetMatriceElt(1,2,0);
703  rotation_mtx[i]->SetMatriceElt(2,2,1);
704  }
705 
706  // Compute scanner elements positions for the first rsector
707  for (int i=0; i < nb_mod ; i++)
708  {
709  // Define the transaxial and axial edge start positions for the rsector
710  FLTNBLUT x_start_m = -dir*(nb_trans_mod*size_trans_mod + (nb_trans_mod-1)*gap_trans_mod) / 2.;
711  FLTNBLUT z_start_m = -(nb_axial_mod*size_axial_mod + (nb_axial_mod-1)*gap_axial_mod) / 2.;
712 
713  // Define the transaxial and axial edge start positions for the i-Module in the rsector.
714  // Enumeration starting with the transaxial modules.
715  x_start_m += dir*(i%nb_trans_mod) * (size_trans_mod + gap_trans_mod);
716  z_start_m += int(i/nb_trans_mod) * (size_axial_mod + gap_axial_mod);
717 
718  for (int j=0 ; j < nb_submod ; j++)
719  {
720  FLTNBLUT x_start_sm = x_start_m;
721  FLTNBLUT z_start_sm = z_start_m;
722 
723  x_start_sm += dir*(j%nb_trans_submod) * (size_trans_submod + gap_trans_submod);
724  z_start_sm += int(j/nb_trans_submod) * (size_axial_submod + gap_axial_submod);
725 
726  for (int k=0 ; k < nb_crystal ; k++)
727  {
728  // Define the transaxial and axial center positions for the j-SubModule (crystal) i-Module of the rsector.
729  // Enumeration starting with the transaxial submodules.
730  FLTNBLUT Xcrist = x_start_sm + dir* ( (k%nb_trans_crystal) * (mp_sizeCrystalTrans[lyr] + gap_trans_crystal) + mp_sizeCrystalTrans[lyr]/2 );
731  FLTNBLUT Ycrist = radius + mp_sizeCrystalDepth[lyr]/2;
732  FLTNBLUT Zcrist = z_start_sm + int(k/nb_trans_crystal) * (mp_sizeCrystalAxial[lyr] + gap_axial_crystal) + mp_sizeCrystalAxial[lyr]/2;
733 
734  crystal_center[0][i][j][k]->SetMatriceElt(0,0,Xcrist);
735  crystal_center[0][i][j][k]->SetMatriceElt(1,0,Ycrist);
736  crystal_center[0][i][j][k]->SetMatriceElt(2,0,Zcrist);
737  }
738  }
739  }
740 
741 
742 
743  // ============================================================================================================
744  // Loop over all the other rsectors.
745  // Positions of the scanner elements are progressively stored in the LUT file.
746  // ============================================================================================================
747  for (int rs=0 ; rs<nb_rsectors ; rs++)
748  for (int m=0 ; m<nb_mod ; m++)
749  for (int sm=0 ; sm<nb_submod ; sm++)
750  for (int c=0 ; c<nb_crystal ; c++)
751  {
752  // crystal indexation
753  int cryID = int(m/nb_trans_mod)*nb_axial_submod*nb_axial_crystal*number_crystals_in_ring // nb indexed crystals in the rings covered by the previous (axial) modules
754  + int(sm/nb_trans_submod)*nb_axial_crystal*number_crystals_in_ring // nb indexed crystals in the rings covered by the previous (axial) submodules
755  + int(c/nb_trans_crystal)*number_crystals_in_ring // nb indexed crystals in the rings covered by the previous (axial) crystals
756  + rs*nb_trans_mod*nb_trans_submod*nb_trans_crystal // nb indexed crystals in the previous rsectors
757  + m%nb_trans_mod*nb_trans_submod*nb_trans_crystal // nb indexed crystals in the previous (transaxial) modules
758  + sm%nb_trans_submod*nb_trans_crystal // nb indexed crystals in the previous (transaxial) submodules
759  + c%nb_trans_crystal // previous crystals in the submodule
760  + nb_crystals_cur; // nb indexed crystals in the previous layer(s)
761 
762 
763 
764  rotation_mtx[rs]->Multiplication(crystal_center[0][m][sm][c], crystal_center[rs+1][m][sm][c]);
765 
766  mp_crystalCentralPositionX[cryID] = (FLTNB)crystal_center[rs+1][m][sm][c]->GetMatriceElt(0,0);
767  mp_crystalCentralPositionY[cryID] = (FLTNB)crystal_center[rs+1][m][sm][c]->GetMatriceElt(1,0);
768  mp_crystalCentralPositionZ[cryID] = (FLTNB)crystal_center[rs+1][m][sm][c]->GetMatriceElt(2,0);
769  mp_crystalCentralPositionZ[cryID] += (FLTNB)zshift[rs%rsector_nb_zshift];
770 
771  // TODO Rotation for non-perfectly cylindric scanner (angles along the Z axis)
772  mp_crystalOrientationX[cryID] = (FLTNB)rotation_mtx[rs]->GetMatriceElt(0,1);
773  mp_crystalOrientationY[cryID] = (FLTNB)rotation_mtx[rs]->GetMatriceElt(0,0);
774  mp_crystalOrientationZ[cryID] = 0.;
775  }
776 
777  // ============================================================================================================
778  // End
779  // ============================================================================================================
780 
781  // Delete objects
782 
783  for (int i = 0; i < nb_rsectors+1 ; i++)
784  for (int j = 0; j<nb_axial_mod*nb_trans_mod; j++)
785  for (int k = 0; k<nb_axial_submod*nb_trans_submod; k++)
786  for (int l = 0; l<nb_axial_crystal*nb_trans_crystal; l++)
787  {
788  delete crystal_center[i][j][k][l];
789  }
790 
791 
792  for(int i = 0; i < nb_rsectors+1 ; i++)
793  for (int j = 0; j<nb_axial_mod*nb_trans_mod; j++)
794  for (int k = 0; k<nb_axial_submod*nb_trans_submod; k++)
795  {
796  delete crystal_center[i][j][k];
797  }
798 
799 
800  for(int i = 0; i < nb_rsectors+1 ; i++)
801  for (int j = 0; j<nb_axial_mod*nb_trans_mod; j++)
802  {
803  delete[] crystal_center[i][j];
804  }
805 
806 
807  for(int i = 0; i < nb_rsectors+1 ; i++)
808  delete[] crystal_center[i];
809 
810  for(int i = 0; i < nb_rsectors ; i++)
811  delete rotation_mtx[i];
812 
813  delete[] crystal_center;
814  delete[] rotation_mtx;
815  }
816 
817  // ============================================================================================================
818  // Save LUT if required
819  // ============================================================================================================
820 
821  if (p_scannerManager->SaveLUTFlag())
822  {
823  // Make file names
824  string path_to_geom_file = p_scannerManager->GetPathToScannerFile();
825  string path_to_LUT = path_to_geom_file.substr(0, path_to_geom_file.find_last_of("."));
826  string path_to_header_LUT = path_to_LUT + ".ghscan";
827  path_to_LUT.append(".glut");
828  // Verbose
829  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Save LUT into file with extension '.glut'" << endl);
830  // Open file
831  ofstream LUT_file, header_LUT_file;
832  LUT_file.open(path_to_LUT.c_str(), ios::binary | ios::out);
833  // Write the corresponding crystal parameters in the binary LUT
834  for (int i=0 ; i<m_nbCrystals ; i++)
835  {
836  LUT_file.write(reinterpret_cast<char*>(&mp_crystalCentralPositionX[i]), sizeof(FLTNB));
837  LUT_file.write(reinterpret_cast<char*>(&mp_crystalCentralPositionY[i]), sizeof(FLTNB));
838  LUT_file.write(reinterpret_cast<char*>(&mp_crystalCentralPositionZ[i]), sizeof(FLTNB));
839  LUT_file.write(reinterpret_cast<char*>(&mp_crystalOrientationX[i]), sizeof(FLTNB));
840  LUT_file.write(reinterpret_cast<char*>(&mp_crystalOrientationY[i]), sizeof(FLTNB));
841  LUT_file.write(reinterpret_cast<char*>(&mp_crystalOrientationZ[i]), sizeof(FLTNB));
842  }
843  // Close file
844  LUT_file.close();
845  // ---------------------
846  // --> Write header file
847  // ---------------------
848  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Save header LUT file with extension '.ghscan'" << endl);
849  // Open file
850  header_LUT_file.open(path_to_header_LUT.c_str(), ios::out);
851  // Start writing
852  string scanner_name = path_to_geom_file.substr(0, path_to_geom_file.find_last_of("."));
853  header_LUT_file << "scanner name:" << " " << GetFileFromPath(scanner_name) << endl;
854  header_LUT_file << "modality:" << " " << "PET" << endl;
855 
856  header_LUT_file << "scanner radius:" << " " << radius_lyr[0];
857  for (int lyr=1 ; lyr<m_nbLayers ; lyr++)
858  header_LUT_file << "," << radius_lyr[lyr] ; header_LUT_file << endl;
859 
860  header_LUT_file << "number of rings in scanner:" << " " << mp_nbRings[0];
861  for (int lyr=1 ; lyr<m_nbLayers ; lyr++)
862  header_LUT_file << ","<< mp_nbRings[lyr] ; header_LUT_file << endl;
863  header_LUT_file << "number of elements:" << " " << m_nbCrystals << endl;
864  header_LUT_file << "number of layers:" << " " << m_nbLayers << endl;
865  header_LUT_file << "number of crystals in layer(s):" << " " << mp_nbCrystalsInLayer[0]; // TODO nb_cry_in_layer
866  for (int lyr=1 ; lyr<m_nbLayers ; lyr++)
867  header_LUT_file << ","<< mp_nbCrystalsInLayer[lyr] ; header_LUT_file << endl;
868 
869  header_LUT_file << "crystals size depth:" << " " << mp_sizeCrystalDepth[0];
870  for (int lyr=1 ; lyr<m_nbLayers ; lyr++)
871  header_LUT_file << ","<< mp_sizeCrystalDepth[lyr] ; header_LUT_file << endl;
872 
873  header_LUT_file << "crystals size transaxial:" << " " << mp_sizeCrystalTrans[0];
874  for (int lyr=1 ; lyr<m_nbLayers ; lyr++)
875  header_LUT_file << ","<< mp_sizeCrystalTrans[lyr] ; header_LUT_file << endl;
876 
877  header_LUT_file << "crystals size axial:" << " " << mp_sizeCrystalAxial[0];
878  for (int lyr=1 ; lyr<m_nbLayers ; lyr++)
879  header_LUT_file << ","<< mp_sizeCrystalAxial[lyr] ; header_LUT_file << endl;
880 
881  // Default reconstruction parameters
882  uint32_t def_dim_trans = 0, def_dim_axial = 0; // default number of voxels
883  FLTNB def_FOV_trans = -1., def_FOV_axial = -1; // computed from modules width and gaps of the 1st layer
884 
885  if( ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "voxels number transaxial", &def_dim_trans, 1, KEYWORD_MANDATORY) ||
886  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "voxels number axial", &def_dim_axial, 1, KEYWORD_MANDATORY) ||
887  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "field of view transaxial", &def_FOV_trans, 1, KEYWORD_MANDATORY) ||
888  ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "field of view axial", &def_FOV_axial, 1, KEYWORD_MANDATORY) )
889  {
890  Cerr("***** iScannerPET::ComputeLUT() -> Error occurred when trying to read transaxial/axial dimensions and voxel sizes from scanner geom file !" << endl);
891  return 1;
892  }
893 
894  header_LUT_file << "voxels number transaxial:" << " " << def_dim_trans << endl;
895  header_LUT_file << "voxels number axial:" << " " << def_dim_axial << endl;
896 
897  header_LUT_file << "field of view transaxial:" << " " << def_FOV_trans << endl;
898  header_LUT_file << "field of view axial:" << " " << def_FOV_axial << endl;
899 
900  header_LUT_file << "min angle difference:" << " " << m_minAngleDifference << " #deg" << endl;
901 
902  header_LUT_file << "mean depth of interaction:" << " " << mp_meanDepthOfInteraction[0];
903  for (int lyr=1 ; lyr<m_nbLayers ; lyr++)
904  header_LUT_file << "," << mp_meanDepthOfInteraction[lyr] ;
905  header_LUT_file << " #optional (default value : center of crystal ). Input value must correspond to the distance from the crystal surface, or negative value if default" << endl;
906 
907  header_LUT_file << "description:" << " " << "LUT generated from geom file: " << GetFileFromPath(p_scannerManager->GetPathToScannerFile()) << endl;
908 
909  if(m_verbose>=2) Cout("iScannerPET::ComputeLUT() -> Header LUT file writing completed" << endl);
910  }
911 
912  delete nb_rsectors_lyr;
913  delete nb_trans_mod_lyr;
914  delete nb_axial_mod_lyr;
915  delete nb_trans_submod_lyr;
916  delete nb_axial_submod_lyr;
917  delete nb_trans_crystal_lyr;
918  delete nb_axial_crystal_lyr;
919 
920  delete radius_lyr;
921  delete rsector_angular_span_lyr;
922  delete rsector_first_angle_lyr;
923  delete zshift;
924  delete gap_trans_mod_lyr;
925  delete gap_axial_mod_lyr;
926  delete gap_trans_submod_lyr;
927  delete gap_axial_submod_lyr;
928  delete gap_trans_crystal_lyr;
929  delete gap_axial_crystal_lyr;
930 
931  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> LUT generation completed" << endl);
932 
933  return 0;
934 }
935 
936 // =====================================================================
937 // ---------------------------------------------------------------------
938 // ---------------------------------------------------------------------
939 // =====================================================================
940 /*
941  \fn GetPositionsAndOrientations
942  \param a_index1 : index of the 1st crystal
943  \param a_index2 : index of the 2nd crystal
944  \param ap_Position1[3] : x,y,z cartesian position of center of the 1st crystal
945  \param ap_Position2[3] : x,y,z cartesian position of center of the 2nd crystal
946  \param ap_Orientation1[3] : x,y,z components of the orientation vector related to the 1st crystal
947  \param ap_Orientation2[3] : x,y,z components of the orientation vector related to the 2nd crystal
948  \param ap_POI1 : x,y,z components of the Point Of Interation related to the 1st crystal
949  \param ap_POI2 : x,y,z components of the Point Of Interation related to the 2nd crystal
950  \brief Get the central positions and orientations of the scanner elements from their indices.
951  \details This method is very general and is used by the vProjector.
952  From these positions and orientations, other methods can be used by specific projectors to get specific positions.
953  Position calculations include POI and mean depth of interaction
954  \todo some cases depending on POI are not implemented
955  \return 0 if success, positive value otherwise
956 */
957 int iScannerPET::GetPositionsAndOrientations( int a_index1, int a_index2,
958  FLTNB ap_Position1[3], FLTNB ap_Position2[3],
959  FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3],
960  FLTNB* ap_POI1, FLTNB* ap_POI2 )
961 {
963 
964  // First check crystals existency
965  if (a_index1<0 || a_index1>=m_nbCrystals)
966  {
967  Cerr("***** iScannerPET::GetPositionsAndOrientations() -> Crystal index 1 (" << a_index1 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl);
968  return 1;
969  }
970  if (a_index2<0 || a_index2>=m_nbCrystals)
971  {
972  Cerr("***** iScannerPET::GetPositionsAndOrientations() -> Crystal index 2 (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl);
973  return 1;
974  }
975 
976  // The POI specification is as follows:
977  // - POI[0] and POI[1] express the local x and y coordinates: x is the tangential axis, and y is colinear to the global z axis.
978  // Their origins are in the middle of the scanner elements so they range from minus half to half the element dimensions.
979  // - POI[2] express the DOI along the local z axis which is colinear to the crystal orientation (which itself is oriented from
980  // the center of the scanner to the exterior).
981  // Its origin is at the entrance of the element and thus ranges from 0 to the local z size of the element (crystal depth).
982  // - If POI[2] has a negative value, this means that the mean depth of interaction should be considered instead, while still
983  // using POI[0] and POI[1].
984  // Remember here that the crystal position is at its center.
985 
986  // Case when POI is not provided (we use the mean depth of interaction)
987  if (ap_POI1==NULL)
988  {
989  FLTNB depth = mp_meanDepthOfInteraction[GetLayer(a_index1)] - mp_sizeCrystalDepth[GetLayer(a_index1)]/2;
990  ap_Position1[0] = mp_crystalCentralPositionX[a_index1] + depth*mp_crystalOrientationX[a_index1];
991  ap_Position1[1] = mp_crystalCentralPositionY[a_index1] + depth*mp_crystalOrientationY[a_index1];
992  ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] + depth*mp_crystalOrientationZ[a_index1];
993  }
994  // Case when POI[2] is negative (meaning we only have POI[0] or POI[1] specified and to be taken into account)
995  else if (ap_POI1[2]<0.)
996  {
997  // TODO
998  }
999  // Case when only the DOI is provided
1000  else if (ap_POI1[0]==0. && ap_POI1[1]==0.)
1001  {
1002  FLTNB depth = ap_POI1[2] - mp_sizeCrystalDepth[GetLayer(a_index1)]/2;
1003  ap_Position1[0] = mp_crystalCentralPositionX[a_index1] + depth*mp_crystalOrientationX[a_index1];
1004  ap_Position1[1] = mp_crystalCentralPositionY[a_index1] + depth*mp_crystalOrientationY[a_index1];
1005  ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] + depth*mp_crystalOrientationZ[a_index1];
1006  }
1007  // Case when the full POI is taken into account
1008  else
1009  {
1010  // TODO
1011  }
1012 
1013  // Case when POI is not provided (we use the mean depth of interaction)
1014  if (ap_POI2==NULL)
1015  {
1016  FLTNB depth = mp_meanDepthOfInteraction[GetLayer(a_index2)] - mp_sizeCrystalDepth[GetLayer(a_index2)]/2;
1017  ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth*mp_crystalOrientationX[a_index2];
1018  ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth*mp_crystalOrientationY[a_index2];
1019  ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth*mp_crystalOrientationZ[a_index2];
1020  }
1021  // Case when POI[2] is negative (meaning we only have POI[0] or POI[1] specified and to be taken into account)
1022  else if (ap_POI2[2]<0.)
1023  {
1024  // TODO
1025  }
1026  // Case when only the DOI is provided
1027  else if (ap_POI2[0]==0. && ap_POI2[1]==0.)
1028  {
1029  FLTNB depth = ap_POI2[2] - mp_sizeCrystalDepth[GetLayer(a_index2)]/2;
1030  ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth*mp_crystalOrientationX[a_index2];
1031  ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth*mp_crystalOrientationY[a_index2];
1032  ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth*mp_crystalOrientationZ[a_index2];
1033  }
1034  // Case when the full POI is taken into account
1035  else
1036  {
1037  // TODO
1038  }
1039 
1040  // Get orientations
1041  ap_Orientation1[0] = mp_crystalOrientationX[a_index1];
1042  ap_Orientation1[1] = mp_crystalOrientationY[a_index1];
1043  ap_Orientation1[2] = mp_crystalOrientationZ[a_index1];
1044  ap_Orientation2[0] = mp_crystalOrientationX[a_index2];
1045  ap_Orientation2[1] = mp_crystalOrientationY[a_index2];
1046  ap_Orientation2[2] = mp_crystalOrientationZ[a_index2];
1047 
1048  // End
1049  return 0;
1050 }
1051 
1052 // =====================================================================
1053 // ---------------------------------------------------------------------
1054 // ---------------------------------------------------------------------
1055 // =====================================================================
1056 /*
1057  \fn GetRdmPositionsAndOrientations
1058  \param a_index1 : index of the 1st crystal
1059  \param a_index2 : index of the 2nd crystal
1060  \param ap_Position1[3] : x,y,z cartesian position of center of the 1st crystal
1061  \param ap_Position2[3] : x,y,z cartesian position of center of the 2nd crystal
1062  \param ap_Orientation1[3] : x,y,z components of the orientation vector related to the 1st crystal
1063  \param ap_Orientation2[3] : x,y,z components of the orientation vector related to the 2nd crystal
1064  \brief Get random positions of the scanner elements and their orientations from their indices.
1065  \details - Find the scanner elements described by the two indexes passed in parameters.
1066  \details - Compute random positions inside the elements described by the indexes passed in parameters
1067  \details - Find the scanner elements described by the two indexes passed in parameters.
1068  \details - Write the corresponding random cartesian coordinates in the positions parameters.
1069  \details Position calculations include POI and mean depth of interaction
1070  \todo fix the possibility to draw LOR outside the actual crystal volume (if mp_meanDepthOfInteraction != 0.5)
1071  \todo some cases depending on POI are not implemented
1072  \return 0 if success, positive value otherwise
1073 */
1074 int iScannerPET::GetRdmPositionsAndOrientations(int a_index1, int a_index2,
1075  FLTNB ap_Position1[3], FLTNB ap_Position2[3],
1076  FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3] )
1077 {
1079 
1080  // First check crystals existency
1081  if (a_index1<0 || a_index1>=m_nbCrystals)
1082  {
1083  Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal index 1 (" << a_index1 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl);
1084  return 1;
1085  }
1086  if (a_index2<0 || a_index2>=m_nbCrystals)
1087  {
1088  Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal index 2 (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl);
1089  return 1;
1090  }
1091 
1092  if (mp_sizeCrystalTrans[GetLayer(a_index1)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index1)]<=0. ||
1093  mp_sizeCrystalTrans[GetLayer(a_index2)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index2)]<=0. )
1094  {
1095  Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal sizes are unknown or equal to 0. Crystal dimensions are mandatory for this function !" << endl);
1096  return 1;
1097  }
1098 
1100 
1101  // Get random numbers for the first crystal
1102  FLTNB rdm_axial1 = p_RNG->GenerateRdmNber();
1103  FLTNB rdm_trans1 = p_RNG->GenerateRdmNber();
1104  FLTNB rdm_depth1 = p_RNG->GenerateRdmNber();
1105 
1106  FLTNB size_crystalTrans1 = mp_sizeCrystalTrans[GetLayer(a_index1)];
1107  FLTNB size_crystalAxial1 = mp_sizeCrystalAxial[GetLayer(a_index1)];
1108  FLTNB size_crystalDepth1 = mp_sizeCrystalDepth[GetLayer(a_index1)];
1109 
1110  FLTNB axial1 = (rdm_axial1-0.5) * size_crystalAxial1;
1111  FLTNB trans1 = (rdm_trans1-0.5) * size_crystalTrans1;
1112  FLTNB depth1 = (rdm_depth1-0.5) * size_crystalDepth1;
1113 
1114  ap_Position1[0] = mp_crystalCentralPositionX[a_index1]
1115  + depth1 * mp_crystalOrientationX[a_index1]
1116  + trans1 * mp_crystalOrientationY[a_index1]
1117  + axial1 * mp_crystalOrientationX[a_index1] * mp_crystalOrientationZ[a_index1];
1118 
1119  ap_Position1[1] = mp_crystalCentralPositionY[a_index1]
1120  + depth1 * mp_crystalOrientationY[a_index1]
1121  + trans1 * mp_crystalOrientationX[a_index1]
1122  + axial1 * mp_crystalOrientationY[a_index1] * mp_crystalOrientationZ[a_index1];
1123 
1124  ap_Position1[2] = mp_crystalCentralPositionZ[a_index1]
1125  + depth1 * mp_crystalOrientationZ[a_index1]
1126  + axial1 * sqrt(1-mp_crystalOrientationZ[a_index1] * mp_crystalOrientationZ[a_index1]);
1127 
1128  // Get orientations
1129  ap_Orientation1[0] = mp_crystalOrientationX[a_index1];
1130  ap_Orientation1[1] = mp_crystalOrientationY[a_index1];
1131  ap_Orientation1[2] = mp_crystalOrientationZ[a_index1];
1132 
1133  // Get random numbers for the second crystal
1134  FLTNB rdm_axial2 = p_RNG->GenerateRdmNber();
1135  FLTNB rdm_trans2 = p_RNG->GenerateRdmNber();
1136  FLTNB rdm_depth2 = p_RNG->GenerateRdmNber();
1137 
1138  FLTNB size_crystalTrans2 = mp_sizeCrystalTrans[GetLayer(a_index2)];
1139  FLTNB size_crystalAxial2 = mp_sizeCrystalAxial[GetLayer(a_index2)];
1140  FLTNB size_crystalDepth2 = mp_sizeCrystalDepth[GetLayer(a_index2)];
1141 
1142  FLTNB axial2 = (rdm_axial2-0.5) * size_crystalAxial2;
1143  FLTNB trans2 = (rdm_trans2-0.5) * size_crystalTrans2;
1144  FLTNB depth2 = (rdm_depth2-0.5) * size_crystalDepth2;
1145 
1146  ap_Position2[0] = mp_crystalCentralPositionX[a_index2]
1147  + depth2 * mp_crystalOrientationX[a_index2]
1148  + trans2 * mp_crystalOrientationY[a_index2]
1149  + axial2 * mp_crystalOrientationX[a_index2] * mp_crystalOrientationZ[a_index2];
1150 
1151  ap_Position2[1] = mp_crystalCentralPositionY[a_index2]
1152  + depth2 * mp_crystalOrientationY[a_index2]
1153  + trans2 * mp_crystalOrientationX[a_index2]
1154  + axial2 * mp_crystalOrientationY[a_index2] * mp_crystalOrientationZ[a_index2];
1155 
1156  ap_Position2[2] = mp_crystalCentralPositionZ[a_index2]
1157  + depth2 * mp_crystalOrientationZ[a_index2]
1158  + axial2 * sqrt(1-mp_crystalOrientationZ[a_index2] * mp_crystalOrientationZ[a_index2]);
1159 
1160  // Get orientations
1161  ap_Orientation2[0] = mp_crystalOrientationX[a_index2];
1162  ap_Orientation2[1] = mp_crystalOrientationY[a_index2];
1163  ap_Orientation2[2] = mp_crystalOrientationZ[a_index2];
1164 
1165  // End
1166  return 0;
1167 }
1168 
1169 // =====================================================================
1170 // ---------------------------------------------------------------------
1171 // ---------------------------------------------------------------------
1172 // =====================================================================
1173 /*
1174  \fn GetPositionWithRandomDepth
1175  \param a_index1 : index of the 1st crystal
1176  \param a_index2 : index of the 2nd crystal
1177  \param ap_Position1[3] : x,y,z cartesian position of the point related to the 1st index (see child function for more details)
1178  \param ap_Position2[3] : x,y,z cartesian position of the point related to the 2st index (see child function for more details)
1179  \brief Get the positions and orientations of scanner elements from their indices, with a random depth.
1180  \details Method for testing purposes. Does not include POI and mean depth of interaction
1181  \return 0 if success, positive value otherwise
1182 */
1183 int iScannerPET::GetPositionWithRandomDepth(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3])
1184 {
1186 
1187  // Get instance of random number generator
1189 
1190  // Shoot first random number
1191  FLTNB shoot1 = p_RNG->GenerateRdmNber();
1192  // Compute associated depth
1193  FLTNB depth1 = (shoot1-0.5) * mp_sizeCrystalDepth[GetLayer(a_index1)];
1194 
1195  // Compute associated position
1196  ap_Position1[0] = mp_crystalCentralPositionX[a_index1] + depth1*mp_crystalOrientationX[a_index1];
1197  ap_Position1[1] = mp_crystalCentralPositionY[a_index1] + depth1*mp_crystalOrientationY[a_index1];
1198  ap_Position1[2] = mp_crystalCentralPositionZ[a_index1] + depth1*mp_crystalOrientationZ[a_index1];
1199 
1200  // Shoot second random number
1201  FLTNB shoot2 = p_RNG->GenerateRdmNber();
1202  // Compute associated depth
1203  FLTNB depth2 = (shoot2-0.5) * mp_sizeCrystalDepth[GetLayer(a_index2)];
1204  // Compute associated position
1205  ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth2*mp_crystalOrientationX[a_index2];
1206  ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth2*mp_crystalOrientationY[a_index2];
1207  ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth2*mp_crystalOrientationZ[a_index2];
1208  // End
1209  return 0;
1210 }
1211 
1212 // =====================================================================
1213 // ---------------------------------------------------------------------
1214 // ---------------------------------------------------------------------
1215 // =====================================================================
1216 /*
1217  \fn GetTwoCorners
1218  \param a_index1 : index of the 1st crystal
1219  \param a_index2 : index of the 2nd crystal
1220  \param ap_CornerInf1[3]
1221  \param ap_CornerSup1[3]
1222  \param ap_CornerInf2[3]
1223  \param ap_CornerSup2[3]
1224  \brief Get the cartesian coordinaters of the two opposite corners of a scanner element.
1225  \todo Not implemented yet
1226  \return 0 if success, positive value otherwise
1227 */
1228 int iScannerPET::GetTwoCorners(int a_index1, int a_index2,
1229  FLTNB ap_CornerInf1[3], FLTNB ap_CornerSup1[3],
1230  FLTNB ap_CornerInf2[3], FLTNB ap_CornerSup2[3])
1231 {
1233 
1234  if (a_index1<0 || a_index1>=m_nbCrystals || a_index2<0 || a_index2>=m_nbCrystals)
1235  {
1236  Cerr("***** iScannerPET::GetTwoCorners() -> Crystal index out of range !" << endl);
1237  return 1;
1238  }
1239 
1240  if (mp_sizeCrystalTrans[GetLayer(a_index1)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index1)]<=0. ||
1241  mp_sizeCrystalTrans[GetLayer(a_index2)]<=0. || mp_sizeCrystalAxial[GetLayer(a_index2)]<=0. )
1242  {
1243  Cerr("***** iScannerPET::GetRdmPositionsAndOrientations() -> Crystal sizes are unknown or equal to 0. Crystal dimensions are mandatory for this function !" << endl);
1244  return 1;
1245  }
1246 
1247  Cerr("***** iScannerPET::GetTwoCorners() -> Not implemented yet !" << endl);
1248  return 1;
1249 }
1250 
1251 // =====================================================================
1252 // ---------------------------------------------------------------------
1253 // ---------------------------------------------------------------------
1254 // =====================================================================
1255 /*
1256  \fn GetLayer
1257  \param a_idx : index of the crystal in the loaded LUT
1258  \brief Get the layer from which the 'a_idx' crystal belongs to
1259  \return layer index
1260 */
1262 {
1264 
1265  int layer=0;
1266  int sum_crystals = mp_nbCrystalsInLayer[layer];
1267  // Get the layer which the crystal belongs to
1268  while (a_idx >= sum_crystals)
1269  {
1270  layer++;
1271  sum_crystals += mp_nbCrystalsInLayer[layer];
1272  }
1273  return layer;
1274 }
1275 
1276 // =====================================================================
1277 // ---------------------------------------------------------------------
1278 // ---------------------------------------------------------------------
1279 // =====================================================================
1280 /*
1281  \fn GetRingID
1282  \param a_idx : index of the crystal in the loaded LUT
1283  \brief Get the ring which the #a_idx crystal belongs to
1284  \todo /!\ Current implementation only works with scanner whose elements are ring-indexed
1285  \todo Need to find a solution to deal with potential geometry containing several layers of crystals with different number of rings
1286  We could use the geometrical position of each elements to get this information,
1287  however maybe too complicated for potential scanners with non-standard geometry
1288  \return layer index
1289 */
1291 {
1293 
1294  int layer = GetLayer(a_idx);
1295  int start_index = 0;
1296 
1297  if (layer>0)
1298  {
1299  // Get the index of the first crystal of the layer
1300  int layer_id = 0;
1301  while(layer_id < layer)
1302  {
1303  start_index += mp_nbCrystalsInLayer[layer_id];
1304  layer_id++;
1305  }
1306  }
1307 
1308  // Get the number of crystals in ring for this layer
1309  int nb_crystals_in_ring = mp_nbCrystalsInLayer[layer]/mp_nbRings[layer];
1310 
1311  // Recover the ring ID
1312  int ringID = (int)((a_idx-start_index)/nb_crystals_in_ring);
1313 
1314  // TODO : Need to know how to deal with potential geometry containing several layers of crystals with different number of rings ?
1315  // In this actual situation, crystals from different layers could have similar rings, but not similar axial positions
1316  // which is what we want to know
1317 
1318  return ringID;
1319 }
1320 
1321 // =====================================================================
1322 // ---------------------------------------------------------------------
1323 // ---------------------------------------------------------------------
1324 // =====================================================================
1325 /*
1326  \fn IsAvailableLOR
1327  \param a_elt1 : index of the 1st crystal
1328  \param a_elt2 : index of the 2nd crystal
1329  \brief Check if the LOR formed by the crystalf whose indices are passed
1330  in parameters is available according to the scanner restrictions
1331  \details This function is dedicated to analytic projection and list-mode sensitivity image generation
1332  The PET implementation checks the LOR availability according to the minimal (transaxial) angle difference
1333  and the maximal ring difference between two crystals
1334  \todo min angle difference (currently just system dependent) may be estimated from the FOV requested by the user ?
1335  \todo Restriction for ring_ID NOT safe for user using their own LUT !!!
1336  Perhaps throw warning in this case
1337  \return 1 if the LOR is available, 0 otherwise
1338 */
1339 int iScannerPET::IsAvailableLOR(int a_elt1, int a_elt2)
1340 {
1342 
1343  // Get absolute angle between the two crystals
1344  FLTNB abs_angle = mp_crystalOrientationX[a_elt1]*mp_crystalOrientationX[a_elt2]
1346 
1347  // Handle boundaries (arg of acos() outside range [-1. ; +1])
1348  abs_angle = (abs_angle>1.) ? 1 : abs_angle;
1349  abs_angle = (abs_angle<-1.) ? -1 : abs_angle;
1350 
1351  FLTNB angle_diff = acos(abs_angle);
1352 
1353  // Restrictions on LORs between different rings
1354  int ring_diff = 0;
1355  ring_diff = abs(GetRingID(a_elt1)-GetRingID(a_elt2) );
1356 
1357  // Check restrictions (Axial constraint on ring and Transaxial constraint on min angle difference)
1358  // TODO : Restriction for ring_ID still NOT safe for user using their own LUT !!!
1359  // TODO : min angle difference (currently just system dependent) may be estimated from the FOV requested by the user ?
1360  if ( ring_diff<m_maxRingDiff &&
1361  (angle_diff>=m_minAngleDifference || FLTNBIsEqual(angle_diff,m_minAngleDifference,.0001)) ) // Returns 1 if computed angle diff ~ min angle diff (E-4)
1362  {
1363  return 1;
1364  }
1365 
1366  return 0;
1367 }
1368 
1369 // =====================================================================
1370 // ---------------------------------------------------------------------
1371 // ---------------------------------------------------------------------
1372 // =====================================================================
1373 /*
1374  \fn GetGeometricInfoFromDatafile
1375  \brief Retrieve PET geometric informations from the datafile
1376  \details Recover the (axial) max ring difference
1377  \return 0 if success, positive value otherwise
1378 */
1380 {
1382 
1383  if (m_verbose>=VERBOSE_DETAIL) Cout("iScannerPET::GetGeometricInfoFromDatafile() -> Specific to PET" << endl);
1384 
1385  // This function is intended to be called after the scanner initialization, so that any field present in the datafile header, similar to
1386  // one in the scanner configuration file, may overload the scanner value.
1387 
1388  // Get maximum ring difference
1389  if (ReadDataASCIIFile(a_pathToDF, "Maximum ring difference", &m_maxRingDiff, 1, KEYWORD_OPTIONAL)==1)
1390  {
1391  Cerr("***** iScannerPET::GetGeometricInfoFromDatafile() -> Error while reading max number of ring difference in the header data file '" << endl);
1392  return 1;
1393  }
1394  // Get bed displacement (possibly overloading the one provided in the scanner configuration file)
1395  if (ReadDataASCIIFile(a_pathToDF, "multiple bed displacement", &m_multiBedDisplacementInMm, 1, KEYWORD_OPTIONAL) == 1)
1396  {
1397  Cerr("***** iScannerPET::GetGeometricInfoFromDatafile() -> An error occurred while trying to read the multiple bed displacement in the datafile header !" << endl);
1398  return 1;
1399  }
1400 
1401  return 0;
1402 }
1403 
1404 // =====================================================================
1405 // ---------------------------------------------------------------------
1406 // ---------------------------------------------------------------------
1407 // =====================================================================
1408 /*
1409  \fn PROJ_GetPETSpecificParameters
1410  \param ap_maxRingDiff
1411  \brief Set pointers passed in argument with the related PET specific variables
1412  This function is used to recover these values in the datafile object
1413  \return 0 if success, positive value otherwise
1414 */
1416 {
1418 
1419  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerPET::PROJ_GetPETSpecificParameters ..." << endl);
1420 
1421  // Verify that all parameters have been correctly checked
1423  {
1424  Cerr("***** iScannerPET::PROJ_GetPETSpecificParameters() -> Parameters have not been checked !" << endl);
1425  return 1;
1426  }
1427 
1428  *ap_maxRingDiff = m_maxRingDiff;
1429  return 0;
1430 }
1431 
1432 
1433 
1434 
1435 // =====================================================================
1436 // ---------------------------------------------------------------------
1437 // ---------------------------------------------------------------------
1438 // =====================================================================
1439 /*
1440  \fn ShowHelp
1441  \brief Display help
1442  \todo Provide informations about PET system initialization ?
1443 */
1445 {
1447  cout << "This scanner class is dedicated to the description of PET systems." << endl;
1448 }
int GetPositionWithRandomDepth(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3])
Get the positions and orientations of scanner elements from their indices, with a random depth...
#define VERBOSE_DEBUG_MAX
int CheckParameters()
Check if all parameters have been correctly initialized.
Definition: iScannerPET.cc:250
int GetRingID(int a_idx)
Get the ring which the #a_idx crystal belongs to.
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
static sRandomNumberGenerator * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
#define VERBOSE_DEBUG_EVENT
FLTNB * mp_sizeCrystalAxial
Definition: iScannerPET.hh:263
int Instantiate(bool a_scannerFileIsLUT)
Get mandatory informations from the scanner file and allocate memory for the member variables...
Definition: iScannerPET.cc:83
int IsAvailableLOR(int a_elt1, int a_elt2)
Check if the LOR formed by the crystalf whose indices are passed in parameters is available according...
#define FLTNB
Definition: gVariables.hh:55
double GenerateRdmNber()
Generate a random number for the thread whose index is recovered from the ompenMP function...
FLTNB * mp_crystalOrientationX
Definition: iScannerPET.hh:258
int Initialize()
Check general initialization and set several parameters to their default value.
Definition: iScannerPET.cc:339
int GetTwoCorners(int a_index1, int a_index2, FLTNB ap_CornerInf1[3], FLTNB ap_CornerSup1[3], FLTNB ap_CornerInf2[3], FLTNB ap_CornerSup2[3])
Get the cartesian coordinaters of the two opposite corners of a scanner element.
FLTNB m_minAngleDifference
Definition: iScannerPET.hh:267
FLTNB * mp_crystalOrientationY
Definition: iScannerPET.hh:259
oMatrix * mp_positionMatrix_out
Definition: vScanner.hh:368
#define VERBOSE_DETAIL
#define GEO_ROT_CCW
Definition: vScanner.hh:30
FLTNB * mp_meanDepthOfInteraction
Definition: iScannerPET.hh:265
string GetPathToScannerFile()
int SetMatriceElt(uint16_t l, uint16_t c, FLTNBLUT a_val)
Set the matrix element corresponding to the argument indices with the provided value.
Definition: oMatrix.cc:127
int GetRdmPositionsAndOrientations(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3], FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3])
Get random positions of the scanner elements and their orientations from their indices.
#define SCANNER_PET
FLTNB * mp_crystalOrientationZ
Definition: iScannerPET.hh:260
string GetFileFromPath(const string &a_pathToFile)
Simply return the file from a path string passed in parameter.
Definition: gOptions.cc:1119
Declaration of class iScannerPET.
FLTNB * mp_crystalCentralPositionZ
Definition: iScannerPET.hh:256
FLTNB m_multiBedDisplacementInMm
Definition: vScanner.hh:370
oMatrix * mp_rotationMatrix
Definition: vScanner.hh:366
#define Cerr(MESSAGE)
#define VERBOSE_DEBUG_LIGHT
virtual int SetRotDirection(string a_rotDirection)
Set rotation direction of the system.
Definition: vScanner.cc:232
#define FLTNBLUT
Definition: gVariables.hh:61
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:111
Singleton class that Instantiate and initialize the scanner object.
int GetLayer(int a_idx)
Get the layer from which the 'a_index' crystal belongs to.
int GetPositionsAndOrientations(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3], FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3], FLTNB *ap_POI1=NULL, FLTNB *ap_POI2=NULL)
Get the central positions and orientations of the scanner elements from their indices.
Definition: iScannerPET.cc:957
Declaration of class sScannerManager.
int Multiplication(oMatrix *a_Mtx, oMatrix *a_MtxResult)
Multiply the member matrix with the matrix provided in 1st parameter Return the result in the matric ...
Definition: oMatrix.cc:173
int m_verbose
Definition: vScanner.hh:364
#define VERBOSE_NORMAL
bool SaveLUTFlag()
Get flag indicating a LUT generated by a geom file should be written on disk or not.
FLTNB * mp_sizeCrystalDepth
Definition: iScannerPET.hh:264
bool m_allParametersChecked
Definition: vScanner.hh:365
#define KEYWORD_MANDATORY
Definition: gOptions.hh:25
int GetGeometricInfoFromDatafile(string a_pathToDataFilename)
Retrieve PET geometric informations from the datafile.
#define KEYWORD_OPTIONAL
Definition: gOptions.hh:27
FLTNB * mp_crystalCentralPositionY
Definition: iScannerPET.hh:255
Singleton class that generate a thread-safe random generator number for openMP As singleton...
int m_scannerType
Definition: vScanner.hh:363
int LoadLUT()
Load a precomputed scanner LUT.
Definition: iScannerPET.cc:374
iScannerPET()
iScannerPET constructor. Initialize the member variables to their default values. ...
Definition: iScannerPET.cc:28
Declaration of class sOutputManager.
void ShowHelp()
Display help.
oMatrix * mp_positionMatrix_ref
Definition: vScanner.hh:367
Structure designed for basic matrices operations.
Definition: oMatrix.hh:20
FLTNB * mp_crystalCentralPositionX
Definition: iScannerPET.hh:254
FLTNB * mp_sizeCrystalTrans
Definition: iScannerPET.hh:262
#define DEBUG_VERBOSE(IGNORED1, IGNORED2)
~iScannerPET()
iScannerPET destructor.
Definition: iScannerPET.cc:60
#define Cout(MESSAGE)
int PROJ_GetPETSpecificParameters(int *ap_maxRingDiff)
Set pointers passed in argument with the related PET specific variables This function is used to reco...
int BuildLUT(bool a_scannerFileIsLUT)
Call the functions to generate the LUT or read the user-made LUT depending on the user choice...
Definition: iScannerPET.cc:208
int m_rotDirection
Definition: vScanner.hh:369
#define GEO_ROT_CW
Definition: vScanner.hh:28
Generic class for scanner objects.
Definition: vScanner.hh:48
int * mp_nbCrystalsInLayer
Definition: iScannerPET.hh:252
int * mp_nbRings
Definition: iScannerPET.hh:251
bool FLTNBIsEqual(FLTNB a, FLTNB b, FLTNB a_eps)
Comparison of FLTNB numbers.
Definition: gOptions.cc:1184
int ComputeLUT()
Compute the LUT of the scanner from a generic (.geom) file.
Definition: iScannerPET.cc:455