CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
toolkits/castor-datafileShuffler.cc
Go to the documentation of this file.
1 
8 #include "gVariables.hh"
9 #include "gOptions.hh"
10 #include "oImageDimensionsAndQuantification.hh"
11 #include "vDataFile.hh"
12 #include "iDataFilePET.hh"
13 #include "iDataFileSPECT.hh"
14 #include "iDataFileCT.hh"
15 #include "sOutputManager.hh"
16 #include "sRandomNumberGenerator.hh"
17 
18 // =============================================================================================================================================
19 // =============================================================================================================================================
20 // =============================================================================================================================================
21 // H E L P F U N C T I O N S
22 // =============================================================================================================================================
23 // =============================================================================================================================================
24 // =============================================================================================================================================
25 
26 
31 void ShowHelp()
32 {
33  // Show help
34  cout << endl;
35  cout << "Usage: castor-datafileShuffler -df datafile.cdh -(f/d)out output" << endl;
36  cout << endl;
37  cout << "This program can be used to shuffle the order of events of a histogram datafile." << endl;
38  cout << endl;
39  cout << "[Mandatory parameters]:" << endl;
40  cout << " -df datafile.cdh : Give a CASToR list-mode datafile." << endl;
41  cout << " -fout name : Give the root name for all output files (no default, alternative to -dout)" << endl;
42  cout << " -dout name : Give the name of the output directory where all output files will be written (no default, alternative to -fout)" << endl;
43  cout << endl;
44  cout << "[Options]:" << endl;
45  cout << " -seed value : Give a seed for the random number generator (should be >=0)" << endl;
46  cout << " -vb value : Give the verbosity level, from 0 (no verbose) to 2 (default: 1)" << endl;
47  cout << " --help,-h,-help : Print out this help page." << endl; // managed by main
48  cout << endl;
49  #ifdef BUILD_DATE
50  cout << " Build date: " << BUILD_DATE << endl;
51  cout << endl;
52  #endif
53  #ifdef CASTOR_VERSION
54  cout << " This program is part of the CASToR release version " << CASTOR_VERSION << "." << endl;
55  cout << endl;
56  #endif
57 }
58 
59 // =============================================================================================================================================
60 // =============================================================================================================================================
61 // =============================================================================================================================================
62 // M A I N P R O G R A M
63 // =============================================================================================================================================
64 // =============================================================================================================================================
65 // =============================================================================================================================================
66 
67 int main(int argc, char** argv)
68 {
69  // ============================================================================================================
70  // MPI stuff (we make all instances except the first one returning 0 directly)
71  // ============================================================================================================
72  int mpi_rank = 0;
73  #ifdef CASTOR_MPI
74  int mpi_size = 1;
75  MPI_Init(&argc, &argv);
76  MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
77  MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
78  if (mpi_rank!=0) return 0;
79  #endif
80 
81  // No argument, then show help
82  if (argc==1)
83  {
84  ShowHelp();
85  Exit(EXIT_SUCCESS);
86  }
87 
88  // ============================================================================================================
89  // Parameterized variables with their default values
90  // ============================================================================================================
91 
92  // --------------------------------------------------------------------------------
93  // Input settings
94  // --------------------------------------------------------------------------------
95 
96  // Input datafile
97  string datafile = "";
98 
99  // --------------------------------------------------------------------------------
100  // Output settings
101  // --------------------------------------------------------------------------------
102 
103  // Output directory name.
104  string path_dout = "";
105  // Or root name
106  string path_fout = "";
107 
108  // --------------------------------------------------------------------------------
109  // Miscellaneous settings
110  // --------------------------------------------------------------------------------
111 
112  // Verbose level
113  int verbose = 1;
114  // Initial seed for random number generator
115  int64_t random_generator_seed = -1;
116 
117  // ============================================================================================================
118  // Read command-line parameters
119  // ============================================================================================================
120 
121  // Must manually increment the option index when an argument is needed after an option
122  for (int i=1; i<argc; i++)
123  {
124  // Get the option as a string
125  string option = (string)argv[i];
126 
127  // --------------------------------------------------------------------------------
128  // Miscellaneous settings
129  // --------------------------------------------------------------------------------
130 
131  // Show help
132  if (option=="-h" || option=="--help" || option=="-help")
133  {
134  ShowHelp();
135  Exit(EXIT_SUCCESS);
136  }
137  // RNG seed
138  else if (option=="-seed")
139  {
140  if (i>=argc-1)
141  {
142  Cerr("***** castor-datafileShuffler() -> Argument missing for option: " << option << endl);
143  Exit(EXIT_FAILURE);
144  }
145  if (ConvertFromString(argv[i+1], &random_generator_seed))
146  {
147  Cerr("***** castor-datafileShuffler() -> Exception when trying to read provided number '" << random_generator_seed << " for option: " << option << endl);
148  Exit(EXIT_FAILURE);
149  }
150  i++;
151  }
152  // General verbosity level
153  else if (option=="-vb")
154  {
155  if (i>=argc-1)
156  {
157  Cerr("***** castor-datafileShuffler() -> Argument missing for option: " << option << endl);
158  Exit(EXIT_FAILURE);
159  }
160  if (ConvertFromString(argv[i+1], &verbose))
161  {
162  Cerr("***** castor-datafileShuffler() -> Exception when trying to read provided verbosity level '" << verbose << " for option: " << option << endl);
163  Exit(EXIT_FAILURE);
164  }
165  i++;
166  }
167 
168  // --------------------------------------------------------------------------------
169  // Input settings
170  // --------------------------------------------------------------------------------
171 
172  // Images
173  else if (option=="-df") // This is a mandatory option
174  {
175  if (i>=argc-1)
176  {
177  Cerr("***** castor-datafileShuffler() -> Argument missing for option: " << option << endl);
178  Exit(EXIT_FAILURE);
179  }
180  datafile = (string)argv[i+1];
181  i++;
182  }
183 
184  // --------------------------------------------------------------------------------
185  // Output settings
186  // --------------------------------------------------------------------------------
187 
188  // Name of the output directory
189  else if (option=="-dout") // This is a mandatory option
190  {
191  if (i>=argc-1)
192  {
193  Cerr("***** castor-datafileShuffler() -> Argument missing for option: " << option << endl);
194  Exit(EXIT_FAILURE);
195  }
196  path_dout = argv[i+1];
197  i++;
198  }
199  // Base name of the output files
200  else if (option=="-fout") // This is a mandatory option
201  {
202  if (i>=argc-1)
203  {
204  Cerr("***** castor-datafileShuffler() -> Argument missing for option: " << option << endl);
205  Exit(EXIT_FAILURE);
206  }
207  path_fout = argv[i+1];
208  i++;
209  }
210 
211  // --------------------------------------------------------------------------------
212  // Unknown option!
213  // --------------------------------------------------------------------------------
214 
215  else
216  {
217  Cerr("***** castor-datafileShuffler() -> Unknown option '" << option << "' !" << endl);
218  Exit(EXIT_FAILURE);
219  }
220  }
221 
222  // ============================================================================================================
223  // Some checks
224  // ============================================================================================================
225 
226  // Data file
227  if (datafile=="")
228  {
229  Cerr("***** castor-datafileShuffler() -> Please provide an input datafile !" << endl);
230  Exit(EXIT_FAILURE);
231  }
232  // Output files
233  if (path_fout.empty() && path_dout.empty())
234  {
235  Cerr("***** castor-datafileShuffler() -> Please provide an output option for output files (-fout or -dout) !" << endl);
236  Exit(EXIT_FAILURE);
237  }
238  // Check that only one option has been provided
239  if (!path_fout.empty() && !path_dout.empty())
240  {
241  Cerr("***** castor-datafileShuffler() -> Please provide either output option -fout or -dout but not both !" << endl);
242  Exit(EXIT_FAILURE);
243  }
244 
245  // ============================================================================================================
246  // Create sOutputManager
247  // ============================================================================================================
248  sOutputManager* p_OutputManager = sOutputManager::GetInstance();
249  // Set verbose level
250  p_OutputManager->SetVerbose(verbose);
251  // Set MPI rank
252  p_OutputManager->SetMPIRank(mpi_rank);
253  // Initialize output directory and base name
254  if (p_OutputManager->InitOutputDirectory(path_fout, path_dout))
255  {
256  Cerr("***** castor-datafileShuffler() -> A problem occurred while initializing output directory !" << endl);
257  Exit(EXIT_FAILURE);
258  }
259  // Log command line
260  if (p_OutputManager->LogCommandLine(argc,argv))
261  {
262  Cerr("***** castor-datafileShuffler() -> A problem occurred while logging command line arguments !" << endl);
263  Exit(EXIT_FAILURE);
264  }
265 
266  // ============================================================================================================
267  // Create sScannerManager (in order to get the datafile type)
268  // ============================================================================================================
269  sScannerManager* p_ScannerManager = sScannerManager::GetInstance();
270  p_ScannerManager->SetVerbose(verbose);
271  // Get system name from the dataFile
272  string scanner_name = "";
273  if (ReadDataASCIIFile(datafile, "Scanner name", &scanner_name, 1, KEYWORD_MANDATORY))
274  {
275  Cerr("***** castor-datafileShuffler() -> A problem occurred while trying to find the system name in the datafile header !" << endl);
276  Exit(EXIT_FAILURE);
277  }
278  if (p_ScannerManager->FindScannerSystem(scanner_name) )
279  {
280  Cerr("***** castor-datafileShuffler() -> A problem occurred while searching for scanner system !" << endl);
281  Exit(EXIT_FAILURE);
282  }
283  if (p_ScannerManager->BuildScannerObject() )
284  {
285  Cerr("***** castor-datafileShuffler() -> A problem occurred during scanner object construction ! !" << endl);
286  Exit(EXIT_FAILURE);
287  }
288  if (p_ScannerManager->InstantiateScanner() )
289  {
290  Cerr("***** castor-datafileShuffler() -> A problem occurred while creating Scanner object !" << endl);
291  Exit(EXIT_FAILURE);
292  }
293  if (p_ScannerManager->GetGeometricInfoFromDataFile(datafile))
294  {
295  Cerr("***** castor-datafileShuffler() -> A problem occurred while retrieving scanner fields from the datafile header !" << endl);
296  Exit(EXIT_FAILURE);
297  }
298  if (p_ScannerManager->BuildLUT() )
299  {
300  Cerr("***** castor-datafileShuffler() -> A problem occurred while generating/reading the LUT !" << endl);
301  Exit(EXIT_FAILURE);
302  }
303 
304  // ============================================================================================================
305  // Create the input datafile
306  // ============================================================================================================
307 
308  // Create a default image dimensions and quantification object
310  p_ID->SetDefault();
311  p_ID->SetVerbose(verbose);
312  // Create the datafile based on the data type
313  vDataFile* p_DataFile = NULL;
314  if (p_ScannerManager->GetScannerType() == SCANNER_PET) p_DataFile = new iDataFilePET();
315  else if (p_ScannerManager->GetScannerType() == SCANNER_SPECT_CONVERGENT) p_DataFile = new iDataFileSPECT();
316  else if (p_ScannerManager->GetScannerType() == SCANNER_CT) p_DataFile = new iDataFileCT();
317  else
318  {
319  Cerr("***** castor-datafileShuffler() -> Unknown scanner type (" << p_ScannerManager->GetScannerType() << ") for datafile construction ! Abort." << endl);
320  Exit(EXIT_FAILURE);
321  }
322  p_DataFile->SetImageDimensionsAndQuantification(p_ID);
323  p_DataFile->SetHeaderDataFileName(datafile);
324  p_DataFile->SetVerbose(verbose);
325  p_DataFile->SetBedIndex(0);
326  bool do_not_affect_quantification = false;
327  if (p_DataFile->ReadInfoInHeader(do_not_affect_quantification))
328  {
329  Cerr("***** castor-datafileShuffler() -> A problem occurred during datafile header reading ! Abort." << endl);
330  Exit(EXIT_FAILURE);
331  }
332  if (p_DataFile->CheckParameters())
333  {
334  Cerr("***** castor-datafileShuffler() -> A problem occurred while checking datafile parameters ! Abort." << endl);
335  Exit(EXIT_FAILURE);
336  }
337  if (p_DataFile->ComputeSizeEvent())
338  {
339  Cerr("***** castor-datafileShuffler() -> A problem occurred in datafile initialization ! Abort." << endl);
340  Exit(EXIT_FAILURE);
341  }
342  if (p_DataFile->InitializeMappedFile())
343  {
344  Cerr("***** castor-datafileShuffler() -> A problem occurred in datafile initialization ! Abort." << endl);
345  Exit(EXIT_FAILURE);
346  }
347  if (p_DataFile->PrepareDataFile())
348  {
349  Cerr("***** castor-datafileShuffler() -> A problem occurred in datafile preparation ! Abort." << endl);
350  Exit(EXIT_FAILURE);
351  }
352  // Check if datafile is a histogram, otherwise, throw an error
353  if (p_DataFile->GetDataMode()!=MODE_HISTOGRAM)
354  {
355  Cerr("***** castor-datafileShuffler() -> The input datafile is not a histogram, this program is only suitable to histogram files !" << endl);
356  Exit(EXIT_FAILURE);
357  }
358 
359  // ============================================================================================================
360  // Create the output datafile
361  // ============================================================================================================
362 
363  // Create output datafile object
364  vDataFile* p_OutputDataFile = NULL;
365  if (p_ScannerManager->GetScannerType() == SCANNER_PET) p_OutputDataFile = new iDataFilePET();
366  else if (p_ScannerManager->GetScannerType() == SCANNER_SPECT_CONVERGENT) p_OutputDataFile = new iDataFileSPECT();
367  else if (p_ScannerManager->GetScannerType() == SCANNER_CT) p_OutputDataFile = new iDataFileCT();
368  // Build output data file from the input one
369  if (p_OutputDataFile->SetParametersFrom(p_DataFile))
370  {
371  Cerr("***** castor-datafileShuffler() -> An error occurred while setting parameters of output file from input file !" << endl);
372  Exit(EXIT_FAILURE);
373  }
374  // Open output file
375  if (p_OutputDataFile->OpenFileForWriting())
376  {
377  Cerr("***** castor-datafileShuffler() -> An error occurred while opening file for writing !" << endl);
378  Exit(EXIT_FAILURE);
379  }
380 
381  // ============================================================================================================
382  // Build random event indices list
383  // ============================================================================================================
384 
385  // Get the number of events
386  int64_t nb_events = p_DataFile->GetSize();
387  // Verbose
388  if (verbose>=1) Cout("castor-datafileShuffler() -> Build a random indices list for " << nb_events << " events" << endl);
389  if (verbose>=2) Cout(" --> Allocate random indices list" << endl);
390  // Allocate the list of indices and a vector of random mnumbers
391  vector<int64_t> p_random_indices(nb_events);
392  vector<int64_t> p_random_numbers(nb_events);
393  // Fill it in the correct order
394  for (int64_t i=0; i<nb_events; i++) p_random_indices[i] = i;
395  // Seed of the random generator
396  if (random_generator_seed==-1)
397  {
398  // Not provided so we set the seed randomly
399  mt19937 rd(time(NULL));
400  random_generator_seed = rd();
401  }
402  else
403  {
404  // Check seed non negativity
405  if (random_generator_seed<0)
406  {
407  Cerr("***** castor-datafileShuffler() -> Must provide a random generator seed that is positive !" << endl);
408  Exit(EXIT_FAILURE);
409  }
410  }
411  // Verbose
412  if (verbose>=2) Cout(" --> General random seed: " << random_generator_seed << endl);
413  // Create a random generator to generate as many random seeds as threads
414  mt19937 random_seeder(random_generator_seed);
415  // Create random generators (as many as threads)
416  int nb_threads = 1;
417  #ifdef CASTOR_OMP
418  nb_threads = omp_get_num_procs();
419  if (verbose>=2) Cout(" --> Use " << nb_threads << " threads" << endl);
420  #endif
421  mt19937** p_random_generators = (mt19937**)malloc(nb_threads*sizeof(mt19937*));
422  for (int th=0; th<nb_threads; th++) p_random_generators[th] = new mt19937(random_seeder());
423  // Create the uniform integer distribution between 0 and the size of the datafile minus 1
424  uniform_int_distribution<int64_t> random_distribution(0, nb_events-1);
425  // Verbose
426  if (verbose>=2) Cout(" --> Shoot random numbers" << endl);
427  // Shoot the random numbers and store them
428  int64_t printing_index = 0;
429  int64_t i = 0;
430  #pragma omp parallel for private(i) schedule(static)
431  for (i=0; i<nb_events; i++)
432  {
433  // The thread index
434  int th = 0;
435  #ifdef CASTOR_OMP
436  th = omp_get_thread_num();
437  #endif
438  // Verbose
439  if (th==0 && verbose>=2)
440  {
441  if (printing_index%5000==0)
442  {
443  int percent = ((int)( ((FLTNB)(i)) * 100. / ((FLTNB)(nb_events/nb_threads)) ));
444  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 "
445  << percent << " % " << flush;
446  }
447  printing_index++;
448  }
449  // Shoot the random number and store it
450  p_random_numbers[i] = random_distribution(*p_random_generators[th]);
451  }
452  // Verbose
453  if (verbose>=2)
454  {
455  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"
456  << " --> 100 % " << endl;
457  Cout(" --> Apply random permutations" << endl);
458  }
459  // Shuffle the list using random permutations
460  printing_index = 0;
461  for (int64_t i=0; i<nb_events; i++)
462  {
463  // Verbose
464  if (verbose>=2)
465  {
466  if (printing_index%5000==0)
467  {
468  int percent = ((int)( ((FLTNB)(i)) * 100. / ((FLTNB)(nb_events)) ));
469  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 "
470  << percent << " % " << flush;
471  }
472  printing_index++;
473  }
474  // Permute the i^th event with a random event
475  int64_t index = p_random_numbers[i];
476  int64_t content = p_random_indices[i];
477  p_random_indices[i] = p_random_indices[index];
478  p_random_indices[index] = content;
479  }
480  // Verbose
481  if (verbose>=2) 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"
482  << " --> 100 % " << endl;
483 
484  // ============================================================================================================
485  // Write shuffled output file
486  // ============================================================================================================
487 
488  // Verbose
489  if (verbose>=1) Cout("castor-datafileShuffler() -> Read and write events" << endl);
490  // Start the loop over events
491  printing_index = 0;
492  for (int64_t i=0; i<nb_events; i++)
493  {
494  // Verbose
495  if (verbose>=2)
496  {
497  if (printing_index%10000==0)
498  {
499  int percent = ((int)( ((FLTNB)(i)) * 100. / ((FLTNB)(nb_events)) ));
500  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 "
501  << percent << " % " << flush;
502  }
503  printing_index++;
504  }
505  // Get the random event
506  int64_t index = p_random_indices[i];
507  vEvent* event = p_DataFile->GetEvent(index);
508  if (event==NULL)
509  {
510  Cerr("***** castor-datafileShuffler() -> An error occurred while getting the event from index " << index << " !" << endl);
511  if (p_OutputDataFile->CloseFile())
512  Cerr("***** castor-datafileShuffler() -> An error occurred while closing file during writing !" << endl);
513  Exit(EXIT_FAILURE);
514  }
515  // Write the event
516  p_OutputDataFile->WriteEvent(event);
517  }
518  // Verbose
519  if (verbose>=2) 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"
520  << " --> 100 % " << endl;
521  // Close file
522  if (p_OutputDataFile->CloseFile())
523  {
524  Cerr("***** castor-datafileShuffler() -> An error occurred while closing file after writing !" << endl);
525  Exit(EXIT_FAILURE);
526  }
527  // Set number of events
528  p_OutputDataFile->SetNbEvents(nb_events);
529  // Write header
530  if (p_OutputDataFile->WriteHeader())
531  {
532  Cerr("***** castor-datafileShuffler() -> An error occurred while writing output header file !" << endl);
533  Exit(EXIT_FAILURE);
534  }
535 
536  // ============================================================================================================
537  // Exit
538  // ============================================================================================================
539 
540  // Ending
541  #ifdef CASTOR_MPI
542  MPI_Finalize();
543  #endif
544  return EXIT_SUCCESS;
545 }
This class is designed to be a mother virtual class for DataFile.
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
#define MODE_HISTOGRAM
#define Cerr(MESSAGE)
int CheckParameters()
Check the initialization of member variables Call the CheckSpecificParameters() function implemente...
virtual int WriteHeader()=0
This function is implemented in child classes. Generate a header file according to the data output ...
int FindScannerSystem(string a_scannerName)
int BuildScannerObject()
Instantiate the specific scanner object related to the modality, and set verbosity of scanner object...
int SetParametersFrom(vDataFile *ap_DataFile)
virtual int WriteEvent(vEvent *ap_Event, int a_th=0)=0
int ReadInfoInHeader(bool a_affectQuantificationFlag=true)
void SetVerbose(int a_verboseLevel)
virtual int ComputeSizeEvent()=0
This function is implemented in child classes Computation of the size of each event according to th...
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 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)
vEvent * GetEvent(int64_t a_eventIndex, int a_th=0)
int CloseFile()
Close as many binary file stream for writing.
#define SCANNER_SPECT_CONVERGENT
int BuildLUT()
Call the eponym function of the scanner class.
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
Singleton class that Instantiate and initialize the scanner object.
virtual int PrepareDataFile()=0
This function is implemented in child classes Store different kind of information inside arrays (da...
Inherit from vDataFile. Class that manages the reading of a SPECT input file (header + data)...
void SetHeaderDataFileName(const string &a_headerFileName)
int OpenFileForWriting(string a_suffix="")
#define KEYWORD_MANDATORY
void SetNbEvents(int64_t a_value)
void SetBedIndex(int a_bedIndex)
Mother class for the Event objects.
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;.
Inherit from vDataFile. Class that manages the reading of a CT input file (header + data)...
This class is designed to manage all dimensions and quantification related stuff. ...
int main(int argc, char **argv)
void SetVerbose(int a_verboseLevel)
void SetDefault()
A function used to set number of threads and MPI instances to 1 and bypass the CheckParameters() func...
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...
void SetImageDimensionsAndQuantification(oImageDimensionsAndQuantification *ap_ImageDimensionsAndQuantification)
Inherit from vDataFile. Class that manages the reading of a PET input file (header + data)...
int GetGeometricInfoFromDataFile(string a_pathToDataFilename)
#define Cout(MESSAGE)