ECCE @ EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DicomHandler.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file DicomHandler.cc
1 //
2 // ********************************************************************
3 // * License and Disclaimer *
4 // * *
5 // * The Geant4 software is copyright of the Copyright Holders of *
6 // * the Geant4 Collaboration. It is provided under the terms and *
7 // * conditions of the Geant4 Software License, included in the file *
8 // * LICENSE and available at http://cern.ch/geant4/license . These *
9 // * include a list of copyright holders. *
10 // * *
11 // * Neither the authors of this software system, nor their employing *
12 // * institutes,nor the agencies providing financial support for this *
13 // * work make any representation or warranty, express or implied, *
14 // * regarding this software system or assume any liability for its *
15 // * use. Please see the license in the file LICENSE and URL above *
16 // * for the full disclaimer and the limitation of liability. *
17 // * *
18 // * This code implementation is the result of the scientific and *
19 // * technical work of the GEANT4 collaboration. *
20 // * By using, copying, modifying or distributing the software (or *
21 // * any work based on the software) you agree to acknowledge its *
22 // * use in resulting scientific publications, and indicate your *
23 // * acceptance of all terms of the Geant4 Software license. *
24 // ********************************************************************
25 //
26 //
29 //
30 // The code was written by :
31 // *Louis Archambault louis.archambault@phy.ulaval.ca,
32 // *Luc Beaulieu beaulieu@phy.ulaval.ca
33 // +Vincent Hubert-Tremblay at tigre.2@sympatico.ca
34 //
35 //
36 // *Centre Hospitalier Universitaire de Quebec (CHUQ),
37 // Hotel-Dieu de Quebec, departement de Radio-oncologie
38 // 11 cote du palais. Quebec, QC, Canada, G1R 2J6
39 // tel (418) 525-4444 #6720
40 // fax (418) 691 5268
41 //
42 // + University Laval, Quebec (QC) Canada
43 //*******************************************************
44 //
45 //*******************************************************
46 //
52 //*******************************************************
53 
54 #include "DicomHandler.hh"
55 #include "globals.hh"
56 #include "G4ios.hh"
57 #include <fstream>
58 
59 #include <cctype>
60 #include <cstring>
61 
64 
65 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
66 
68 
69 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
70 
72 {
73  if (fInstance == 0)
74  {
75  static DicomHandler dicomhandler;
76  fInstance = &dicomhandler;
77  }
78  return fInstance;
79 }
80 
81 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
82 
84 {
85  // default is current directory
86  G4String driverPath = ".";
87  // check environment
88  const char* path = std::getenv("DICOM_PATH");
89 
90  if(path)
91  {
92  // if is set in environment
93  return G4String(path);
94  }
95  else
96  {
97  // if DICOM_USE_HEAD, look for data installation
98 #ifdef DICOM_USE_HEAD
99  G4cerr << "Warning! DICOM was compiled with DICOM_USE_HEAD option but "
100  << "the DICOM_PATH was not set!" << G4endl;
101  G4String datadir = G4GetEnv<G4String>("G4ENSDFSTATEDATA", "");
102  if(datadir.length() > 0)
103  {
104  auto _last = datadir.rfind("/");
105  if(_last != std::string::npos)
106  datadir.erase(_last);
107  driverPath = datadir + "/DICOM1.1/DICOM_HEAD_TEST";
108  G4int rc = setenv("DICOM_PATH", driverPath.c_str(), 0);
109  G4cerr << "\t --> Using '" << driverPath << "'..." << G4endl;
111  }
112 #endif
113  }
114  return driverPath;
115 }
116 
117 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
118 
120 {
121 #if defined(DICOM_USE_HEAD) && defined(G4_DCMTK)
122  return GetDicomDataPath() + "/Data.dat.new";
123 #else
124  return GetDicomDataPath() + "/Data.dat";
125 #endif
126 }
127 
128 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
129 
130 #ifdef DICOM_USE_HEAD
132 :DATABUFFSIZE(8192), LINEBUFFSIZE(5020), FILENAMESIZE(512),
133  fCompression(0),fNFiles(0), fRows(0), fColumns(0),
134  fBitAllocated(0), fMaxPixelValue(0),
135  fMinPixelValue(0),fPixelSpacingX(0.),
136  fPixelSpacingY(0.),fSliceThickness(0.),
137  fSliceLocation(0.),fRescaleIntercept(0),
138  fRescaleSlope(0),fLittleEndian(true),
139  fImplicitEndian(false),fPixelRepresentation(0),
140  fNbrequali(0),fValueDensity(NULL),fValueCT(NULL),fReadCalibration(false),
141  fMergedSlices(NULL),fCt2DensityFile("null.dat")
142 {
145  G4cout << "Reading the DICOM_HEAD project " << fDriverFile << G4endl;
146 }
147 #else
149 :DATABUFFSIZE(8192), LINEBUFFSIZE(5020), FILENAMESIZE(512),
150  fCompression(0), fNFiles(0), fRows(0), fColumns(0),
151  fBitAllocated(0), fMaxPixelValue(0), fMinPixelValue(0),
152  fPixelSpacingX(0.), fPixelSpacingY(0.),fSliceThickness(0.),
153  fSliceLocation(0.), fRescaleIntercept(0), fRescaleSlope(0),
154  fLittleEndian(true),fImplicitEndian(false),fPixelRepresentation(0),
155  fNbrequali(0),fValueDensity(NULL),fValueCT(NULL),
156  fReadCalibration(false),fMergedSlices(NULL),
157  fDriverFile("Data.dat"),fCt2DensityFile("CT2Density.dat")
158 {
160 }
161 #endif
162 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
163 
165 {}
166 
167 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
168 
169 G4int DicomHandler::ReadFile(FILE* dicom, char* filename2)
170 {
171  G4cout << " ReadFile " << filename2 << G4endl;
172 
173  G4int returnvalue = 0; size_t rflag = 0;
174  char * buffer = new char[LINEBUFFSIZE];
175 
176  fImplicitEndian = false;
177  fLittleEndian = true;
178 
179  rflag = std::fread( buffer, 1, 128, dicom ); // The first 128 bytes
180  //are not important
181  // Reads the "DICOM" letters
182  rflag = std::fread( buffer, 1, 4, dicom );
183  // if there is no preamble, the FILE pointer is rewinded.
184  if(std::strncmp("DICM", buffer, 4) != 0) {
185  std::fseek(dicom, 0, SEEK_SET);
186  fImplicitEndian = true;
187  }
188 
189  short readGroupId; // identify the kind of input data
190  short readElementId; // identify a particular type information
191  short elementLength2; // deal with element length in 2 bytes
192  //unsigned int elementLength4;
193  // deal with element length in 4 bytes
194  unsigned long elementLength4; // deal with element length in 4 bytes
195 
196  char * data = new char[DATABUFFSIZE];
197 
198  // Read information up to the pixel data
199  while(true) {
200 
201  //Reading groups and elements :
202  readGroupId = 0;
203  readElementId = 0;
204  // group ID
205  rflag = std::fread(buffer, 2, 1, dicom);
206  GetValue(buffer, readGroupId);
207  // element ID
208  rflag = std::fread(buffer, 2, 1, dicom);
209  GetValue(buffer, readElementId);
210 
211  // Creating a tag to be identified afterward
212  G4int tagDictionary = readGroupId*0x10000 + readElementId;
213 
214  // beginning of the pixels
215  if(tagDictionary == 0x7FE00010) {
216  // Following 2 fread's are modifications to original DICOM example
217  // (Jonathan Madsen)
218  if(!fImplicitEndian)
219  rflag = std::fread(buffer,2,1,dicom); // Reserved 2 bytes
220  // (not used for pixels)
221  rflag = std::fread(buffer,4,1,dicom); // Element Length
222  // (not used for pixels)
223  break; // Exit to ReadImageData()
224  }
225 
226  // VR or element length
227  rflag = std::fread(buffer,2,1,dicom);
228  GetValue(buffer, elementLength2);
229 
230  // If value representation (VR) is OB, OW, SQ, UN, added OF and UT
231  //the next length is 32 bits
232  if((elementLength2 == 0x424f || // "OB"
233  elementLength2 == 0x574f || // "OW"
234  elementLength2 == 0x464f || // "OF"
235  elementLength2 == 0x5455 || // "UT"
236  elementLength2 == 0x5153 || // "SQ"
237  elementLength2 == 0x4e55) && // "UN"
238  !fImplicitEndian ) { // explicit VR
239 
240  rflag = std::fread(buffer, 2, 1, dicom); // Skip 2 reserved bytes
241 
242  // element length
243  rflag = std::fread(buffer, 4, 1, dicom);
244  GetValue(buffer, elementLength4);
245 
246  if(elementLength2 == 0x5153)
247  {
248  if(elementLength4 == 0xFFFFFFFF)
249  {
250  read_undefined_nested( dicom );
251  elementLength4=0;
252  } else{
253  if(read_defined_nested( dicom, elementLength4 )==0){
254  G4Exception("DicomHandler::ReadFile",
255  "DICOM001",
257  "Function read_defined_nested() failed!");
258  }
259  }
260  } else {
261  // Reading the information with data
262  rflag = std::fread(data, elementLength4,1,dicom);
263  }
264 
265  } else {
266 
267  // explicit with VR different than previous ones
268  if(!fImplicitEndian || readGroupId == 2) {
269 
270  //G4cout << "Reading DICOM files with Explicit VR"<< G4endl;
271  // element length (2 bytes)
272  rflag = std::fread(buffer, 2, 1, dicom);
273  GetValue(buffer, elementLength2);
274  elementLength4 = elementLength2;
275 
276  rflag = std::fread(data, elementLength4, 1, dicom);
277 
278  } else { // Implicit VR
279 
280  //G4cout << "Reading DICOM files with Implicit VR"<< G4endl;
281 
282  // element length (4 bytes)
283  if(std::fseek(dicom, -2, SEEK_CUR) != 0) {
284  G4Exception("DicomHandler::ReadFile",
285  "DICOM001",
287  "fseek failed");
288  }
289 
290  rflag = std::fread(buffer, 4, 1, dicom);
291  GetValue(buffer, elementLength4);
292 
293  //G4cout << std::hex<< elementLength4 << G4endl;
294 
295  if(elementLength4 == 0xFFFFFFFF)
296  {
297  read_undefined_nested(dicom);
298  elementLength4=0;
299  } else{
300  rflag = std::fread(data, elementLength4, 1, dicom);
301  }
302 
303  }
304  }
305 
306  // NULL termination
307  data[elementLength4] = '\0';
308 
309  // analyzing information
310  GetInformation(tagDictionary, data);
311  }
312 
313  G4String fnameG4DCM = G4String(filename2) + ".g4dcm";
314 
315  // Perform functions originally written straight to file
316  DicomPhantomZSliceHeader* zslice = new DicomPhantomZSliceHeader(fnameG4DCM);
317  for(auto ite = fMaterialIndices.cbegin();
318  ite != fMaterialIndices.cend(); ++ite)
319  {
320  zslice->AddMaterial(ite->second);
321  }
322 
324  zslice->SetNoVoxelY(fRows/fCompression);
325  zslice->SetNoVoxelZ(1);
326 
327  zslice->SetMinX(-fPixelSpacingX*fColumns/2.);
328  zslice->SetMaxX(fPixelSpacingX*fColumns/2.);
329 
330  zslice->SetMinY(-fPixelSpacingY*fRows/2.);
331  zslice->SetMaxY(fPixelSpacingY*fRows/2.);
332 
335 
336  //===
337 
338  ReadData( dicom, filename2 );
339 
340  // DEPRECIATED
341  //StoreData( foutG4DCM );
342  //foutG4DCM.close();
343 
344  StoreData( zslice );
345 
346  // Dumped 2 file after DicomPhantomZSliceMerged has checked for consistency
347  //zslice->DumpToFile();
348 
349  fMergedSlices->AddZSlice(zslice);
350 
351  //
352  delete [] buffer;
353  delete [] data;
354 
355  if (rflag) return returnvalue;
356  return returnvalue;
357 }
358 
359 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
360 
361 void DicomHandler::GetInformation(G4int & tagDictionary, char * data)
362 {
363  if(tagDictionary == 0x00280010 ) { // Number of Rows
364  GetValue(data, fRows);
365  std::printf("[0x00280010] Rows -> %i\n",fRows);
366 
367  } else if(tagDictionary == 0x00280011 ) { // Number of fColumns
368  GetValue(data, fColumns);
369  std::printf("[0x00280011] Columns -> %i\n",fColumns);
370 
371  } else if(tagDictionary == 0x00280102 ) { // High bits ( not used )
372  short highBits;
373  GetValue(data, highBits);
374  std::printf("[0x00280102] High bits -> %i\n",highBits);
375 
376  } else if(tagDictionary == 0x00280100 ) { // Bits allocated
377  GetValue(data, fBitAllocated);
378  std::printf("[0x00280100] Bits allocated -> %i\n", fBitAllocated);
379 
380  } else if(tagDictionary == 0x00280101 ) { // Bits stored ( not used )
381  short bitStored;
382  GetValue(data, bitStored);
383  std::printf("[0x00280101] Bits stored -> %i\n",bitStored);
384 
385  } else if(tagDictionary == 0x00280106 ) { // Min. pixel value
386  GetValue(data, fMinPixelValue);
387  std::printf("[0x00280106] Min. pixel value -> %i\n", fMinPixelValue);
388 
389  } else if(tagDictionary == 0x00280107 ) { // Max. pixel value
390  GetValue(data, fMaxPixelValue);
391  std::printf("[0x00280107] Max. pixel value -> %i\n", fMaxPixelValue);
392 
393  } else if(tagDictionary == 0x00281053) { // Rescale slope
394  fRescaleSlope = atoi(data);
395  std::printf("[0x00281053] Rescale Slope -> %d\n", fRescaleSlope);
396 
397  } else if(tagDictionary == 0x00281052 ) { // Rescalse intercept
398  fRescaleIntercept = atoi(data);
399  std::printf("[0x00281052] Rescale Intercept -> %d\n",
401 
402  } else if(tagDictionary == 0x00280103 ) {
403  // Pixel representation ( functions not design to read signed bits )
404  fPixelRepresentation = atoi(data); // 0: unsigned 1: signed
405  std::printf("[0x00280103] Pixel Representation -> %i\n",
407  if(fPixelRepresentation == 1 ) {
408  std::printf("### PIXEL REPRESENTATION = 1, BITS ARE SIGNED, ");
409  std::printf("DICOM READING SCAN FOR UNSIGNED VALUE, POSSIBLE ");
410  std::printf("ERROR !!!!!! -> \n");
411  }
412 
413  } else if(tagDictionary == 0x00080006 ) { // Modality
414  std::printf("[0x00080006] Modality -> %s\n", data);
415 
416  } else if(tagDictionary == 0x00080070 ) { // Manufacturer
417  std::printf("[0x00080070] Manufacturer -> %s\n", data);
418 
419  } else if(tagDictionary == 0x00080080 ) { // Institution Name
420  std::printf("[0x00080080] Institution Name -> %s\n", data);
421 
422  } else if(tagDictionary == 0x00080081 ) { // Institution Address
423  std::printf("[0x00080081] Institution Address -> %s\n", data);
424 
425  } else if(tagDictionary == 0x00081040 ) { // Institution Department Name
426  std::printf("[0x00081040] Institution Department Name -> %s\n", data);
427 
428  } else if(tagDictionary == 0x00081090 ) { // Manufacturer's Model Name
429  std::printf("[0x00081090] Manufacturer's Model Name -> %s\n", data);
430 
431  } else if(tagDictionary == 0x00181000 ) { // Device Serial Number
432  std::printf("[0x00181000] Device Serial Number -> %s\n", data);
433 
434  } else if(tagDictionary == 0x00080008 ) { // Image type ( not used )
435  std::printf("[0x00080008] Image Types -> %s\n", data);
436 
437  } else if(tagDictionary == 0x00283000 ) { //Modality LUT Sequence(not used)
438  std::printf("[0x00283000] Modality LUT Sequence SQ 1 -> %s\n", data);
439 
440  } else if(tagDictionary == 0x00283002 ) { // LUT Descriptor ( not used )
441  std::printf("[0x00283002] LUT Descriptor US or SS 3 -> %s\n", data);
442 
443  } else if(tagDictionary == 0x00283003 ) { // LUT Explanation ( not used )
444  std::printf("[0x00283003] LUT Explanation LO 1 -> %s\n", data);
445 
446  } else if(tagDictionary == 0x00283004 ) { // Modality LUT ( not used )
447  std::printf("[0x00283004] Modality LUT Type LO 1 -> %s\n", data);
448 
449  } else if(tagDictionary == 0x00283006 ) { // LUT Data ( not used )
450  std::printf("[0x00283006] LUT Data US or SS -> %s\n", data);
451 
452  } else if(tagDictionary == 0x00283010 ) { // VOI LUT ( not used )
453  std::printf("[0x00283010] VOI LUT Sequence SQ 1 -> %s\n", data);
454 
455  } else if(tagDictionary == 0x00280120 ) { // Pixel Padding Value (not used)
456  std::printf("[0x00280120] Pixel Padding Value US or SS 1 -> %s\n", data);
457 
458  } else if(tagDictionary == 0x00280030 ) { // Pixel Spacing
459  G4String datas(data);
460  G4int iss = G4int(datas.find('\\'));
461  fPixelSpacingX = atof( datas.substr(0,iss).c_str() );
462  fPixelSpacingY = atof( datas.substr(iss+1,datas.length()).c_str() );
463 
464  } else if(tagDictionary == 0x00200037 ) { // Image Orientation ( not used )
465  std::printf("[0x00200037] Image Orientation (Phantom) -> %s\n", data);
466 
467  } else if(tagDictionary == 0x00200032 ) { // Image Position ( not used )
468  std::printf("[0x00200032] Image Position (Phantom,mm) -> %s\n", data);
469 
470  } else if(tagDictionary == 0x00180050 ) { // Slice Thickness
471  fSliceThickness = atof(data);
472  std::printf("[0x00180050] Slice Thickness (mm) -> %f\n", fSliceThickness);
473 
474  } else if(tagDictionary == 0x00201041 ) { // Slice Location
475  fSliceLocation = atof(data);
476  std::printf("[0x00201041] Slice Location -> %f\n", fSliceLocation);
477 
478  } else if(tagDictionary == 0x00280004 ) { // Photometric Interpretation
479  // ( not used )
480  std::printf("[0x00280004] Photometric Interpretation -> %s\n", data);
481 
482  } else if(tagDictionary == 0x00020010) { // Endian
483  if(strcmp(data, "1.2.840.10008.1.2") == 0)
484  fImplicitEndian = true;
485  else if(strncmp(data, "1.2.840.10008.1.2.2", 19) == 0)
486  fLittleEndian = false;
487  //else 1.2.840..10008.1.2.1 (explicit little endian)
488 
489  std::printf("[0x00020010] Endian -> %s\n", data);
490  }
491 
492  // others
493  else {
494  //std::printf("[0x%x] -> %s\n", tagDictionary, data);
495  ;
496  }
497 
498 }
499 
500 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
501 
503 {
504  G4int mean;
505  G4double density;
506  G4bool overflow = false;
507 
508  if(!dcmPZSH) { return; }
509 
511 
512  //----- Print indices of material
513  if(fCompression == 1) { // no fCompression: each pixel has a density value)
514  for( G4int ww = 0; ww < fRows; ++ww) {
515  dcmPZSH->AddRow();
516  for( G4int xx = 0; xx < fColumns; ++xx) {
517  mean = fTab[ww][xx];
518  density = Pixel2density(mean);
519  dcmPZSH->AddValue(density);
520  dcmPZSH->AddMateID(GetMaterialIndex(G4float(density)));
521  }
522  }
523 
524  } else {
525  // density value is the average of a square region of
526  // fCompression*fCompression pixels
527  for(G4int ww = 0; ww < fRows ;ww += fCompression ) {
528  dcmPZSH->AddRow();
529  for(G4int xx = 0; xx < fColumns ;xx +=fCompression ) {
530  overflow = false;
531  mean = 0;
532  for(G4int sumx = 0; sumx < fCompression; ++sumx) {
533  for(G4int sumy = 0; sumy < fCompression; ++sumy) {
534  if(ww+sumy >= fRows || xx+sumx >= fColumns) overflow = true;
535  mean += fTab[ww+sumy][xx+sumx];
536  }
537  if(overflow) break;
538  }
539  mean /= fCompression*fCompression;
540 
541  if(!overflow) {
542  density = Pixel2density(mean);
543  dcmPZSH->AddValue(density);
544  dcmPZSH->AddMateID(GetMaterialIndex(G4float(density)));
545  }
546  }
547  }
548  }
549 
550  dcmPZSH->FlipData();
551 }
552 
553 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
554 // This function is depreciated as it is handled by
555 // DicomPhantomZSliceHeader::DumpToFile
556 void DicomHandler::StoreData(std::ofstream& foutG4DCM)
557 {
558  G4int mean;
559  G4double density;
560  G4bool overflow = false;
561 
562  //----- Print indices of material
563  if(fCompression == 1) { // no fCompression: each pixel has a density value)
564  for( G4int ww = 0; ww < fRows; ++ww) {
565  for( G4int xx = 0; xx < fColumns; ++xx) {
566  mean = fTab[ww][xx];
567  density = Pixel2density(mean);
568  foutG4DCM << GetMaterialIndex( G4float(density) ) << " ";
569  }
570  foutG4DCM << G4endl;
571  }
572 
573  } else {
574  // density value is the average of a square region of
575  // fCompression*fCompression pixels
576  for(G4int ww = 0; ww < fRows ;ww += fCompression ) {
577  for(G4int xx = 0; xx < fColumns ;xx +=fCompression ) {
578  overflow = false;
579  mean = 0;
580  for(G4int sumx = 0; sumx < fCompression; ++sumx) {
581  for(G4int sumy = 0; sumy < fCompression; ++sumy) {
582  if(ww+sumy >= fRows || xx+sumx >= fColumns) overflow = true;
583  mean += fTab[ww+sumy][xx+sumx];
584  }
585  if(overflow) break;
586  }
587  mean /= fCompression*fCompression;
588 
589  if(!overflow) {
590  density = Pixel2density(mean);
591  foutG4DCM << GetMaterialIndex( G4float(density) ) << " ";
592  }
593  }
594  foutG4DCM << G4endl;
595  }
596 
597  }
598 
599  //----- Print densities
600  if(fCompression == 1) { // no fCompression: each pixel has a density value)
601  for( G4int ww = 0; ww < fRows; ww++) {
602  for( G4int xx = 0; xx < fColumns; xx++) {
603  mean = fTab[ww][xx];
604  density = Pixel2density(mean);
605  foutG4DCM << density << " ";
606  if( xx%8 == 3 ) foutG4DCM << G4endl; // just for nicer reading
607  }
608  }
609 
610  } else {
611  // density value is the average of a square region of
612  // fCompression*fCompression pixels
613  for(G4int ww = 0; ww < fRows ;ww += fCompression ) {
614  for(G4int xx = 0; xx < fColumns ;xx +=fCompression ) {
615  overflow = false;
616  mean = 0;
617  for(G4int sumx = 0; sumx < fCompression; ++sumx) {
618  for(G4int sumy = 0; sumy < fCompression; ++sumy) {
619  if(ww+sumy >= fRows || xx+sumx >= fColumns) overflow = true;
620  mean += fTab[ww+sumy][xx+sumx];
621  }
622  if(overflow) break;
623  }
624  mean /= fCompression*fCompression;
625 
626  if(!overflow) {
627  density = Pixel2density(mean);
628  foutG4DCM << density << " ";
629  if( xx/fCompression%8 == 3 ) foutG4DCM << G4endl; // just for nicer
630  // reading
631  }
632  }
633  }
634 
635  }
636 
637 }
638 
639 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo..
640 void DicomHandler::ReadMaterialIndices( std::ifstream& finData)
641 {
642  unsigned int nMate;
643  G4String mateName;
644  G4float densityMax;
645  finData >> nMate;
646  if( finData.eof() ) return;
647 
648  G4cout << " ReadMaterialIndices " << nMate << G4endl;
649  for( unsigned int ii = 0; ii < nMate; ++ii )
650  {
651  finData >> mateName >> densityMax;
652  fMaterialIndices[densityMax] = mateName;
653  // G4cout << ii << " ReadMaterialIndices " << mateName << " "
654  // << densityMax << G4endl;
655  }
656 
657 }
658 
659 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
660 
662 {
663  std::map<G4float,G4String>::const_reverse_iterator ite;
664  std::size_t ii = fMaterialIndices.size();
665 
666  for( ite = fMaterialIndices.crbegin(); ite != fMaterialIndices.crend();
667  ++ite, ii-- )
668  {
669  if( density >= (*ite).first ) { break; }
670  }
671 
672  if(ii == fMaterialIndices.size()) { ii = fMaterialIndices.size()-1; }
673 
674  return unsigned(ii);
675 }
676 
677 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
678 
679 G4int DicomHandler::ReadData(FILE *dicom,char * filename2)
680 {
681  G4int returnvalue = 0; size_t rflag = 0;
682 
683  // READING THE PIXELS :
684  G4int w = 0;
685 
686  fTab = new G4int*[fRows];
687  for ( G4int i = 0; i < fRows; ++i )
688  {
689  fTab[i] = new G4int[fColumns];
690  }
691 
692  if(fBitAllocated == 8) { // Case 8 bits :
693 
694  std::printf("@@@ Error! Picture != 16 bits...\n");
695  std::printf("@@@ Error! Picture != 16 bits...\n");
696  std::printf("@@@ Error! Picture != 16 bits...\n");
697 
698  unsigned char ch = 0;
699 
700  for(G4int j = 0; j < fRows; ++j) {
701  for(G4int i = 0; i < fColumns; ++i) {
702  w++;
703  rflag = std::fread( &ch, 1, 1, dicom);
704  fTab[j][i] = ch*fRescaleSlope + fRescaleIntercept;
705  }
706  }
707  returnvalue = 1;
708 
709  } else { // from 12 to 16 bits :
710  char sbuff[2];
711  short pixel;
712  for( G4int j = 0; j < fRows; ++j) {
713  for( G4int i = 0; i < fColumns; ++i) {
714  w++;
715  rflag = std::fread(sbuff, 2, 1, dicom);
716  GetValue(sbuff, pixel);
717  fTab[j][i] = pixel*fRescaleSlope + fRescaleIntercept;
718  }
719  }
720  }
721 
722  // Creation of .g4 files wich contains averaged density data
723  char * nameProcessed = new char[FILENAMESIZE];
724  FILE* fileOut;
725 
726  std::sprintf(nameProcessed,"%s.g4dcmb",filename2);
727  fileOut = std::fopen(nameProcessed,"w+b");
728  std::printf("### Writing of %s ###\n",nameProcessed);
729 
730  unsigned int nMate = fMaterialIndices.size();
731  rflag = std::fwrite(&nMate, sizeof(unsigned int), 1, fileOut);
732  //--- Write materials
733  for( auto ite = fMaterialIndices.cbegin();
734  ite != fMaterialIndices.cend(); ++ite )
735  {
736  G4String mateName = (*ite).second;
737  for( std::size_t ii = (*ite).second.length(); ii < 40; ++ii )
738  {
739  mateName += " ";
740  } //mateName = const_cast<char*>(((*ite).second).c_str());
741 
742  const char* mateNameC = mateName.c_str();
743  rflag = std::fwrite(mateNameC, sizeof(char),40, fileOut);
744  }
745 
746  unsigned int fRowsC = fRows/fCompression;
747  unsigned int fColumnsC = fColumns/fCompression;
748  unsigned int planesC = 1;
749  G4float pixelLocationXM = -G4float(fPixelSpacingX*fColumns/2.);
750  G4float pixelLocationXP = G4float(fPixelSpacingX*fColumns/2.);
751  G4float pixelLocationYM = -G4float(fPixelSpacingY*fRows/2.);
752  G4float pixelLocationYP = G4float(fPixelSpacingY*fRows/2.);
753  G4float fSliceLocationZM = G4float(fSliceLocation-fSliceThickness/2.);
754  G4float fSliceLocationZP = G4float(fSliceLocation+fSliceThickness/2.);
755  //--- Write number of voxels (assume only one voxel in Z)
756  rflag = std::fwrite(&fRowsC, sizeof(unsigned int), 1, fileOut);
757  rflag = std::fwrite(&fColumnsC, sizeof(unsigned int), 1, fileOut);
758  rflag = std::fwrite(&planesC, sizeof(unsigned int), 1, fileOut);
759  //--- Write minimum and maximum extensions
760  rflag = std::fwrite(&pixelLocationXM, sizeof(G4float), 1, fileOut);
761  rflag = std::fwrite(&pixelLocationXP, sizeof(G4float), 1, fileOut);
762  rflag = std::fwrite(&pixelLocationYM, sizeof(G4float), 1, fileOut);
763  rflag = std::fwrite(&pixelLocationYP, sizeof(G4float), 1, fileOut);
764  rflag = std::fwrite(&fSliceLocationZM, sizeof(G4float), 1, fileOut);
765  rflag = std::fwrite(&fSliceLocationZP, sizeof(G4float), 1, fileOut);
766  // rflag = std::fwrite(&fCompression, sizeof(unsigned int), 1, fileOut);
767 
768  std::printf("%8i %8i\n",fRows,fColumns);
770  std::printf("%8f\n", fSliceThickness);
771  std::printf("%8f\n", fSliceLocation);
772  std::printf("%8i\n", fCompression);
773 
774  G4int compSize = fCompression;
775  G4int mean;
776  G4float density;
777  G4bool overflow = false;
778 
779  //----- Write index of material for each pixel
780  if(compSize == 1) { // no fCompression: each pixel has a density value)
781  for( G4int ww = 0; ww < fRows; ++ww) {
782  for( G4int xx = 0; xx < fColumns; ++xx) {
783  mean = fTab[ww][xx];
784  density = Pixel2density(mean);
785  unsigned int mateID = GetMaterialIndex( density );
786  rflag = std::fwrite(&mateID, sizeof(unsigned int), 1, fileOut);
787  }
788  }
789 
790  } else {
791  // density value is the average of a square region of
792  // fCompression*fCompression pixels
793  for(G4int ww = 0; ww < fRows ;ww += compSize ) {
794  for(G4int xx = 0; xx < fColumns ;xx +=compSize ) {
795  overflow = false;
796  mean = 0;
797  for(G4int sumx = 0; sumx < compSize; ++sumx) {
798  for(G4int sumy = 0; sumy < compSize; ++sumy) {
799  if(ww+sumy >= fRows || xx+sumx >= fColumns) overflow = true;
800  mean += fTab[ww+sumy][xx+sumx];
801  }
802  if(overflow) break;
803  }
804  mean /= compSize*compSize;
805 
806  if(!overflow) {
807  density = Pixel2density(mean);
808  unsigned int mateID = GetMaterialIndex( density );
809  rflag = std::fwrite(&mateID, sizeof(unsigned int), 1, fileOut);
810  }
811  }
812 
813  }
814  }
815 
816  //----- Write density for each pixel
817  if(compSize == 1) { // no fCompression: each pixel has a density value)
818  for( G4int ww = 0; ww < fRows; ++ww) {
819  for( G4int xx = 0; xx < fColumns; ++xx) {
820  mean = fTab[ww][xx];
821  density = Pixel2density(mean);
822  rflag = std::fwrite(&density, sizeof(G4float), 1, fileOut);
823  }
824  }
825 
826  } else {
827  // density value is the average of a square region of
828  // fCompression*fCompression pixels
829  for(G4int ww = 0; ww < fRows ;ww += compSize ) {
830  for(G4int xx = 0; xx < fColumns ;xx +=compSize ) {
831  overflow = false;
832  mean = 0;
833  for(G4int sumx = 0; sumx < compSize; ++sumx) {
834  for(G4int sumy = 0; sumy < compSize; ++sumy) {
835  if(ww+sumy >= fRows || xx+sumx >= fColumns) overflow = true;
836  mean += fTab[ww+sumy][xx+sumx];
837  }
838  if(overflow) break;
839  }
840  mean /= compSize*compSize;
841 
842  if(!overflow) {
843  density = Pixel2density(mean);
844  rflag = std::fwrite(&density, sizeof(G4float), 1, fileOut);
845  }
846  }
847 
848  }
849  }
850 
851  rflag = std::fclose(fileOut);
852 
853  delete [] nameProcessed;
854 
855  /* for ( G4int i = 0; i < fRows; i ++ ) {
856  delete [] fTab[i];
857  }
858  delete [] fTab;
859  */
860 
861  if (rflag) return returnvalue;
862  return returnvalue;
863 }
864 
865 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo.......
866 
867 // DICOM HEAD does not use the calibration curve
868 
869 #ifdef DICOM_USE_HEAD
871 {
872 fNbrequali = 0;
873 fReadCalibration = false;
874 G4cout << "No calibration curve for the DICOM HEAD needed!" << G4endl;
875 }
876 #else
877 // Separated out of Pixel2density
878 // No need to read in same calibration EVERY time
879 // Increases the speed of reading file by several orders of magnitude
880 
882 {
883  fNbrequali = 0;
884 // CT2Density.dat contains the calibration curve to convert CT (Hounsfield)
885 // number to physical density
886  std::ifstream calibration(fCt2DensityFile.c_str());
887  calibration >> fNbrequali;
890 
891  if(!calibration) {
892  G4Exception("DicomHandler::ReadFile","DICOM001", FatalException,
893  "@@@ No value to transform pixels in density!");
894  }
895  else { // calibration was successfully opened
896  for(G4int i = 0; i < fNbrequali; ++i)
897  { // Loop to store all the pts in CT2Density.dat
898  calibration >> fValueCT[i] >> fValueDensity[i];
899  }
900  }
901 calibration.close();
902 fReadCalibration = true;
903 }
904 #endif
905 
906 #ifdef DICOM_USE_HEAD
908 {
909  G4float density = -1;
910 
911 //Air
912  if (pixel == -998.) density = 0.001290;
913 //Soft Tissue
914  else if ( pixel == 24.) density = 1.00;
915 //Brain
916  else if ( pixel == 52.) density = 1.03;
917 // Spinal disc
918  else if ( pixel == 92.) density = 1.10;
919 // Trabecular bone
920  else if ( pixel == 197.) density = 1.18;
921 // Cortical Bone
922  else if ( pixel == 923.) density = 1.85;
923 // Tooth dentine
924  else if ( pixel == 1280.) density = 2.14;
925 //Tooth enamel
926  else if ( pixel == 2310.) density = 2.89;
927 
928 return density;
929 }
930 
931 #else
932 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo.....
933 
935 {
937 
938  G4float density = -1.;
939  G4double deltaCT = 0;
940  G4double deltaDensity = 0;
941 
942 
943  for(G4int j = 1; j < fNbrequali; ++j) {
944  if( pixel >= fValueCT[j-1] && pixel < fValueCT[j]) {
945 
946  deltaCT = fValueCT[j] - fValueCT[j-1];
947  deltaDensity = fValueDensity[j] - fValueDensity[j-1];
948 
949  // interpolating linearly
950  density = G4float(fValueDensity[j]
951  -((fValueCT[j] - pixel)*deltaDensity/deltaCT ));
952  break;
953  }
954  }
955 
956  if(density < 0.) {
957  std::printf("@@@ Error density = %f && Pixel = %i \
958  (0x%x) && deltaDensity/deltaCT = %f\n",density,pixel,pixel,
959  deltaDensity/deltaCT);
960  }
961 
962  return density;
963 }
964 #endif
965 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
966 
968 {
969  std::ifstream checkData(fDriverFile.c_str());
970  char * oneLine = new char[128];
971 
972  if(!(checkData.is_open())) { //Check existance of Data.dat
973 
974  G4String message =
975  "\nDicomG4 needs Data.dat (or another driver file specified";
976  message += " in command line):\n";
977  message += "\tFirst line: number of image pixel for a voxel (G4Box)\n";
978  message += "\tSecond line: number of images (CT slices) to read\n";
979  message += "\tEach following line contains the name of a Dicom image";
980  message += " except for the .dcm extension";
981  G4Exception("DicomHandler::ReadFile",
982  "DICOM001",
984  message.c_str());
985  }
986 
987  checkData >> fCompression;
988  checkData >> fNFiles;
989  G4String oneName;
990  checkData.getline(oneLine,100);
991  std::ifstream testExistence;
992  G4bool existAlready = true;
993  for(G4int rep = 0; rep < fNFiles; ++rep) {
994  checkData.getline(oneLine,100);
995  oneName = oneLine;
996  oneName += ".g4dcm"; // create dicomFile.g4dcm
997  G4cout << fNFiles << " test file " << oneName << G4endl;
998  testExistence.open(oneName.data());
999  if(!(testExistence.is_open())) {
1000  existAlready = false;
1001  testExistence.clear();
1002  testExistence.close();
1003  }
1004  testExistence.clear();
1005  testExistence.close();
1006  }
1007 
1008  ReadMaterialIndices( checkData );
1009 
1010  checkData.close();
1011  delete [] oneLine;
1012 
1013  if( existAlready == false ) { // The files *.g4dcm have to be created
1014 
1015  G4cout << "\nAll the necessary images were not found in processed form "
1016  << ", starting with .dcm images\n";
1017 
1018  FILE * dicom;
1019  FILE * lecturePref;
1020  char * fCompressionc = new char[LINEBUFFSIZE];
1021  char * maxc = new char[LINEBUFFSIZE];
1022  //char name[300], inputFile[300];
1023  char * name = new char[FILENAMESIZE];
1024  char * inputFile = new char[FILENAMESIZE];
1025  G4int rflag;
1026  lecturePref = std::fopen(fDriverFile.c_str(),"r");
1027 
1028  rflag = std::fscanf(lecturePref,"%s",fCompressionc);
1029  fCompression = atoi(fCompressionc);
1030  rflag = std::fscanf(lecturePref,"%s",maxc);
1031  fNFiles = atoi(maxc);
1032  G4cout << " fNFiles " << fNFiles << G4endl;
1033 
1035 
1036 #ifdef DICOM_USE_HEAD
1037  for( G4int i = 1; i <= fNFiles; ++i )
1038  { // Begin loop on filenames
1039  rflag = std::fscanf(lecturePref,"%s",inputFile);
1040  G4String path = GetDicomDataPath() + "/";
1041 
1042  std::sprintf(name,"%s.dcm",inputFile);
1043  //Writes the results to a character string buffer.
1044 
1045  char name2[200];
1046  strcpy(name2, path.c_str());
1047  strcat(name2, name);
1048  // Open input file and give it to gestion_dicom :
1049  std::printf("### Opening %s and reading :\n",name2);
1050  dicom = std::fopen(name2,"rb");
1051  // Reading the .dcm in two steps:
1052  // 1. reading the header
1053  // 2. reading the pixel data and store the density in Moyenne.dat
1054  if( dicom != 0 ) {
1055  ReadFile(dicom,inputFile);
1056  } else {
1057  G4cout << "\nError opening file : " << name2 << G4endl;
1058  }
1059  rflag = std::fclose(dicom);
1060  }
1061 #else
1062 
1063  for( G4int i = 1; i <= fNFiles; ++i )
1064  { // Begin loop on filenames
1065  rflag = std::fscanf(lecturePref,"%s",inputFile);
1066 
1067  std::sprintf(name,"%s.dcm",inputFile);
1068  //Writes the results to a character string buffer.
1069 
1070  //G4cout << "check: " << name << G4endl;
1071  // Open input file and give it to gestion_dicom :
1072  std::printf("### Opening %s and reading :\n",name);
1073  dicom = std::fopen(name,"rb");
1074  // Reading the .dcm in two steps:
1075  // 1. reading the header
1076  // 2. reading the pixel data and store the density in Moyenne.dat
1077  if( dicom != 0 ) {
1078  ReadFile(dicom,inputFile);
1079  } else {
1080  G4cout << "\nError opening file : " << name << G4endl;
1081  }
1082  rflag = std::fclose(dicom);
1083  }
1084 #endif
1085 
1086  rflag = std::fclose(lecturePref);
1087 
1088  // Checks the spacing is correct. Dumps to files
1090 
1091  delete [] fCompressionc;
1092  delete [] maxc;
1093  delete [] name;
1094  delete [] inputFile;
1095  if (rflag) return;
1096 
1097  }
1098 
1099  if(fValueDensity) { delete [] fValueDensity; }
1100  if(fValueCT) { delete [] fValueCT; }
1101  if(fMergedSlices) { delete fMergedSlices; }
1102 
1103 }
1104 
1105 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
1106 
1108 {
1109  // VARIABLES
1110  unsigned short item_GroupNumber;
1111  unsigned short item_ElementNumber;
1112  G4int item_Length;
1113  G4int items_array_length=0;
1114  char * buffer= new char[LINEBUFFSIZE];
1115  size_t rflag = 0;
1116 
1117  while(items_array_length < SQ_Length)
1118  {
1119  rflag = std::fread(buffer, 2, 1, nested);
1120  GetValue(buffer, item_GroupNumber);
1121 
1122  rflag = std::fread(buffer, 2, 1, nested);
1123  GetValue(buffer, item_ElementNumber);
1124 
1125  rflag = std::fread(buffer, 4, 1, nested);
1126  GetValue(buffer, item_Length);
1127 
1128  rflag = std::fread(buffer, item_Length, 1, nested);
1129 
1130  items_array_length= items_array_length+8+item_Length;
1131  }
1132 
1133  delete [] buffer;
1134 
1135  if( SQ_Length>items_array_length )
1136  return 0;
1137  else
1138  return 1;
1139  if (rflag) return 1;
1140 }
1141 
1142 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
1143 
1145 {
1146  // VARIABLES
1147  unsigned short item_GroupNumber;
1148  unsigned short item_ElementNumber;
1149  unsigned int item_Length;
1150  char * buffer= new char[LINEBUFFSIZE];
1151  size_t rflag = 0;
1152 
1153  do
1154  {
1155  rflag = std::fread(buffer, 2, 1, nested);
1156  GetValue(buffer, item_GroupNumber);
1157 
1158  rflag = std::fread(buffer, 2, 1, nested);
1159  GetValue(buffer, item_ElementNumber);
1160 
1161  rflag = std::fread(buffer, 4, 1, nested);
1162  GetValue(buffer, item_Length);
1163 
1164  if(item_Length!=0xffffffff)
1165  rflag = std::fread(buffer, item_Length, 1, nested);
1166  else
1167  read_undefined_item(nested);
1168 
1169 
1170  } while(item_GroupNumber!=0xFFFE || item_ElementNumber!=0xE0DD
1171  || item_Length!=0);
1172 
1173  delete [] buffer;
1174  if (rflag) return;
1175 }
1176 
1177 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo.....
1178 
1180 {
1181  // VARIABLES
1182  unsigned short item_GroupNumber;
1183  unsigned short item_ElementNumber;
1184  G4int item_Length; size_t rflag = 0;
1185  char *buffer= new char[LINEBUFFSIZE];
1186 
1187  do
1188  {
1189  rflag = std::fread(buffer, 2, 1, nested);
1190  GetValue(buffer, item_GroupNumber);
1191 
1192  rflag = std::fread(buffer, 2, 1, nested);
1193  GetValue(buffer, item_ElementNumber);
1194 
1195  rflag = std::fread(buffer, 4, 1, nested);
1196  GetValue(buffer, item_Length);
1197 
1198 
1199  if(item_Length!=0)
1200  rflag = std::fread(buffer,item_Length,1,nested);
1201 
1202  }
1203  while(item_GroupNumber!=0xFFFE || item_ElementNumber!=0xE00D
1204  || item_Length!=0);
1205 
1206  delete [] buffer;
1207  if (rflag) return;
1208 }
1209 
1210 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo.....
1211 
1212 template <class Type>
1213 void DicomHandler::GetValue(char * _val, Type & _rval) {
1214 
1215 #if BYTE_ORDER == BIG_ENDIAN
1216  if(fLittleEndian) { // little endian
1217 #else // BYTE_ORDER == LITTLE_ENDIAN
1218  if(!fLittleEndian) { // big endian
1219 #endif
1220  const G4int SIZE = sizeof(_rval);
1221  char ctemp;
1222  for(G4int i = 0; i < SIZE/2; ++i) {
1223  ctemp = _val[i];
1224  _val[i] = _val[SIZE - 1 - i];
1225  _val[SIZE - 1 - i] = ctemp;
1226  }
1227  }
1228  _rval = *(Type *)_val;
1229  }
1230 
1231 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....