CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
iScannerSPECTConv.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 
30 #include "iScannerSPECTConv.hh"
31 #include "sOutputManager.hh"
32 #include "sScannerManager.hh"
33 
34 // =====================================================================
35 // ---------------------------------------------------------------------
36 // ---------------------------------------------------------------------
37 // =====================================================================
38 
40 {
41  // Set member variables to default values
43  m_nbCrystals = -1;
44  m_nbHeads = -1;
45  m_acquisitionZoom = 1.;
46  m_nbPixelsTrans = 0;
47  m_pixelsSizeTrans = -1.;
48  m_gapSizeTrans = -1.;
49  m_nbPixelsAxial = 0;
50  m_pixelsSizeAxial = -1.;
51  m_gapSizeAxial = -1.;
52  m_vPixelsSizeTrans = -1.;
53  m_vPixelsSizeAxial = -1.;
54  m_vNbPixelsTrans = 0;
55  m_vNbPixelsAxial = 0;
56  mp_nbOfBins[0] = 0;
57  mp_nbOfBins[1] = 0;
58  m_crystalDepth = -1.;
59  mp_focalModelTrans = NULL;
60  mp_nbCoefModelTrans = NULL;
62  mp_focalModelAxial = NULL;
63  mp_nbCoefModelAxial = NULL;
66  mp_projectionAngles = NULL;
67  mp_radius = NULL;
73  mp_crystalOrientationX = NULL;
74  mp_crystalOrientationY = NULL;
75  mp_crystalOrientationZ = NULL;
79 }
80 
81 // =====================================================================
82 // ---------------------------------------------------------------------
83 // ---------------------------------------------------------------------
84 // =====================================================================
85 
87 {
90 
92  {
93  for ( int i = 0; i < m_nbHeads; ++i )
94  delete m2p_transFocalParameters[ i ];
95  delete[] m2p_transFocalParameters;
96  }
97 
100 
102  {
103  for ( int i = 0; i < m_nbHeads; ++i )
104  delete m2p_axialFocalParameters[ i ];
105  delete[] m2p_axialFocalParameters;
106  }
107 
109  if (mp_radius) delete[] mp_radius;
111 
115 
119 
123 }
124 
125 
126 
127 
128 // =====================================================================
129 // ---------------------------------------------------------------------
130 // ---------------------------------------------------------------------
131 // =====================================================================
137 {
139  if (m_verbose==0) return;
140 
141  // Describe the scanner
142  Cout("iScannerCT::DescribeSpecific() -> Here is some specific content of the SPECT convergent camera" << endl);
143 
144 
145  Cout(" --> Total number of heads: " << m_nbHeads << endl);
146  Cout(" --> Total number of crystals/pixels: " << m_nbCrystals << endl);
147  Cout(" --> Total number of projections: " << m_nbOfProjections << endl);
148  Cout(" --> Total number of transaxial bins: " << mp_nbOfBins[0] << endl);
149  Cout(" --> Total number of axial bins: " << mp_nbOfBins[1] << endl);
150 
151 
152  if( mp_projectionAngles )
153  {
154  int pr=0;
155  int32_t nb_projections_by_head = m_nbOfProjections/m_nbHeads;
156 
157  Cout(" --> Projection angles (degree): "<< endl);
158 
159  for (int h=0 ; h<m_nbHeads ; h++)
160  {
161  Cout(" Head #"<<h<<": "<< endl);
162 
163  // Display ten by ten
164  if( (nb_projections_by_head) > 10)
165  {
166  while ( pr< (h+1)*nb_projections_by_head-1
167  && pr< ( m_nbOfProjections-1 ) )
168  {
169  for (int p=0 ; p<10 ; p++)
170  {
171  if(pr == (h+1)*nb_projections_by_head
172  || pr == m_nbOfProjections)
173  break;
174  Cout(mp_projectionAngles[pr] << ", ");
175  pr++;
176  }
177  Cout(endl);
178  }
179  }
180  else
181  {
182  for (int p = h*nb_projections_by_head ; p<(h+1)*nb_projections_by_head ; p++)
183  Cout(mp_projectionAngles[p] << ", ");
184  }
185  }
186  Cout(endl);
187  }
188 
190  {
191  int pr=0;
192  int32_t nb_projections_by_head = m_nbOfProjections/m_nbHeads;
193 
194  Cout(" --> Distance between the center of rotation and the detector surface: "<< endl);
195 
196  for (int h=0 ; h<m_nbHeads ; h++)
197  {
198  Cout(" Head #"<<h<<": "<< endl);
199 
200  // Display ten by ten
201  if( (nb_projections_by_head) > 10)
202  {
203  while ( pr< (h+1)*nb_projections_by_head-1
204  && pr< ( m_nbOfProjections-1 ) )
205  {
206  for (int p=0 ; p<10 ; p++)
207  {
208  if(pr == (h+1)*nb_projections_by_head
209  || pr == m_nbOfProjections)
210  break;
211  Cout(mp_CORtoDetectorDistance[pr] << ", ");
212  pr++;
213  }
214  Cout(endl);
215  }
216  }
217  else
218  {
219  for (int p = h*nb_projections_by_head ; p<(h+1)*nb_projections_by_head ; p++)
220  Cout(mp_CORtoDetectorDistance[p] << ", ");
221  }
222  }
223  Cout(endl);
224  }
225 
226 
227  if( mp_radius )
228  {
229  // Display ten by ten
230  Cout(" --> Default radius for each head: " << endl);
231  for (int h=0 ; h<m_nbHeads ; h++)
232  Cout(" For head #"<<h<<": " << mp_radius[h] << endl);
233  }
234 
235 
236  Cout(" --> Total number of transaxial pixels as defined in the system file: " << m_nbPixelsTrans << endl);
237  Cout(" --> Size of transaxial pixels as defined in the system file: " << m_pixelsSizeTrans << endl);
238  Cout(" --> Gap size between each transaxial pixe as defined in the system file: " << m_gapSizeTrans << endl);
239  Cout(" --> Total number of axial pixels as defined in the system file: " << m_nbPixelsAxial << endl);
240  Cout(" --> Size of axial pixels as defined in the system file: " << m_pixelsSizeAxial << endl);
241  Cout(" --> Gap size between each axial pixel as defined in the system file: " << m_gapSizeAxial << endl);
242  Cout(" --> Number of transaxial virtual pixels (pixels actually used in reconstruction): " << m_vNbPixelsTrans << endl);
243  Cout(" --> Number of axial virtual pixels (pixels actually used in reconstruction): " << m_vNbPixelsAxial << endl);
244  Cout(" --> Transaxial size of virtual pixels (pixels actually used in reconstruction): " << m_vPixelsSizeTrans << endl);
245  Cout(" --> Axial size of virtual pixels (pixels actually used in reconstruction): " << m_vPixelsSizeAxial << endl);
246 
247  Cout(" --> Focal models parameters: "<< endl);
248 
249  for (int h=0 ; h<m_nbHeads ; h++)
250  {
251  Cout(" For head #"<<h<<":" << endl);
252  if( mp_focalModelTrans ) Cout(" Transaxial focal model: " << mp_focalModelTrans[h] << endl);
253  if( mp_nbCoefModelTrans )
254  {
255  Cout(" Number of coefficients: " << ( uint16_t ) mp_nbCoefModelTrans[h] << endl);
256  Cout(" Coefficients: ");
257  for( int p=0 ; p<mp_nbCoefModelTrans[h] ; p++)
258  if( m2p_transFocalParameters[h] ) Cout( m2p_transFocalParameters[h][p] << ", " << endl);
259  }
260 
261  if( mp_focalModelAxial ) Cout(" Axial focal model: " << mp_focalModelAxial[h] << endl);
262  if( mp_nbCoefModelAxial )
263  {
264  Cout(" Number of coefficients: " << ( uint16_t ) mp_nbCoefModelAxial[h] << endl);
265  Cout(" Coefficients: ");
266  for( int p=0 ; p<mp_nbCoefModelAxial[h] ; p++)
267  if( m2p_axialFocalParameters[h] ) Cout( m2p_axialFocalParameters[h][p] << ", " << endl);
268  }
269  Cout(endl);
270  }
271 }
272 
273 
274 
275 
276 // =====================================================================
277 // ---------------------------------------------------------------------
278 // ---------------------------------------------------------------------
279 // =====================================================================
280 
281 int iScannerSPECTConv::Instantiate(bool a_scannerFileIsLUT)
282 {
284 
285  // Verbose
286  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerSPECTConv::Instantiate() -> Create scanner structure and read parameters from configuration file"<< endl);
287 
288  // Get scanner manager
289  sScannerManager* p_scannerManager;
290  p_scannerManager = sScannerManager::GetInstance();
291 
292  // Get the number of heads in the scanner
293  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "number of detector heads", &m_nbHeads, 1, KEYWORD_MANDATORY))
294  {
295  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the number of SPECT heads !" << endl);
296  return 1;
297  }
298 
299  // Allocating memories that depend on number of heads
300  mp_radius = new FLTNB[m_nbHeads];
301  mp_focalModelTrans = new string[ m_nbHeads ];
302  mp_focalModelAxial = new string[ m_nbHeads ];
303  mp_nbCoefModelTrans = new uint8_t[ m_nbHeads ];
304  mp_nbCoefModelAxial = new uint8_t[ m_nbHeads ];
307 
308  // Get default radius
309  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "scanner radius", mp_radius, m_nbHeads, KEYWORD_MANDATORY))
310  {
311  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the number of SPECT heads !" << endl);
312  return 1;
313  }
314  for (int i = 0; i < m_nbHeads; i++) if (mp_radius[i]<=0.)
315  {
316  Cerr("***** iScannerSPECTConv::Instantiate() -> Scanner radius <= 0. ? really ? :) ... " << endl);
317  return 1;
318  }
319  // Detector dimensions
320  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans number of pixels", &m_nbPixelsTrans, 1, KEYWORD_MANDATORY))
321  {
322  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the transaxial number of pixels !" << endl);
323  return 1;
324  }
325  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans pixel size", &m_pixelsSizeTrans, 1, KEYWORD_MANDATORY))
326  {
327  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the transaxial pixel size !" << endl);
328  return 1;
329  }
330  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial number of pixels", &m_nbPixelsAxial, 1, KEYWORD_MANDATORY))
331  {
332  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the axial number of pixels !" << endl);
333  return 1;
334  }
335  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial pixel size", &m_pixelsSizeAxial, 1, KEYWORD_MANDATORY))
336  {
337  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the axial pixel size !" << endl);
338  return 1;
339  }
340  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "detector depth", &m_crystalDepth, 1, KEYWORD_MANDATORY))
341  {
342  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read detector depth !" << endl);
343  return 1;
344  }
345 
346  // Head strings to get information from scanner configuration file
347  string *head_name = new string[ m_nbHeads + 1 ];
348  for ( int i = 0; i < m_nbHeads; i++ )
349  {
350  ostringstream oss( ostringstream::out );
351  oss << "head" << i+1;
352  head_name[ i ] = oss.str();
353  }
354  head_name[ m_nbHeads ] = "eof";
355 
356  // Read informations about SPECT heads
357  for ( int i = 0; i < m_nbHeads; i++ )
358  {
359  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans focal model", &mp_focalModelTrans[ i ], 1, KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ]))
360  {
361  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read transaxial focal model of head " << i << " !" << endl);
362  return 1;
363  }
364  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans number of coef model", &mp_nbCoefModelTrans[ i ], 1, KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ]))
365  {
366  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read transaxial number of coef model of head " << i << " !" << endl);
367  return 1;
368  }
369  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial focal model", &mp_focalModelAxial[ i ], 1, KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ]))
370  {
371  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read axial focal model of head " << i << " !" << endl);
372  return 1;
373  }
374  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial number of coef model", &mp_nbCoefModelAxial[ i ], 1, KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ]))
375  {
376  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read axial number of coef model of head " << i << " !" << endl);
377  return 1;
378  }
381  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans parameters", m2p_transFocalParameters[i], mp_nbCoefModelTrans[ i ], KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ]))
382  {
383  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read transaxial model parameters of head " << i << " !" << endl);
384  return 1;
385  }
386  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial parameters", m2p_axialFocalParameters[i], mp_nbCoefModelAxial[ i ], KEYWORD_MANDATORY, head_name[ i ], head_name[ i + 1 ]))
387  {
388  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read axial model parameters of head " << i << " !" << endl);
389  return 1;
390  }
391  }
392 
393  // Bed displacement
395  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "multiple bed displacement", &m_defaultBedDisplacementInMm, 1, KEYWORD_OPTIONAL) == 1)
396  {
397  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the multiple bed displacement in the scanner header file !" << endl);
398  return 1;
399  }
400 
401  // Delete heads strings
402  delete[] head_name;
403 
404  // End
405  return 0;
406 }
407 
408 // =====================================================================
409 // ---------------------------------------------------------------------
410 // ---------------------------------------------------------------------
411 // =====================================================================
412 /*
413  \fn BuildLUT
414  \param a_scannerFileIsLUT : boolean indicating if the file describing
415  the SPECT camera is a generic file (0) or custom Look-up-table (1)
416  \brief Call the functions to generate the LUT or read the user-made LUT depending on the user choice
417  \return 0 if success, positive value otherwise
418 */
419 int iScannerSPECTConv::BuildLUT(bool a_scannerFileIsLUT)
420 {
422 
423  // Verbose
424  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerSPECTConv::BuildLUT -> Build LUT for scanner elements coordinates and orientations"<< endl);
425 
426  // Initialize nb_crystals
427  if (mp_nbOfBins[0] == 0 && mp_nbOfBins[1] == 0) // Number of bins not initialized in the datafile, then we read the number of crystals from the geom file
428  {
429  if (m_nbPixelsTrans==0 || m_nbPixelsAxial==0)
430  {
431  Cerr("***** iScannerSPECTConv::BuildLUT() -> Transaxial or axial number of pixels in the geometric file should be >0 !" << endl);
432  return 1;
433  }
435  }
436  else // Initialization with number of bins
437  {
439  }
440 
450 
451  // Either generate the LUT from a generic file, or load the user precomputed LUT
452  if (!a_scannerFileIsLUT)
453  {
454  if (ComputeLUT())
455  {
456  Cerr("***** iScannerSPECTConv::BuildLUT() -> A problem occurred while generating scanner LUT !" << endl);
457  return 1;
458  }
459  }
460  else
461  {
462  if (LoadLUT())
463  {
464  Cerr("***** iScannerSPECTConv::BuildLUT() -> A problem occurred while loading scanner LUT !" << endl);
465  return 1;
466  }
467  }
468 
469  // End
470  return 0;
471 }
472 
473 // =====================================================================
474 // ---------------------------------------------------------------------
475 // ---------------------------------------------------------------------
476 // =====================================================================
477 /*
478  \fn CheckParameters
479  \brief Check that all parameters have been correctly initialized.
480  \return 0 if success, positive value otherwise
481  \todo Keep the check on crystal depth ?
482 */
484 {
486 
487  // Check if all parameters have been correctly initialized. Return error otherwise
488  if (m_nbCrystals<0)
489  {
490  Cerr("***** iScannerSPECTConv::CheckParameters()-> Number of crystals has not been initialized !" <<endl);
491  return 1;
492  }
493  if (m_nbHeads<0)
494  {
495  Cerr("***** iScannerSPECTConv::CheckParameters()-> Number of heads in the SPECT system has not been initialized !" <<endl);
496  return 1;
497  }
498  if (m_nbOfProjections<=0)
499  {
500  Cerr("***** iScannerSPECTConv::CheckParameters()-> Number of projection angles has not been initialized !" <<endl);
501  return 1;
502  }
503  if (m_nbPixelsTrans<=0 || m_nbPixelsAxial<=0 )
504  {
505  Cerr("***** iScannerSPECTConv::CheckParameters()-> Number of transaxial/axial pixels have not correctly been initialized ! (should be >0)" <<endl);
506  return 1;
507  }
508  if (m_pixelsSizeTrans<=0 || m_pixelsSizeAxial<=0 )
509  {
510  Cerr("***** iScannerSPECTConv::CheckParameters()-> Transaxial/axial pixel sizes have not correctly been initialized ! (should be >0)" <<endl);
511  return 1;
512  }
513  if (m_vNbPixelsTrans<=0 || m_vNbPixelsAxial<=0 )
514  {
515  Cerr("***** iScannerSPECTConv::CheckParameters()-> Transaxial/axial number of virtual pixels have not correctly been initialized ! (should be >0)" <<endl);
516  return 1;
517  }
519  {
520  Cerr("***** iScannerSPECTConv::CheckParameters()-> Transaxial/axial virtual pixel sizes have not correctly been initialized ! (should be >0)" <<endl);
521  return 1;
522  }
523  if (m_gapSizeTrans<0 || m_gapSizeAxial<0 )
524  {
525  Cerr("***** iScannerSPECTConv::CheckParameters()-> Transaxial/axial pixel gap sizes have not correctly been initialized ! (should be >0)" <<endl);
526  return 1;
527  }
528  // todo : maybe delete this as we don't used it
529  if (m_crystalDepth<=0)
530  {
531  Cerr("***** iScannerSPECTConv::CheckParameters()-> Crystal depth has not correctly been initialized ! (should be >0)" <<endl);
532  return 1;
533  }
534  if (mp_focalModelTrans == NULL || mp_focalModelAxial == NULL)
535  {
536  Cerr("***** iScannerSPECTConv::CheckParameters()-> Transaxial/axial focal models have not correctly been initialized !" <<endl);
537  return 1;
538  }
539  if (mp_nbCoefModelTrans == NULL || mp_nbCoefModelAxial == NULL)
540  {
541  Cerr("***** iScannerSPECTConv::CheckParameters()-> Number of coefficients for the transaxial/axial focal models have not correctly been initialized !" <<endl);
542  return 1;
543  }
544  if (m2p_transFocalParameters == NULL || m2p_axialFocalParameters == NULL)
545  {
546  Cerr("***** iScannerSPECTConv::CheckParameters()-> Parameters for the transaxial/axial focal models have not correctly been initialized !" <<endl);
547  return 1;
548  }
549  if (mp_projectionAngles == NULL)
550  {
551  Cerr("***** iScannerSPECTConv::CheckParameters()-> Projection angles have not correctly been initialized !" <<endl);
552  return 1;
553  }
554  if (mp_radius == NULL)
555  {
556  Cerr("***** iScannerSPECTConv::CheckParameters()-> Default scanner radius for each detector heads (scanner file) not correctly initialized !" <<endl);
557  return 1;
558  }
559  if (mp_CORtoDetectorDistance == NULL)
560  {
561  Cerr("***** iScannerSPECTConv::CheckParameters()-> Distances between center of rotation and each detector heads have not correctly been initialized !" <<endl);
562  return 1;
563  }
564  if (mp_crystalCentralPositionX == NULL ||
565  mp_crystalCentralPositionY == NULL ||
567  {
568  Cerr("***** iScannerSPECTConv::CheckParameters()-> LUT elements (crystal central positions) have not correctly been initialized !" <<endl);
569  return 1;
570  }
571  if (mp_crystalOrientationX == NULL ||
572  mp_crystalOrientationY == NULL ||
573  mp_crystalOrientationZ == NULL )
574  {
575  Cerr("***** iScannerSPECTConv::CheckParameters()-> LUT elements (crystal orientations) have not correctly been initialized !" <<endl);
576  return 1;
577  }
578  if (mp_crystalFocalPositionX == NULL ||
579  mp_crystalFocalPositionY == NULL ||
580  mp_crystalFocalPositionZ == NULL )
581  {
582  Cerr("***** iScannerSPECTConv::CheckParameters()-> LUT elements (crystal focal positions) have not correctly been initialized !" <<endl);
583  return 1;
584  }
585  // No need to check mp_nbOfBins, Default values = 0
586 
587  // End
588  m_allParametersChecked = true;
589  return 0;
590 }
591 
592 // =====================================================================
593 // ---------------------------------------------------------------------
594 // ---------------------------------------------------------------------
595 // =====================================================================
596 /*
597  \fn Initialize
598  \brief Check general initialization and set several parameters to their default value
599  \return 0 if success, positive value otherwise
600 */
602 {
604 
605  // Verbose
606  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerSPECTConv::Initialize() -> Initialize remaining stuff for scanner to be ready"<< endl);
607 
608  // Parameters checked ?
610  {
611  Cerr("***** iScannerSPECTConv::Initialize() -> Parameters have not been checked !" << endl);
612  return 1;
613  }
614 
615  // Any initialization
616 
617  return 0;
618 }
619 
620 // =====================================================================
621 // ---------------------------------------------------------------------
622 // ---------------------------------------------------------------------
623 // =====================================================================
624 /*
625  \fn LoadLUT
626  \brief Load a precomputed scanner LUT
627  \details Read mandatory data from the header of the LUT. Then load the LUT elements for each crystal
628  \todo Not yet implemented
629  \return 0 if success, positive value otherwise
630 */
632 {
634  // This has not been designed yet
635  Cerr("iScannerSPECTConv::LoadLUT() -> Not yet implemented !" << endl);
636  return 1;
637 }
638 
639 // =====================================================================
640 // ---------------------------------------------------------------------
641 // ---------------------------------------------------------------------
642 // =====================================================================
643 /*
644  \fn ComputeLUT
645  \brief Computes the LUT of the scanner from a generic (.geom) file.
646  \details Read mandatory data from the geom file. Then compute the LUT elements for each crystal from the geometry described in the file
647  Compute the look-up-tables of the system containing the locations of the scanner elements center in space and their orientations
648  \todo center of rotation & head first angles : get this from acquisition header file
649  \return 0 if success, positive value otherwise
650 */
652 {
654 
655  if(m_verbose>=VERBOSE_DETAIL) Cout("iScannerSPECTConv::ComputeLUT() -> Start LUT generation" << endl);
656 
657  // Compute LUT according to SPECT camera geom files. Check the Castor presentation file for more details about the related mandatory/optional fields to be read
658 
659  // ============================================================================================================
660  // Parameters declarations
661  // ============================================================================================================
662 
663  // todo center of rotation & head first angles : get this from acquisition header file
664  FLTNB *CORx, *CORy, *CORz;
665  FLTNB *head_angleX, *head_angleY, *head_angleZ;
666 
667  CORx = new FLTNB[m_nbHeads];
668  CORy = new FLTNB[m_nbHeads];
669  CORz = new FLTNB[m_nbHeads];
670  head_angleX = new FLTNB[m_nbHeads];
671  head_angleY = new FLTNB[m_nbHeads];
672  head_angleZ = new FLTNB[m_nbHeads];
673 
674  // for(int hId=0 ; hId<m_nbHeads ; hId++)
675  // todo Initialisation COR and head_angle
676 
677  // ============================================================================================================
678  // Generate the LUT
679  // ============================================================================================================
680 
681  // oMatrix crystal_center_ref will be used to gather cartesian positions of the crystals center (on the surface of the crystals)
682  // in the reference head (directly above isocenter)
683  oMatrix** crystal_center_ref = new oMatrix *[m_nbCrystals];
684  // oMatrix crystal_center_out will be used to recover positions of each crystal after each rotation (each projection angles)
685  oMatrix* crystal_center_out = new oMatrix(3,1);
686 
687  // same method for matrices recovering the positions of focal point
688  oMatrix* focal_projection_position_mtx = new oMatrix(3,3);
689  oMatrix* focal_projection_position_mtx_output = new oMatrix(3,3);
690 
691  for (int c = 0; c<m_nbCrystals; c++)
692  crystal_center_ref[c] = new oMatrix(3,1);
693 
694 
695  for(int i=0 ; i<3 ; i++)
696  {
697  crystal_center_out->SetMatriceElt(i,0,0);
698 
699  for(int j=0 ; j<3 ; j++)
700  {
701  focal_projection_position_mtx->SetMatriceElt(i,j,0);
702  focal_projection_position_mtx_output->SetMatriceElt(i,j,0);
703  }
704  }
705 
706  // Generation of the rotation matrix allowing to compute the position of all the rsectors.
707  oMatrix** rotation_mtx = new oMatrix*[m_nbOfProjections];
708 
709  for(int i=0; i<m_nbOfProjections; i++)
710  {
711  rotation_mtx[i] = new oMatrix(3,3);
712  rotation_mtx[i]->SetMatriceElt(0,0, cos(mp_projectionAngles[i] * M_PI/180.) );
713  rotation_mtx[i]->SetMatriceElt(1,0, sin(mp_projectionAngles[i] * M_PI/180.) );
714  rotation_mtx[i]->SetMatriceElt(2,0,0);
715  rotation_mtx[i]->SetMatriceElt(0,1, -sin(mp_projectionAngles[i] * M_PI/180.) );
716  rotation_mtx[i]->SetMatriceElt(1,1, cos(mp_projectionAngles[i] * M_PI/180.) );
717  rotation_mtx[i]->SetMatriceElt(2,1,0);
718  rotation_mtx[i]->SetMatriceElt(0,2,0);
719  rotation_mtx[i]->SetMatriceElt(1,2,0);
720  rotation_mtx[i]->SetMatriceElt(2,2,1);
721  }
722 
723  // Recover the trans/axial gap size from the geom file
724  sScannerManager* p_scannerManager;
725  p_scannerManager = sScannerManager::GetInstance();
726  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans gap size", &m_gapSizeTrans, 1, KEYWORD_MANDATORY))
727  {
728  Cerr("***** iScannerSPECTConv::ComputeLUT() -> An error occurred while trying to read the transaxial gap size !" << endl);
729  return 1;
730  }
731  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial gap size", &m_gapSizeAxial, 1, KEYWORD_MANDATORY))
732  {
733  Cerr("***** iScannerSPECTConv::ComputeLUT() -> An error occurred while trying to read the axial gap size !" << endl);
734  return 1;
735  }
736  // Check for negative gap sizes
737  if (m_gapSizeTrans<0. || m_gapSizeAxial<0.)
738  {
739  Cerr("***** iScannerSPECTConv::ComputeLUT() -> Crystal gap sizes cannot be negative !" << endl);
740  return 1;
741  }
742 
743  // Number of bins have been recovered from the datafile (>0)
744  if (mp_nbOfBins[0]>0 && mp_nbOfBins[1]>0)
745  {
746  // In this case, we suppose that we have only one monolithic crystal, otherwise, we throw an error
747  if (m_nbPixelsTrans>1 || m_nbPixelsAxial>1)
748  {
749  Cerr("***** iScannerSPECTConv::ComputeLUT() -> Number of bins provided in the datafile header while the scanner is not a monolithic crystal !" << endl);
750  return 1;
751  }
752  // Check also that positive gap sizes is not possible
753  if (m_gapSizeTrans>0. || m_gapSizeAxial>0.)
754  {
755  Cerr("***** iScannerSPECTConv::ComputeLUT() -> Positive crystal gap sizes has no sense with monolithic crystals !" << endl);
756  return 1;
757  }
758  // Check that the zoom is not less than one
759  if (m_acquisitionZoom<1.)
760  {
761  Cerr("***** iScannerSPECTConv::ComputeLUT() -> An acquisition zoom less than 1 has no sense !" << endl);
762  return 1;
763  }
764  // Compute the size of the pseudo crystals from the number of bins and the size of the monolithic crystal
766  m_vPixelsSizeAxial = m_pixelsSizeAxial/(((FLTNB)(mp_nbOfBins[1]))*m_acquisitionZoom);
767  // Set the number of crystals to be used in the reconstruction
770  }
771  // Otherwise, we recover these informations from the geom file, which means that we have an already discretized detector
772  else
773  {
774  // Check that no zoom has been provided, because it makes sense only with monolithic detectors
775  if (m_acquisitionZoom!=1.)
776  {
777  Cerr("***** iScannerSPECTConv::ComputeLUT() -> An acquisition zoom has no sense with pixelated detectors !" << endl);
778  return 1;
779  }
784  }
785 
786  // Generate cartesian coordinates of the crystal centers for the SPECT at the reference position (directly above isocenter)
787  // Starting position of the SPECT camera for all axis
790  // Loop on crystals
791  for ( uint32_t jj = 0; jj < m_vNbPixelsAxial; ++jj )
792  {
793  FLTNB Zcrist = z_start + jj * (m_vPixelsSizeAxial + m_gapSizeAxial);
794  for ( uint32_t ii = 0; ii < m_vNbPixelsTrans; ++ ii )
795  {
796  FLTNB Xcrist = x_start + ii * (m_vPixelsSizeTrans + m_gapSizeTrans);
797  crystal_center_ref[ii + m_vNbPixelsTrans * jj]->SetMatriceElt(0,0,Xcrist);
798  crystal_center_ref[ii + m_vNbPixelsTrans * jj]->SetMatriceElt(1,0, 0); // This value will be set for each projection angles
799  crystal_center_ref[ii + m_vNbPixelsTrans * jj]->SetMatriceElt(2,0,Zcrist);
800  }
801  }
802 
803  // ============================================================================================================
804  // Loop over all the projection angles
805  // Positions of the scanner elements are progressively stored in the LUT file.
806  // ============================================================================================================
807  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Generate positions for each projection angle"<< endl);
808  // Loop on projections
809  for (int a=0 ; a<m_nbOfProjections ; a++)
810  {
811  // Set y-position of the crystal reference matrix according to the distance between SPECT camera and center of rotation of the specific head.(mp_CORtoDetectorDistance)
812  int hId = a*m_nbHeads/m_nbOfProjections;
813  // Get surface crystal position (and not center crystal positions for SPECT), and set the reference crystal matrix
814  FLTNB Ycrist = mp_CORtoDetectorDistance[hId];
815  // Loop on crystals
816  for (int c=0 ; c < m_nbCrystals ; c++)
817  {
818  crystal_center_ref[c]->SetMatriceElt(1,0,Ycrist);
819  // Crystal indexation
820  int cryID = a*m_nbCrystals + c;
821  // Set cartesian coordinates of the crystal surface center positions
822  rotation_mtx[a]->Multiplication(crystal_center_ref[c], crystal_center_out);
823  // Get crystal surface positions
824  mp_crystalCentralPositionX[cryID] = crystal_center_out->GetMatriceElt(0,0);
825  mp_crystalCentralPositionY[cryID] = crystal_center_out->GetMatriceElt(1,0);
826  mp_crystalCentralPositionZ[cryID] = crystal_center_out->GetMatriceElt(2,0);
827  // Compute focal positions
828  if (ComputeFocalPositions(crystal_center_ref[c]->GetMatriceElt(0,0),
829  crystal_center_ref[c]->GetMatriceElt(1,0),
830  crystal_center_ref[c]->GetMatriceElt(2,0),
831  hId,
832  cryID) )
833  {
834  Cerr("***** iScannerSPECTConv::ComputeLUT() -> An error occurred while computing the focal positions ! " << endl);
835  return 1;
836  }
837  // Set elements of the focal matrix
838  focal_projection_position_mtx->SetMatriceElt(0,0,mp_crystalFocalPositionX[cryID] );
839  focal_projection_position_mtx->SetMatriceElt(1,0,mp_crystalFocalPositionY[cryID] );
840  focal_projection_position_mtx->SetMatriceElt(2,0,mp_crystalFocalPositionZ[cryID] );
841  focal_projection_position_mtx->SetMatriceElt(0,1,0);
842  focal_projection_position_mtx->SetMatriceElt(1,1,0);
843  focal_projection_position_mtx->SetMatriceElt(2,1,0);
844  focal_projection_position_mtx->SetMatriceElt(0,2,0);
845  focal_projection_position_mtx->SetMatriceElt(1,2,0);
846  focal_projection_position_mtx->SetMatriceElt(2,2,1);
847  // Rotate and compute projections of focal position for this crystal
848  rotation_mtx[a]->Multiplication(focal_projection_position_mtx, focal_projection_position_mtx_output);
849  mp_crystalFocalPositionX[cryID] = focal_projection_position_mtx_output->GetMatriceElt(0,0);
850  mp_crystalFocalPositionY[cryID] = focal_projection_position_mtx_output->GetMatriceElt(1,0);
851  mp_crystalFocalPositionZ[cryID] = focal_projection_position_mtx_output->GetMatriceElt(2,0);
852  // Get crystal orientations
853  mp_crystalOrientationX[cryID] = rotation_mtx[a]->GetMatriceElt(0,0);
854  mp_crystalOrientationY[cryID] = rotation_mtx[a]->GetMatriceElt(0,1);
855  mp_crystalOrientationZ[cryID] = 0;
856  }
857  }
858 
859  // Delete all temporary tables
860  for (int i=0; i<m_nbOfProjections; i++) delete rotation_mtx[i];
861  delete[] rotation_mtx;
862  for (int c=0; c<m_nbCrystals; c++) delete crystal_center_ref[c];
863  delete[] crystal_center_ref;
864  delete crystal_center_out;
865  delete focal_projection_position_mtx;
866  delete focal_projection_position_mtx_output;
867  delete[] head_angleX;
868  delete[] head_angleY;
869  delete[] head_angleZ;
870  delete[] CORx;
871  delete[] CORy;
872  delete[] CORz;
873 
874  // End
875  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> LUT generation completed" << endl);
876  return 0;
877 }
878 
879 // =====================================================================
880 // ---------------------------------------------------------------------
881 // ---------------------------------------------------------------------
882 // =====================================================================
883 /*
884  \fn ComputeFocalPositions
885  \param a_posX : cartesian position of the crystal on the x-axis
886  \param a_posY : cartesian position of the crystal on the y-axis
887  \param a_posZ : cartesian position of the crystal on the z-axis
888  \param a_headID : head index of the crystal
889  (required to select the related focal model parameters
890  and distance to center of rotation)
891  \param a_cryID : crystal index in the LUT
892  \brief Compute focal positions for a specific crystal ID
893  \details Compute the focal positions using the implemented "constant",
894  "polynomial", "slanthole" focal models related to the gamma cameras
895  The "custom" focal model is dedicated to user-made focal model
896  and should be implemented by the user
897  \return 0 if success, positive value otherwise
898 */
899 int iScannerSPECTConv::ComputeFocalPositions(FLTNB a_posX, FLTNB a_posY, FLTNB a_posZ, int a_headID, int a_cryID)
900 {
902 
903  mp_crystalFocalPositionY[a_cryID] = -a_posY;
904 
905  // ===================================================================
906  // AXIAL FOCAL POSITIONS
907  // ===================================================================
908  // constant
909  if(mp_focalModelAxial[a_headID] == "constant")
910  {
911  mp_crystalFocalPositionZ[a_cryID] = a_posZ;
912  }
913 
914  // polynomial
915  else if(mp_focalModelAxial[a_headID] == "polynomial" )
916  {
917  if (mp_nbCoefModelAxial[a_headID] >= 1 && mp_nbCoefModelAxial[a_headID] <= 3)
918  {
919  FLTNB focal_posZ = 0;
920 
921  for(int coef=0 ; coef<mp_nbCoefModelAxial[a_headID] ; coef++)
922  focal_posZ += m2p_axialFocalParameters[a_headID][coef]*abs(pow(a_posZ,coef));
923 
924  // Compute the projection of the focal position on the Y plane (thales) in order to be sure that the focal positions are not in the field of view (LOR would not go through the whole FOV)
925  mp_crystalFocalPositionZ[a_cryID] = -(2*mp_CORtoDetectorDistance[a_headID]-focal_posZ)*a_posZ/focal_posZ;
926 
927  }
928  else if (mp_nbCoefModelAxial[a_headID] > 3) // Error (nb parameters >3)
929  {
930  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for the axial model should be <4 (max : polynom order 2 = 3 coeffs) ! " << endl);
931  return 1;
932  }
933  else // Error (nb parameters == 0)
934  {
935  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for the axial model should be >0 ('axial number of coef model' in geom file) ! " << endl);
936  return 1;
937  }
938  }
939 
940  // slanthole
941  else if(mp_focalModelAxial[a_headID] == "slanthole" )
942  {
943  if (mp_nbCoefModelAxial[a_headID] == 1)
944  {
945  // Compute the projection of the focal position on the Y plane
946  mp_crystalFocalPositionZ[a_cryID] = -2.*m2p_axialFocalParameters[a_headID][0]*mp_CORtoDetectorDistance[a_headID] + a_posZ;
947  }
948  else // Error (nb parameters != 1)
949  {
950  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for a slanthole model should be equal to 1 (slope) : 'axial number of coef model' in geom file ! " << endl);
951  return 1;
952  }
953  }
954 
955  // custom
956  else if(mp_focalModelAxial[a_headID] == "custom" )
957  {
958  if (mp_nbCoefModelAxial[a_headID] == 1) // Uni-Parameter
959  {
960  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl);
961  return 1;
962  }
963  else if (mp_nbCoefModelAxial[a_headID] > 1) // Multi-Parameters
964  {
965  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl);
966  return 1;
967  }
968  else // Error (nb parameters == 0)
969  {
970  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl);
971  return 1;
972  }
973  }
974  else // Error, unknown model
975  {
976  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, current model " << mp_focalModelAxial[a_headID] << " is unknown !" << endl);
977  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Should be either 'constant' (parallel), 'polynomial', 'hyperbolic, or 'custom'" << endl);
978  return 1;
979  }
980 
981  // ===================================================================
982  // TRANSAXIAL FOCAL POSITIONS
983  // ===================================================================
984  // constant
985  if(mp_focalModelTrans[a_headID] == "constant")
986  {
987  mp_crystalFocalPositionX[a_cryID] = a_posX;
988  }
989 
990  // polynomial
991  else if(mp_focalModelTrans[a_headID] == "polynomial" )
992  {
993  if (mp_nbCoefModelTrans[a_headID] >= 1 && mp_nbCoefModelTrans[a_headID] <= 3)
994  {
995  FLTNB focal_posX = 0;
996 
997  for(int coef=0 ; coef<mp_nbCoefModelTrans[a_headID] ; coef++)
998  focal_posX += m2p_transFocalParameters[a_headID][coef]*abs(pow(a_posX,coef));
999 
1000  // Compute the projection of the focal position on the Y plane (thales) in order to be sure that the focal positions are not in the field of view (LOR would not go through the whole FOV)
1001  mp_crystalFocalPositionX[a_cryID] = -(2*mp_CORtoDetectorDistance[a_headID]-focal_posX)*a_posX/focal_posX;
1002 
1003  }
1004  else if (mp_nbCoefModelTrans[a_headID] > 3) // Error (nb parameters >3)
1005  {
1006  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for the trans model should be <4 (max : polynom order 2 = 3 coeffs) ! " << endl);
1007  return 1;
1008  }
1009  else // Error (nb parameters == 0)
1010  {
1011  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for the axial model should be >0 ('trans number of coef model' in geom file) ! " << endl);
1012  return 1;
1013  }
1014  }
1015 
1016  // slanthole
1017  else if(mp_focalModelTrans[a_headID] == "slanthole" )
1018  {
1019  if (mp_nbCoefModelTrans[a_headID] == 1)
1020  {
1021  // Compute the projection of the focal position on the Y plane
1022  mp_crystalFocalPositionX[a_cryID] = -2.*m2p_transFocalParameters[a_headID][0]*mp_CORtoDetectorDistance[a_headID] + a_posX;
1023  }
1024  else // Error (nb parameters != 1
1025  {
1026  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, the number of coeffs for a slanthole model should be equal to 1 (slope) : 'trans number of coef model' in geom file ! " << endl);
1027  return 1;
1028  }
1029  }
1030 
1031  // custom
1032  else if(mp_focalModelTrans[a_headID] == "custom" )
1033  {
1034  if (mp_nbCoefModelTrans[a_headID] == 1) // Uni-Parameter
1035  {
1036  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl);
1037  return 1;
1038  }
1039  else if (mp_nbCoefModelTrans[a_headID] > 1) // Multi-Parameters
1040  {
1041  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl);
1042  return 1;
1043  }
1044  else // Error (nb parameters == 0)
1045  {
1046  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Custom model should be implemented by the user (in iScannerSPECTConv::ComputeFocalPositions()) ! " << endl);
1047  return 1;
1048  }
1049  }
1050  else // Error, unknown model
1051  {
1052  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Error, current model " << mp_focalModelTrans[a_headID] << " is unknown !" << endl);
1053  Cerr("***** iScannerSPECTConv::ComputeFocalPositions() -> Should be either 'constant' (parallel), 'polynomial', 'hyperbolic, or 'custom'" << endl);
1054  return 1;
1055  }
1056 
1057  return 0;
1058 }
1059 
1060 // =====================================================================
1061 // ---------------------------------------------------------------------
1062 // ---------------------------------------------------------------------
1063 // =====================================================================
1064 
1065 int iScannerSPECTConv::GetPositionsAndOrientations( int a_index1, int a_index2,
1066  FLTNB ap_Position1[3], FLTNB ap_Position2[3],
1067  FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3],
1068  FLTNB* ap_POI1, FLTNB* ap_POI2 )
1069 {
1071 
1072  // SPECT indexes : 1st for the projection, 2nd for the crystal
1073 
1074  // First check projection angle existency
1075  if (a_index1<0 || a_index1>=m_nbOfProjections)
1076  {
1077  Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> Projection index (" << a_index1 << ") out of range [0:" << m_nbOfProjections-1 << "] !" << endl);
1078  return 1;
1079  }
1080 
1081  // Second check crystals existency
1082  if (a_index2<0 || a_index2>=m_nbCrystals)
1083  {
1084  Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> Crystal index (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl);
1085  return 1;
1086  }
1087 
1088  // Get crystal index related to the projection
1089  int index = a_index1*m_nbCrystals + a_index2;
1090 
1091  // Get position of the focal point related to the crystal
1092  ap_Position1[0] = mp_crystalFocalPositionX[index];
1093  ap_Position1[1] = mp_crystalFocalPositionY[index];
1094  ap_Position1[2] = mp_crystalFocalPositionZ[index];
1095 
1096  // todo : Due to the current implementation of SPECT projection, POI
1097  // and DOI are not handled and ignored.
1098  // An error is returned if POI are provided
1099 
1100  // Case when POI is not provided
1101  if (ap_POI2==NULL)
1102  {
1103  //FLTNB depth = mp_meanDepthOfInteraction[GetLayer(a_index2)] - mp_sizeCrystalDepth[GetLayer(a_index2)]/2;
1104  //ap_Position2[0] = mp_crystalCentralPositionX[a_index2] + depth*mp_crystalOrientationX[a_index2];
1105  //ap_Position2[1] = mp_crystalCentralPositionY[a_index2] + depth*mp_crystalOrientationY[a_index2];
1106  //ap_Position2[2] = mp_crystalCentralPositionZ[a_index2] + depth*mp_crystalOrientationZ[a_index2];
1107  ap_Position2[0] = mp_crystalCentralPositionX[index];
1108  ap_Position2[1] = mp_crystalCentralPositionY[index];
1109  ap_Position2[2] = mp_crystalCentralPositionZ[index];
1110  }
1111  // Case when POI[2] is negative (meaning we only have POI[0] or POI[1] specified and to be taken into account)
1112  else if (ap_POI2[2]<0.)
1113  {
1114  Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> POI management not implemented yet for SPECT !" << endl);
1115  return 1;
1116  }
1117  // Case when only the DOI is provided
1118  else if (ap_POI2[0]==0. && ap_POI2[1]==0.)
1119  {
1120  Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> POI management not implemented yet for SPECT !" << endl);
1121  return 1;
1122  }
1123  // Case when the full POI is taken into account
1124  else
1125  {
1126  Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> POI management not implemented yet for SPECT !" << endl);
1127  return 1;
1128  }
1129 
1130  // Get orientations
1131  ap_Orientation1[0] = -mp_crystalOrientationX[a_index2];
1132  ap_Orientation1[1] = -mp_crystalOrientationY[a_index2];
1133  ap_Orientation1[2] = -mp_crystalOrientationZ[a_index2];
1134  ap_Orientation2[0] = mp_crystalOrientationX[a_index2];
1135  ap_Orientation2[1] = mp_crystalOrientationY[a_index2];
1136  ap_Orientation2[2] = mp_crystalOrientationZ[a_index2];
1137 
1138  return 0;
1139 }
1140 
1141 // =====================================================================
1142 // ---------------------------------------------------------------------
1143 // ---------------------------------------------------------------------
1144 // =====================================================================
1145 
1147  FLTNB ap_Position1[3], FLTNB ap_Position2[3],
1148  FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3] )
1149 {
1151 
1152  // SPECT indexes : 1st for the projection, 2nd for the crystal
1153 
1154  // First check projection angle existency
1155  if (a_index1<0 || a_index1>=m_nbOfProjections)
1156  {
1157  Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> Projection index (" << a_index1 << ") out of range [0:" << m_nbOfProjections-1 << "] !" << endl);
1158  return 1;
1159  }
1160 
1161  // Second check crystals existency
1162  if (a_index2<0 || a_index2>=m_nbCrystals)
1163  {
1164  Cerr("***** iScannerSPECTConv::GetPositionsAndOrientations() -> Crystal index (" << a_index2 << ") out of range [0:" << m_nbCrystals-1 << "] !" << endl);
1165  return 1;
1166  }
1167 
1168  // Get crystal index related to the projection
1169  int index = a_index1*m_nbCrystals + a_index2;
1170 
1171  // Get position of the focal point related to the crystal
1172  ap_Position1[0] = mp_crystalFocalPositionX[index];
1173  ap_Position1[1] = mp_crystalFocalPositionY[index];
1174  ap_Position1[2] = mp_crystalFocalPositionZ[index];
1175 
1176  // Get instance of random number generator
1178 
1179  // Get random numbers for the first crystal
1180  FLTNB axial = (p_RNG->GenerateRdmNber()-0.5) * m_pixelsSizeAxial;
1181  FLTNB trans = (p_RNG->GenerateRdmNber()-0.5) * m_pixelsSizeTrans;
1182  // Do not consider random depth (position on the surface of the crystal)
1183  //FLTNB depth = (p_RNG->GenerateRdmNber()-0.5) * m_crystalDepth;
1184 
1185  ap_Position2[0] = mp_crystalCentralPositionX[index] + trans*mp_crystalOrientationY[index] + axial*mp_crystalOrientationX[index]*mp_crystalOrientationZ[index];
1186  ap_Position2[1] = mp_crystalCentralPositionY[index] + trans*mp_crystalOrientationX[index] + axial*mp_crystalOrientationY[index]*mp_crystalOrientationZ[index];
1187  ap_Position2[2] = mp_crystalCentralPositionZ[index] + axial*sqrt(1-mp_crystalOrientationZ[index]*mp_crystalOrientationZ[index]);
1188 
1189  // Get orientations
1190  ap_Orientation1[0] = -1.;
1191  ap_Orientation1[1] = -1.;
1192  ap_Orientation1[2] = -1.;
1193  ap_Orientation2[0] = mp_crystalOrientationX[a_index2];
1194  ap_Orientation2[1] = mp_crystalOrientationY[a_index2];
1195  ap_Orientation2[2] = mp_crystalOrientationZ[a_index2];
1196 
1197  return 0;
1198 }
1199 
1200 // =====================================================================
1201 // ---------------------------------------------------------------------
1202 // ---------------------------------------------------------------------
1203 // =====================================================================
1204 
1205 int iScannerSPECTConv::GetPositionWithRandomDepth(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3])
1206 {
1208  // This function was first implemented for PET testing purpose. Not implemented yet.
1209  Cerr("***** iScannerSPECTConv::GetPositionWithRandomDepth() -> This function was implemented for PET testing purpose. Not implemented for SPECT !" << endl);
1210  return 1;
1211 }
1212 
1213 // =====================================================================
1214 // ---------------------------------------------------------------------
1215 // ---------------------------------------------------------------------
1216 // =====================================================================
1217 
1218 int iScannerSPECTConv::GetTwoCorners(int a_index1, int a_index2,
1219  FLTNB ap_CornerInf1[3], FLTNB ap_CornerSup1[3],
1220  FLTNB ap_CornerInf2[3], FLTNB ap_CornerSup2[3])
1221 {
1223  Cerr("***** iScannerSPECTConv::GetTwoCorners() -> Not implemented yet !" << endl);
1224  return 1;
1225 }
1226 
1227 // =====================================================================
1228 // ---------------------------------------------------------------------
1229 // ---------------------------------------------------------------------
1230 // =====================================================================
1231 
1232 int iScannerSPECTConv::GetEdgesCenterPositions( int a_index1, int a_index2,
1233  FLTNB ap_pos_line_point1[3], FLTNB ap_pos_line_point2[3],
1234  FLTNB ap_pos_point1_x[4], FLTNB ap_pos_point1_y[4], FLTNB ap_pos_point1_z[4],
1235  FLTNB ap_pos_point2_x[4], FLTNB ap_pos_point2_y[4], FLTNB ap_pos_point2_z[4]
1236 )
1237 {
1239  Cerr("***** iScannerSPECTConv::GetEdgesCenterPositions() -> Not implemented yet !" << endl);
1240  return 1;
1241 }
1242 
1243 // =====================================================================
1244 // ---------------------------------------------------------------------
1245 // ---------------------------------------------------------------------
1246 // =====================================================================
1247 /*
1248  \fn GetGeometricInfoFromDataFile
1249  \param a_pathToDF : string containing the path to datafile header
1250  \brief Recover geometric informations specific to the scanner class from the datafile header
1251  \details -Recover nb of bins and projections
1252  -Recover the projection angles from the header
1253  (directly read from the datafile, or extrapolated from a first and last angle)
1254  -Recover the distance between the gamma camera detector surfaces and the center of rotation
1255  (directly read from the datafile, or extracted from the gamma camera configuratino file)
1256  \return 0 if success, positive value otherwise
1257 */
1259 {
1261 
1262  if (m_verbose>=VERBOSE_DETAIL) Cout("iScannerSPECTConv::GetGeometricInfoFromDataFile() -> Specific to SPECT" << endl);
1263 
1264  // This function is intended to be called after the scanner initialization, so that any field present in the datafile header, similar to
1265  // one in the scanner configuration file, may overload the scanner value.
1266 
1267  // Get the number of bins for monolithic detectors
1268  if (ReadDataASCIIFile(a_pathToDF , "Number of bins", mp_nbOfBins, 2, KEYWORD_OPTIONAL) == 1)
1269  {
1270  Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDataFile() -> Error while reading number of bins in the header data file " << endl);
1271  return 1;
1272  }
1273  // Get the acquisition zoom for monolithic detectors
1274  if (ReadDataASCIIFile(a_pathToDF , "Zoom", &m_acquisitionZoom, 1, KEYWORD_OPTIONAL) == 1)
1275  {
1276  Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDataFile() -> Error while reading acquisition zoom in the header data file " << endl);
1277  return 1;
1278  }
1279  // Get the number of projections
1280  if (ReadDataASCIIFile(a_pathToDF , "Number of projections", &m_nbOfProjections, 1, KEYWORD_MANDATORY))
1281  {
1282  Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDataFile() -> Error while reading number of projections in the header data file " << endl);
1283  return 1;
1284  }
1285  // Get the head rotation direction
1286  string rotation_direction = "";
1287  if (ReadDataASCIIFile(a_pathToDF , "Head rotation direction", &rotation_direction, 1, KEYWORD_OPTIONAL) == 1)
1288  {
1289  Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDataFile() -> Error while reading head rotation orientation in the header data file " << endl);
1290  return 1;
1291  }
1292  // Set and check the rotation direction
1293  if (SetRotDirection(rotation_direction) )
1294  {
1295  Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDataFile() ->Error occurred while trying to initialize head rotation orientation " << endl);
1296  return 1;
1297  }
1298 
1299  // Allocate a one-dimensional vector to retrieve angles from the datafile, then copy it in the member variables
1300  FLTNB* angles = new FLTNB[m_nbOfProjections];
1301  FLTNB first_and_last_angles[2] = {-1.,-1.};
1302 
1303  // Two possible initializations for projection angles :
1304  // - All angles are provided with the keyword "Angles"
1305  // - The first and last angles are provided, and the intermediate angles are extrapolated from the number of projections
1306 
1307  // 'First/Last angles' tags : checking issue during data reading/conversion (==1)
1308  if (ReadDataASCIIFile(a_pathToDF, "First and last projection angles", first_and_last_angles, 2, KEYWORD_OPTIONAL) == 1)
1309  {
1310  Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDataFile() -> Error while reading Angle mandatory field in the header data file '" << endl);
1311  return 1;
1312  }
1313 
1314  // Check for 'Projection angles' tag
1315  int rvalue = ReadDataASCIIFile(a_pathToDF, "Projection angles", angles, m_nbOfProjections, KEYWORD_OPTIONAL);
1316 
1317  // Error while reading "Angles" tag (==1)
1318  if(rvalue==1)
1319  {
1320  Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDataFile() -> Error while reading Angles field in the header data file !'" << endl);
1321  return 1;
1322  }
1323 
1324  // Check if information on projection angles has been provided
1325  if ( rvalue>=2 && // "Angles" tag not found
1326  (first_and_last_angles[0] <0 || first_and_last_angles[1] <0) ) // Tags first/last angles not found)
1327  {
1328  Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDataFile() -> No information on projection angles provided in the datafile !'" << endl);
1329  Cerr(" This information should be provided using either the 'Angles' tag, or both 'First angles', 'Last angles' tags !'" << endl);
1330  return 1;
1331  }
1332  else if (rvalue>=2) // "Angles" tag not found, but first angle and last angle provided
1333  {
1334  // Put first and last angles in [0:360[
1335  while (first_and_last_angles[0]>=360.) first_and_last_angles[0] -= 360.;
1336  while (first_and_last_angles[0]<0.) first_and_last_angles[0] += 360.;
1337  while (first_and_last_angles[1]>=360.) first_and_last_angles[1] -= 360.;
1338  while (first_and_last_angles[1]<0.) first_and_last_angles[1] += 360.;
1339  // Rotation direction
1340  FLTNB dir = (m_rotDirection == GEO_ROT_CCW) ? -1. : 1.;
1341  // Compute angle increment
1342  FLTNB angle_increment = dir*(first_and_last_angles[1] - first_and_last_angles[0]);
1343  while (angle_increment>=360.) angle_increment -= 360.;
1344  while (angle_increment<0.) angle_increment += 360.;
1345  angle_increment /= ((FLTNB)(m_nbOfProjections-1));
1346  // Compute all projection angles
1347  for(int a=0 ; a<m_nbOfProjections ; a++)
1348  {
1349  angles[a] = first_and_last_angles[0] + dir * angle_increment * ((FLTNB)a);
1350  while (angles[a]>=360.) angles[a] -= 360.;
1351  while (angles[a]<0.) angles[a] += 360.;
1352  }
1353  // Set the last angle to be sure it is the one requested (rounding errors may change it a bit)
1354  angles[m_nbOfProjections-1] = first_and_last_angles[1];
1355  }
1356  // else : Angles have been recovered using ReadDataASCIIFile() above
1357 
1358  // Instanciate here the projection angles variable
1360 
1361  for(int a=0 ; a<m_nbOfProjections ; a++)
1362  mp_projectionAngles[a] = angles[a];
1363 
1364  // Recover distance between the detectors and scanner center of rotation
1365  // Allocate with the number of projections by default
1367 
1368  // Initialize by default with the scanner radius
1369  for(int hId=0 ; hId<m_nbHeads ; hId++)
1370  for(int a=0 ; a<m_nbOfProjections/m_nbHeads ; a++)
1371  mp_CORtoDetectorDistance[a+hId*m_nbOfProjections/m_nbHeads] = mp_radius[hId];
1372 
1373  // Read datafile value if any. First case : we have a radius specific to each projections
1374  int read_flag = 0;
1375  read_flag = ReadDataASCIIFile(a_pathToDF, "Distance camera surface to COR", mp_CORtoDetectorDistance, m_nbOfProjections, KEYWORD_OPTIONAL);
1376 
1377  if (read_flag==1)
1378  {
1379  // Error during reading
1380  Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDataFile() -> Error while reading the distance between the camera detectors to the center of rotation in the header data file " << endl);
1381  return 1;
1382  }
1383  // Check the second case : we have a global radius for each projection
1384  else if (read_flag==2)
1385  {
1386  read_flag = ReadDataASCIIFile(a_pathToDF, "Global distance camera surface to COR", mp_CORtoDetectorDistance, 1, KEYWORD_OPTIONAL);
1387 
1388  if(read_flag==0)
1389  {
1390  // Field was found : Initialize the distance for each projection angle with the global value
1391  for(int a=1 ; a<m_nbOfProjections ; a++)
1393  }
1394  else if(read_flag==1)
1395  {
1396  // Error during reading
1397  Cerr("***** iScannerSPECTConv::GetGeometricInfoFromDataFile() -> Error while reading the global distance between the camera detectors to the center of rotation in the header data file " << endl);
1398  return 1;
1399  }
1400  else if (read_flag==2)
1401  {
1402  // Initialization with default values from the scanner file
1403  for(int hId=0 ; hId<m_nbHeads ; hId++)
1404  for(int a=0 ; a<m_nbOfProjections/m_nbHeads ; a++)
1405  mp_CORtoDetectorDistance[a+hId*m_nbOfProjections/m_nbHeads] = mp_radius[hId];
1406  }
1407  }
1408 
1409  delete[] angles;
1410 
1411  return 0;
1412 }
1413 
1414 // =====================================================================
1415 // ---------------------------------------------------------------------
1416 // ---------------------------------------------------------------------
1417 // =====================================================================
1418 
1419 int iScannerSPECTConv::GetSPECTSpecificParameters(uint16_t* ap_nbOfProjections,
1420  uint16_t* ap_nbHeads,
1421  FLTNB* ap_acquisitionZoom,
1422  uint16_t* ap_nbOfBins,
1423  FLTNB* ap_pixSizeXY,
1424  FLTNB*& ap_angles,
1425  FLTNB*& ap_CORtoDetectorDistance,
1426  int* ap_headRotDirection)
1427 {
1429  // Verbose
1430  if (m_verbose>=VERBOSE_DETAIL) Cout("iScannerSPECTConv::GetSPECTSpecificParameters ..." << endl);
1431  // Verify that all parameters have been correctly checked
1433  {
1434  Cerr("***** iScannerSPECTConv::GetSPECTSpecificParameters() -> Parameters have not been checked !" << endl);
1435  return 1;
1436  }
1437  // Get them
1438  *ap_nbOfProjections = m_nbOfProjections;
1439  *ap_nbHeads = m_nbHeads;
1440  *ap_acquisitionZoom = m_acquisitionZoom;
1441  ap_nbOfBins[0] = mp_nbOfBins[0];
1442  ap_nbOfBins[1] = mp_nbOfBins[1];
1443  ap_pixSizeXY[0] = m_vPixelsSizeTrans;
1444  ap_pixSizeXY[1] = m_vPixelsSizeAxial;
1445  ap_angles = mp_projectionAngles;
1446  ap_CORtoDetectorDistance = mp_CORtoDetectorDistance;
1447  *ap_headRotDirection = m_rotDirection;
1448  // End
1449  return 0;
1450 }
1451 
1452 // =====================================================================
1453 // ---------------------------------------------------------------------
1454 // ---------------------------------------------------------------------
1455 // =====================================================================
1456 /*
1457  \fn PROJ_SetSPECTAngles
1458  \param ap_projectionAngles : an array containing the projection angles
1459  \brief Set the projection angles with the array provided in parameter
1460  \return 0 if success, positive value otherwise
1461 */
1463 {
1465 
1466  if(m_verbose>=VERBOSE_NORMAL) Cout("iScannerSPECTConv::PROJ_SetSPECTAngles ..." << endl);
1467 
1468  // Check initialization of the number of projections
1469  if(m_nbOfProjections <= 0)
1470  {
1471  Cerr("***** iScannerSPECTConv::PROJ_SetSPECTAngles -> Error number of projection should be >0 ! '" << endl);
1472  return 1;
1473  }
1474  else
1475  {
1477 
1478  for(int a=0 ; a<m_nbOfProjections ; a++)
1479  mp_projectionAngles[a] = ap_projectionAngles[a];
1480  }
1481 
1482  return 0;
1483 }
1484 
1485 // =====================================================================
1486 // ---------------------------------------------------------------------
1487 // ---------------------------------------------------------------------
1488 // =====================================================================
1489 /*
1490  \fn PROJ_SetSPECTCORtoDetectorDistance
1491  \param a_distance
1492  \brief Set distance between the center of rotation and SPECT detectors if arg value>0,
1493  Set with the geometric information in the scanner configuration file otherwise
1494  \return 0 if success, positive value otherwise
1495 */
1497 {
1499 
1500  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerSPECTConv::PROJ_SetSPECTCORtoDetectorDistance ..." << endl);
1501 
1502  // Check initialization of the number of projections
1503  if(m_nbOfProjections <= 0)
1504  {
1505  Cerr("***** iScannerSPECTConv::PROJ_SetSPECTCORtoDetectorDistance -> Error number of projection should be >0 ! '" << endl);
1506  return 1;
1507  }
1508  else
1509  {
1511 
1512  if(a_distance>0)
1513  {
1514  // Set all radius to the provided distance
1515  for(int a=0 ; a<m_nbOfProjections ; a++)
1516  mp_CORtoDetectorDistance[a] = a_distance;
1517  }
1518  else
1519  {
1520  // Set all distance according to scanner geometric informations (default)
1521  for(int hId=0 ; hId<m_nbHeads ; hId++)
1522  for(int a=0 ; a<m_nbOfProjections/m_nbHeads ; a++)
1523  mp_CORtoDetectorDistance[a+hId*m_nbOfProjections/m_nbHeads] = mp_radius[hId];
1524  }
1525  }
1526  return 0;
1527 }
1528 
1529 // =====================================================================
1530 // ---------------------------------------------------------------------
1531 // ---------------------------------------------------------------------
1532 // =====================================================================
1533 /*
1534  \fn PROJ_GetSPECTNbBins
1535  \brief Get the number of SPECT heads in the pointer provided in parameter
1536  \return 0 by default (no error)
1537 */
1538 int iScannerSPECTConv::PROJ_GetSPECTNbBins(uint16_t* ap_nbOfBins)
1539 {
1541  ap_nbOfBins = mp_nbOfBins;
1542  return 0;
1543 }
1544 
1545 // =====================================================================
1546 // ---------------------------------------------------------------------
1547 // ---------------------------------------------------------------------
1548 // =====================================================================
1549 /*
1550  \fn PROJ_SetSPECTNbBins
1551  \param ap_nbOfBins
1552  \brief Set number of bins
1553  \return 0 by default (no error)
1554 */
1555 int iScannerSPECTConv::PROJ_SetSPECTNbBins(uint16_t* ap_nbOfBins)
1556 {
1558  for(int i=0 ; i<2 ; i++)
1559  mp_nbOfBins[i]=ap_nbOfBins[i];
1560  return 0;
1561 }
1562 
1563 // =====================================================================
1564 // ---------------------------------------------------------------------
1565 // ---------------------------------------------------------------------
1566 // =====================================================================
1567 /*
1568  \fn PROJ_SetSPECTNbProjections
1569  \param a_nbOfProjections
1570  \brief Set number of projections
1571  \return 0 by default (no error)
1572 */
1573 int iScannerSPECTConv::PROJ_SetSPECTNbProjections(uint32_t a_nbOfProjections)
1574 {
1576  m_nbOfProjections = a_nbOfProjections;
1577  return 0;
1578 }
1579 
1580 
1581 // =====================================================================
1582 // ---------------------------------------------------------------------
1583 // ---------------------------------------------------------------------
1584 // =====================================================================
1585 /*
1586  \fn ShowHelp
1587  \brief Display help
1588  \todo Provide informations about SPECT system initialization ?
1589 */
1591 {
1593  cout << "This scanner class is dedicated to the description of parallel, convergent and multi-convergent SPECT systems." << endl;
1594 }
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
#define FLTNB
Definition: gVariables.hh:81
FLTNB * mp_crystalCentralPositionX
#define VERBOSE_DETAIL
#define GEO_ROT_CCW
Definition: vScanner.hh:48
int GetSPECTSpecificParameters(uint16_t *ap_nbOfProjections, uint16_t *ap_nbHeads, FLTNB *ap_acquisitionZoom, uint16_t *ap_nbOfBins, FLTNB *ap_pixSizeXY, FLTNB *&ap_angles, FLTNB *&ap_CORtoDetectorDistance, int *ap_headRotDirection)
Set pointers passed in argument with the related SPECT specific variables This function is used to ...
FLTNB m_defaultBedDisplacementInMm
Definition: vScanner.hh:444
FLTNB ** m2p_axialFocalParameters
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...
void DescribeSpecific()
Implementation of the pure virtual eponym function that simply prints info about the scanner...
FLTNB ** m2p_transFocalParameters
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)
This is a pure virtual method that must be implemented by children. Get the central positions and o...
string GetPathToScannerFile()
void ShowHelp()
Display help.
int GetGeometricInfoFromDataFile(string a_pathToDF)
Recover geometric informations specific to the scanner class from the datafile header.
int PROJ_SetSPECTCORtoDetectorDistance(FLTNB a_distance)
Set distance between the center of rotation and SPECT detectors if arg value>0, Set with the geomet...
FLTNB * mp_crystalCentralPositionY
iScannerSPECTConv()
iScannerSPECTConv constructor. Initialize the member variables to their default values.
#define Cerr(MESSAGE)
int PROJ_SetSPECTAngles(FLTNB *ap_projectionAngles)
Set the projection angles with the array provided in parameter.
#define SCANNER_SPECT_CONVERGENT
int ComputeLUT()
Computes the LUT of the scanner from a generic (.geom) file.
#define VERBOSE_DEBUG_LIGHT
virtual int SetRotDirection(string a_rotDirection)
Set rotation direction of the system.
Definition: vScanner.cc:289
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
Singleton class that Instantiate and initialize the scanner object.
Declaration of class sScannerManager.
int m_verbose
Definition: vScanner.hh:437
Declaration of class iScannerSPECTConv.
#define VERBOSE_NORMAL
int BuildLUT(bool a_scannerFileIsLUT)
Call the functions to generate the LUT or read the user-made LUT depending on the user choice...
HPFLTNB GenerateRdmNber()
Generate a random number for the thread which index is recovered from the OpenMP function.
HPFLTNB GetMatriceElt(uint16_t l, uint16_t c)
Definition: oMatrix.cc:165
bool m_allParametersChecked
Definition: vScanner.hh:439
#define KEYWORD_MANDATORY
Definition: gOptions.hh:47
#define KEYWORD_OPTIONAL
Definition: gOptions.hh:49
Singleton class that generate a thread-safe random generator number for openMP As singleton...
int ComputeFocalPositions(FLTNB a_posX, FLTNB a_posY, FLTNB a_posZ, int a_headID, int a_cryID)
Compute focal positions for a specific crystal ID.
int GetEdgesCenterPositions(int a_index1, int a_index2, FLTNB ap_pos_line_point1[3], FLTNB ap_pos_line_point2[3], FLTNB ap_pos_point1_x[4], FLTNB ap_pos_point1_y[4], FLTNB ap_pos_point1_z[4], FLTNB ap_pos_point2_x[4], FLTNB ap_pos_point2_y[4], FLTNB ap_pos_point2_z[4])
Implementation of the pure virtual function from vScanner. Not yet implemented for the SPECT conver...
int m_scannerType
Definition: vScanner.hh:436
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 the focal point and random positions on the crystal surface and its orientations from the event i...
Declaration of class sOutputManager.
int Initialize()
Check general initialization and set several parameters to their default value.
Structure designed for basic matrices operations.
Definition: oMatrix.hh:41
int PROJ_SetSPECTNbBins(uint16_t *ap_nbOfBins)
Set number of bins.
int CheckParameters()
Check that all parameters have been correctly initialized.
int PROJ_SetSPECTNbProjections(uint32_t a_nbOfProjections)
Set number of projections.
FLTNB * mp_crystalCentralPositionZ
#define DEBUG_VERBOSE(IGNORED1, IGNORED2)
int PROJ_GetSPECTNbBins(uint16_t *ap_nbOfBins)
Get the number of SPECT heads in the pointer provided in parameter.
#define Cout(MESSAGE)
int Instantiate(bool a_scannerFileIsLUT)
Get mandatory informations from the scanner file and allocate memory for the member variables...
~iScannerSPECTConv()
iScannerSPECTConv destructor.
int Multiplication(oMatrix *ap_Mtx, oMatrix *ap_MtxResult)
Multiply the member matrix with the matrix provided in 1st parameter Return the result in the matric ...
Definition: oMatrix.cc:184
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.
int m_rotDirection
Definition: vScanner.hh:443
int SetMatriceElt(uint16_t l, uint16_t c, HPFLTNB a_val)
Set the matrix element corresponding to the argument indices with the provided value.
Definition: oMatrix.cc:138
#define GEO_ROT_CW
Definition: vScanner.hh:46
Generic class for scanner objects.
Definition: vScanner.hh:61
int LoadLUT()
Load a precomputed scanner LUT.
#define VERBOSE_DEBUG_NORMAL