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