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