CASToR  2.0
Tomographic Reconstruction (PET/SPECT/CT)
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
iScannerCT.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 "iScannerCT.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_nbPixels = -1;
45  m_nbPixelsTrans = 0;
46  m_pixelsSizeTrans = -1.;
47  m_gapSizeTrans = -1.;
48  m_nbPixelsAxial = 0;
49  m_pixelsSizeAxial = -1.;
50  m_gapSizeAxial = -1.;
52  mp_projectionAngles = NULL;
56  m_detectorDepth = -1.;
57  m_spotSizeWidth = -1.;
58  m_spotSizeDepth = -1.;
62  mp_crystalOrientationX = NULL;
63  mp_crystalOrientationY = NULL;
64  mp_crystalOrientationZ = NULL;
65  mp_sourcePositionX = NULL;
66  mp_sourcePositionY = NULL;
67  mp_sourcePositionZ = NULL;
68 }
69 
70 // =====================================================================
71 // ---------------------------------------------------------------------
72 // ---------------------------------------------------------------------
73 // =====================================================================
74 
76 {
87 }
88 
89 
90 
91 
92 // =====================================================================
93 // ---------------------------------------------------------------------
94 // ---------------------------------------------------------------------
95 // =====================================================================
101 {
103  if (m_verbose==0) return;
104 
105  // Describe the scanner
106  Cout("iScannerCT::DescribeSpecific() -> Here is some specific content of the CT scanner" << endl);
107  Cout(" --> Total number of pixels: " << m_nbPixels << endl);
108  Cout(" --> Total number of projections: " << m_nbOfProjections << endl);
109  if( mp_projectionAngles )
110  {
111  // Display ten by ten
112  Cout(" --> Projection angles: " << endl);
113  if(m_nbOfProjections > 10)
114  {
115  int pr=0;
116  while ( pr<m_nbOfProjections-1 )
117  {
118  for (int p=0 ; p<10 ; p++)
119  {
120  if(pr == m_nbOfProjections)
121  break;
122  Cout(mp_projectionAngles[pr] << ", ");
123  pr++;
124  }
125  Cout(endl);
126  }
127  }
128  else
129  {
130  for (int p=0 ; p<m_nbOfProjections-1 ; p++)
131  Cout(mp_projectionAngles[p] << ", ");
132  Cout(mp_projectionAngles[m_nbOfProjections-1] << endl);
133  }
134  }
135 
136  Cout(" --> Distance between the center of rotation and the detector surface: " << m_CORtoDetectorDistance << endl);
137  Cout(" --> Distance between the center of rotation and the source surface: " << m_CORtoSourceDistance << endl);
138 
139  Cout(" --> Total number of transaxial pixels as defined in the system file: " << m_nbPixelsTrans << endl);
140  Cout(" --> Size of transaxial pixels as defined in the system file: " << m_pixelsSizeTrans << endl);
141  Cout(" --> Gap size between each transaxial pixe as defined in the system file: " << m_gapSizeTrans << endl);
142  Cout(" --> Total number of axial pixels as defined in the system file: " << m_nbPixelsAxial << endl);
143  Cout(" --> Size of axial pixels as defined in the system file: " << m_pixelsSizeAxial << endl);
144  Cout(" --> Gap size between each axial pixel as defined in the system file: " << m_gapSizeAxial << endl);
145  Cout(" --> Depth of detection pixels: " << m_detectorDepth << endl);
146  Cout(" --> Width of the source, along the direction tangential to the scanner radius: " << m_spotSizeWidth << endl);
147  Cout(" --> Depth of the source, along the direction of the scanner radius: " << m_spotSizeDepth << endl);
148 }
149 
150 
151 
152 // =====================================================================
153 // ---------------------------------------------------------------------
154 // ---------------------------------------------------------------------
155 // =====================================================================
156 
157 int iScannerCT::Instantiate(bool a_scannerFileIsLUT)
158 {
160 
161  // Verbose
162  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerCT::Instantiate() -> Create scanner structure and read parameters from configuration file" << endl);
163 
164  // Get scanner manager
165  sScannerManager* p_scannerManager = sScannerManager::GetInstance();
166 
167  // Detector dimensions
168  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans number of pixels", &m_nbPixelsTrans, 1, KEYWORD_MANDATORY))
169  {
170  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the transaxial number of pixels !" << endl);
171  return 1;
172  }
173  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans pixel size", &m_pixelsSizeTrans, 1, KEYWORD_MANDATORY))
174  {
175  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the transaxial pixel size !" << endl);
176  return 1;
177  }
178  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial number of pixels", &m_nbPixelsAxial, 1, KEYWORD_MANDATORY))
179  {
180  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the axial number of pixels !" << endl);
181  return 1;
182  }
183  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial pixel size", &m_pixelsSizeAxial, 1, KEYWORD_MANDATORY))
184  {
185  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the axial pixel size !" << endl);
186  return 1;
187  }
188  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "detector depth", &m_detectorDepth, 1, KEYWORD_MANDATORY))
189  {
190  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read detector depth !" << endl);
191  return 1;
192  }
193  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "detector radius", &m_CORtoDetectorDistance, 1, KEYWORD_MANDATORY))
194  {
195  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read detector to COR distance as \"detector radius\" !" << endl);
196  return 1;
197  }
198  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "source radius", &m_CORtoSourceDistance, 1, KEYWORD_MANDATORY))
199  {
200  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read source to COR distance as \"source radius\" !" << endl);
201  return 1;
202  }
203  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "spot size width", &m_spotSizeWidth, 1, KEYWORD_MANDATORY))
204  {
205  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the spot size width !" << endl);
206  return 1;
207  }
208  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "spot size height", &m_spotSizeDepth, 1, KEYWORD_MANDATORY))
209  {
210  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the spot size height !" << endl);
211  return 1;
212  }
213 
214  // Bed displacement
216  if (ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "multiple bed displacement", &m_defaultBedDisplacementInMm, 1, KEYWORD_OPTIONAL) == 1)
217  {
218  Cerr("***** iScannerSPECTConv::Instantiate() -> An error occurred while trying to read the multiple bed displacement in the scanner header file !" << endl);
219  return 1;
220  }
221 
222  // End
223  return 0;
224 }
225 
226 // =====================================================================
227 // ---------------------------------------------------------------------
228 // ---------------------------------------------------------------------
229 // =====================================================================
230 
231 int iScannerCT::BuildLUT(bool a_scannerFileIsLUT)
232 {
234 
235  // Verbose
236  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerCT::BuildLUT -> Build LUT for scanner elements coordinates and orientations"<< endl);
237 
238  // Initialize nb_pixels
240 
241  // Allocate LUT structures
251 
252  // Either generate the LUT from a generic file, or load the user precomputed LUT
253  if (!a_scannerFileIsLUT)
254  {
255  if (ComputeLUT())
256  {
257  Cerr("***** iScannerCT::BuildLUT() -> A problem occured while generating scanner LUT !" << endl);
258  return 1;
259  }
260  }
261  else
262  {
263  if (LoadLUT())
264  {
265  Cerr("***** iScannerCT::BuildLUT() -> A problem occured while loading scanner LUT !" << endl);
266  return 1;
267  }
268  }
269 
270  // End
271  return 0;
272 }
273 
274 // =====================================================================
275 // ---------------------------------------------------------------------
276 // ---------------------------------------------------------------------
277 // =====================================================================
278 
280 {
282 
283  // Check if all parameters have been correctly initialized. Return error otherwise
284  if (m_nbPixels<0)
285  {
286  Cerr("***** iScannerCT::CheckParameters()-> Number of crystals has not been initialized !" <<endl);
287  return 1;
288  }
289  if (m_nbOfProjections<=0)
290  {
291  Cerr("***** iScannerCT::CheckParameters()-> Number of projection angles has not been initialized !" <<endl);
292  return 1;
293  }
294  if (m_nbPixelsTrans<=0 || m_nbPixelsAxial<=0)
295  {
296  Cerr("***** iScannerCT::CheckParameters()-> Number of transaxial/axial pixels have not correctly been initialized ! (should be >0)" <<endl);
297  return 1;
298  }
300  {
301  Cerr("***** iScannerCT::CheckParameters()-> Transaxial/axial pixel sizes have not correctly been initialized ! (should be >0)" <<endl);
302  return 1;
303  }
304  if (m_gapSizeTrans<0. || m_gapSizeAxial<0.)
305  {
306  Cerr("***** iScannerCT::CheckParameters()-> Transaxial/axial pixel gap sizes have not correctly been initialized ! (should be >0)" <<endl);
307  return 1;
308  }
309  if (m_detectorDepth<=0)
310  {
311  Cerr("***** iScannerCT::CheckParameters()-> Crystal depth has not correctly been initialized ! (should be >0)" <<endl);
312  return 1;
313  }
314  if (mp_projectionAngles == NULL)
315  {
316  Cerr("***** iScannerCT::CheckParameters()-> Projection angles have not correctly been initialized !" <<endl);
317  return 1;
318  }
319  if (m_CORtoDetectorDistance<=0.)
320  {
321  Cerr("***** iScannerCT::CheckParameters()-> Distance between center of rotation and detector surface has not correctly been initialized !" <<endl);
322  return 1;
323  }
324  if (m_CORtoSourceDistance<=0.)
325  {
326  Cerr("***** iScannerCT::CheckParameters()-> Distance between center of rotation and source surface has not correctly been initialized !" <<endl);
327  return 1;
328  }
329  if (mp_crystalCentralPositionX == NULL ||
330  mp_crystalCentralPositionY == NULL ||
332  {
333  Cerr("***** iScannerCT::CheckParameters()-> LUT elements (crystal central positions) have not correctly been initialized !" <<endl);
334  return 1;
335  }
336  if (mp_crystalOrientationX == NULL ||
337  mp_crystalOrientationY == NULL ||
338  mp_crystalOrientationZ == NULL )
339  {
340  Cerr("***** iScannerCT::CheckParameters()-> LUT elements (crystal orientations) have not correctly been initialized !" <<endl);
341  return 1;
342  }
343  if (mp_sourcePositionX == NULL ||
344  mp_sourcePositionY == NULL ||
345  mp_sourcePositionZ == NULL )
346  {
347  Cerr("***** iScannerCT::CheckParameters()-> LUT elements (crystal focal positions) have not correctly been initialized !" <<endl);
348  return 1;
349  }
350 
351  // End
352  m_allParametersChecked = true;
353  return 0;
354 }
355 
356 // =====================================================================
357 // ---------------------------------------------------------------------
358 // ---------------------------------------------------------------------
359 // =====================================================================
360 
362 {
364 
365  // Verbose
366  if (m_verbose>=VERBOSE_NORMAL) Cout("iScannerCT::Initialize() -> Initialize remaining stuff for scanner to be ready"<< endl);
367 
368  // Parameters checked ?
370  {
371  Cerr("***** iScannerCT::Initialize() -> Parameters have not been checked !" << endl);
372  return 1;
373  }
374 
375  // Any initialization
376 
377  return 0;
378 }
379 
380 // =====================================================================
381 // ---------------------------------------------------------------------
382 // ---------------------------------------------------------------------
383 // =====================================================================
384 
386 {
388  // This has not been designed yet
389  Cerr("iScannerCT::LoadLUT() -> Not yet implemented !" << endl);
390  return 1;
391 }
392 
393 // =====================================================================
394 // ---------------------------------------------------------------------
395 // ---------------------------------------------------------------------
396 // =====================================================================
397 
399 {
401 
402  if(m_verbose>=VERBOSE_DETAIL) Cout("iScannerCT::ComputeLUT() -> Start LUT generation" << endl);
403 
404  // Use multi-threading here as the rotation of the pixels and source can take some times if many projection angles are used
405  int nb_threads = 1;
406  if (mp_ID!=NULL) nb_threads = mp_ID->GetNbThreadsMax();
407  #ifdef CASTOR_OMP
408  omp_set_num_threads(nb_threads);
409  #endif
410 
411  // ============================================================================================================
412  // Generate the LUT
413  // ============================================================================================================
414 
415  // oMatrix crystal_center_ref will be used to gather cartesian positions of the crystals center (on the surface of the crystals)
416  // in the reference head (directly above isocenter = +y). Same for the source reference position but below the isocenter ;-)
417  oMatrix** crystal_center_ref = new oMatrix *[m_nbPixels];
418  for (int c = 0; c<m_nbPixels; c++)
419  crystal_center_ref[c] = new oMatrix(3,1);
420  oMatrix* source_center_ref = new oMatrix(3,1);
421  // oMatrix crystal_center_out will be used to recover positions of each crystal after each rotation (each projection angles).
422  // Same for the source_center_out
423  oMatrix** crystal_center_out = new oMatrix *[nb_threads];
424  oMatrix** source_center_out = new oMatrix *[nb_threads];
425  for (int th=0; th<nb_threads; th++)
426  {
427  crystal_center_out[th] = new oMatrix(3,1);
428  source_center_out[th] = new oMatrix(3,1);
429  }
430 
431  // Generation of the rotation matrix allowing to compute the position of all the projections.
432  oMatrix** rotation_mtx = new oMatrix*[m_nbOfProjections];
433  for(int i=0; i<m_nbOfProjections; i++)
434  {
435  rotation_mtx[i] = new oMatrix(3,3);
436  rotation_mtx[i]->SetMatriceElt(0,0, cos(mp_projectionAngles[i] * M_PI/180.) );
437  rotation_mtx[i]->SetMatriceElt(1,0, sin(mp_projectionAngles[i] * M_PI/180.) );
438  rotation_mtx[i]->SetMatriceElt(2,0,0);
439  rotation_mtx[i]->SetMatriceElt(0,1, -sin(mp_projectionAngles[i] * M_PI/180.) );
440  rotation_mtx[i]->SetMatriceElt(1,1, cos(mp_projectionAngles[i] * M_PI/180.) );
441  rotation_mtx[i]->SetMatriceElt(2,1,0);
442  rotation_mtx[i]->SetMatriceElt(0,2,0);
443  rotation_mtx[i]->SetMatriceElt(1,2,0);
444  rotation_mtx[i]->SetMatriceElt(2,2,1);
445  }
446 
447  // Recover the trans/axial gap size from the geom file
448  sScannerManager* p_scannerManager = sScannerManager::GetInstance();
449  if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "trans gap size", &m_gapSizeTrans, 1, KEYWORD_MANDATORY))
450  {
451  Cerr("***** iScannerCT::ComputeLUT() -> An error occurred while trying to read the transaxial gap size !" << endl);
452  return 1;
453  }
454 
455  if(ReadDataASCIIFile(p_scannerManager->GetPathToScannerFile(), "axial gap size", &m_gapSizeAxial, 1, KEYWORD_MANDATORY))
456  {
457  Cerr("***** iScannerCT::ComputeLUT() -> An error occurred while trying to read the axial gap size !" << endl);
458  return 1;
459  }
460 
461  // Generate cartesian coordinates of the pixels centers for the CT at the reference position (directly above isocenter = +y)
464  for(uint32_t jj = 0; jj < m_nbPixelsAxial; ++jj )
465  {
466  FLTNB Zcryst = z_start + jj * (m_pixelsSizeAxial + m_gapSizeAxial);
467  for(uint32_t ii = 0; ii < m_nbPixelsTrans; ++ ii )
468  {
469  FLTNB Xcryst = x_start + ii * (m_pixelsSizeTrans + m_gapSizeTrans);
470  crystal_center_ref[ii + m_nbPixelsTrans * jj]->SetMatriceElt(0,0,Xcryst);
471  // Set y-position of the crystal reference matrix according to the distance between CT detector and center of rotation of the scanner (m_CORtoDetectorDistance)
472  crystal_center_ref[ii + m_nbPixelsTrans * jj]->SetMatriceElt(1,0,m_CORtoDetectorDistance);
473  crystal_center_ref[ii + m_nbPixelsTrans * jj]->SetMatriceElt(2,0,Zcryst);
474  }
475  }
476  // Generate cartesian coordinates of the source for the CT at the reference position (directly above isocenter = +y)
477  source_center_ref->SetMatriceElt(0,0,0.);
478  // Set y-position of the source reference matrix according to the distance between CT source and center of rotation of the scanner (m_CORtoSourceDistance)
479  source_center_ref->SetMatriceElt(1,0,-m_CORtoSourceDistance);
480  source_center_ref->SetMatriceElt(2,0,0.); // The source is supposed to be centered along the Z axis
481 
482  // ============================================================================================================
483  // Loop over all the projection angles
484  // Positions of the scanner elements are progressively stored in the LUT file.
485  // ============================================================================================================
486  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Generate positions for each projection angle"<< endl);
487 
488  // Multi-threading loop over projection angles to compute the LUT
489  int a;
490  #pragma omp parallel for private(a) schedule(guided)
491  for (a=0 ; a<m_nbOfProjections ; a++)
492  {
493  // Get the thread index
494  int th = 0;
495  #ifdef CASTOR_OMP
496  th = omp_get_thread_num();
497  #endif
498  // Get surface crystal position (and not center crystal positions for SPECT), and set the reference crystal matrix
499  for (int c=0 ; c < m_nbPixels ; c++)
500  {
501  // crystal indexation
502  int cryID = a*m_nbPixels + c;
503 
504  // Compute rotation of the crystal center position from the reference and the rotation matrix associated to the projection
505  rotation_mtx[a]->Multiplication(crystal_center_ref[c], crystal_center_out[th]);
506  // Store crystal surface positions
507  mp_crystalCentralPositionX[cryID] = crystal_center_out[th]->GetMatriceElt(0,0);
508  mp_crystalCentralPositionY[cryID] = crystal_center_out[th]->GetMatriceElt(1,0);
509  mp_crystalCentralPositionZ[cryID] = crystal_center_out[th]->GetMatriceElt(2,0);
510  // Store crystal orientations
511  mp_crystalOrientationX[cryID] = rotation_mtx[a]->GetMatriceElt(0,1);
512  mp_crystalOrientationY[cryID] = rotation_mtx[a]->GetMatriceElt(0,0);
513  mp_crystalOrientationZ[cryID] = 0;
514  }
515  // Compute rotation of the source center position from the reference and the rotation matrix associated to the projection
516  rotation_mtx[a]->Multiplication(source_center_ref, source_center_out[th]);
517  // Store crystal surface positions
518  mp_sourcePositionX[a] = source_center_out[th]->GetMatriceElt(0,0);
519  mp_sourcePositionY[a] = source_center_out[th]->GetMatriceElt(1,0);
520  mp_sourcePositionZ[a] = source_center_out[th]->GetMatriceElt(2,0);
521  }
522 
523  for(int i=0; i<m_nbOfProjections; i++)
524  delete rotation_mtx[i];
525  delete[] rotation_mtx;
526  for (int c=0; c<m_nbPixels; c++)
527  delete crystal_center_ref[c];
528  delete[] crystal_center_ref;
529  delete source_center_ref;
530  for (int th=0; th<nb_threads; th++)
531  {
532  delete crystal_center_out[th];
533  delete source_center_out[th];
534  }
535  delete[] crystal_center_out;
536  delete[] source_center_out;
537 
538  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> LUT generation completed" << endl);
539 
540  return 0;
541 }
542 
543 // =====================================================================
544 // ---------------------------------------------------------------------
545 // ---------------------------------------------------------------------
546 // =====================================================================
547 
548 int iScannerCT::GetPositionsAndOrientations( int a_index1, int a_index2,
549  FLTNB ap_Position1[3], FLTNB ap_Position2[3],
550  FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3],
551  FLTNB* ap_POI1, FLTNB* ap_POI2 )
552 {
554 
555  // CT indices : 1st for the projection, 2nd for the pixel
556 
557  // First check projection angle existency
558  if (a_index1<0 || a_index1>=m_nbOfProjections)
559  {
560  Cerr("***** iScannerCT::GetPositionsAndOrientations() -> Projection index (" << a_index1 << ") out of range [0:" << m_nbOfProjections-1 << "] !" << endl);
561  return 1;
562  }
563 
564  // Second check pixels existency
565  if (a_index2<0 || a_index2>=m_nbPixels)
566  {
567  Cerr("***** iScannerCT::GetPositionsAndOrientations() -> Pixel index (" << a_index2 << ") out of range [0:" << m_nbPixels-1 << "] !" << endl);
568  return 1;
569  }
570 
571  // Get crystal index related to the projection
572  int index = a_index1*m_nbPixels + a_index2;
573 
574  // Get position of the source
575  ap_Position1[0] = mp_sourcePositionX[a_index1];
576  ap_Position1[1] = mp_sourcePositionY[a_index1];
577  ap_Position1[2] = mp_sourcePositionZ[a_index1];
578 
579  // todo : Due to the current implementation of CT projection, POI
580  // and DOI are not handled and ignored.
581  // An error is returned if POI are provided
582 
583  // Case when POI is not provided
584  if (ap_POI2==NULL)
585  {
586  ap_Position2[0] = mp_crystalCentralPositionX[index];
587  ap_Position2[1] = mp_crystalCentralPositionY[index];
588  ap_Position2[2] = mp_crystalCentralPositionZ[index];
589  }
590  // Case when POI[2] is negative (meaning we only have POI[0] or POI[1] specified and to be taken into account)
591  else if (ap_POI2[2]<0.)
592  {
593  Cerr("***** iScannerCT::GetPositionsAndOrientations() -> POI management not implemented yet for CT !" << endl);
594  return 1;
595  }
596  // Case when only the DOI is provided
597  else if (ap_POI2[0]==0. && ap_POI2[1]==0.)
598  {
599  Cerr("***** iScannerCT::GetPositionsAndOrientations() -> POI management not implemented yet for CT !" << endl);
600  return 1;
601  }
602  // Case when the full POI is taken into account
603  else
604  {
605  Cerr("***** iScannerCT::GetPositionsAndOrientations() -> POI management not implemented yet for CT !" << endl);
606  return 1;
607  }
608 
609  // Get orientations
610  ap_Orientation1[0] = -mp_crystalOrientationX[a_index2];
611  ap_Orientation1[1] = -mp_crystalOrientationY[a_index2];
612  ap_Orientation1[2] = -mp_crystalOrientationZ[a_index2];
613  ap_Orientation2[0] = mp_crystalOrientationX[a_index2];
614  ap_Orientation2[1] = mp_crystalOrientationY[a_index2];
615  ap_Orientation2[2] = mp_crystalOrientationZ[a_index2];
616 
617  return 0;
618 }
619 
620 // =====================================================================
621 // ---------------------------------------------------------------------
622 // ---------------------------------------------------------------------
623 // =====================================================================
624 
625 int iScannerCT::GetRdmPositionsAndOrientations(int a_index1, int a_index2,
626  FLTNB ap_Position1[3], FLTNB ap_Position2[3],
627  FLTNB ap_Orientation1[3], FLTNB ap_Orientation2[3] )
628 {
630 
631  Cerr("***** iScannerCT::GetRdmPositionsAndOrientations() -> Not yet implemented for CT !" << endl);
632 
633  return 0;
634 }
635 
636 // =====================================================================
637 // ---------------------------------------------------------------------
638 // ---------------------------------------------------------------------
639 // =====================================================================
640 
641 int iScannerCT::GetPositionWithRandomDepth(int a_index1, int a_index2, FLTNB ap_Position1[3], FLTNB ap_Position2[3])
642 {
644  // This function was first implemented for PET testing purpose. Not implemented yet.
645  Cerr("***** iScannerCT::GetPositionWithRandomDepth() -> This function was implemented for PET testing purpose. Not implemented for CT !" << endl);
646  return 1;
647 }
648 
649 // =====================================================================
650 // ---------------------------------------------------------------------
651 // ---------------------------------------------------------------------
652 // =====================================================================
653 
654 int iScannerCT::GetTwoCorners(int a_index1, int a_index2,
655  FLTNB ap_CornerInf1[3], FLTNB ap_CornerSup1[3],
656  FLTNB ap_CornerInf2[3], FLTNB ap_CornerSup2[3])
657 {
659  Cerr("***** iScannerCT::GetTwoCorners() -> Not implemented yet !" << endl);
660  return 1;
661 }
662 
663 // =====================================================================
664 // ---------------------------------------------------------------------
665 // ---------------------------------------------------------------------
666 // =====================================================================
667 
668 int iScannerCT::GetEdgesCenterPositions( int a_index1, int a_index2,
669  FLTNB ap_pos_line_point1[3], FLTNB ap_pos_line_point2[3],
670  FLTNB ap_pos_point1_x[4], FLTNB ap_pos_point1_y[4], FLTNB ap_pos_point1_z[4],
671  FLTNB ap_pos_point2_x[4], FLTNB ap_pos_point2_y[4], FLTNB ap_pos_point2_z[4]
672 )
673 {
675 
676  if ( a_index1<0 || a_index1>=m_nbOfProjections
677  || a_index2<0 || a_index2>=m_nbPixels )
678  {
679  Cerr("***** iScannerCT::GetPositionEdge() -> Crystal index or projection index out of range !" << endl);
680  return 1;
681  }
682 
683  // Computing global index
684  int global_index = m_nbPixels * a_index1 + a_index2;
685 
686  // Getting the half size of axial/trans crystal 1 and 2 depending on the layer
687  FLTNB half_spot_size_trans = m_spotSizeWidth / 2.0;
688  FLTNB half_spot_size_axial = m_spotSizeDepth / 2.0;
689  FLTNB half_pixel_trans = m_pixelsSizeTrans / 2.0;
690  FLTNB half_pixel_axial = m_pixelsSizeAxial / 2.0;
691 
693  // Computing coordinates depending on the orientation for point1
694  // X-axis
695  ap_pos_point1_x[ 0 ] = ap_pos_line_point1[ 0 ] - half_spot_size_trans * -mp_crystalOrientationY[ global_index ];
696  ap_pos_point1_x[ 1 ] = ap_pos_line_point1[ 0 ] + half_spot_size_trans * -mp_crystalOrientationY[ global_index ];
697  ap_pos_point1_x[ 2 ] = ap_pos_line_point1[ 0 ];
698  ap_pos_point1_x[ 3 ] = ap_pos_line_point1[ 0 ];
699 
700  // Y-axis
701  ap_pos_point1_y[ 0 ] = ap_pos_line_point1[ 1 ] - half_spot_size_trans * mp_crystalOrientationX[ global_index ];
702  ap_pos_point1_y[ 1 ] = ap_pos_line_point1[ 1 ] + half_spot_size_trans * mp_crystalOrientationX[ global_index ];
703  ap_pos_point1_y[ 2 ] = ap_pos_line_point1[ 1 ];
704  ap_pos_point1_y[ 3 ] = ap_pos_line_point1[ 1 ];
705 
706  // Z-axis
707  ap_pos_point1_z[ 0 ] = ap_pos_line_point1[ 2 ];
708  ap_pos_point1_z[ 1 ] = ap_pos_line_point1[ 2 ];
709  ap_pos_point1_z[ 2 ] = ap_pos_line_point1[ 2 ] - half_spot_size_axial;
710  ap_pos_point1_z[ 3 ] = ap_pos_line_point1[ 2 ] + half_spot_size_axial;
711 
713  // Computing coordinates depending on the orientation for point2
714  // X-axis
715  ap_pos_point2_x[ 0 ] = ap_pos_line_point2[ 0 ] + half_pixel_trans * mp_crystalOrientationY[ global_index ];
716  ap_pos_point2_x[ 1 ] = ap_pos_line_point2[ 0 ] - half_pixel_trans * mp_crystalOrientationY[ global_index ];
717  ap_pos_point2_x[ 2 ] = ap_pos_line_point2[ 0 ];
718  ap_pos_point2_x[ 3 ] = ap_pos_line_point2[ 0 ];
719 
720  // Y-axis
721  ap_pos_point2_y[ 0 ] = ap_pos_line_point2[ 1 ] + half_pixel_trans * -mp_crystalOrientationX[ global_index ];
722  ap_pos_point2_y[ 1 ] = ap_pos_line_point2[ 1 ] - half_pixel_trans * -mp_crystalOrientationX[ global_index ];
723  ap_pos_point2_y[ 2 ] = ap_pos_line_point2[ 1 ];
724  ap_pos_point2_y[ 3 ] = ap_pos_line_point2[ 1 ];
725 
726  // Z-axis
727  ap_pos_point2_z[ 0 ] = ap_pos_line_point2[ 2 ];
728  ap_pos_point2_z[ 1 ] = ap_pos_line_point2[ 2 ];
729  ap_pos_point2_z[ 2 ] = ap_pos_line_point2[ 2 ] - half_pixel_axial;
730  ap_pos_point2_z[ 3 ] = ap_pos_line_point2[ 2 ] + half_pixel_axial;
731 
732  // Normal end
733  return 0;
734 }
735 
736 // =====================================================================
737 // ---------------------------------------------------------------------
738 // ---------------------------------------------------------------------
739 // =====================================================================
740 
741 // TODO SS: If possible, simplify all this ping-pong between scanner and datafile information. For example, this function should
742 // be implemented inside the datafile, and then the information are passed to the scanner, instead of mixing everything.
744 {
746 
747  if (m_verbose>=VERBOSE_DETAIL) Cout("iScannerCT::GetGeometricInfoFromDataFile() -> Specific to CT" << endl);
748 
749  // This function is intended to be called after the scanner initialization, so that any field present in the datafile header, similar to
750  // one in the scanner configuration file, may overload the scanner value.
751 
752  if (ReadDataASCIIFile(a_pathToDF , "Number of projections", &m_nbOfProjections, 1, KEYWORD_MANDATORY))
753  {
754  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> Error while reading number of projections in the header data file " << endl);
755  return 1;
756  }
757 
758  // Allocate a one-dimensional vector to retrieve angles from the datafile, then copy it in the member variables
759  FLTNB* angles = new FLTNB[m_nbOfProjections];
760  FLTNB first_and_last_angles[2] = {-1.,-1.};
761 
762  // Two possible initializations for projection angles :
763  // - All angles are provided with the keyword "Angles"
764  // - The first and last angles are provided, and the intermediate angles are extrapolated from the number of projections and the rotation direction
765 
766  // 'First/Last angles' tags : checking issue during data reading/conversion (==1)
767  if (ReadDataASCIIFile(a_pathToDF, "First and last projection angles", first_and_last_angles, 2, KEYWORD_OPTIONAL) == 1)
768  {
769  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> Error while reading Angle mandatory field in the header data file '" << endl);
770  return 1;
771  }
772  // Scanner rotation direction
773  string rotation_direction = "";
774  if (ReadDataASCIIFile(a_pathToDF , "Scanner rotation direction", &rotation_direction, 1, KEYWORD_OPTIONAL) == 1)
775  {
776  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> Error while reading head rotation orientation in the header data file " << endl);
777  return 1;
778  }
779  if (SetRotDirection(rotation_direction) )
780  {
781  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> Error occured while trying to initialize scanner rotation orientation " << endl);
782  return 1;
783  }
784 
785  // Check for 'Projection angles' tag
786  int rvalue = ReadDataASCIIFile(a_pathToDF, "Projection angles", angles, m_nbOfProjections, KEYWORD_OPTIONAL);
787 
788  // Error while reading "Angles" tag (==1)
789  if(rvalue==1)
790  {
791  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> Error while reading Angles field in the header data file !'" << endl);
792  return 1;
793  }
794 
795  // Check if information on projection angles has been provided
796  if ( rvalue>=2 && // "Angles" tag not found
797  (first_and_last_angles[0] <0 || first_and_last_angles[1] <0) ) // Tags first/last angles not found)
798  {
799  Cerr("***** iScannerCT::GetGeometricInfoFromDataFile() -> No information on projection angles provided in the datafile !'" << endl);
800  Cerr(" This information should be provided using either the 'Angles' tag, or both 'First angles', 'Last angles' tags !'" << endl);
801  return 1;
802  }
803  else if (rvalue>=2) // "Angles" tag not found, but first angle and last angle provided
804  {
805  // Put first and last angles in [0:360[
806  while (first_and_last_angles[0]>=360.) first_and_last_angles[0] -= 360.;
807  while (first_and_last_angles[0]<0.) first_and_last_angles[0] += 360.;
808  while (first_and_last_angles[1]>=360.) first_and_last_angles[1] -= 360.;
809  while (first_and_last_angles[1]<0.) first_and_last_angles[1] += 360.;
810  // Rotation direction
811  FLTNB dir = (m_rotDirection == GEO_ROT_CCW) ? -1. : 1.;
812  // Compute angle increment
813  FLTNB angle_increment = dir*(first_and_last_angles[1] - first_and_last_angles[0]);
814  while (angle_increment>=360.) angle_increment -= 360.;
815  while (angle_increment<0.) angle_increment += 360.;
816  angle_increment /= ((FLTNB)(m_nbOfProjections-1));
817  // Compute all projection angles
818  for(int a=0 ; a<m_nbOfProjections ; a++)
819  {
820  angles[a] = first_and_last_angles[0] + dir * angle_increment * ((FLTNB)a);
821  while (angles[a]>=360.) angles[a] -= 360.;
822  while (angles[a]<0.) angles[a] += 360.;
823  }
824  // Set the last angle to be sure it is the one requested (rounding errors may change it a bit)
825  angles[m_nbOfProjections-1] = first_and_last_angles[1];
826  }
827  // else : Angles have been recovered using ReadDataASCIIFile() above
828 
829  // Instanciate here the projection angles variable
831  for(int a=0 ; a<m_nbOfProjections ; a++) mp_projectionAngles[a] = angles[a];
832 
833  delete[] angles;
834 
835  return 0;
836 }
837 
838 // =====================================================================
839 // ---------------------------------------------------------------------
840 // ---------------------------------------------------------------------
841 // =====================================================================
842 
843 int iScannerCT::GetCTSpecificParameters(uint16_t* ap_nbOfProjections,
844  FLTNB*& ap_angles,
845  int* ap_detectorRotDirection)
846 {
848  // Verbose
849  if (m_verbose>=VERBOSE_DETAIL) Cout("iScannerCT::GetCTSpecificParameters() -> Copy pointers inside scanner to the datafile to get information" << endl
850  << " depending on the datafile inside the scanner" << endl);
851  // Verify that all parameters have been correctly checked
853  {
854  Cerr("***** iScannerCT::GetCTSpecificParameters() -> Parameters have not been checked !" << endl);
855  return 1;
856  }
857  // Get them
858  *ap_nbOfProjections = m_nbOfProjections;
859  ap_angles = mp_projectionAngles;
860  *ap_detectorRotDirection = m_rotDirection;
861  // End
862  return 0;
863 }
864 
865 // =====================================================================
866 // ---------------------------------------------------------------------
867 // ---------------------------------------------------------------------
868 // =====================================================================
869 
871 {
873  cout << "This scanner class is dedicated to the description of CT systems." << endl;
874 }
875 
876 // =====================================================================
877 // ---------------------------------------------------------------------
878 // ---------------------------------------------------------------------
879 // =====================================================================
iScannerCT()
iScannerCT constructor. Initialize the member variables to their default values.
Definition: iScannerCT.cc:40
int LoadLUT()
Load a precomputed scanner LUT.
Definition: iScannerCT.cc:385
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
#define VERBOSE_DEBUG_EVENT
FLTNB m_spotSizeWidth
Definition: iScannerCT.hh:277
#define FLTNB
Definition: gVariables.hh:81
FLTNB m_detectorDepth
Definition: iScannerCT.hh:276
FLTNB * mp_crystalOrientationZ
Definition: iScannerCT.hh:287
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...
Definition: iScannerCT.cc:641
uint32_t m_nbPixelsTrans
Definition: iScannerCT.hh:268
FLTNB m_CORtoDetectorDistance
Definition: iScannerCT.hh:265
FLTNB * mp_projectionAngles
Definition: iScannerCT.hh:264
FLTNB * mp_sourcePositionX
Definition: iScannerCT.hh:289
#define VERBOSE_DETAIL
#define GEO_ROT_CCW
Definition: vScanner.hh:49
int ComputeLUT()
Computes the LUT of the scanner from a generic (.geom) file.
Definition: iScannerCT.cc:398
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...
Definition: iScannerCT.cc:625
FLTNB m_defaultBedDisplacementInMm
Definition: vScanner.hh:445
FLTNB * mp_crystalOrientationX
Definition: iScannerCT.hh:285
int Initialize()
Check general initialization and set several parameters to their default value.
Definition: iScannerCT.cc:361
FLTNB GetMatriceElt(uint16_t l, uint16_t c)
Definition: oMatrix.cc:166
FLTNB m_pixelsSizeAxial
Definition: iScannerCT.hh:273
Declaration of class iScannerCT.
string GetPathToScannerFile()
int GetGeometricInfoFromDataFile(string a_pathToDF)
Recover geometric informations specific to the scanner class from the datafile header.
Definition: iScannerCT.cc:743
uint16_t m_nbOfProjections
Definition: iScannerCT.hh:263
int BuildLUT(bool a_scannerFileIsLUT)
Call the functions to generate the LUT or read the user-made LUT depending on the user choice...
Definition: iScannerCT.cc:231
oImageDimensionsAndQuantification * mp_ID
Definition: vScanner.hh:439
FLTNB m_CORtoSourceDistance
Definition: iScannerCT.hh:266
#define Cerr(MESSAGE)
#define VERBOSE_DEBUG_LIGHT
virtual int SetRotDirection(string a_rotDirection)
Set rotation direction of the system.
Definition: vScanner.cc:290
int CheckParameters()
Check that all parameters have been correctly initialized.
Definition: iScannerCT.cc:279
FLTNB * mp_sourcePositionY
Definition: iScannerCT.hh:290
FLTNB * mp_crystalOrientationY
Definition: iScannerCT.hh:286
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
int GetNbThreadsMax()
Get the maximum between the number of threads used for projections and image operations.
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
FLTNB * mp_crystalCentralPositionX
Definition: iScannerCT.hh:281
uint32_t m_nbPixelsAxial
Definition: iScannerCT.hh:272
void DescribeSpecific()
Implementation of the pure virtual eponym function that simply prints info about the scanner...
Definition: iScannerCT.cc:100
Declaration of class sScannerManager.
FLTNB m_pixelsSizeTrans
Definition: iScannerCT.hh:269
int m_verbose
Definition: vScanner.hh:438
#define VERBOSE_NORMAL
FLTNB m_gapSizeAxial
Definition: iScannerCT.hh:274
FLTNB m_spotSizeDepth
Definition: iScannerCT.hh:278
int m_nbPixels
Definition: iScannerCT.hh:261
bool m_allParametersChecked
Definition: vScanner.hh:440
#define KEYWORD_MANDATORY
Definition: gOptions.hh:48
#define KEYWORD_OPTIONAL
Definition: gOptions.hh:50
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.
Definition: iScannerCT.cc:654
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. Get the cartesian coordinaters of the ce...
Definition: iScannerCT.cc:668
int m_scannerType
Definition: vScanner.hh:437
Declaration of class sOutputManager.
~iScannerCT()
iScannerCT destructor.
Definition: iScannerCT.cc:75
Structure designed for basic matrices operations.
Definition: oMatrix.hh:42
FLTNB * mp_crystalCentralPositionY
Definition: iScannerCT.hh:282
#define DEBUG_VERBOSE(IGNORED1, IGNORED2)
#define Cout(MESSAGE)
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...
Definition: iScannerCT.cc:548
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
#define SCANNER_CT
int GetCTSpecificParameters(uint16_t *ap_nbOfProjections, FLTNB *&ap_angles, int *ap_detectorRotDirection)
Set pointers passed in argument with the related CT specific variables This function is used to rec...
Definition: iScannerCT.cc:843
int m_rotDirection
Definition: vScanner.hh:444
int Instantiate(bool a_scannerFileIsLUT)
Get mandatory informations from the scanner file and allocate memory for the member variables...
Definition: iScannerCT.cc:157
FLTNB m_gapSizeTrans
Definition: iScannerCT.hh:270
FLTNB * mp_crystalCentralPositionZ
Definition: iScannerCT.hh:283
#define GEO_ROT_CW
Definition: vScanner.hh:47
Generic class for scanner objects.
Definition: vScanner.hh:62
void ShowHelp()
Display help.
Definition: iScannerCT.cc:870
FLTNB * mp_sourcePositionZ
Definition: iScannerCT.hh:291