CASToR  3.2
Tomographic Reconstruction (PET/SPECT/CT)
src/datafile/vDataFile.cc
Go to the documentation of this file.
1 
9 #include "vDataFile.hh"
10 
11 // =====================================================================
12 // ---------------------------------------------------------------------
13 // ---------------------------------------------------------------------
14 // =====================================================================
15 
17 {
18  mp_ID = NULL;
19  m_verbose = -1;
20 
21  // Variables related to the acquisition
22  m2p_dataFile = NULL;
23  m_headerFileName = "";
24  m_dataFileName = "";
25  m_scannerName = "";
29  m_nbEvents = -1;
30  m_bedIndex = -1;
32  m_bedPositionFlag = false;
33  m_startTimeInSec = 0.;
34  m_durationInSec = -1.;
36  m_sizeEvent = -1;
37 
38  // Default POI (meaning we do not have any)
39  mp_POIResolution[0] = -1.;
40  mp_POIResolution[1] = -1.;
41  mp_POIResolution[2] = -1.;
42  mp_POIDirectionFlag[0] = false;
43  mp_POIDirectionFlag[1] = false;
44  mp_POIDirectionFlag[2] = false;
45  m_POIInfoFlag = false;
46  m_ignorePOIFlag = false;
47 
48  // Variable related to Buffer/Container arrays
49  m2p_BufferEvent = NULL;
50  m_mpi1stEvent = -1;
51  m_mpiLastEvent = -1;
52  m_mpiNbEvents = -1;
53  mp_MappedFile = NULL;
54  mp_mappedMemory = NULL;
55 
56  // Additional data
57  m_loadedAdditional = false;
58  m2p_additionalData = NULL;
60  mp_additionalDataSize = NULL;
61 
62  // Custon data
65 }
66 
67 // =====================================================================
68 // ---------------------------------------------------------------------
69 // ---------------------------------------------------------------------
70 // =====================================================================
71 
73 {
74  // Free the (vEvent) buffer event
75  if (m2p_BufferEvent)
76  {
77  for(int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
78  if (m2p_BufferEvent[th]) delete m2p_BufferEvent[th];
79  delete[] m2p_BufferEvent;
80  }
81  // Close datafiles and delete them
82  if (m2p_dataFile)
83  {
84  for(int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
85  if (m2p_dataFile[th]) m2p_dataFile[th]->close();
86  delete m2p_dataFile;
87  }
88  // Delete the mapped memory object
89  if (mp_MappedFile!=NULL) delete mp_MappedFile;
90  // Free additional data
92  {
93  for (int nb=0; nb<m_nbAdditionalData; nb++) if (m2p_additionalData[nb]) free(m2p_additionalData[nb]);
94  free(m2p_additionalData);
95  m2p_additionalData = NULL;
96  }
97 }
98 
99 // =====================================================================
100 // ---------------------------------------------------------------------
101 // ---------------------------------------------------------------------
102 // =====================================================================
103 
104 int vDataFile::ReadInfoInHeader(bool a_affectQuantificationFlag)
105 {
106  // Verbose
108  if (m_verbose>=VERBOSE_DETAIL) Cout("vDataFile::ReadInfoInHeader() -> Read datafile header from '" << m_headerFileName << " ...'" << endl);
109 
110  // Check that the image dimensions and quantification object has been set if the a_affectQuantificationFlag is true
111  if (a_affectQuantificationFlag && mp_ID==NULL)
112  {
113  Cerr("***** vDataFile::ReadInfoInHeader() -> Image dimensions and quantification object has not been set while it is asked to affect" << endl);
114  Cerr(" its parameters from information read into the header !" << endl);
115  return 1;
116  }
117 
118  // Read mandatory general fields in the header, check if errors (either mandatory tag not found or issue during data reading/conversion (==1) )
119  if (ReadDataASCIIFile(m_headerFileName, "Number of events", &m_nbEvents, 1, KEYWORD_MANDATORY) )
120  {
121  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while trying to read number of events in the header data file !" << endl);
122  return 1;
123  }
125  {
126  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while trying to read scanner name in the header data file !" << endl);
127  return 1;
128  }
129 
130  // Get data file name
132  {
133  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while trying to read data filename in the header data file !" << endl);
134  return 1;
135  }
136  // If it is an absolute path (start with a OS_SEP) then let it as is, otherwise past the path of the header file name (because we suppose the two
137  // files are side by side).
138  // This is currently only unix compatible...
139  if (m_dataFileName.substr(0,1)!=OS_SEP && m_headerFileName.find(OS_SEP)!=string::npos)
140  {
141  // Extract the path from the header file name
142  size_t last_slash = m_headerFileName.find_last_of(OS_SEP);
143  // Paste the path to the data file name
144  m_dataFileName = m_headerFileName.substr(0,last_slash+1) + m_dataFileName;
145  }
146 
147  // For data mode, multiple declarations are allowed, so we get the mode as a string and do some checks
148  string data_mode = "";
149  if (ReadDataASCIIFile(m_headerFileName, "Data mode", &data_mode, 1, KEYWORD_MANDATORY) )
150  {
151  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while trying to read data mode in the header data file !" << endl);
152  return 1;
153  }
154  if ( data_mode=="LIST" || data_mode=="LISTMODE" || data_mode=="LIST-MODE" ||
155  data_mode=="list" || data_mode=="listmode" || data_mode=="list-mode" || data_mode=="0" ) m_dataMode = MODE_LIST;
156  else if ( data_mode=="HISTOGRAM" || data_mode=="histogram" || data_mode=="Histogram" ||
157  data_mode=="HISTO" || data_mode=="histo" || data_mode=="Histo" || data_mode=="1" ) m_dataMode = MODE_HISTOGRAM;
158  else if ( data_mode=="NORMALIZATION" || data_mode=="normalization" || data_mode=="Normalization" ||
159  data_mode=="NORM" || data_mode=="norm" || data_mode=="Norm" || data_mode=="2" ) m_dataMode = MODE_NORMALIZATION;
160  else
161  {
162  Cerr("***** vDataFile::ReadInfoInHeader() -> Unknown data mode '" << data_mode << "' found in header file !" << endl);
163  return 1;
164  }
165 
166  // For data type, multiple declarations are allowed, so we get the mode as a string and do some checks
167  string data_type = "";
168  if (ReadDataASCIIFile(m_headerFileName, "Data type", &data_type, 1, KEYWORD_MANDATORY) )
169  {
170  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while trying to read data type in the header data file !" << endl);
171  return 1;
172  }
173  if ( data_type=="PET" || data_type=="pet" || data_type=="0" )
174  {
177  }
178  else if ( data_type=="SPECT" || data_type=="spect" || data_type=="1" )
179  {
182  }
183  else if ( data_type=="CT" || data_type=="ct" || data_type=="2" )
184  {
187  }
188  else
189  {
190  Cerr("***** vDataFile::ReadInfoInHeader() -> Unknown data type '" << data_type << "' found in header file !" << endl);
191  return 1;
192  }
193 
194  // Get start time and duration (optional for the normalization mode
196  {
198  {
199  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while trying to read acquisition start time in the header data file !" << endl);
200  return 1;
201  }
203  {
204  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while trying to read acquisition stop time in the header data file !" << endl);
205  return 1;
206  }
207  }
208  else
209  {
210  if (ReadDataASCIIFile(m_headerFileName, "Start time (s)", &m_startTimeInSec, 1, KEYWORD_OPTIONAL) == 1 )
211  {
212  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while reading acquisition start time in the header data file !" << endl);
213  return 1;
214  }
215  if (ReadDataASCIIFile(m_headerFileName, "Duration (s)", &m_durationInSec, 1, KEYWORD_OPTIONAL) == 1 )
216  {
217  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while reading acquisition stop time in the header data file !" << endl);
218  return 1;
219  }
220  }
221 
222  string gate_list_durations_in_sec = "";
223  if (ReadDataASCIIFile(m_headerFileName, "Gate duration (s)", &gate_list_durations_in_sec, 1, KEYWORD_OPTIONAL) == 1 )
224  {
225  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while reading acquisition gate duration in the header data file !" << endl);
226  return 1;
227  }
228 
229 
230  // Set the acquisition timing to the quantification factors
231  if (a_affectQuantificationFlag && mp_ID->SetAcquisitionTime(m_bedIndex, m_startTimeInSec, m_durationInSec, gate_list_durations_in_sec))
232  {
233  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while passing acquisition start and stop time to the ImageDimensionsAndQuantification !" << endl);
234  return 1;
235  }
236 
237  // Read optional bed relative position
238  int read_bed_position_status = ReadDataASCIIFile(m_headerFileName, "Horizontal bed relative position (mm)", &m_relativeBedPosition, 1, KEYWORD_OPTIONAL);
239  if (read_bed_position_status==KEYWORD_OPTIONAL_SUCCESS) m_bedPositionFlag = true;
240  else if (read_bed_position_status==KEYWORD_OPTIONAL_SUCCESS)
241  {
242  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while reading the 'Bed relative position (mm)' field in the data file header !" << endl);
243  return 1;
244  }
245 
246  // Read optional fields in the header, check if errors (issue during data reading/conversion (==1) )
247  if (ReadDataASCIIFile(m_headerFileName, "Calibration factor", &m_calibrationFactor, 1, KEYWORD_OPTIONAL) == 1 ||
249  ReadDataASCIIFile(m_headerFileName, "POI correction flag", &m_POIInfoFlag, 3, KEYWORD_OPTIONAL) == 1 )
250  {
251  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while reading optional field in the header data file !" << endl);
252  return 1;
253  }
254 
255  // Read fields specific to the modality (call to the related function implemented in child classes)
256  if (ReadSpecificInfoInHeader(a_affectQuantificationFlag) )
257  {
258  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while trying to read modality-specific informations from the header data file !" << endl);
259  return 1;
260  }
261 
262  // Give the calibration factor to the oImageDimensionsAndQuantification that manages the quantification factors
263  if (a_affectQuantificationFlag && mp_ID->SetCalibrationFactor(m_bedIndex, m_calibrationFactor))
264  {
265  Cerr("***** vDataFile::ReadSpecificInfoInHeader() -> A problem occurred while setting the calibration factor to oImageDimensionsAndQuantification !" << endl);
266  return 1;
267  }
268 
269  if (ReadDataASCIIFile(m_headerFileName, "Custom INT data", &m_nbCustomINTData, 1, KEYWORD_OPTIONAL) == 1 )
270  {
271  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while reading 'Custom INT data' field in the header data file !" << endl);
272  return 1;
273  }
274 
275  if (ReadDataASCIIFile(m_headerFileName, "Custom FLT data", &m_nbCustomFLTData, 1, KEYWORD_OPTIONAL) == 1 )
276  {
277  Cerr("***** vDataFile::ReadInfoInHeader() -> Error while reading 'Custom FLT data' field in the header data file !" << endl);
278  return 1;
279  }
280 
281  return 0;
282 }
283 
284 // =====================================================================
285 // ---------------------------------------------------------------------
286 // ---------------------------------------------------------------------
287 // =====================================================================
288 
289 int vDataFile::InitializeAdditionalData(const string& a_pathToAdditionalData)
290 {
291  // Check if already loaded
292  if (m_loadedAdditional)
293  {
294  Cerr("!!!!! vDataFile::InitializeAdditionalData() -> Additional data already loaded; do nothing." << endl);
295  return 0;
296  }
297  // Flag as loaded
298  m_loadedAdditional = true;
299  // Allocate and read only if some files are provided.
300  if (a_pathToAdditionalData!="")
301  {
302  // Other additional data matrices may have been instantiated using the GetNexAdditionalDataMatrix() function
303  int already_existing_additional_data = 0;
304  if (m_nbAdditionalData>0) already_existing_additional_data = m_nbAdditionalData;
305  // We will parse the input string to separate the file names separated by commas, if any
306  vector<string> p_file_names;
307  string list_of_names = a_pathToAdditionalData;
308  size_t comma = list_of_names.find_first_of(",");
309  // Loop on the filenames as long as we have a comma
310  while (comma!=string::npos)
311  {
312  // Get the file name before the colon
313  p_file_names.push_back(list_of_names.substr(0,comma));
314  // Get the rest of the names after the colon
315  list_of_names = list_of_names.substr(comma+1);
316  // Search for the next comma, if any
317  comma = list_of_names.find_first_of(",");
318  }
319  // Get the last file name
320  p_file_names.push_back(list_of_names);
321  // Get the number of file names
322  int nb_additional_data_to_read = p_file_names.size();
323  // Total number of additional data
324  m_nbAdditionalData = nb_additional_data_to_read + already_existing_additional_data;
325  // Reallocate for the total number of additional data if already existing data
326  if (already_existing_additional_data>1)
327  {
330  }
331  // Allocate for the number of additional data to be read
332  else
333  {
334  m2p_additionalData = (FLTNB**)malloc(m_nbAdditionalData*sizeof(FLTNB*));
336  }
337  // Start reading the data
338  for (int nb=0; nb<nb_additional_data_to_read; nb++)
339  {
340  // Verbose
341  if (m_verbose>=3) Cout("vDataFile::InitializeAdditionalData() -> From file '" << p_file_names.at(nb) << "'"<< endl);
342  // Search in the header file for the number of TOF bins
343  INTNB nb_tof_bins_in_additional_data = 1;
344  string tof_bin_key_word = "Histo TOF number of bins";
345  if (ReadDataASCIIFile(p_file_names.at(nb), tof_bin_key_word, &nb_tof_bins_in_additional_data, 1, KEYWORD_OPTIONAL)==KEYWORD_OPTIONAL_ERROR)
346  {
347  Cerr("***** vDataFile::InitializeAdditionalData() -> An error occured while searching for the optional '" << tof_bin_key_word << "' keyword !" << endl);
348  return 1;
349  }
350  // Compute the expected size of the additional data matrix
351  mp_additionalDataSize[already_existing_additional_data+nb] = m_nbEvents * nb_tof_bins_in_additional_data;
352  // Allocate the additional data matrix
353  m2p_additionalData[already_existing_additional_data+nb] = (FLTNB*)malloc(m_nbEvents*sizeof(FLTNB));
354  // Interfile data reading (interpolation not allowed)
355  if (IntfReadAdditionalData(p_file_names.at(nb), m2p_additionalData[already_existing_additional_data+nb], mp_additionalDataSize[already_existing_additional_data+nb], m_verbose))
356  {
357  Cerr("***** vDataFile::InitializeAdditionalData() -> Error reading interfile additional data '" << p_file_names.at(nb) << "' !" << endl);
358  return 1;
359  }
360  }
361  }
362  // No additional data provided
363  else m_nbAdditionalData = 0;
364 
365  // End
366  return 0;
367 }
368 
369 // =====================================================================
370 // ---------------------------------------------------------------------
371 // ---------------------------------------------------------------------
372 // =====================================================================
373 
375 {
376  // Verbose
377  if (m_verbose>=VERBOSE_DETAIL) Cout("vDataFile::GetNewAdditionalDataMatrix() -> Create a new additional data matrix with " << a_nbDataPerEvent << " data per event" << endl);
378  // Update number of additional data based on the possible existence of other additional data matrices
380  else m_nbAdditionalData = 1;
381  // Reallocate for the total number of additional data if already existing data
382  if (m_nbAdditionalData>1)
383  {
386  }
387  // Allocate for the number of additional data to be read
388  else
389  {
390  m2p_additionalData = (FLTNB**)malloc(m_nbAdditionalData*sizeof(FLTNB*));
392  }
393  // Compute data size
394  mp_additionalDataSize[m_nbAdditionalData-1] = m_nbEvents * a_nbDataPerEvent;
395  // Allocate the additional data matrix
397  // Return this pointer
399 }
400 
401 // =====================================================================
402 // ---------------------------------------------------------------------
403 // ---------------------------------------------------------------------
404 // =====================================================================
405 
406 int vDataFile::SetParametersFrom(vDataFile* ap_DataFile)
407 {
408  // Verbose
409  m_verbose = ap_DataFile->GetVerbose();
410  // Variables related to the acquisition
411  m2p_dataFile = NULL;
412  m_scannerName = ap_DataFile->GetScannerName();
413  m_dataMode = ap_DataFile->GetDataMode();
414  m_dataType = ap_DataFile->GetDataType();
415  m_dataSpec = ap_DataFile->GetDataSpec();
416  m_bedIndex = ap_DataFile->GetBedIndex();
418  m_bedPositionFlag = ap_DataFile->GetBedPositionFlag();
419  m_startTimeInSec = ap_DataFile->GetStartTime();
420  m_durationInSec = ap_DataFile->GetDuration();
421  m_calibrationFactor = ap_DataFile->GetCalibrationFactor();
422  m_sizeEvent = ap_DataFile->GetEventSize();
423  // Default POI (meaning we do not have any)
424  m_POIInfoFlag = ap_DataFile->GetPOIInfoFlag();
425  mp_POIResolution[0] = ap_DataFile->GetPOIResolution()[0];
426  mp_POIResolution[1] = ap_DataFile->GetPOIResolution()[1];
427  mp_POIResolution[2] = ap_DataFile->GetPOIResolution()[2];
428  mp_POIDirectionFlag[0] = ap_DataFile->GetPOIDirectionFlag()[0];
429  mp_POIDirectionFlag[1] = ap_DataFile->GetPOIDirectionFlag()[1];
430  mp_POIDirectionFlag[2] = ap_DataFile->GetPOIDirectionFlag()[2];
431  // Call the specific function
432  if (SetSpecificParametersFrom(ap_DataFile))
433  {
434  Cerr("***** vDataFile::SetParametersFrom() -> An error occurred while setting specific parameters fro mthe provided datafile !" << endl);
435  return 1;
436  }
437  // End
438  return 0;
439 }
440 
441 // =====================================================================
442 // ---------------------------------------------------------------------
443 // ---------------------------------------------------------------------
444 // =====================================================================
445 
447 {
448  // Verbose
450  if (m_verbose>=VERBOSE_DETAIL) Cout("vDataFile::CheckParameters() -> Check mandatory parameters" << endl);
451  // Check mandatory parameters
452  if (mp_ID == NULL)
453  {
454  Cerr("***** vDataFile::CheckParameters() -> ImageDimensionsAndQuantification object not initialized !" << endl);
455  return 1;
456  }
457  if (m_headerFileName == "")
458  {
459  Cerr("***** vDataFile::CheckParameters() -> String containing path to header file not initialized !" << endl);
460  return 1;
461  }
462  if (m_dataFileName == "")
463  {
464  Cerr("***** vDataFile::CheckParameters() -> String containing path to raw data file not initialized !" << endl);
465  return 1;
466  }
467  if (m_nbEvents<0)
468  {
469  Cerr("***** vDataFile::CheckParameters() -> Number of events incorrectly initialized !" << endl);
470  return 1;
471  }
473  {
474  Cerr("***** vDataFile::CheckParameters() -> Data mode incorrectly initialized !" << endl);
475  return 1;
476  }
478  {
479  Cerr("***** vDataFile::CheckParameters() -> Acquisition duration (s) incorrectly initialized !" << endl);
480  return 1;
481  }
483  {
484  Cerr("***** vDataFile::CheckParameters() -> Data type incorrectly initialized !" << endl);
485  return 1;
486  }
488  {
489  Cerr("***** vDataFile::CheckParameters() -> Data physical property incorrectly initialized !" << endl);
490  return 1;
491  }
492  if (m_bedIndex<0)
493  {
494  Cerr("***** vDataFile::CheckParameters() -> Bed position index incorrectly initialized !" << endl);
495  return 1;
496  }
497  if (m_verbose<0)
498  {
499  Cerr("***** vDataFile::CheckParameters() -> Verbosity incorrectly initialized !" << endl);
500  return 1;
501  }
502  // Call to the related function implemented in child classes
504  {
505  Cerr("***** vDataFile::CheckParameters() -> Error while checking specific parameters !" << endl);
506  return 1;
507  }
508  // End
509  return 0;
510 }
511 
512 // =====================================================================
513 // ---------------------------------------------------------------------
514 // ---------------------------------------------------------------------
515 // =====================================================================
516 
518 {
519  // Verbose
521  if (m_verbose>=VERBOSE_DETAIL) Cout("vDataFile::CheckConsistencyWithAnotherBedDataFile() -> Check consistency between this and the provided datafiles" << endl);
522  // Check data type
523  if (m_dataType!=ap_DataFile->GetDataType())
524  {
525  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> Data types are inconsistent !" << endl);
526  return 1;
527  }
528  // Check data mode
529  if (m_dataMode!=ap_DataFile->GetDataMode())
530  {
531  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> Data modes are inconsistent !" << endl);
532  return 1;
533  }
534  // For histogram and normalization modes, check the number of events (i.e. the number of histogram bins)
536  {
537  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> Total number of events are inconsistent !" << endl);
538  return 1;
539  }
540  // Check the scanner name
541  if (m_scannerName!=ap_DataFile->GetScannerName())
542  {
543  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> Scanner names are inconsistent !" << endl);
544  return 1;
545  }
546  // Check the calibration factor
547  if (m_calibrationFactor!=ap_DataFile->GetCalibrationFactor())
548  {
549  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> Calibration factors are inconsistent !" << endl);
550  return 1;
551  }
552  // Check event size
553  if (m_sizeEvent!=ap_DataFile->GetEventSize())
554  {
555  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> Events sizes are inconsistent !" << endl);
556  return 1;
557  }
558  // Check POI info flag
559  if (m_POIInfoFlag!=ap_DataFile->GetPOIInfoFlag())
560  {
561  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> POI flags are inconsistent !" << endl);
562  return 1;
563  }
564  // Check POI resolutions
565  if ( (mp_POIResolution[0]!=ap_DataFile->GetPOIResolution()[0]) ||
566  (mp_POIResolution[1]!=ap_DataFile->GetPOIResolution()[1]) ||
567  (mp_POIResolution[2]!=ap_DataFile->GetPOIResolution()[2]) )
568  {
569  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> POI resolutions are inconsistent !" << endl);
570  return 1;
571  }
572  // Check the bed position flag
573  if (m_bedPositionFlag!=ap_DataFile->GetBedPositionFlag())
574  {
575  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> Bed relative positions must be provided for all beds or not at all !" << endl);
576  return 1;
577  }
578  // Check the number of additional data files
579  if (m_nbAdditionalData!=ap_DataFile->GetNbAdditionalData())
580  {
581  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> Number of additional data files are inconsistent !" << endl);
582  return 1;
583  }
584  // Call specific function
586  {
587  Cerr("***** vDataFile::CheckConsistencyWithAnotherBedDataFile() -> Inconsistency detected for specific characteristics !" << endl);
588  return 1;
589  }
590  // End
591  return 0;
592 }
593 
594 // =====================================================================
595 // ---------------------------------------------------------------------
596 // ---------------------------------------------------------------------
597 // =====================================================================
598 
600 {
601  // Verbose
603  if (m_verbose>=VERBOSE_NORMAL) Cout("vDataFile::InitializeMappedFile() -> Map datafile to memory" << endl);
604 
605  // Check file size consistency here (this is a pure virtual function implemented by children)
607  {
608  Cerr("***** vDataFile::InitializeMappedFile() -> A problem occurred while checking file size consistency !" << endl);
609  return 1;
610  }
611 
612  // Create mapped file
614  // Open the file
616  {
617  Cerr("***** vDataFile::InitializeMappedFile() -> Failed to open data file '" << m_dataFileName << "' !" << endl);
618  Cerr(" Provided in data file header '" << m_headerFileName << "'." << endl);
619  return 1;
620  }
621 
622  // Get raw pointer to mapped memory
624 
625  // ------------------ Compute here the piece of file that each MPI instance manages --------------------
626 
627  // 1. Compute the number of events that each instance has to manage
628  int64_t instance_size = m_nbEvents / mp_ID->GetMPISize();
629  // All instances manage an equal part of the file but the last one which also manages the few last events
630  if (mp_ID->GetMPIRank()!=mp_ID->GetMPISize()-1) m_mpiNbEvents = instance_size;
631  else m_mpiNbEvents = instance_size + (m_nbEvents - instance_size*mp_ID->GetMPISize());
632  // 2. Compute the first event managed by the instance
633  m_mpi1stEvent = mp_ID->GetMPIRank() * instance_size;
634  // 3. Compute the last event managed by the instance
635  m_mpiLastEvent = m_mpi1stEvent + m_mpiNbEvents - 1;
636 
637  // End
638  return 0;
639 }
640 
641 
642 // =====================================================================
643 // ---------------------------------------------------------------------
644 // ---------------------------------------------------------------------
645 // =====================================================================
646 
647 void vDataFile::Describe()
648 {
650  if (m_verbose==0) return;
651  // Describe the datafile
652  Cout("vDataFile::Describe() -> Here is some generic content of the datafile" << endl);
653  Cout(" --> Header file name: " << m_headerFileName << endl);
654  Cout(" --> Data file name: " << m_dataFileName << endl);
655  Cout(" --> Data type: " << GetDataTypeToString() << endl);
656  Cout(" --> Data mode: " << GetDataModeToString() << endl);
657  Cout(" --> Data spec: " << GetDataSpecToString() << endl);
658  Cout(" --> Scanner name: " << m_scannerName << endl);
659  Cout(" --> Number of specific events: " << m_nbEvents << endl);
660  Cout(" --> Size of an event: " << m_sizeEvent << " bytes" << endl);
661  Cout(" --> Time start: " << m_startTimeInSec << " sec" << endl);
662  Cout(" --> Duration: " << m_durationInSec << " sec" << endl);
663  Cout(" --> Calibration factor: " << m_calibrationFactor << endl);
664  if (m_bedPositionFlag) Cout(" --> Relative axial bed position: " << m_relativeBedPosition << " mm" << endl);
665  // Call the specific function
667 }
668 
669 // =====================================================================
670 // ---------------------------------------------------------------------
671 // ---------------------------------------------------------------------
672 // =====================================================================
673 
674 int vDataFile::OpenFileForWriting(string a_suffix)
675 {
676  // Check that file is not already open
677  if (m2p_dataFile && m2p_dataFile[0]->is_open())
678  {
679  Cerr("***** vDataFile::OpenFileForWriting() -> Output data file is already open !" << endl);
680  return 1;
681  }
682 
683  // Allocate the m2p_dataFile for each thread
684  m2p_dataFile = new fstream*[1];
685 
686  // Set the name of output files
688  string path_name = p_OutMgr->GetPathName();
689  string base_name = p_OutMgr->GetBaseName();
690  m_headerFileName = path_name + base_name + a_suffix + ".cdh";
691  m_dataFileName = path_name + base_name + a_suffix + ".cdf";
692 
693  // Verbose
694  if (m_verbose>=2) Cout("vDataFile::OpenFileForWriting() -> Output data file header is '" << m_headerFileName << "'" << endl);
695 
696  // Open file
697  m2p_dataFile[0] = new fstream( m_dataFileName.c_str(), ios::binary | ios::out );
698 
699  // Check
700  if (!m2p_dataFile[0]->is_open())
701  {
702  Cerr("***** vDataFile::OpenFileForWriting() -> Failed to create output file '" << m_dataFileName << "' !" << endl);
703  return 1;
704  }
705 
706  // End
707  return 0;
708 }
709 
710 // =====================================================================
711 // ---------------------------------------------------------------------
712 // ---------------------------------------------------------------------
713 // =====================================================================
714 
716 {
717  // Close binary data file
718  if (m2p_dataFile)
719  {
720  // Verbose
721  if (m_verbose>=2) Cout("vDataFile::CloseFile() -> Closing output binary data file" << endl);
722  // Close file
723  if (m2p_dataFile[0]) m2p_dataFile[0]->close();
724  delete m2p_dataFile;
725  }
726  // End
727  return 0;
728 }
729 
730 // =====================================================================
731 // ---------------------------------------------------------------------
732 // ---------------------------------------------------------------------
733 // =====================================================================
734 
735 vEvent* vDataFile::GetEvent(int64_t a_eventIndex, int a_th)
736 {
738  // The event pointer that will be returned
739  vEvent* p_event;
740  // Compute the adress into the buffer
741  char* event_address_in_array = mp_mappedMemory + a_eventIndex*m_sizeEvent;
742  // Get the event
743  p_event = GetEventSpecific(event_address_in_array, a_th);
744  // Set the current event index
745  p_event->SetEventIndex(a_eventIndex);
746  // Return the event
747  return p_event;
748 }
749 
750 
751 
752 
753 // =====================================================================
754 // ---------------------------------------------------------------------
755 // ---------------------------------------------------------------------
756 // =====================================================================
757 
759  // dev tool, but it can be used currently by the datafile
760  // Ignored for now in windows compilation
761 
762 int vDataFile::Shuffle( int64_t nb_events_to_load )
763 {
764  #ifndef _WIN32
765  if (m_verbose >=2) Cout("vDataFile::Shuffle() : Everyday I shuffle in... " << endl);
766  // Buffer storing ordered indices from ( 0 ) to ( nb_events_to_load - 1 )
767  int64_t *rndmIdx = new int64_t[ nb_events_to_load ];
768  std::iota( rndmIdx, rndmIdx + nb_events_to_load, 0 );
769 
770  // Initializing random
771  std::random_device rd;
772  std::mt19937 rndm( rd() );
773  rndm.seed( 1100001001 );
774 
775  // Shuffling the buffer of indices
776  std::shuffle( rndmIdx, rndmIdx + nb_events_to_load, rndm );
777 
778  // Creating a tmp buffer
779  char *mp_arrayEvents_tmp = new char[ nb_events_to_load*m_sizeEvent ];
780 
781  // Copy sorted buffer to shuffled buffer
782  for( int64_t i = 0; i < nb_events_to_load; ++i )
783  {
784  for( int64_t j = 0; j < m_sizeEvent; ++j )
785  {
786  //mp_arrayEvents_tmp[ i * m_sizeEvent + j ] = mp_arrayEvents[ rndmIdx[ i ] * m_sizeEvent + j ];
787  mp_arrayEvents_tmp[ i * m_sizeEvent + j ] = mp_mappedMemory[ rndmIdx[ i ] * m_sizeEvent + j ];
788  }
789  }
790 
791  // Freeing memory
792  delete[] rndmIdx;
793  //delete[] mp_mappedMemory;
794 
795  //mp_arrayEvents = mp_arrayEvents_tmp;
796  mp_mappedMemory = mp_arrayEvents_tmp;
797 
798  if (m_verbose >=2) Cout("vDataFile::Shuffle OK" << endl);
799  #endif
800  return 0;
801 }
802 
803 
804 
805 // =====================================================================
806 // ---------------------------------------------------------------------
807 // ---------------------------------------------------------------------
808 // =====================================================================
809 
810 void vDataFile::GetEventIndexStartAndStop( int64_t* ap_indexStart, int64_t* ap_indexStop, int a_subsetNum, int a_nbSubsets )
811 {
813  // Basically here, the index start for a single MPI instance will be the current subset number.
814  // If multiple instances are used, the whole datafile is split into equal-size concatenated pieces.
815  // So for each instance, we just have to found the first index falling in its range (assuming that
816  // the event index step is always equal to the number of subsets).
817 
818  // For the first machine, index start is the current subset number
819  if (mp_ID->GetMPIRank()==0) *ap_indexStart = a_subsetNum;
820  // For the other machines, we must search for the first index falling in their range belonging to this
821  // subset (a subset being starting at a_subsetNum with a step equal to the number of subsets, a_nbSubsets)
822  else
823  {
824  // Compute the modulo of the first index of this machine minus the subset number with respect to the number of subsets
825  int64_t modulo = (m_mpi1stEvent-a_subsetNum) % a_nbSubsets;
826  // If this modulo is null, then the index start is the first index
827  if (modulo==0) *ap_indexStart = m_mpi1stEvent;
828  // Otherwise, the index start is equal to the first index plus the number of subsets minus the modulo
829  else *ap_indexStart = m_mpi1stEvent + (a_nbSubsets - modulo);
830  }
831 
832  // For index stop, we simply get the last event of the MPI instance (+1 because the for loop is exclusive)
833  *ap_indexStop = m_mpiLastEvent + 1;
834 }
835 
836 // =====================================================================
837 // ---------------------------------------------------------------------
838 // ---------------------------------------------------------------------
839 // =====================================================================
840 
842 {
844  Cerr("*****vDataFile::GetMaxRingDiff() -> This function is not implemented for the used system" << endl);
845  Cerr(" (this error may be prompted if the present function is erroneously called for a SPECT system)" << endl);
846  return -1;
847 }
848 
849 // =====================================================================
850 // ---------------------------------------------------------------------
851 // ---------------------------------------------------------------------
852 // =====================================================================
853 
855 {
856  // Verbose
858 
859  // Close all multithreaded datafiles before merging
860  for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
861  if (m2p_dataFile[th]) m2p_dataFile[th]->close();
862 
863  // If the output file doesn't exist yet, simply rename the first temporary file name using the ouput file name
864  if (!ifstream(m_dataFileName))
865  {
866  // If only one projection is required (i.e one threads && no projection of frame or gates)...
867  if(mp_ID->GetNbThreadsForProjection()==1 // No multithreading so no multiple tmp datafiles
868  && mp_ID->GetNbTimeFrames()
870  * mp_ID->GetNb2ndMotImgsForLMS() == 1)
871  {
872  // rename the first temporary file name to the global name
873  string tmp_file_name = m_dataFileName + "_0";
874 
875  // ... then just rename the first temporary file name using the ouput file name
876  rename(tmp_file_name.c_str(),m_dataFileName.c_str());
877  // no need to concatenate, so we leave here.
878  return 0 ;
879  }
880  }
881 
882  // Create the final output file which will concatenate the events inside the thread-specific data files
883  ofstream merged_file(m_dataFileName.c_str(), ios::out | ios::binary | ios::app);
884 
885  // Concatenation : generate input file ("data_file") to read the buffer of the thread-specific data files and store the information in the final output file
886  for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
887  {
888  // Build thread file name
889  stringstream ss; ss << th;
890  string file_name = m_dataFileName;
891  file_name.append("_").append(ss.str());
892  // Open it
893  // Note SS: Maybe we can use the m2p_dataFile[th] here by just rewarding to the beginning of the file ?
894  // Note TM: There were some issues involving the use of rdbuf and concatenation of the file in this case (ifstream were needed instead of fstream for some reasons)
895  // But we should have another look on how the projection data writing works with the implementation of the new analytical simulator.
896  ifstream data_file(file_name.c_str(), ios::binary | ios::in);
897 
898  if (!data_file)
899  {
900  Cerr(endl);
901  Cerr("***** vDataFile::PROJ_WriteData() -> Input temporary thread file '" << file_name << "' is missing or corrupted !" << endl);
902  return 1;
903  }
904 
905  // Concatenate it to the merged file
906  merged_file << data_file.rdbuf();
907  // Close file
908  data_file.close();
909 
910  // Re-open datafiles (needed if projecting frame/gates, as the contents of the temporary datafile are copied to the main datafile after each frame/gate)
911  m2p_dataFile[th]->open( file_name.c_str(), ios::binary | ios::out | ios::trunc);
912  }
913 
914  // Close merged file
915  merged_file.close();
916 
917  return 0;
918 }
919 
920 // =====================================================================
921 // ---------------------------------------------------------------------
922 // ---------------------------------------------------------------------
923 // =====================================================================
924 
926 {
927  // Verbose
929  if (m_verbose>=VERBOSE_DETAIL) Cout("vDataFile::PROJ_DeleteTmpDataFile() ..." << endl);
930  // Generate input file ("data_file") to read the buffer of the thread-specific data files and store the information in the final output file
931  for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
932  {
933  // Build thread file name
934  stringstream ss; ss << th;
935 
936  if (m2p_dataFile[th]) m2p_dataFile[th]->close();
937 
938  string file_name = m_dataFileName;
939  file_name.append("_").append(ss.str());
940 
941  // Remove temporary file for data output writing (Projection script only)
942  ifstream fcheck(file_name.c_str());
943  if(fcheck.good())
944  {
945  fcheck.close();
946  #ifdef _WIN32
947  string dos_instruction = "del " + file_name;
948  system(dos_instruction.c_str());
949  #else
950  remove(file_name.c_str());
951  #endif
952  }
953  }
954  // End
955  return 0;
956 }
957 
958 // =====================================================================
959 // ---------------------------------------------------------------------
960 // ---------------------------------------------------------------------
961 // =====================================================================
962 
963 vEvent* vDataFile::PROJ_GenerateEvent(int a_idxElt1, int a_idxElt2, int a_th)
964 {
966  // Only 1 line required for projection/sensitivity generation
967  m2p_BufferEvent[a_th]->SetNbLines(1);
968  m2p_BufferEvent[a_th]->SetID1(0, a_idxElt1);
969  m2p_BufferEvent[a_th]->SetID2(0, a_idxElt2);
970  // Return the event
971  return m2p_BufferEvent[a_th];
972 }
973 
974 // =====================================================================
975 // ---------------------------------------------------------------------
976 // ---------------------------------------------------------------------
977 // =====================================================================
978 
980 {
981  // Simply switch between the different data types
982  string data_type = "";
983  if (m_dataType==TYPE_CT) data_type = "CT";
984  else if (m_dataType==TYPE_PET) data_type = "PET";
985  else if (m_dataType==TYPE_SPECT) data_type = "SPECT";
986  else data_type = "unknown";
987  return data_type;
988 }
989 
990 // =====================================================================
991 // ---------------------------------------------------------------------
992 // ---------------------------------------------------------------------
993 // =====================================================================
994 
996 {
997  // Simply switch between the different data modes
998  string data_mode = "";
999  if (m_dataMode==MODE_HISTOGRAM) data_mode = "histogram";
1000  else if (m_dataMode==MODE_LIST) data_mode = "list-mode";
1001  else if (m_dataMode==MODE_NORMALIZATION) data_mode = "normalization";
1002  else data_mode = "unknown";
1003  return data_mode;
1004 }
1005 
1006 // =====================================================================
1007 // ---------------------------------------------------------------------
1008 // ---------------------------------------------------------------------
1009 // =====================================================================
1010 
1012 {
1013  // Simply switch between the different data specs
1014  string data_spec = "";
1015  if (m_dataSpec==SPEC_EMISSION) data_spec = "emission";
1016  else if (m_dataSpec==SPEC_TRANSMISSION) data_spec = "transmission";
1017  else data_spec = "unknown";
1018  return data_spec;
1019 }
1020 
1021 // =====================================================================
1022 // ---------------------------------------------------------------------
1023 // ---------------------------------------------------------------------
1024 // =====================================================================
everything ... be careful when file is larger than memory
#define KEYWORD_OPTIONAL_SUCCESS
This class is designed to be a mother virtual class for DataFile.
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)
void SetEventIndex(int a_eventIndex)
Set current index associated to the event.
#define MODE_NORMALIZATION
#define Cerr(MESSAGE)
int IntfReadAdditionalData(const string &a_pathToHeaderFile, FLTNB *ap_DataMatrix, int64_t a_matrixSize, int vb)
Main function dedicated to Interfile 3D additional data loading.
int CheckParameters()
Check the initialization of member variables Call the CheckSpecificParameters() function implemente...
virtual int CheckFileSizeConsistency()=0
int SetParametersFrom(vDataFile *ap_DataFile)
int ReadInfoInHeader(bool a_affectQuantificationFlag=true)
int SetCalibrationFactor(int a_bed, FLTNB a_calibrationFactor)
virtual int ReadSpecificInfoInHeader(bool a_affectQuantificationFlag=true)=0
int CheckConsistencyWithAnotherBedDataFile(vDataFile *ap_DataFile)
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
virtual ~vDataFile()
vDataFile destructor.
virtual int GetMaxRingDiff()
Return an error by default. This function is surcharged by the PET (and CT) scanner daughter class...
int Open(const std::string &filename, size_t mappedBytes=WholeFile, CacheHint hint=Normal)
open file, mappedBytes = 0 maps the whole file
int InitializeMappedFile()
Check the datafile existency, map it to memory and get the raw char* pointer. .
virtual int CheckSpecificConsistencyWithAnotherDataFile(vDataFile *ap_DataFile)=0
FLTNB * GetNewAdditionalDataMatrix(INTNB a_nbDataPerEvent)
Allocate the memory for this additional data matrix and return the pointer to the matrix...
int PROJ_WriteData()
Write/Merge chunk of data in a general data file.
vEvent * GetEvent(int64_t a_eventIndex, int a_th=0)
int CloseFile()
Close as many binary file stream for writing.
#define DEBUG_VERBOSE(IGNORED1, IGNORED2)
virtual int CheckSpecificParameters()=0
This function is implemented in child classes Check specific parameters of child classes...
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
vDataFile()
vDataFile constructor.
int GetMPISize()
Get the MPI size (the number of MPI instances)
int OpenFileForWriting(string a_suffix="")
vEvent * PROJ_GenerateEvent(int idx_elt1, int idx_elt2, int a_th)
virtual void DescribeSpecific()=0
A pure virtual function used to describe the specific parts of the datafile.
#define KEYWORD_MANDATORY
#define KEYWORD_OPTIONAL
int GetNbAdditionalData()
Get the number of additional data.
#define SPEC_TRANSMISSION
void SetID2(int a_line, uint32_t a_value)
int InitializeAdditionalData(const string &a_pathToAdditionalData)
Memory allocation and initialization for the additional data matrices.
Mother class for the Event objects.
int GetNb2ndMotImgsForLMS()
call the eponym function from the oDynamicDataManager object
const unsigned char * GetData() const
raw access
void Describe()
A function used to describe the generic parts of the datafile.
int GetNbThreadsForProjection()
Get the number of threads used for projections.
virtual vEvent * GetEventSpecific(char *ap_buffer, int a_th)=0
Portable read-only memory mapping (Windows and Linux)
int PROJ_DeleteTmpDataFile()
Delete temporary datafile used for multithreaded output writing if needed.
int GetVerbose()
Get the verbose level.
virtual int Shuffle(int64_t)
!!!\ This function has been modified to be used specifically with a
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...
int SetAcquisitionTime(int a_bed, FLTNB a_timeStartInSec, FLTNB a_durationInSec, string a_GateListDurationsInSec)
#define Cout(MESSAGE)
virtual int SetSpecificParametersFrom(vDataFile *ap_DataFile)=0
#define KEYWORD_OPTIONAL_ERROR
oImageDimensionsAndQuantification * mp_ID
void SetID1(int a_line, uint32_t a_value)