CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
castor-norm.cc
Go to the documentation of this file.
1 
8 #include "gVariables.hh"
9 #include "gOptions.hh"
10 #include "iEventHistoPET.hh"
11 #include "iEventNorm.hh"
12 #include "iProjectorClassicSiddon.hh"
13 #include "oImageDimensionsAndQuantification.hh"
14 #include "oInterfileIO.hh"
15 #include "oProjectionLine.hh"
16 #include "oProjectorManager.hh"
17 #include "vDataFile.hh"
18 #include "iDataFilePET.hh"
19 #include "iDataFileSPECT.hh"
20 #include "iDataFileCT.hh"
21 #include "sOutputManager.hh"
22 #include "sRandomNumberGenerator.hh"
23 #include <cstdint>
24 
25 // =============================================================================================================================================
26 // =============================================================================================================================================
27 // =============================================================================================================================================
28 // H E L P F U N C T I O N S
29 // =============================================================================================================================================
30 // =============================================================================================================================================
31 // =============================================================================================================================================
32 
33 
38 void ShowHelp()
39 {
40  // Show help
41  cout << endl;
42  cout << "Usage: castor-norm -df datafile.cdh -img path_to_img.hdr -sc scanner_alias -(f/d)out output" << endl;
43  cout << endl;
44  cout << "This program can be used to compute direct normalization factors from a normalization phantom." << endl;
45  cout << endl;
46  cout << "Direct normalization factors are used to counteract on the variation of sensitivity across all LORs of a PET system.";
47  cout << " They are computed from a normalization scan (-df), typically of a symmetric and uniform phantom (-img), which illuminates as evenly as possible the different possible LORs.";
48  cout << " The normalization factors are given by" << endl;
49  cout << " NF_uivj=A/C_uivj" << endl;
50  cout << "where A is the activity of the uniform phantom, and C_uivj is the event count measured for LOR uivj.";
51  cout << " The program outputs a normalization datafile that contains the computed NFs." << endl;
52  cout << endl;
53  cout << "[Mandatory parameters]:" << endl;
54  cout << " -df datafile.cdh : Give a CASToR histogram datafile of a normalization scan." << endl;
55  cout << " -img path_to_img.hdr : Give the interfile header of the normalization phantom (no default)" << endl;
56  cout << " -sc scanner_alias : Give the scanner model. It must correspond to the name of one of the file in the scanner repository (w/o file extension)" << endl;
57  cout << " -fout name : Give the root name for all output files (no default, alternative to -dout)" << endl;
58  cout << " -dout name : Give the name of the output directory where all output files will be written (no default, alternative to -fout)" << endl;
59  cout << endl;
60  cout << "[Options]:" << endl;
61  cout << " -proj param : Give the projector to be used for forward projection, along with a configuration file (proj:file.conf)" << endl;
62  cout << " -proj-common : Give common projector-related options." << endl;
63  cout << " or the list of parameters associated to the projector (proj,param1,param2,...). If the projector only is specified, the" << endl;
64  cout << " default configuration file is used. By default, the Siddon projector is used." << endl;
65  cout << " -proj-comp : Give the strategy for projection line computation. Here are the three different strategies that can be used:" << endl;
66  cout << " 1 : Image-based system matrix elements storage: The voxels weights are added in a matrix representing the whole image, so" << endl;
67  cout << " the addition of a new line to the previous ones is straightforward only by adding the weights to the corresponding voxels." << endl;
68  cout << " As it is insanely long, it can possibly be used for example with extremely complex projectors that makes use of huge number" << endl;
69  cout << " of ray tracings for a single event, where the list of contributions can become longer than the number of voxels in the image." << endl;
70  cout << " This strategy is not compatible with SPECT reconstruction including attenuation correction." << endl;
71  cout << " 2 : Fixed-size list storage of system matrix elements: The voxels are added one by one in two separated lists, one containing voxel" << endl;
72  cout << " indices and the other voxel weights. When a voxel is added to the oProjectionLine, it is simply pilled-up to the list. The list" << endl;
73  cout << " has a fixed size which is provided by the EstimateMaxNumberOfVoxelsPerLine() function from the vProjector class. There are no" << endl;
74  cout << " checks at all for possible buffer overflows. This is the fastest strategy and default one." << endl;
75  cout << " 3 : Adaptative-size list storage of system matrix elements: This is the same as the fixed-size strategy except that the size can be" << endl;
76  cout << " upgraded if the current number of contributing voxels exceed the list's size. The first allocated size corresponds to the diagonal" << endl;
77  cout << " of the image. During the first iteration, this size will be upgraded until it will reach a size suitable for all events. Thus it" << endl;
78  cout << " is a bit slower than the fixed-list strategy during the first iteration, but is optimal with respect to RAM usage." << endl;
79  cout << " -vb value : Give the verbosity level, from 0 (no verbose) to 2 (default: 1)" << endl;
80  cout << " --help,-h,-help : Print out this help page." << endl; // managed by main
81  cout << endl;
82  #ifdef BUILD_DATE
83  cout << " Build date: " << BUILD_DATE << endl;
84  cout << endl;
85  #endif
86  #ifdef CASTOR_VERSION
87  cout << " This program is part of the CASToR release version " << CASTOR_VERSION << "." << endl;
88  cout << endl;
89  #endif
90 }
91 
92 // =============================================================================================================================================
93 // =============================================================================================================================================
94 // =============================================================================================================================================
95 // M A I N P R O G R A M
96 // =============================================================================================================================================
97 // =============================================================================================================================================
98 // =============================================================================================================================================
99 
100 int main(int argc, char** argv)
101 {
102  // ============================================================================================================
103  // MPI stuff (we make all instances except the first one returning 0 directly)
104  // ============================================================================================================
105  int mpi_rank = 0;
106  #ifdef CASTOR_MPI
107  int mpi_size = 1;
108  MPI_Init(&argc, &argv);
109  MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
110  MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
111  if (mpi_rank!=0) return 0;
112  #endif
113 
114  // No argument, then show help
115  if (argc==1)
116  {
117  ShowHelp();
118  Exit(EXIT_SUCCESS);
119  }
120 
121  // ============================================================================================================
122  // Parameterized variables with their default values
123  // ============================================================================================================
124 
125  // --------------------------------------------------------------------------------
126  // Input settings
127  // --------------------------------------------------------------------------------
128 
129  // Path to the normalization datafile
130  string path_to_normalization_datafile = "";
131 
132  // Path to the normalization phantom. no default
133  string path_to_normalization_phantom = "";
134 
135  // Scanner name provided by the user
136  string scanner_name = "";
137 
138  // --------------------------------------------------------------------------------
139  // Output settings
140  // --------------------------------------------------------------------------------
141 
142  // Output directory name.
143  string path_dout = "";
144  // Or root name
145  string path_fout = "";
146 
147  // --------------------------------------------------------------------------------
148  // Projector settings
149  // --------------------------------------------------------------------------------
150 
151  string options_projectorF = "incrementalSiddon";
152  string options_projector_common = "3.,1,1";
153 
154  // Default projector computation strategy
155  int projector_computation_strategy = FIXED_LIST_COMPUTATION_STRATEGY;
156 
157  // --------------------------------------------------------------------------------
158  // Miscellaneous settings
159  // --------------------------------------------------------------------------------
160 
161  // Verbose level
162  int verbose = 1;
163 
164  // ============================================================================================================
165  // Read command-line parameters
166  // ============================================================================================================
167 
168  // Must manually increment the option index when an argument is needed after an option
169  for (int i=1; i<argc; i++)
170  {
171  // Get the option as a string
172  string option = (string)argv[i];
173 
174  // --------------------------------------------------------------------------------
175  // Miscellaneous settings
176  // --------------------------------------------------------------------------------
177 
178  // Show help
179  if (option=="-h" || option=="--help" || option=="-help")
180  {
181  ShowHelp();
182  Exit(EXIT_SUCCESS);
183  }
184  // General verbosity level
185  else if (option=="-vb")
186  {
187  if (i>=argc-1)
188  {
189  Cerr("***** castor-norm() -> Argument missing for option: " << option << endl);
190  Exit(EXIT_FAILURE);
191  }
192  if (ConvertFromString(argv[i+1], &verbose))
193  {
194  Cerr("***** castor-norm() -> Exception when trying to read provided verbosity level '" << verbose << " for option: " << option << endl);
195  Exit(EXIT_FAILURE);
196  }
197  i++;
198  }
199 
200  // --------------------------------------------------------------------------------
201  // Input settings
202  // --------------------------------------------------------------------------------
203 
204  // Images
205  else if (option=="-df") // This is a mandatory option
206  {
207  if (i>=argc-1)
208  {
209  Cerr("***** castor-norm() -> Argument missing for option: " << option << endl);
210  Exit(EXIT_FAILURE);
211  }
212  path_to_normalization_datafile = (string)argv[i+1];
213  i++;
214  }
215  // Normalization scan
216  else if (option=="-img")
217  {
218  if (i>=argc-1)
219  {
220  Cerr("***** castor-norm() -> Argument missing for option: " << option << endl);
221  Exit(EXIT_FAILURE);
222  }
223  path_to_normalization_phantom = argv[i+1];
224  i++;
225  }
226  // Scanner name
227  else if (option=="-sc")
228  {
229  if (i>=argc-1)
230  {
231  Cerr("***** castor-norm() -> Argument missing for option: " << option << endl);
232  Exit(EXIT_FAILURE);
233  }
234  scanner_name = argv[i+1];
235  i++;
236  }
237 
238  // --------------------------------------------------------------------------------
239  // Output settings
240  // --------------------------------------------------------------------------------
241 
242  // Name of the output directory
243  else if (option=="-dout") // This is a mandatory option
244  {
245  if (i>=argc-1)
246  {
247  Cerr("***** castor-norm() -> Argument missing for option: " << option << endl);
248  Exit(EXIT_FAILURE);
249  }
250  path_dout = argv[i+1];
251  i++;
252  }
253  // Base name of the output files
254  else if (option=="-fout") // This is a mandatory option
255  {
256  if (i>=argc-1)
257  {
258  Cerr("***** castor-norm() -> Argument missing for option: " << option << endl);
259  Exit(EXIT_FAILURE);
260  }
261  path_fout = argv[i+1];
262  i++;
263  }
264 
265  // --------------------------------------------------------------------------------
266  // Projector settings
267  // --------------------------------------------------------------------------------
268 
269  // Projector settings
270  else if (option=="-proj")
271  {
272  if (i>=argc-1)
273  {
274  Cerr("***** castor-norm() -> Argument missing for option: " << option << endl);
275  Exit(EXIT_FAILURE);
276  }
277  options_projectorF = (string)argv[i+1];
278  i++;
279  }
280  // Common projector settings
281  else if (option=="-proj-common")
282  {
283  if (i>=argc-1)
284  {
285  Cerr("***** castor-norm() -> Argument missing for option: " << option << endl);
286  Exit(EXIT_FAILURE);
287  }
288  options_projector_common = (string)argv[i+1];
289  i++;
290  }
291  // Projection line computation strategy
292  else if (option=="-proj-comp")
293  {
294  if (i>=argc-1)
295  {
296  Cerr("***** castor-recon() -> Argument missing for option: " << option << endl);
297  Exit(EXIT_FAILURE);
298  }
299  projector_computation_strategy = atoi(argv[i+1]);
300  i++;
301  }
302 
303  // --------------------------------------------------------------------------------
304  // Unknown option!
305  // --------------------------------------------------------------------------------
306 
307  else
308  {
309  Cerr("***** castor-norm() -> Unknown option '" << option << "' !" << endl);
310  Exit(EXIT_FAILURE);
311  }
312  }
313 
314  // ============================================================================================================
315  // Some checks
316  // ============================================================================================================
317 
318  // Output files
319  if (path_fout.empty() && path_dout.empty())
320  {
321  Cerr("***** castor-norm() -> Please provide an output option for output files (-fout or -dout) !" << endl);
322  Exit(EXIT_FAILURE);
323  }
324  // Check that only one option has been provided
325  if (!path_fout.empty() && !path_dout.empty())
326  {
327  Cerr("***** castor-norm() -> Please provide either output option -fout or -dout but not both !" << endl);
328  Exit(EXIT_FAILURE);
329  }
330 
331  // ============================================================================================================
332  // Create sOutputManager
333  // ============================================================================================================
334  sOutputManager *p_OutputManager = sOutputManager::GetInstance();
335  // Set verbose level
336  p_OutputManager->SetVerbose(verbose);
337  // Set MPI rank
338  p_OutputManager->SetMPIRank(mpi_rank);
339  // Initialize output directory and base name
340  if (p_OutputManager->InitOutputDirectory(path_fout, path_dout))
341  {
342  Cerr("***** castor-norm() -> A problem occurred while initializing output directory !" << endl);
343  Exit(EXIT_FAILURE);
344  }
345  // Log command line
346  if (p_OutputManager->LogCommandLine(argc,argv))
347  {
348  Cerr("***** castor-norm() -> A problem occurred while logging command line arguments !" << endl);
349  Exit(EXIT_FAILURE);
350  }
351 
352  // ============================================================================================================
353  // Create sScannerManager
354  // ============================================================================================================
355  sScannerManager *p_ScannerManager = sScannerManager::GetInstance();
356  p_ScannerManager->SetVerbose(verbose);
357  if (p_ScannerManager->FindScannerSystem(scanner_name) )
358  {
359  Cerr("***** castor-norm() -> A problem occurred while searching for scanner system !" << endl);
360  Exit(EXIT_FAILURE);
361  }
362  if (p_ScannerManager->BuildScannerObject() )
363  {
364  Cerr("***** castor-norm() -> A problem occurred during scanner object construction ! !" << endl);
365  Exit(EXIT_FAILURE);
366  }
367  if (p_ScannerManager->InstantiateScanner() )
368  {
369  Cerr("***** castor-norm() -> A problem occurred while creating Scanner object !" << endl);
370  Exit(EXIT_FAILURE);
371  }
372  if (p_ScannerManager->BuildLUT() )
373  {
374  Cerr("***** castor-norm() -> A problem occurred while generating/reading the LUT !" << endl);
375  Exit(EXIT_FAILURE);
376  }
377  if (p_ScannerManager->CheckParameters())
378  {
379  Cerr("***** castor-norm() -> A problem occurred while checking scanner manager parameters !" << endl);
380  Exit(EXIT_FAILURE);
381  }
382  if (p_ScannerManager->Initialize())
383  {
384  Cerr("***** castor-norm() -> A problem occurred while initializing scanner !" << endl);
385  Exit(EXIT_FAILURE);
386  }
387 
388  // Get the number of normalization factors
389  int64_t nb_elems = p_ScannerManager->GetSystemNbElts();
390 
391  // ============================================================================================================
392  // Recover image dimensions informations from the interfile header of the image to project
393  // ============================================================================================================
394 
395  // (Required for options using interfile I/O)
397 
398  Intf_fields IF;
399  IntfKeyInitFields(&IF);
400 
401  if(IntfReadHeader(path_to_normalization_phantom, &IF, verbose) )
402  {
403  Cerr("***** castor-norm() -> An error occurred while trying to read the "
404  "interfile header of file reading file "
405  << path_to_normalization_phantom << " !" << endl);
406  Exit(EXIT_FAILURE);
407  }
408 
409  if(verbose >= 3) IntfKeyPrintFields(IF);
410 
411  int nb_VoxX = IF.mtx_size[0];
412  int nb_VoxY = IF.mtx_size[1];
413  int nb_VoxZ = IF.mtx_size[2];
414 
415  FLTNB vox_SizeX = IF.vox_size[0];
416  FLTNB vox_SizeY = IF.vox_size[1];
417  FLTNB vox_SizeZ = IF.vox_size[2];
418 
419  FLTNB offsetX = IF.vox_offset[0];
420  FLTNB offsetY = IF.vox_offset[1];
421  FLTNB offsetZ = IF.vox_offset[2];
422 
423  int nb_time_basis = IF.nb_time_frames > 1 ? IF.nb_time_frames : 1;
424  int nb_resp_basis = IF.nb_resp_gates > 1 ? IF.nb_resp_gates : 1;
425  int nb_card_basis = IF.nb_card_gates > 1 ? IF.nb_card_gates : 1;
426 
427  if (verbose>=2)
428  {
429  Cout(" Image dimensions for projections: " << endl);
430  Cout(" voxels number (X,Y,Z): " << nb_VoxX << "," << nb_VoxY << "," << nb_VoxZ << endl);
431  Cout(" voxels size (X,Y,Z): " << vox_SizeX << "," << vox_SizeY << "," << vox_SizeZ << endl);
432  Cout(" offset (X,Y,Z): " << offsetX << "," << offsetY << "," << offsetZ << endl);
433  }
434 
435  // ============================================================================================================
436  // oImageDimensionsAndQuantification settings
437  // ============================================================================================================
438 
439  // Create oImageDimensionsAndQuantification
440  oImageDimensionsAndQuantification* p_ImageDimensionsAndQuantification = new oImageDimensionsAndQuantification();
441  p_ImageDimensionsAndQuantification->SetDefault();
442 
443  p_ImageDimensionsAndQuantification->SetNbVoxX(nb_VoxX);
444  p_ImageDimensionsAndQuantification->SetNbVoxY(nb_VoxY);
445  p_ImageDimensionsAndQuantification->SetNbVoxZ(nb_VoxZ);
446  p_ImageDimensionsAndQuantification->SetVoxSizeX(vox_SizeX);
447  p_ImageDimensionsAndQuantification->SetVoxSizeY(vox_SizeY);
448  p_ImageDimensionsAndQuantification->SetVoxSizeZ(vox_SizeZ);
449  p_ImageDimensionsAndQuantification->SetOffsetX(offsetX);
450  p_ImageDimensionsAndQuantification->SetOffsetY(offsetY);
451  p_ImageDimensionsAndQuantification->SetOffsetZ(offsetZ);
452 
453  p_ImageDimensionsAndQuantification->SetNbTimeBasisFunctions(nb_time_basis);
454  p_ImageDimensionsAndQuantification->SetNbRespBasisFunctions(nb_resp_basis);
455  p_ImageDimensionsAndQuantification->SetNbCardBasisFunctions(nb_card_basis);
456 
457  p_ImageDimensionsAndQuantification->SetNbBeds(1);
458 
459  // Threading
460  int nb_threads = 1;
461  #ifdef CASTOR_OMP
462  nb_threads = omp_get_num_procs();
463  if (verbose>=2) Cout(" --> Use " << nb_threads << " threads" << endl);
464  #endif
465  if(p_ImageDimensionsAndQuantification->SetNbThreads(to_string(nb_threads)))
466  {
467  Cerr("***** castor-norm() -> A problem occurred while setting the number of threads !" << endl);
468  Exit(EXIT_FAILURE);
469  }
470 
471  if (p_ImageDimensionsAndQuantification->CheckParameters())
472  {
473  Cerr("***** castor-norm() -> A problem occurred while checking image dimensions parameters !" << endl);
474  Exit(EXIT_FAILURE);
475  }
476  if (p_ImageDimensionsAndQuantification->Initialize())
477  {
478  Cerr("***** castor-norm() -> A problem occurred while initializing image dimensions !" << endl);
479  Exit(EXIT_FAILURE);
480  }
481 
482  // ============================================================================================================
483  // Set image space and initialise image
484  // ============================================================================================================
485 
486  // --- Image space initialization ---
487  oImageSpace* p_ImageSpace = new oImageSpace();
488 
489  p_ImageSpace->SetImageDimensionsAndQuantification(p_ImageDimensionsAndQuantification);
490  p_ImageSpace->SetVerbose(verbose);
491 
492  // Allocate memory for main image
493  p_ImageSpace->InstantiateImage();
494  p_ImageSpace->InitImage(path_to_normalization_phantom, 0);
495 
496  // ============================================================================================================
497  // Create the input datafile
498  // ============================================================================================================
499 
500  // Compute the expected number of normalization factors
501  int64_t nb_nf = (int) (nb_elems * (nb_elems - 1) / 2);
502 
503  // Create the datafile based on the data type
504  iDataFilePET* p_DataFile = new iDataFilePET(); // do we want to normalize other modalities?
505  p_DataFile->SetImageDimensionsAndQuantification(p_ImageDimensionsAndQuantification);
506  p_DataFile->SetHeaderDataFileName(path_to_normalization_datafile);
507  p_DataFile->SetIgnoreTOFFlag(true); // force to sum all TOF bins
508  p_DataFile->SetVerbose(verbose);
509  p_DataFile->SetBedIndex(0);
510  bool do_not_affect_quantification = false;
511  if (p_DataFile->ReadInfoInHeader(do_not_affect_quantification))
512  {
513  Cerr("***** castor-norm() -> A problem occurred during datafile header reading ! Abort." << endl);
514  Exit(EXIT_FAILURE);
515  }
516  if (p_DataFile->GetDataType() != TYPE_PET)
517  {
518  Cerr("***** castor-norm() -> Only PET data is currently supported ! Abort." << endl);
519  Exit(EXIT_FAILURE);
520  }
521  if (p_DataFile->GetDataMode() != MODE_HISTOGRAM)
522  {
523  Cerr("***** castor-norm() -> Only histogram data mode is currently supported. Aborting." << endl);
524  Exit(EXIT_FAILURE);
525  }
526  if (p_DataFile->CheckParameters())
527  {
528  Cerr("***** castor-norm() -> A problem occurred while checking datafile parameters ! Abort." << endl);
529  Exit(EXIT_FAILURE);
530  }
531  if (p_DataFile->ComputeSizeEvent())
532  {
533  Cerr("***** castor-norm() -> A problem occurred in datafile initialization ! Abort." << endl);
534  Exit(EXIT_FAILURE);
535  }
536  if (p_DataFile->GetSize() != nb_nf) {
537  Cerr("***** castor-norm() -> The input datafile does not contain expected number of events (got" << p_DataFile->GetSize() << ", expected " << nb_nf << ") ! Abort." << endl);
538  Exit(EXIT_FAILURE);
539  }
540  if (p_DataFile->InitializeMappedFile())
541  {
542  Cerr("***** castor-norm() -> A problem occurred in datafile initialization ! Abort." << endl);
543  Exit(EXIT_FAILURE);
544  }
545  if (p_DataFile->PrepareDataFile())
546  {
547  Cerr("***** castor-norm() -> A problem occurred in datafile preparation ! Abort." << endl);
548  Exit(EXIT_FAILURE);
549  }
550 
551  // ============================================================================================================
552  // Create the output datafile
553  // ============================================================================================================
554 
555  // Create output datafile object
556  iDataFilePET* p_OutputDataFile = new iDataFilePET();
557  p_OutputDataFile->SetVerbose(verbose);
558  p_OutputDataFile->SetImageDimensionsAndQuantification(p_ImageDimensionsAndQuantification);
559  p_OutputDataFile->SetNormCorrectionFlagOn();
560  p_OutputDataFile->SetDataMode(MODE_NORMALIZATION);
561  p_OutputDataFile->SetBedIndex(0);
562  if (p_OutputDataFile->PROJ_InitFile())
563  {
564  Cerr("***** castor-norm() -> An error occurred while opening file for writing !" << endl);
565  Exit(EXIT_FAILURE);
566  }
567  p_OutputDataFile->SetNbEvents(nb_nf);
568  if (p_OutputDataFile->ComputeSizeEvent())
569  {
570  Cerr("***** castor-norm() -> A problem occurred in datafile initialization ! Abort." << endl);
571  Exit(EXIT_FAILURE);
572  }
573  if (p_OutputDataFile->PrepareDataFile())
574  {
575  Cerr("***** castor-norm() -> A problem occurred in datafile preparation ! Abort." << endl);
576  Exit(EXIT_FAILURE);
577  }
578 
579  // ============================================================================================================
580  // Create Projector Manager
581  // ============================================================================================================
582 
583  // Verbose
584  if (verbose>=5) Cout("----- Projector initialization ... -----" << endl);
585  // Create object
586  oProjectorManager* p_ProjectorManager = new oProjectorManager();
587  // Set all parameters
588  p_ProjectorManager->SetScanner(p_ScannerManager->GetScannerObject());
589  p_ProjectorManager->SetImageDimensionsAndQuantification(p_ImageDimensionsAndQuantification);
590  p_ProjectorManager->SetDataFile(p_DataFile);
591  p_ProjectorManager->SetComputationStrategy(projector_computation_strategy);
592  p_ProjectorManager->SetOptionsForward(options_projectorF);
593  p_ProjectorManager->SetOptionsBackward("incrementalSiddon"); // will not be used
594  p_ProjectorManager->SetOptionsCommon(options_projector_common);
595  p_ProjectorManager->SetVerbose(verbose);
596  // Check parameters
597  if (p_ProjectorManager->CheckParameters())
598  {
599  Cerr("***** castor-norm() -> A problem occurred while checking projector manager's parameters !" << endl);
600  Exit(EXIT_FAILURE);
601  }
602  // Initialize projector manager
603  if (p_ProjectorManager->Initialize())
604  {
605  Cerr("***** castor-norm() -> A problem occurred while initializing projector manager !" << endl);
606  Exit(EXIT_FAILURE);
607  }
608  // Verbose
609  if (verbose>=5) Cout("----- Projector initialization OK -----" << endl);
610 
611  // ============================================================================================================
612  // Compute normalization factors
613  // ============================================================================================================
614 
615  // Verbose
616  if (verbose>=1) Cout("castor-norm() -> Compute normalization factors for " << p_DataFile->GetSize() << " events" << endl);
617 
618  // Variables used in loop
619  int64_t printing_index = 0;
620  bool problem = false; // used to report problems in the parallel loop
621 
622  // Number of normalization factors that were correctly computed
623  int64_t nf_correctly_computed = 0;
624 
625  // Create normalization event objects
626  iEventNorm** p2_EventNorm = new iEventNorm*[nb_threads];
627  for (INTNB th = 0; th < nb_threads; th++)
628  {
629  iEventNorm* p_EventNorm = new iEventNorm();
630  p_EventNorm->SetNbLines(1); // 1 = lines in event
631  p_EventNorm->AllocateID();
632  p2_EventNorm[th] = p_EventNorm;
633  }
634 
635  // Compute start and stop indices
636  int64_t index = 0;
637  int64_t index_start = 0;
638  int64_t index_stop = 0;
639  p_DataFile->GetEventIndexStartAndStop(&index_start, &index_stop);
640 
641  #pragma omp parallel for private(index) schedule(static), num_threads(nb_threads)
642  for (index = index_start; index < index_stop; index++)
643  {
644  // The thread index
645  int th = 0;
646  #ifdef CASTOR_OMP
647  th = omp_get_thread_num();
648  #endif
649 
650  // Verbose
651  if (th==0 && verbose>=2)
652  {
653  if (printing_index%((int)(index_stop/100/nb_threads))==0)
654  {
655  int percent = ((int)( ((FLTNB)(index)) * (FLTNB)nb_threads * 100. / index_stop ));
656  cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b "
657  << percent << " % " << flush;
658  }
659  printing_index++;
660  }
661  // Read the event
662  iEventHistoPET* p_EventHistoPET = (iEventHistoPET*) p_DataFile->GetEvent(index,th);
663  // id1 always less than id2
664  uint32_t id1 = p_EventHistoPET->GetID1(0);
665  uint32_t id2 = p_EventHistoPET->GetID2(0);
666 
667  // Compute the normalization factor
668  FLTNBDATA nf_index = 0.; // Normalization factor default value
669  FLTNBDATA event_value = p_EventHistoPET->GetEventValue(0); // 0 = no TOF
670  if (event_value > 0.){
671  // Compute the projection line
672  oProjectionLine* p_ProjectionLine = p_ProjectorManager->ComputeProjectionLine(p_EventHistoPET, th);
673  if (p_ProjectionLine == NULL){
674  Cerr("***** castor-norm() -> A problem occurred while computing the projection line !" << endl);
675  // Specify that there was a problem
676  problem = true;
677  // We must continue here because we are inside an OpenMP loop
678  continue;
679  }
680  FLTNB activity = 0.;
681  if (p_ProjectionLine->NotEmptyLine())
682  {
683  activity = p_ProjectionLine->ForwardProject(p_ImageSpace->m4p_image[0][0][0]);
684  if (activity > 0.)
685  {
686  nf_index = activity / event_value;
687  nf_correctly_computed++;
688  // Cout(id1 << "," << id2 << ": activity=" << activity << ", event_value=" << event_value << ", nf_index=" << nf_index << endl); // Debug
689  } else if (activity < 0.) {
690  Cerr("***** castor-norm() -> A negative projection has been computed !" << endl);
691  Cerr("*****" << id1 << "," << id2 << ": activity=" << activity << ", event_value=" << event_value << endl);
692  // Specify that there was a problem
693  problem = true;
694  // We must continue here because we are inside an OpenMP loop
695  continue;
696  }
697  }
698  // If this part of code is reached, then !p_ProjectionLine->NonEmptyLine() or activity == 0.
699  // In any case, we set the normalization to 0.
700  if (activity == 0. && verbose >= 3)
701  {
702  Cerr("!!!!! castor-norm() -> The projection of the line of response having ID1=" << id1 << " and ID2=" << id2 << " is null, but the corresponding event activity is non-null (" << event_value << ").");
703  Cerr(" The normalization correction factor (" << nf_index << ") will be erroneous for this line of response." << endl);
704  }
705  }
706 
707  // Write the normalization factor
708  iEventNorm* p_EventNorm = p2_EventNorm[th];
709  p_EventNorm->SetID1(0, id1);
710  p_EventNorm->SetID2(0, id2);
711  p_EventNorm->SetNormalizationFactor(nf_index);
712  p_OutputDataFile->WriteEvent(p_EventNorm, th);
713  }
714 
715  // If a problem was encountered, then report it here
716  if (problem)
717  {
718  Cerr("***** castor-norm() -> A problem occurred inside the parallel loop over events !" << endl);
719  Exit(EXIT_FAILURE);
720  }
721 
722  // Verbose
723  if (verbose>=2)
724  {
725  cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
726  << " --> 100 % " << endl;
727  Cout(" --> Compute normalization factors" << endl);
728  }
729 
730  // Summary of normalisation
731  if (verbose >= 1){
732  float percent = 100. * nf_correctly_computed / nb_nf;
733  Cout("castor-norm() -> " << nf_correctly_computed << " out of " << nb_nf << " (" << percent << "%) normalization factors correctly computed." << endl);
734  Cout("Please be aware that a low percentage may result in poor reconstruction quality!" << endl);
735  }
736 
737  // ============================================================================================================
738  // Write final output
739  // ============================================================================================================
740 
741  // Write final file
742  if(p_OutputDataFile->PROJ_WriteData())
743  {
744  Cerr("***** castor-norm() -> An error occurred while writing the final file !" << endl);
745  Exit(EXIT_FAILURE);
746  }
747  // Delete temporary files
748  if(p_OutputDataFile->PROJ_DeleteTmpDataFile())
749  {
750  Cerr("***** castor-norm() -> An error occurred while deleting temporary files !" << endl);
751  Exit(EXIT_FAILURE);
752  }
753  // Write header
754  if (p_OutputDataFile->WriteHeader())
755  {
756  Cerr("***** castor-norm() -> An error occurred while writing output header file !" << endl);
757  Exit(EXIT_FAILURE);
758  }
759 
760  // ============================================================================================================
761  // Exit
762  // ============================================================================================================
763 
764  // Delete objects in the inverse order in which they were created
765  if (verbose>=5) Cout("----- Deleting CASToR objects ... -----" << endl);
766 
767  for (INTNB th = 0; th < nb_threads; th++)
768  {
769  delete p2_EventNorm[th];
770  }
771  delete[] p2_EventNorm;
772 
773  delete p_ProjectorManager;
774  delete p_OutputDataFile;
775  delete p_DataFile;
776 
777  p_ImageSpace->DeallocateImage();
778  delete p_ImageSpace;
779 
780  delete p_ImageDimensionsAndQuantification;
781  if (verbose>=5) Cout("----- CASToR objects successfully deleted -----" << endl);
782 
783  // Ending
784  #ifdef CASTOR_MPI
785  MPI_Finalize();
786  #endif
787  return EXIT_SUCCESS;
788 }
int WriteEvent(vEvent *ap_Event, int a_th)
int main(int argc, char **argv)
Definition: castor-norm.cc:100
void SetDataMode(int a_dataMode)
uint32_t GetID2(int a_line)
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
void GetEventIndexStartAndStop(int64_t *ap_indexStart, int64_t *ap_indexStop, int a_subsetNum=0, int a_NbSubsets=1)
#define MODE_HISTOGRAM
void SetNbLines(uint16_t a_value)
#define MODE_NORMALIZATION
#define Cerr(MESSAGE)
void SetOptionsCommon(const string &a_optionsCommon)
int Initialize()
A function used to initialize the manager and the projectors or system matrices it manages...
void DeallocateImage()
Free memory for the main image matrices.
int CheckParameters()
Check the initialization of member variables Call the CheckSpecificParameters() function implemente...
int FindScannerSystem(string a_scannerName)
void ShowHelp()
Definition: castor-norm.cc:38
int BuildScannerObject()
Instantiate the specific scanner object related to the modality, and set verbosity of scanner object...
int ReadInfoInHeader(bool a_affectQuantificationFlag=true)
void SetOptionsForward(const string &a_optionsForward)
void SetVerbose(int a_verboseLevel)
void InstantiateImage()
Allocate memory for the main image matrices.
void Exit(int code)
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
int InitImage(const string &a_pathToInitialImage, FLTNB a_value)
int ComputeSizeEvent()
Computation of the size of each event according to the mandatory/optional correction fields...
void IntfKeyPrintFields(Intf_fields a_IF)
Print all the keys of the Intf_fields structure passed in parameter, as well as their values for debu...
int CheckParameters()
Check if all parameters have been correctly initialized, and call the CheckParameters function of the...
int InitializeMappedFile()
Check the datafile existency, map it to memory and get the raw char* pointer. .
int InstantiateScanner()
Instantiate scanner using the related function in the scanner classes.
int LogCommandLine(int argc, char **argv)
int PROJ_WriteData()
Write/Merge chunk of data in a general data file.
vEvent * GetEvent(int64_t a_eventIndex, int a_th=0)
#define FIXED_LIST_COMPUTATION_STRATEGY
int CheckParameters()
A function used to check the parameters settings.
Inherit from iEventPET. Class for PET histogram mode events.
int BuildLUT()
Call the eponym function of the scanner class.
int Initialize()
A function used to initialize all that is needed.
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
void SetComputationStrategy(int a_computationStrategy)
void SetOptionsBackward(const string &a_optionsBackward)
void SetDataFile(vDataFile *ap_DataFile)
Singleton class that Instantiate and initialize the scanner object.
void SetNormalizationFactor(FLTNBDATA a_value)
Cast the FLTNBDATA value passed as parameter in FLTNB, and set it to the normalization term...
void SetImageDimensionsAndQuantification(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
oProjectionLine * ComputeProjectionLine(vEvent *ap_Event, int a_th)
void SetHeaderDataFileName(const string &a_headerFileName)
int CheckParameters()
A function used to check the parameters settings.
int WriteHeader()
Generate a header file according to the data output information.
int AllocateID()
Instantiate the mp_ID1 and mp_ID2 indices arrays.
int PROJ_InitFile()
Initialize the fstream objets for output writing as well as some other variables specific to the Proj...
bool NotEmptyLine()
This function is used to know if the line contains any voxel contribution.
void SetImageDimensionsAndQuantification(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
void IntfKeyInitFields(Intf_fields *ap_IF)
Init the file of an Interfile fields structure passed in parameter to their default values...
Inherit from vEvent. Used for normalization events for sensitivity computation.
void SetNbEvents(int64_t a_value)
int Initialize()
Initialization : .
int PrepareDataFile()
Store different kind of information inside arrays (data relative to specific correction as well as ba...
void SetID2(int a_line, uint32_t a_value)
This class is designed to manage and store system matrix elements associated to a vEvent...
This class is designed to manage the projection part of the reconstruction.
Interfile fields. This structure contains all the Interfile keys currently managed by CASToR Decl...
void SetBedIndex(int a_bedIndex)
void GetUserEndianness()
Check user/host computer endianness and write it to the global variable User_Endianness.
This class holds all the matrices in the image domain that can be used in the algorithm: image...
int InitOutputDirectory(const string &a_pathFout, const string &a_pathDout)
int ConvertFromString(const string &a_str, string *a_result)
Copy the &#39;a_str&#39; string in the position pointed by &#39;a_result&#39;.
FLTNB ForwardProject(FLTNB *ap_image=NULL)
void SetNormCorrectionFlagOn()
set to true the flag indicating the presence of normalization correction factors in the datafile ...
This class is designed to manage all dimensions and quantification related stuff. ...
void SetVerbose(int a_verboseLevel)
uint32_t GetID1(int a_line)
int PROJ_DeleteTmpDataFile()
Delete temporary datafile used for multithreaded output writing if needed.
int IntfReadHeader(const string &a_pathToHeaderFile, Intf_fields *ap_IntfFields, int vb)
Read an Interfile header.
void SetDefault()
A function used to set number of threads and MPI instances to 1 and bypass the CheckParameters() func...
void SetImageDimensionsAndQuantification(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
Inherit from vDataFile. Class that manages the reading of a PET input file (header + data)...
#define Cout(MESSAGE)
void SetVerbose(int a_verboseLevel)
void SetIgnoreTOFFlag(bool a_ignoreTOFFlag)
void SetID1(int a_line, uint32_t a_value)