CASToR  3.0
Tomographic Reconstruction (PET/SPECT/CT)
iDataFileSPECT.cc
Go to the documentation of this file.
1 /*
2 This file is part of CASToR.
3 
4  CASToR is free software: you can redistribute it and/or modify it under the
5  terms of the GNU General Public License as published by the Free Software
6  Foundation, either version 3 of the License, or (at your option) any later
7  version.
8 
9  CASToR is distributed in the hope that it will be useful, but WITHOUT ANY
10  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12  details.
13 
14  You should have received a copy of the GNU General Public License along with
15  CASToR (in file GNU_GPL.TXT). If not, see <http://www.gnu.org/licenses/>.
16 
17 Copyright 2017-2019 all CASToR contributors listed below:
18 
19  --> Didier BENOIT, Claude COMTAT, Marina FILIPOVIC, Thibaut MERLIN, Mael MILLARDET, Simon STUTE, Valentin VIELZEUF
20 
21 This is CASToR version 3.0.
22 */
23 
31 #include "iDataFileSPECT.hh"
32 
33 // =====================================================================
34 // ---------------------------------------------------------------------
35 // ---------------------------------------------------------------------
36 // =====================================================================
37 
39 {
40  // Set all members to default values
43  m_isotope = "unknown";
44  mp_nbOfBins[0] = 1;
45  mp_nbOfBins[1] = 1;
46  m_acquisitionZoom = 1.;
48  mp_angles = NULL;
49  m_nbHeads = 1;
51  m_eventKindFlag = false;
52  m_normCorrectionFlag = false;
54  m_scatCorrectionFlag = false;
57 }
58 
59 // =====================================================================
60 // ---------------------------------------------------------------------
61 // ---------------------------------------------------------------------
62 // =====================================================================
63 
65 {
66  if (mp_angles) delete[] mp_angles;
68 }
69 
70 // =====================================================================
71 // ---------------------------------------------------------------------
72 // ---------------------------------------------------------------------
73 // =====================================================================
74 
75 int iDataFileSPECT::ReadSpecificInfoInHeader(bool a_affectQuantificationFlag)
76 {
77  // Verbose
79  if (m_verbose>=VERBOSE_DETAIL) Cout("iDataFileSPECT::ReadSpecificInfoInHeader() -> Read information specific to SPECT" << endl);
80 
81  // Create pointers dedicated to recover the addresses of the member variables of the scanner object
82  FLTNB* p_angles = NULL;
83  FLTNB* p_CORtoDetectorDistance = NULL;
84  FLTNB p_pixSizeXY[2]; // not used here
85 
86  // Get Geometric parameters recovered from the scanner object
87  sScannerManager* p_scannermanager;
88  p_scannermanager = sScannerManager::GetInstance();
90  &m_nbHeads,
93  p_pixSizeXY,
94  p_angles,
95  p_CORtoDetectorDistance,
97 
98  // Check m_nbOfProjections first before allocating projection angles and radius using this variable
99  if (m_nbOfProjections==0)
100  {
101  Cerr("***** iDataFileSPECT::ReadSpecificInfoInHeader() -> Number of projections should be strictly positive !" << endl);
102  return 1;
103  }
104 
105  // Allocation and initialization of Projection angles and Center of rotation to SPECT detector surface distance (radius) :
108 
109  // Recover values
110  for (int a=0 ; a<m_nbOfProjections ; a++)
111  {
112  mp_angles[a] = p_angles[a];
113  mp_CORtoDetectorDistance[a] = p_CORtoDetectorDistance[a];
114  }
115 
116  // Feedback to user
118  {
119  Cout(" --> Provided projection angles | distance Center of Rotation (COR) to detector: " << endl);
120  for (int a=0 ; a<m_nbOfProjections ; a++) Cout(" " << mp_angles[a] << " | " << mp_CORtoDetectorDistance[a] << endl);
121  }
122 
123  // Read optional fields in the header, check if errors (issue during data reading/conversion (==1) )
124  if (ReadDataASCIIFile(m_headerFileName, "Isotope", &m_isotope, 1, 0) == 1 ||
125  ReadDataASCIIFile(m_headerFileName, "Event kind flag", &m_eventKindFlag, 1, 0) == 1 ||
126  ReadDataASCIIFile(m_headerFileName, "Normalization correction flag", &m_normCorrectionFlag, 1, 0) == 1 ||
127  ReadDataASCIIFile(m_headerFileName, "Scatter correction flag", &m_scatCorrectionFlag, 1, 0) == 1 )
128  {
129  Cerr("***** iDataFileSPECT::ReadSpecificInfoInHeader() -> Error while reading optional fields in the header data file !" << endl);
130  return 1;
131  }
132 
133  // Give the SPECT isotope to the oImageDimensionsAndQuantification that manages the quantification factors
134  if (a_affectQuantificationFlag && mp_ID->SetSPECTIsotope(m_bedIndex, m_isotope))
135  {
136  Cerr("***** iDataFileSPECT::ReadSpecificInfoInHeader() -> A problem occurred while setting the isotope to oImageDimensionsAndQuantification !" << endl);
137  return 1;
138  }
139 
140  // Normal end
141  return 0;
142 }
143 
144 // =====================================================================
145 // ---------------------------------------------------------------------
146 // ---------------------------------------------------------------------
147 // =====================================================================
148 
150 {
151  iDataFileSPECT* p_DataFileSPECT = (dynamic_cast<iDataFileSPECT*>(ap_DataFile));
152  m_isotope = p_DataFileSPECT->GetIsotope();
153  mp_nbOfBins[0] = p_DataFileSPECT->GetNbBins(0);
154  mp_nbOfBins[1] = p_DataFileSPECT->GetNbBins(1);
155  m_nbOfProjections = p_DataFileSPECT->GetNbProjections();
156  mp_angles = p_DataFileSPECT->GetAngles();
157  m_nbHeads = p_DataFileSPECT->GetNbHeads();
159  m_eventKindFlag = p_DataFileSPECT->GetEventKindFlag();
160  m_normCorrectionFlag = p_DataFileSPECT->GetNormCorrectionFlag();
161  m_scatCorrectionFlag = p_DataFileSPECT->GetScatCorrectionFlag();
162  m_headRotDirection = p_DataFileSPECT->GetHeadRotDirection();
163  // End
164  return 0;
165 }
166 
167 // =====================================================================
168 // ---------------------------------------------------------------------
169 // ---------------------------------------------------------------------
170 // =====================================================================
171 
173 {
174  // Verbose
176  if (m_verbose>=VERBOSE_DETAIL) Cout("iDataFileSPECT::ComputeSizeEvent() -> In bytes" << endl);
177 
178  // For MODE_LIST events
179  if (m_dataMode == MODE_LIST)
180  {
181  // Size of the mandatory element in a list-mode event: Time + 2*crystalID
182  m_sizeEvent = sizeof(uint32_t) + 2*sizeof(uint32_t);
183  // Optional flags
184  if (m_eventKindFlag) m_sizeEvent += sizeof(FLTNBDATA);
187  for (int i=0 ; i<3; i++)
188  {
189  // POI available for this direction
190  if (mp_POIDirectionFlag[i]) m_sizeEvent += sizeof(FLTNBDATA);
191  }
192  }
193  // For MODE_HISTOGRAM events
195  {
196  // Size of the mandatory element in a histo event: Time + event_value + 2*crystalID
197  m_sizeEvent = sizeof(uint32_t) + sizeof(FLTNBDATA) + 2*sizeof(uint32_t);
198  // Optional flags
201  }
202  // Unknown event type
203  else
204  {
205  Cerr("***** iDataFileSPECT::ComputeSizeEvent() -> Unknown event mode !" << endl);
206  return 1;
207  }
208 
209  // Check
210  if (m_sizeEvent<=0)
211  {
212  Cerr("***** iDataFileSPECT::ComputeSizeEvent() -> Error, the Event size in bytes should be >= 0 !" << endl;);
213  return 1;
214  }
215 
216  // Verbose
217  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Event size: " << m_sizeEvent << " bytes" << endl);
218  // End
219  return 0;
220 }
221 
222 // =====================================================================
223 // ---------------------------------------------------------------------
224 // ---------------------------------------------------------------------
225 // =====================================================================
226 
228 {
229  // Verbose
232  {
233  if (m_dataMode==MODE_HISTOGRAM) Cout("iDataFileSPECT::PrepareDataFile() -> Build histogram events" << endl);
234  else if (m_dataMode==MODE_LIST) Cout("iDataFileSPECT::PrepareDataFile() -> Build listmode events" << endl);
235  else if (m_dataMode==MODE_NORMALIZATION) Cout("iDataFileSPECT::PrepareDataFile() -> Build normalization events" << endl);
236  }
237 
238  // ==============================================================================
239  // Allocate event buffers (one for each thread)
240  // ==============================================================================
241 
242  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Allocate an event buffer for each thread" << endl);
243  // Instanciation of the event buffer according to the data type
245 
246  // Allocate the events per each thread
247  for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
248  {
249  // For MODE_LIST events
250  if (m_dataMode == MODE_LIST)
251  {
252  m2p_BufferEvent[th] = new iEventListSPECT();
253  }
254  // For MODE_HISTOGRAM events
255  if (m_dataMode == MODE_HISTOGRAM)
256  {
257  m2p_BufferEvent[th] = new iEventHistoSPECT();
258  }
259  // Allocate crystal/view IDs
260  if (m2p_BufferEvent[th]->AllocateID())
261  {
262  Cerr("*****iDataFileSPECT::PrepareDataFile() -> Error while trying to allocate memory for the Event object!" << endl);
263  return 1;
264  }
265  }
266 
267  // ==============================================================================
268  // Deal with specific corrections
269  // ==============================================================================
270 
271  // In case of normalization correction flag, see if we ignore this correction
273  {
274  // Affect the ignored flag from the ignored corrections list processed by the oImageDimensionsAndQuantification
276  // Verbose
278  {
279  if (m_ignoreNormCorrectionFlag) Cout(" --> Ignore normalization correction" << endl);
280  else Cout(" --> Correct for normalization" << endl);
281  }
282  }
283  // In case of scatter correction flag, see if we ignore this correction
285  {
286  // Affect the ignored flag from the ignored corrections list processed by the oImageDimensionsAndQuantification
288  // Verbose
290  {
291  if (m_ignoreScatCorrectionFlag) Cout(" --> Ignore scatter correction" << endl);
292  else Cout(" --> Correct for scatter events" << endl);
293  }
294  }
295 
296  // Normal end
297  return 0;
298 }
299 
300 // =====================================================================
301 // ---------------------------------------------------------------------
302 // ---------------------------------------------------------------------
303 // =====================================================================
304 
305 vEvent* iDataFileSPECT::GetEventSpecific(char* ap_buffer, int a_th)
306 {
308 
309  // Work on a copy of the input pointer
310  char* file_position = ap_buffer;
311 
312  // For MODE_LIST SPECT data
313  if (m_dataMode == MODE_LIST)
314  {
315  // Cast the event pointer
316  iEventListSPECT* event = (dynamic_cast<iEventListSPECT*>(m2p_BufferEvent[a_th]));
317  // Mandatory time field: [uint32_t (time)]
318  event->SetTimeInMs(*reinterpret_cast<uint32_t*>(file_position));
319  file_position += sizeof(uint32_t);
320  // Optional kind: [uint8_t kind]
321  if (m_eventKindFlag)
322  {
323  event->SetKind(*reinterpret_cast<uint8_t*>(file_position));
324  file_position += sizeof(uint8_t);
325  }
326  // Optional scatter correction field: [FLTNBDATA (scatter)]
328  {
329  if (!m_ignoreScatCorrectionFlag) event->SetScatterRate(*reinterpret_cast<FLTNBDATA*>(file_position));
330  file_position += sizeof(FLTNBDATA);
331  }
332  // Optional normalization correction field: [FLTNBDATA (norm)]
334  {
335  if (!m_ignoreNormCorrectionFlag) event->SetNormalizationFactor(*reinterpret_cast<FLTNBDATA*>(file_position));
336  file_position += sizeof(FLTNBDATA);
337  }
338  // Optional POI correction field: [FLTNBDATA (POI1[3])]
339  for (int i=0; i<3; i++)
340  {
341  if (mp_POIDirectionFlag[i])
342  {
343  if (!m_ignorePOIFlag) event->SetPOI(i,*reinterpret_cast<FLTNBDATA*>(file_position));
344  file_position += sizeof(FLTNBDATA);
345  }
346  }
347  // Mandatory angular projection ID: [uint32_t (ID1)]
348  event->SetID1(0, *reinterpret_cast<uint32_t*>(file_position));
349  file_position += sizeof(uint32_t);
350  // Mandatory crystal ID: [uint32_t (ID2)]
351  event->SetID2(0, *reinterpret_cast<uint32_t*>(file_position));
352  file_position += sizeof(uint32_t);
353  }
354 
355  // For MODE_HISTOGRAM SPECT DATA
356  if (m_dataMode == MODE_HISTOGRAM)
357  {
358  // Cast the event pointer
359  iEventHistoSPECT* event = (dynamic_cast<iEventHistoSPECT*>(m2p_BufferEvent[a_th]));
360  // Mandatory time field: [uint32_t (time)]
361  event->SetTimeInMs(*reinterpret_cast<uint32_t*>(file_position));
362  file_position += sizeof(uint32_t);
363  // Mandatory bin value: [FLTNBDATA bin value]
364  event->SetEventValue(0, *reinterpret_cast<FLTNBDATA*>(file_position));
365  file_position += sizeof(FLTNBDATA);
366  // Optional scatter correction field: [FLTNBDATA (scatter)]
368  {
369  if (!m_ignoreScatCorrectionFlag) event->SetScatterRate(*reinterpret_cast<FLTNBDATA*>(file_position));
370  file_position += sizeof(FLTNBDATA);
371  }
372  // Optional normalization correction field: [FLTNBDATA (norm)]
374  {
375  if (!m_ignoreNormCorrectionFlag) event->SetNormalizationFactor(*reinterpret_cast<FLTNBDATA*>(file_position));
376  file_position += sizeof(FLTNBDATA);
377  }
378  // Mandatory angular projection ID: [uint32_t (ID1)]
379  event->SetID1(0, *reinterpret_cast<uint32_t*>(file_position));
380  file_position += sizeof(uint32_t);
381  // Mandatory crystal ID: [uint32_t (ID2)]
382  event->SetID2(0, *reinterpret_cast<uint32_t*>(file_position));
383  file_position += sizeof(uint32_t);
384  }
385 
386  // Return the updated event
387  return m2p_BufferEvent[a_th];
388 }
389 
390 // =====================================================================
391 // ---------------------------------------------------------------------
392 // ---------------------------------------------------------------------
393 // =====================================================================
394 
396 {
398  if (m_verbose==0) return;
399  // Describe the datafile
400  Cout("iDataFileSPECT::DescribeSpecific() -> Here is some specific content of the SPECT datafile" << endl);
401  Cout(" --> Isotope: " << m_isotope << endl);
402  if (m_dataMode==MODE_LIST && m_eventKindFlag) Cout(" --> Event kind is present" << endl);
403  if (m_scatCorrectionFlag) Cout(" --> Scatter correction is present" << endl);
404  if (m_normCorrectionFlag) Cout(" --> Normalization correction is present" << endl);
405  Cout(" --> Number of heads: " << m_nbHeads << endl);
406  if (mp_nbOfBins[0]!=1) Cout(" --> Transaxial number of bins: " << mp_nbOfBins[0] << endl);
407  if (mp_nbOfBins[1]!=1) Cout(" --> Axial number of bins: " << mp_nbOfBins[1] << endl);
408  if (m_acquisitionZoom!=1.) Cout(" --> Acquisition zoom: " << m_acquisitionZoom << endl);
409  if (m_headRotDirection==GEO_ROT_CW) Cout(" --> Head rotation is clockwise" << endl);
410  else if (m_headRotDirection==GEO_ROT_CCW) Cout(" --> Head rotation is counter-clockwise" << endl);
411  else Cout(" --> Head rotation is undefined !!!" << endl);
412  Cout(" --> Number of acquisition projections: " << m_nbOfProjections << endl);
413  for (uint16_t p=0; p<m_nbOfProjections; p++) Cout(" | Projection " << p << " at " << mp_angles[p] << " deg and "
414  << mp_CORtoDetectorDistance[p] << " mm from center of rotation" << endl);
415 }
416 
417 // =====================================================================
418 // ---------------------------------------------------------------------
419 // ---------------------------------------------------------------------
420 // =====================================================================
421 
423 {
425  // Error if m_dataType != SPECT
426  if (m_dataType != TYPE_SPECT)
427  {
428  Cerr("***** iDataFileSPECT::CheckSpecificParameters() -> Data type should be SPECT !'" << endl);
429  return 1;
430  }
431  // Check number of projections
432  if (m_nbOfProjections == 0)
433  {
434  Cerr("***** iDataFileSPECT::CheckSpecificParameters() -> Number of projections not initialized (should be >0) !" << endl);
435  return 1;
436  }
437  // Check projection angles
438  if (mp_angles == NULL)
439  {
440  Cerr("***** iDataFileSPECT::CheckSpecificParameters() -> Projection angles not initialized !" << endl);
441  return 1;
442  }
443  // End
444  return 0;
445 }
446 
447 // =====================================================================
448 // ---------------------------------------------------------------------
449 // ---------------------------------------------------------------------
450 // =====================================================================
451 
453 {
455 
456  // Create the stream
457  fstream* p_file = new fstream( m_dataFileName.c_str(), ios::binary| ios::in );
458  // Check that datafile exists
459  if (!p_file->is_open())
460  {
461  Cerr("***** iDataFilePET::CheckFileSizeConsistency() -> Failed to open input file '" << m_dataFileName.c_str() << "' !" << endl);
462  Cerr(" (Provided in the data file header: " << m_headerFileName << ")" << endl);
463  return 1;
464  }
465  // Get file size in bytes
466  p_file->seekg(0, ios::end);
467  int64_t sizeInBytes = p_file->tellg();
468  // Close stream and delete it
469  p_file->close();
470  delete p_file;
471  // Check datafile self-consistency
472  if (m_nbEvents*m_sizeEvent != sizeInBytes)
473  {
474  Cerr("----------------------------------------------------------------------------------------------------------------------------------------" << endl);
475  Cerr("***** iDataFileSPECT::CheckFileSizeConsistency() -> DataFile size is not consistent with the information provided by the user/datafile !" << endl);
476  Cerr(" --> Expected size: "<< m_nbEvents*m_sizeEvent << endl);
477  Cerr(" --> Actual size: "<< sizeInBytes << endl << endl);
478  Cerr(" ADDITIONAL INFORMATION ABOUT THE DATAFILE INITIALIZATION" << endl);
479  if (m_eventKindFlag) Cerr(" --> Event kind term is enabled" << endl);
480  else Cerr(" --> No information about the kind of events in the data" << endl);
481  if (m_normCorrectionFlag) Cerr(" --> Normalization correction term is enabled" << endl);
482  else Cerr(" --> No normalization correction term in the data" << endl);
483  if (m_scatCorrectionFlag) Cerr(" --> Scatter correction term is enabled" << endl);
484  else Cerr(" --> No scatter correction term in the data" << endl);
485  Cerr(" --> Calibration factor value is: " << m_calibrationFactor << endl);
486  Cerr(" --> Number of bins are equal to " << mp_nbOfBins[0] << "," << mp_nbOfBins[1] << " for transaxial,axial axis respectively" << endl);
487  if (mp_POIResolution[0]<0.) Cerr(" --> No POI enabled on the radial axis" << endl);
488  else Cerr(" --> POI resolution on the radial axis is: " << mp_POIResolution[0] << endl);
489  if (mp_POIResolution[1]<0.) Cerr(" --> No POI enabled on the tangential axis" << endl);
490  else Cerr(" --> POI resolution on the tangential axis is: " << mp_POIResolution[1] << endl);
491  if (mp_POIResolution[2]<0.) Cerr(" --> No POI enabled on the axial axis" << endl);
492  else Cerr(" --> POI resolution on the axial axis is: " << mp_POIResolution[2] << endl);
493  Cerr("----------------------------------------------------------------------------------------------------------------------------------------" << endl);
494  return 1;
495  }
496  // End
497  return 0;
498 }
499 
500 // =====================================================================
501 // ---------------------------------------------------------------------
502 // ---------------------------------------------------------------------
503 // =====================================================================
504 
506 {
508  // Dynamic cast the vDataFile to a iDataFilePET
509  iDataFileSPECT* p_data_file = (dynamic_cast<iDataFileSPECT*>(ap_DataFile));
510  // Check isotope
511  if (m_isotope!=p_data_file->GetIsotope())
512  {
513  Cerr("***** iDataFileSPECT::CheckSpecificConsistencyWithAnotherDataFile() -> Isotopes are inconsistent !" << endl);
514  return 1;
515  }
516  // Check event kind flag
517  if (m_eventKindFlag!=p_data_file->GetEventKindFlag())
518  {
519  Cerr("***** iDataFileSPECT::CheckSpecificConsistencyWithAnotherDataFile() -> Event kind flags are inconsistent !" << endl);
520  return 1;
521  }
522  // Check normalization correction flag
523  if (m_normCorrectionFlag!=p_data_file->GetNormCorrectionFlag())
524  {
525  Cerr("***** iDataFileSPECT::CheckSpecificConsistencyWithAnotherDataFile() -> Normalization correction flags are inconsistent !" << endl);
526  return 1;
527  }
528  // Check scatter correction flag
529  if (m_scatCorrectionFlag!=p_data_file->GetScatCorrectionFlag())
530  {
531  Cerr("***** iDataFileSPECT::CheckSpecificConsistencyWithAnotherDataFile() -> Scatter correction flags are inconsistent !" << endl);
532  return 1;
533  }
534  // Check data mode
535  if (m_dataMode!=p_data_file->GetDataMode())
536  {
537  Cerr("***** iDataFileSPECT::CheckSpecificConsistencyWithAnotherDataFile() -> Data modes are inconsistent (list-mode or histogram) !" << endl);
538  return 1;
539  }
540  // End
541  return 0;
542 }
543 
544 // =====================================================================
545 // ---------------------------------------------------------------------
546 // ---------------------------------------------------------------------
547 // =====================================================================
548 
550 {
552  // Check
553  if (m_nbOfProjections == 0)
554  {
555  Cerr("***** iDataFileSPECT::InitAngles() -> Number of projection angles not initialized !'" << endl);
556  return 1;
557  }
558  // Allocate
560  // Affect
561  for (uint16_t a=0 ; a<m_nbOfProjections ; a++) mp_angles[a] = ap_angles[a];
562  // End
563  return 0;
564 }
565 
566 // =====================================================================
567 // ---------------------------------------------------------------------
568 // ---------------------------------------------------------------------
569 // =====================================================================
570 
571 int iDataFileSPECT::InitCorToDetectorDistance(FLTNB* ap_CORtoDetectorDistance)
572 {
574  // Check
575  if (m_nbOfProjections == 0)
576  {
577  Cerr("***** iDataFileSPECT::InitAngles() -> Number of projection angles not initialized !'" << endl);
578  return 1;
579  }
580  // Allocate
582  // Affect
583  for (uint16_t a=0 ; a<m_nbOfProjections ; a++) mp_CORtoDetectorDistance[a] = ap_CORtoDetectorDistance[a];
584  // End
585  return 0;
586 }
587 
588 // =====================================================================
589 // ---------------------------------------------------------------------
590 // ---------------------------------------------------------------------
591 // =====================================================================
592 
594 {
595  // Verbose
597  if (m_verbose>=VERBOSE_DETAIL) Cout("iDataFileSPECT::PROJ_InitFile() -> Initialize datafile for writing" << endl);
598 
599  m_startTimeInSec = 0.; //Std initialization for projection
600  m_durationInSec = 1.; //Std initialization for projection
601  m_nbEvents = 0; //Std initialization for projection
602  m_isotope = "unknown";
603  mp_POIResolution[0] = -1.;
604  mp_POIResolution[1] = -1.;
605  mp_POIResolution[2] = -1.;
606 
607  // Instanciate a fstream datafile for each thread
608  m2p_dataFile = new fstream*[mp_ID->GetNbThreadsForProjection()];
609 
610  // Set name of the projection data header
611  sOutputManager* p_OutMgr;
612  p_OutMgr = sOutputManager::GetInstance();
613  string path_name = p_OutMgr->GetPathName();
614  string img_name = p_OutMgr->GetBaseName();
615  m_headerFileName = path_name.append(img_name).append("_df").append(".Cdh");
616 
617  for (int th=0 ; th<mp_ID->GetNbThreadsForProjection() ; th++)
618  {
619  m_dataFileName = m_headerFileName.substr(0, m_headerFileName.find_last_of(".")).append(".Cdf"); // Generate datafile name from header file
620 
621  // Projeted data will be written in several files (corresponding to the number of thread) to be concatenated at the end of the projection process.
622  stringstream ss;
623  ss << th;
624 
625  string datafile_name = m_dataFileName;
626  datafile_name.append("_").append(ss.str());
627 
628  m2p_dataFile[th] = new fstream( datafile_name.c_str(), ios::binary | ios::out | ios::trunc);
629  }
630 
631  //remove content from the output data file, in case it already exists
632  //todo warn the user a datafile with the same name will be erased (eventually add an option to disable the warning)
633  ofstream output_file(m_dataFileName.c_str(), ios::out | ios::trunc);
634  output_file.close();
635 
636  if (m_verbose>=VERBOSE_DETAIL) Cout(" --> Output datafile name :" << m_dataFileName << endl);
637 
638  return 0;
639 }
640 
641 // =====================================================================
642 // ---------------------------------------------------------------------
643 // ---------------------------------------------------------------------
644 // =====================================================================
645 
646 int iDataFileSPECT::WriteEvent(vEvent* ap_Event, int a_th)
647 {
649 
650  if (m_dataMode == MODE_LIST)
651  {
652  // TODO should create as many vEvent as (int)fp.
653  if (WriteListEvent((iEventListSPECT*)ap_Event, a_th))
654  {
655  Cerr("*****iDataFileSPECT::WriteEvent() -> Error while trying to write projection datafile (list-mode)" << endl);
656  return 1;
657  }
658  }
659 
660  if (m_dataMode == MODE_HISTOGRAM)
661  {
662  if (WriteHistoEvent((iEventHistoSPECT*)ap_Event, a_th))
663  {
664  Cerr("*****iDataFileSPECT::WriteEvent() -> Error while trying to write projection datafile (histogram)" << endl);
665  return 1;
666  }
667  }
668 
669  return 0;
670 }
671 
672 // =====================================================================
673 // ---------------------------------------------------------------------
674 // ---------------------------------------------------------------------
675 // =====================================================================
676 
678 {
680 
681  // Write sequentially each field of the event according to the type of the event.
682  m2p_dataFile[a_th]->clear();
683 
684  uint32_t time = ap_Event->GetTimeInMs();
685  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&time), sizeof(uint32_t));
686 
687  FLTNBDATA event_value = ap_Event->GetEventValue(0);
688  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&event_value), sizeof(FLTNBDATA));
689 
691  {
692  FLTNBDATA scat_rate = ap_Event->GetEventScatRate();
693  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&scat_rate), sizeof(FLTNBDATA));
694  }
695 
697  {
698  FLTNBDATA norm_corr_factor = ap_Event->GetNormFactor();
699  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&norm_corr_factor), sizeof(FLTNBDATA));
700  }
701 
702  uint32_t id1 = ap_Event->GetID1(0);
703  uint32_t id2 = ap_Event->GetID2(0);
704  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&id1), sizeof(uint32_t));
705  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&id2), sizeof(uint32_t));
706 
707  return 0;
708 }
709 
710 // =====================================================================
711 // ---------------------------------------------------------------------
712 // ---------------------------------------------------------------------
713 // =====================================================================
714 
716 {
718 
719  // Write sequentially each field of the event according to the type of the event.
720  m2p_dataFile[a_th]->clear();
721 
722  uint32_t time = ap_Event->GetTimeInMs();
723  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&time), sizeof(uint32_t));
724 
725  if(m_eventKindFlag)
726  {
727  uint8_t event_kind = ap_Event->GetKind();
728  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&event_kind), sizeof(uint8_t));
729  }
730 
732  {
733  FLTNBDATA scat_rate = ap_Event->GetEventScatRate();
734  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&scat_rate), sizeof(FLTNBDATA));
735  }
736 
738  {
739  FLTNBDATA norm_corr_factor = ap_Event->GetNormFactor();
740  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&norm_corr_factor), sizeof(FLTNBDATA));
741  }
742 
743  if(mp_POIResolution[0]>0)
744  {
745  FLTNBDATA POI = ap_Event->GetPOI(0);
746  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&POI), sizeof(FLTNBDATA));
747  }
748 
749  if(mp_POIResolution[1]>0)
750  {
751  FLTNBDATA POI = ap_Event->GetPOI(1);
752  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&POI), sizeof(FLTNBDATA));
753  }
754 
755  if(mp_POIResolution[2]>0)
756  {
757  FLTNBDATA POI = ap_Event->GetPOI(2);
758  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&POI), sizeof(FLTNBDATA));
759  }
760 
761  uint32_t id1 = ap_Event->GetID1(0);
762  uint32_t id2 = ap_Event->GetID2(0);
763  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&id1), sizeof(uint32_t));
764  m2p_dataFile[a_th]->write(reinterpret_cast<char*>(&id2), sizeof(uint32_t));
765 
766  return 0;
767 }
768 
769 // =====================================================================
770 // ---------------------------------------------------------------------
771 // ---------------------------------------------------------------------
772 // =====================================================================
773 
775 {
776  // Verbose
778  if (m_verbose>=VERBOSE_NORMAL) Cout("iDataFileSPECT::WriteHeader() -> Write header file '" << m_headerFileName << "'" << endl);
779  // Open file
780  fstream headerFile;
781  headerFile.open(m_headerFileName.c_str(), ios::out);
782  if (!headerFile.is_open())
783  {
784  Cerr("***** iDataFileSPECT::WriteHeader() -> Failed to open output header file '" << m_headerFileName << "' !" << endl);
785  return 1;
786  }
787  // Data file name
788  headerFile << "Data filename: " << GetFileFromPath(m_dataFileName) << endl;
789  // Number of events
790  headerFile << "Number of events: " << m_nbEvents << endl;
791  // Data mode
792  if (m_dataMode==MODE_HISTOGRAM) headerFile << "Data mode: histogram" << endl;
793  else if (m_dataMode==MODE_LIST) headerFile << "Data mode: list-mode" << endl;
794  else if (m_dataMode==MODE_NORMALIZATION) headerFile << "Data mode: normalization" << endl;
795  // SPECT data type
796  headerFile << "Data type: SPECT" << endl;
797  // Acquisition start time in seconds
798  headerFile << "Start time (s): " << m_startTimeInSec << endl;
799  // Acquisition duration in seconds
800  headerFile << "Duration (s): " << m_durationInSec << endl;
801  // Scanner name
802  headerFile << "Scanner name: " << sScannerManager::GetInstance()->GetScannerName() << endl;
803  // Number of bins
804  if (mp_nbOfBins[0] != 0 && mp_nbOfBins[1] != 0)
805  headerFile << "Number of bins: " << mp_nbOfBins[0] << ", " << mp_nbOfBins[1] << endl;
806  // Number of projections
807  headerFile << "Number of projections: " << m_nbOfProjections << endl;
808  // Projection angles
809  headerFile << "Projection angles: " << mp_angles[0];
810  for (int a=1 ; a<m_nbOfProjections ; a++) headerFile << ", " << mp_angles[a];
811  headerFile << endl;
812 
813  // Distance camera surface to COR
814  bool global_distance_flag = true;
815 
816  // Check if we have constant radius
817  for (int p=1 ; p<m_nbOfProjections ; p++)
818  {
820  global_distance_flag = false;
821  }
822 
823  if(global_distance_flag)
824  headerFile << "Global distance camera surface to COR: " << mp_CORtoDetectorDistance[0];
825  else
826  {
827  headerFile << "Distance camera surface to COR: " << mp_CORtoDetectorDistance[0];
828  for (int p=1 ; p<m_nbOfProjections ; p++) headerFile << ", " << mp_CORtoDetectorDistance[p];
829  }
830  headerFile << endl;
831  // Calibration factor
832  headerFile << "Calibration factor: " << m_calibrationFactor << endl;
833  // Isotope
834  headerFile << "Isotope: " << m_isotope << endl;
835  // Normalization correction flag
836  headerFile << "Normalization correction flag: " << m_normCorrectionFlag << endl;
837  // Scatter correction flag
838  headerFile << "Scatter correction flag: " << m_scatCorrectionFlag << endl;
839  // Head rotation direction
840  string rot_direction = (m_headRotDirection == GEO_ROT_CCW) ? "CCW" : "CW";
841  headerFile << "Head rotation direction: " << rot_direction << endl;
842  // Close file
843  headerFile.close();
844  // End
845  return 0;
846 }
847 
848 // =====================================================================
849 // ---------------------------------------------------------------------
850 // ---------------------------------------------------------------------
851 // =====================================================================
852 
854 {
855  // Verbose
857  if (m_verbose>=VERBOSE_DETAIL) Cout("iDataFileSPECT::PROJ_GetScannerSpecificParameters() ..."<< endl);
858  // Create pointers dedicated to recover the addresses of the member variables of the scanner object
859  FLTNB* p_angles = NULL;
860  FLTNB* p_CORtoDetectorDistance = NULL;
861  FLTNB p_pixSizeXY[2] ; // not used here
862  // Get pointers to SPECT specific parameters in scanner
863  if (sScannerManager::GetInstance()->GetSPECTSpecificParameters(&m_nbOfProjections, &m_nbHeads, &m_acquisitionZoom, mp_nbOfBins, p_pixSizeXY, p_angles, p_CORtoDetectorDistance, &m_headRotDirection) )
864  {
865  Cerr("*****iDataFileSPECT::PROJ_GetScannerSpecificParameters() -> An error occurred while trying to get SPECT geometric parameters from the scanner object !" << endl);
866  return 1;
867  }
868  // In projection, mp_CORtoDetectorDistance is allocated according to the number of heads, and not the number of projections
869  // (Projection does not currently allow to set a radius specific to the projection angles)
871  // Retrieve SPECT distance between the detector and center of rotation
872  for (int h=0 ; h<m_nbHeads ; h++)
873  mp_CORtoDetectorDistance[h] = p_CORtoDetectorDistance[0];
874  // Retrieve projection angles
876  for (int a=0 ; a<m_nbOfProjections ; a++)
877  mp_angles[a] = p_angles[a];
878  // End
879  return 0;
880 }
881 
882 // =====================================================================
883 // ---------------------------------------------------------------------
884 // ---------------------------------------------------------------------
885 // =====================================================================
int CheckSpecificConsistencyWithAnotherDataFile(vDataFile *ap_DataFile)
Check consistency between &#39;this&#39; and the provided datafile, for specific characteristics.
This class is designed to be a mother virtual class for DataFile.
Definition: vDataFile.hh:102
int SetSPECTIsotope(int a_bed, const string &a_isotope)
Set the SPECT isotope for the provided bed.
static sScannerManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
#define VERBOSE_DEBUG_EVENT
#define MODE_HISTOGRAM
Definition: vDataFile.hh:58
bool GetNormCorrectionFlag()
Simply return m_normCorrectionFlag.
int64_t m_sizeEvent
Definition: vDataFile.hh:597
bool mp_POIDirectionFlag[3]
Definition: vDataFile.hh:593
string m_dataFileName
Definition: vDataFile.hh:577
int InitAngles(FLTNB *ap_angles)
allocate memory for the mp_angles variable using m_nbProjections and initialize the projection angles...
#define MODE_LIST
Definition: vDataFile.hh:56
int ReadSpecificInfoInHeader(bool a_affectQuantificationFlag)
Read through the header file and recover specific SPECT information.
#define FLTNB
Definition: gVariables.hh:81
#define MODE_NORMALIZATION
Definition: vDataFile.hh:60
bool GetEventKindFlag()
Simply return m_eventKindFlag.
iDataFileSPECT()
iDataFileSPECT constructor. Initialize the member variables to their default values.
uint32_t GetID2(int a_line)
Definition: vEvent.hh:107
FLTNB * GetAngles()
#define VERBOSE_DETAIL
#define GEO_ROT_CCW
Definition: vScanner.hh:48
FLTNB GetEventValue(int a_bin)
FLTNB m_durationInSec
Definition: vDataFile.hh:583
int WriteHeader()
Generate a header file according to the data output information.
Inherit from iEventSPECT. Class for SPECT histogram mode events.
string m_headerFileName
Definition: vDataFile.hh:576
void DescribeSpecific()
Implementation of the pure virtual eponym function that simply prints info about the datafile...
int m_dataMode
Definition: vDataFile.hh:579
int GetSPECTSpecificParameters(uint16_t *ap_nbOfProjections, uint16_t *ap_nbHeads, FLTNB *ap_acquisitionZoom, uint16_t *ap_nbOfBins, FLTNB *ap_pixSizeXY, FLTNB *&ap_angles, FLTNB *&ap_CORtoDetectorDistance, int *ap_headRotDirection)
Transfer geometric information recovered from the datafile to the scanner object. ...
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
#define TYPE_SPECT
Definition: vDataFile.hh:75
int WriteListEvent(iEventListSPECT *ap_Event, int a_th)
Write a SPECT list-mode event.
int m_dataSpec
Definition: vDataFile.hh:581
FLTNB m_startTimeInSec
Definition: vDataFile.hh:582
Declaration of class iDataFileSPECT.
string GetFileFromPath(const string &a_pathToFile)
Simply return the file from a path string passed in parameter.
Definition: gOptions.cc:1152
~iDataFileSPECT()
iDataFileSPECT destructor.
uint16_t m_nbOfProjections
bool GetIgnoreNormCorrectionFlag()
Get the boolean that says if the normalization correction is ignored or not.
#define FLTNBDATA
Definition: gVariables.hh:87
int PROJ_GetScannerSpecificParameters()
Get SPECT specific parameters for projections from the scanner object, through the scannerManager...
#define Cerr(MESSAGE)
const string & GetPathName()
int ComputeSizeEvent()
Computation of the size of each event according to the mandatory/optional correction fields...
#define VERBOSE_DEBUG_LIGHT
bool m_ignorePOIFlag
Definition: vDataFile.hh:592
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
int CheckSpecificParameters()
Check parameters specific to SPECT data.
int CheckFileSizeConsistency()
This function is implemented in child classes Check if file size is consistent. ...
int ReadDataASCIIFile(const string &a_file, const string &a_keyword, T *ap_return, int a_nbElts, bool a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_file" file matching the "a_keyword" string passed as parameter a...
Definition: gOptions.cc:129
Singleton class that Instantiate and initialize the scanner object.
int m_dataType
Definition: vDataFile.hh:580
int64_t m_nbEvents
Definition: vDataFile.hh:578
int InitCorToDetectorDistance(FLTNB *ap_CORtoDetectorDistance)
allocate memory for the ap_CORtoDetectorDistance variable using m_nbProjections, and initialize the p...
oImageDimensionsAndQuantification * mp_ID
Definition: vDataFile.hh:572
uint16_t GetNbBins(int axis)
uint16_t GetNbProjections()
fstream ** m2p_dataFile
Definition: vDataFile.hh:598
Inherit from vDataFile. Class that manages the reading of a SPECT input file (header + data)...
#define VERBOSE_NORMAL
int GetHeadRotDirection()
Simply return m_headRotDirection.
int WriteHistoEvent(iEventHistoSPECT *ap_Event, int a_th)
Write a SPECT histogram event.
int m_bedIndex
Definition: vDataFile.hh:585
vEvent * GetEventSpecific(char *ap_buffer, int a_th)
Read an event from the position pointed by &#39;ap_buffer&#39;, parse the generic or modality-specific inform...
FLTNB GetNormFactor()
Definition: iEventSPECT.hh:78
FLTNB mp_POIResolution[3]
Definition: vDataFile.hh:594
int PROJ_InitFile()
Initialize the fstream objets for output writing as well as some other variables specific to the Proj...
bool m_ignoreNormCorrectionFlag
int SetSpecificParametersFrom(vDataFile *ap_DataFile)
Initialize all parameters specific to SPECT from the provided datafile.
FLTNB * mp_CORtoDetectorDistance
const string & GetBaseName()
void SetTimeInMs(uint32_t a_value)
Set the timestamp of the Event.
Definition: vEvent.hh:138
Mother class for the Event objects.
Definition: vEvent.hh:42
int GetDataMode()
Definition: vDataFile.hh:299
int WriteEvent(vEvent *ap_Event, int a_th=0)
Write event according to the chosen type of data.
vEvent ** m2p_BufferEvent
Definition: vDataFile.hh:599
#define SPEC_EMISSION
Definition: vDataFile.hh:90
uint32_t GetID1(int a_line)
Definition: vEvent.hh:100
int GetNbThreadsForProjection()
Get the number of threads used for projections.
bool GetIgnoreScatCorrectionFlag()
Get the boolean that says if the scatter correction is ignored or not.
FLTNB * GetCORtoDetectorDistance()
#define DEBUG_VERBOSE(IGNORED1, IGNORED2)
#define Cout(MESSAGE)
FLTNB m_calibrationFactor
Definition: vDataFile.hh:584
uint16_t GetNbHeads()
int PrepareDataFile()
Store different kind of information inside arrays (data relative to specific correction as well as ba...
bool m_ignoreScatCorrectionFlag
int m_verbose
Definition: vDataFile.hh:573
uint16_t mp_nbOfBins[2]
uint32_t GetTimeInMs()
Definition: vEvent.hh:87
#define GEO_ROT_CW
Definition: vScanner.hh:46
FLTNB GetEventScatRate()
Definition: iEventSPECT.hh:84
bool GetScatCorrectionFlag()
Simply return m_scatCorrectionFlag.
Inherit from iEventSPECT. Class for SPECT list-mode events.