Loading...
Searching...
No Matches
DicomHandler.cc
Go to the documentation of this file.
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//
27/// \file medical/DICOM/src/DicomHandler.cc
28/// \brief Implementation of the DicomHandler class
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//
47/// DicomHandler.cc :
48/// - Handling of DICM images
49/// - Reading headers and pixels
50/// - Transforming pixel to density and creating *.g4dcm
51/// files
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;
110 G4ConsumeParameters(rc);
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
166
167//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
168
169G4int 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",
256 FatalException,
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",
286 FatalException,
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 {
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
325 zslice->SetNoVoxelsZ(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
361void 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
387 std::printf("[0x00280106] Min. pixel value -> %i\n", fMinPixelValue);
388
389 } else if(tagDictionary == 0x00280107 ) { // Max. pixel value
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 }
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
556void 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 }
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 }
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..
640void 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
661unsigned int DicomHandler::GetMaterialIndex( G4float density )
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
679G4int DicomHandler::ReadData(FILE *dicom,char * filename2)
680{
681 G4int returnvalue = 0; size_t rflag = 0;
682
683 // READING THE PIXELS
684
685 fTab = new G4int*[fRows];
686 for ( G4int i = 0; i < fRows; ++i )
687 {
688 fTab[i] = new G4int[fColumns];
689 }
690
691 if(fBitAllocated == 8) { // Case 8 bits :
692
693 std::printf("@@@ Error! Picture != 16 bits...\n");
694 std::printf("@@@ Error! Picture != 16 bits...\n");
695 std::printf("@@@ Error! Picture != 16 bits...\n");
696
697 unsigned char ch = 0;
698
699 for(G4int j = 0; j < fRows; ++j) {
700 for(G4int i = 0; i < fColumns; ++i) {
701 rflag = std::fread( &ch, 1, 1, dicom);
703 }
704 }
705 returnvalue = 1;
706
707 } else { // from 12 to 16 bits :
708 char sbuff[2];
709 short pixel;
710 for( G4int j = 0; j < fRows; ++j) {
711 for( G4int i = 0; i < fColumns; ++i) {
712 rflag = std::fread(sbuff, 2, 1, dicom);
713 GetValue(sbuff, pixel);
714 fTab[j][i] = pixel*fRescaleSlope + fRescaleIntercept;
715 }
716 }
717 }
718
719 // Creation of .g4 files wich contains averaged density data
720 G4String nameProcessed = filename2 + G4String(".g4dcmb");
721 FILE* fileOut = std::fopen(nameProcessed.c_str(),"w+b");
722
723 G4cout << "### Writing of "<< nameProcessed << " ###\n";
724
725 unsigned int nMate = fMaterialIndices.size();
726 rflag = std::fwrite(&nMate, sizeof(unsigned int), 1, fileOut);
727 //--- Write materials
728 for( auto ite = fMaterialIndices.cbegin();
729 ite != fMaterialIndices.cend(); ++ite )
730 {
731 G4String mateName = (*ite).second;
732 for( std::size_t ii = (*ite).second.length(); ii < 40; ++ii )
733 {
734 mateName += " ";
735 } //mateName = const_cast<char*>(((*ite).second).c_str());
736
737 const char* mateNameC = mateName.c_str();
738 rflag = std::fwrite(mateNameC, sizeof(char),40, fileOut);
739 }
740
741 unsigned int fRowsC = fRows/fCompression;
742 unsigned int fColumnsC = fColumns/fCompression;
743 unsigned int planesC = 1;
744 G4float pixelLocationXM = -G4float(fPixelSpacingX*fColumns/2.);
745 G4float pixelLocationXP = G4float(fPixelSpacingX*fColumns/2.);
746 G4float pixelLocationYM = -G4float(fPixelSpacingY*fRows/2.);
747 G4float pixelLocationYP = G4float(fPixelSpacingY*fRows/2.);
748 G4float fSliceLocationZM = G4float(fSliceLocation-fSliceThickness/2.);
749 G4float fSliceLocationZP = G4float(fSliceLocation+fSliceThickness/2.);
750 //--- Write number of voxels (assume only one voxel in Z)
751 rflag = std::fwrite(&fRowsC, sizeof(unsigned int), 1, fileOut);
752 rflag = std::fwrite(&fColumnsC, sizeof(unsigned int), 1, fileOut);
753 rflag = std::fwrite(&planesC, sizeof(unsigned int), 1, fileOut);
754 //--- Write minimum and maximum extensions
755 rflag = std::fwrite(&pixelLocationXM, sizeof(G4float), 1, fileOut);
756 rflag = std::fwrite(&pixelLocationXP, sizeof(G4float), 1, fileOut);
757 rflag = std::fwrite(&pixelLocationYM, sizeof(G4float), 1, fileOut);
758 rflag = std::fwrite(&pixelLocationYP, sizeof(G4float), 1, fileOut);
759 rflag = std::fwrite(&fSliceLocationZM, sizeof(G4float), 1, fileOut);
760 rflag = std::fwrite(&fSliceLocationZP, sizeof(G4float), 1, fileOut);
761 // rflag = std::fwrite(&fCompression, sizeof(unsigned int), 1, fileOut);
762
763 std::printf("%8i %8i\n",fRows,fColumns);
764 std::printf("%8f %8f\n",fPixelSpacingX,fPixelSpacingY);
765 std::printf("%8f\n", fSliceThickness);
766 std::printf("%8f\n", fSliceLocation);
767 std::printf("%8i\n", fCompression);
768
769 G4int compSize = fCompression;
770 G4int mean;
771 G4float density;
772 G4bool overflow = false;
773
774 //----- Write index of material for each pixel
775 if(compSize == 1) { // no fCompression: each pixel has a density value)
776 for( G4int ww = 0; ww < fRows; ++ww) {
777 for( G4int xx = 0; xx < fColumns; ++xx) {
778 mean = fTab[ww][xx];
779 density = Pixel2density(mean);
780 unsigned int mateID = GetMaterialIndex( density );
781 rflag = std::fwrite(&mateID, sizeof(unsigned int), 1, fileOut);
782 }
783 }
784
785 } else {
786 // density value is the average of a square region of
787 // fCompression*fCompression pixels
788 for(G4int ww = 0; ww < fRows ;ww += compSize ) {
789 for(G4int xx = 0; xx < fColumns ;xx +=compSize ) {
790 overflow = false;
791 mean = 0;
792 for(G4int sumx = 0; sumx < compSize; ++sumx) {
793 for(G4int sumy = 0; sumy < compSize; ++sumy) {
794 if(ww+sumy >= fRows || xx+sumx >= fColumns) overflow = true;
795 mean += fTab[ww+sumy][xx+sumx];
796 }
797 if(overflow) break;
798 }
799 mean /= compSize*compSize;
800
801 if(!overflow) {
802 density = Pixel2density(mean);
803 unsigned int mateID = GetMaterialIndex( density );
804 rflag = std::fwrite(&mateID, sizeof(unsigned int), 1, fileOut);
805 }
806 }
807
808 }
809 }
810
811 //----- Write density for each pixel
812 if(compSize == 1) { // no fCompression: each pixel has a density value)
813 for( G4int ww = 0; ww < fRows; ++ww) {
814 for( G4int xx = 0; xx < fColumns; ++xx) {
815 mean = fTab[ww][xx];
816 density = Pixel2density(mean);
817 rflag = std::fwrite(&density, sizeof(G4float), 1, fileOut);
818 }
819 }
820
821 } else {
822 // density value is the average of a square region of
823 // fCompression*fCompression pixels
824 for(G4int ww = 0; ww < fRows ;ww += compSize ) {
825 for(G4int xx = 0; xx < fColumns ;xx +=compSize ) {
826 overflow = false;
827 mean = 0;
828 for(G4int sumx = 0; sumx < compSize; ++sumx) {
829 for(G4int sumy = 0; sumy < compSize; ++sumy) {
830 if(ww+sumy >= fRows || xx+sumx >= fColumns) overflow = true;
831 mean += fTab[ww+sumy][xx+sumx];
832 }
833 if(overflow) break;
834 }
835 mean /= compSize*compSize;
836
837 if(!overflow) {
838 density = Pixel2density(mean);
839 rflag = std::fwrite(&density, sizeof(G4float), 1, fileOut);
840 }
841 }
842
843 }
844 }
845
846 rflag = std::fclose(fileOut);
847
848 delete [] nameProcessed;
849
850 /* for ( G4int i = 0; i < fRows; i ++ ) {
851 delete [] fTab[i];
852 }
853 delete [] fTab;
854 */
855
856 if (rflag) return returnvalue;
857 return returnvalue;
858}
859
860//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo.......
861
862// DICOM HEAD does not use the calibration curve
863
864#ifdef DICOM_USE_HEAD
866{
867fNbrequali = 0;
868fReadCalibration = false;
869G4cout << "No calibration curve for the DICOM HEAD needed!" << G4endl;
870}
871#else
872// Separated out of Pixel2density
873// No need to read in same calibration EVERY time
874// Increases the speed of reading file by several orders of magnitude
875
877{
878 fNbrequali = 0;
879// CT2Density.dat contains the calibration curve to convert CT (Hounsfield)
880// number to physical density
881 std::ifstream calibration(fCt2DensityFile.c_str());
882 calibration >> fNbrequali;
883 fValueDensity = new G4double[fNbrequali];
884 fValueCT = new G4double[fNbrequali];
885
886 if(!calibration) {
887 G4Exception("DicomHandler::ReadFile","DICOM001", FatalException,
888 "@@@ No value to transform pixels in density!");
889 }
890 else { // calibration was successfully opened
891 for(G4int i = 0; i < fNbrequali; ++i)
892 { // Loop to store all the pts in CT2Density.dat
893 calibration >> fValueCT[i] >> fValueDensity[i];
894 }
895 }
896calibration.close();
897fReadCalibration = true;
898}
899#endif
900
901#ifdef DICOM_USE_HEAD
902G4float DicomHandler::Pixel2density(G4int pixel)
903{
904 G4float density = -1;
905
906//Air
907 if (pixel == -998.) density = 0.001290;
908//Soft Tissue
909 else if ( pixel == 24.) density = 1.00;
910//Brain
911 else if ( pixel == 52.) density = 1.03;
912// Spinal disc
913 else if ( pixel == 92.) density = 1.10;
914// Trabecular bone
915 else if ( pixel == 197.) density = 1.18;
916// Cortical Bone
917 else if ( pixel == 923.) density = 1.85;
918// Tooth dentine
919 else if ( pixel == 1280.) density = 2.14;
920//Tooth enamel
921 else if ( pixel == 2310.) density = 2.89;
922
923return density;
924}
925
926#else
927//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo.....
928
929G4float DicomHandler::Pixel2density(G4int pixel)
930{
932
933 G4float density = -1.;
934 G4double deltaCT = 0;
935 G4double deltaDensity = 0;
936
937
938 for(G4int j = 1; j < fNbrequali; ++j) {
939 if( pixel >= fValueCT[j-1] && pixel < fValueCT[j]) {
940
941 deltaCT = fValueCT[j] - fValueCT[j-1];
942 deltaDensity = fValueDensity[j] - fValueDensity[j-1];
943
944 // interpolating linearly
945 density = G4float(fValueDensity[j]
946 -((fValueCT[j] - pixel)*deltaDensity/deltaCT ));
947 break;
948 }
949 }
950
951 if(density < 0.) {
952 std::printf("@@@ Error density = %f && Pixel = %i \
953 (0x%x) && deltaDensity/deltaCT = %f\n",density,pixel,pixel,
954 deltaDensity/deltaCT);
955 }
956
957 return density;
958}
959#endif
960//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
961
963{
964 std::ifstream checkData(fDriverFile.c_str());
965 char * oneLine = new char[128];
966
967 if(!(checkData.is_open())) { //Check existance of Data.dat
968
970 "\nDicomG4 needs Data.dat (or another driver file specified";
971 message += " in command line):\n";
972 message += "\tFirst line: number of image pixel for a voxel (G4Box)\n";
973 message += "\tSecond line: number of images (CT slices) to read\n";
974 message += "\tEach following line contains the name of a Dicom image";
975 message += " except for the .dcm extension";
976 G4Exception("DicomHandler::ReadFile",
977 "DICOM001",
978 FatalException,
979 message.c_str());
980 }
981
982 checkData >> fCompression;
983 checkData >> fNFiles;
984 G4String oneName;
985 checkData.getline(oneLine,100);
986 std::ifstream testExistence;
987 G4bool existAlready = true;
988 for(G4int rep = 0; rep < fNFiles; ++rep) {
989 checkData.getline(oneLine,100);
990 oneName = oneLine;
991 oneName += ".g4dcm"; // create dicomFile.g4dcm
992 G4cout << fNFiles << " test file " << oneName << G4endl;
993 testExistence.open(oneName.data());
994 if(!(testExistence.is_open())) {
995 existAlready = false;
996 testExistence.clear();
997 testExistence.close();
998 }
999 testExistence.clear();
1000 testExistence.close();
1001 }
1002
1003 ReadMaterialIndices( checkData );
1004
1005 checkData.close();
1006 delete [] oneLine;
1007
1008 if( existAlready == false ) { // The files *.g4dcm have to be created
1009
1010 G4cout << "\nAll the necessary images were not found in processed form "
1011 << ", starting with .dcm images\n";
1012
1013 FILE * dicom;
1014 FILE * lecturePref;
1015 char * fCompressionc = new char[LINEBUFFSIZE];
1016 char * maxc = new char[LINEBUFFSIZE];
1017 //char name[300], inputFile[300];
1018 char * inputFile = new char[FILENAMESIZE];
1019 G4int rflag;
1020 lecturePref = std::fopen(fDriverFile.c_str(),"r");
1021
1022 rflag = std::fscanf(lecturePref,"%s",fCompressionc);
1023 fCompression = atoi(fCompressionc);
1024 rflag = std::fscanf(lecturePref,"%s",maxc);
1025 fNFiles = atoi(maxc);
1026 G4cout << " fNFiles " << fNFiles << G4endl;
1027
1028/////////////////////
1029
1030#ifdef DICOM_USE_HEAD
1031 for( G4int i = 1; i <= fNFiles; ++i )
1032 { // Begin loop on filenames
1033 rflag = std::fscanf(lecturePref,"%s",inputFile);
1034 G4String path = GetDicomDataPath() + "/";
1035
1036 G4String name = inputFile + G4String(".dcm");
1037 //Writes the results to a character string buffer.
1038
1039 G4String name2 = path + name;
1040 // Open input file and give it to gestion_dicom :
1041 G4cout << "### Opening " << name2 << " and reading :\n";
1042 dicom = std::fopen(name2.c_str(),"rb");
1043 // Reading the .dcm in two steps:
1044 // 1. reading the header
1045 // 2. reading the pixel data and store the density in Moyenne.dat
1046 if( dicom != 0 ) {
1047 ReadFile(dicom,inputFile);
1048 } else {
1049 G4cout << "\nError opening file : " << name2 << G4endl;
1050 }
1051 rflag = std::fclose(dicom);
1052 }
1053#else
1054
1055 for( G4int i = 1; i <= fNFiles; ++i )
1056 { // Begin loop on filenames
1057 rflag = std::fscanf(lecturePref,"%s",inputFile);
1058
1059 G4String name = inputFile + G4String(".dcm");
1060 //Writes the results to a character string buffer.
1061
1062 //G4cout << "check: " << name << G4endl;
1063 // Open input file and give it to gestion_dicom :
1064 G4cout << "### Opening " << name << " and reading :\n";
1065 dicom = std::fopen(name.c_str(),"rb");
1066 // Reading the .dcm in two steps:
1067 // 1. reading the header
1068 // 2. reading the pixel data and store the density in Moyenne.dat
1069 if( dicom != 0 ) {
1070 ReadFile(dicom,inputFile);
1071 } else {
1072 G4cout << "\nError opening file : " << name << G4endl;
1073 }
1074 rflag = std::fclose(dicom);
1075 }
1076#endif
1077
1078 rflag = std::fclose(lecturePref);
1079
1080 // Checks the spacing is correct. Dumps to files
1082
1083 delete [] fCompressionc;
1084 delete [] maxc;
1085 delete [] inputFile;
1086 if (rflag) return;
1087
1088 }
1089
1090 if(fValueDensity) { delete [] fValueDensity; }
1091 if(fValueCT) { delete [] fValueCT; }
1092 if(fMergedSlices) { delete fMergedSlices; }
1093
1094}
1095
1096//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
1097
1098G4int DicomHandler::read_defined_nested(FILE * nested,G4int SQ_Length)
1099{
1100 // VARIABLES
1101 unsigned short item_GroupNumber;
1102 unsigned short item_ElementNumber;
1103 G4int item_Length;
1104 G4int items_array_length=0;
1105 char * buffer= new char[LINEBUFFSIZE];
1106 size_t rflag = 0;
1107
1108 while(items_array_length < SQ_Length)
1109 {
1110 rflag = std::fread(buffer, 2, 1, nested);
1111 GetValue(buffer, item_GroupNumber);
1112
1113 rflag = std::fread(buffer, 2, 1, nested);
1114 GetValue(buffer, item_ElementNumber);
1115
1116 rflag = std::fread(buffer, 4, 1, nested);
1117 GetValue(buffer, item_Length);
1118
1119 rflag = std::fread(buffer, item_Length, 1, nested);
1120
1121 items_array_length= items_array_length+8+item_Length;
1122 }
1123
1124 delete [] buffer;
1125
1126 if( SQ_Length>items_array_length )
1127 return 0;
1128 else
1129 return 1;
1130 if (rflag) return 1;
1131}
1132
1133//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
1134
1136{
1137 // VARIABLES
1138 unsigned short item_GroupNumber;
1139 unsigned short item_ElementNumber;
1140 unsigned int item_Length;
1141 char * buffer= new char[LINEBUFFSIZE];
1142 size_t rflag = 0;
1143
1144 do
1145 {
1146 rflag = std::fread(buffer, 2, 1, nested);
1147 GetValue(buffer, item_GroupNumber);
1148
1149 rflag = std::fread(buffer, 2, 1, nested);
1150 GetValue(buffer, item_ElementNumber);
1151
1152 rflag = std::fread(buffer, 4, 1, nested);
1153 GetValue(buffer, item_Length);
1154
1155 if(item_Length!=0xffffffff)
1156 rflag = std::fread(buffer, item_Length, 1, nested);
1157 else
1158 read_undefined_item(nested);
1159
1160
1161 } while(item_GroupNumber!=0xFFFE || item_ElementNumber!=0xE0DD
1162 || item_Length!=0);
1163
1164 delete [] buffer;
1165 if (rflag) return;
1166}
1167
1168//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo.....
1169
1171{
1172 // VARIABLES
1173 unsigned short item_GroupNumber;
1174 unsigned short item_ElementNumber;
1175 G4int item_Length; size_t rflag = 0;
1176 char *buffer= new char[LINEBUFFSIZE];
1177
1178 do
1179 {
1180 rflag = std::fread(buffer, 2, 1, nested);
1181 GetValue(buffer, item_GroupNumber);
1182
1183 rflag = std::fread(buffer, 2, 1, nested);
1184 GetValue(buffer, item_ElementNumber);
1185
1186 rflag = std::fread(buffer, 4, 1, nested);
1187 GetValue(buffer, item_Length);
1188
1189
1190 if(item_Length!=0)
1191 rflag = std::fread(buffer,item_Length,1,nested);
1192
1193 }
1194 while(item_GroupNumber!=0xFFFE || item_ElementNumber!=0xE00D
1195 || item_Length!=0);
1196
1197 delete [] buffer;
1198 if (rflag) return;
1199}
1200
1201//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo.....
1202
1203template <class Type>
1204void DicomHandler::GetValue(char * _val, Type & _rval) {
1205
1206#if BYTE_ORDER == BIG_ENDIAN
1207 if(fLittleEndian) { // little endian
1208#else // BYTE_ORDER == LITTLE_ENDIAN
1209 if(!fLittleEndian) { // big endian
1210#endif
1211 const G4int SIZE = sizeof(_rval);
1212 char ctemp;
1213 for(G4int i = 0; i < SIZE/2; ++i) {
1214 ctemp = _val[i];
1215 _val[i] = _val[SIZE - 1 - i];
1216 _val[SIZE - 1 - i] = ctemp;
1217 }
1218 }
1219 _rval = *(Type *)_val;
1220 }
1221
1222//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo....
Definition of the DicomHandler class.
Definition of the DicomPhantomZSliceHeader class.
Definition of the DicomPhantomZSliceMerged class.
void StoreData(std::ofstream &foutG4DCM)
DicomPhantomZSliceMerged * fMergedSlices
void ReadCalibration()
G4int ReadData(FILE *, char *)
G4double fSliceThickness
G4int ReadFile(FILE *, char *)
G4bool fReadCalibration
G4float Pixel2density(G4int pixel)
G4String fCt2DensityFile
void GetValue(char *, Type &)
static DicomHandler * Instance()
void CheckFileFormat()
std::map< G4float, G4String > fMaterialIndices
G4double fPixelSpacingX
void read_undefined_nested(FILE *)
G4int read_defined_nested(FILE *, G4int)
G4String fDriverFile
void ReadMaterialIndices(std::ifstream &finData)
G4double * fValueCT
const G4int LINEBUFFSIZE
unsigned int GetMaterialIndex(G4float density)
void read_undefined_item(FILE *)
const G4int FILENAMESIZE
static G4String GetDicomDataPath()
G4bool fLittleEndian
const G4int DATABUFFSIZE
void GetInformation(G4int &, char *)
G4double fSliceLocation
G4double * fValueDensity
G4double fPixelSpacingY
G4int fRescaleIntercept
G4bool fImplicitEndian
static DicomHandler * fInstance
DicomHandler.cc :
short fPixelRepresentation
static G4String GetDicomDataFile()
DicomPhantomZSliceHeader class.
void SetMinZ(const G4double &val)
void AddMaterial(const G4String &val)
void SetSliceLocation(const G4double &val)
void SetMinX(const G4double &val)
void SetNoVoxelsX(const G4int &val)
void SetMaxX(const G4double &val)
void SetMaxY(const G4double &val)
void SetNoVoxelsY(const G4int &val)
void SetMinY(const G4double &val)
void SetNoVoxelsZ(const G4int &val)
void SetMaxZ(const G4double &val)
void AddZSlice(DicomPhantomZSliceHeader *val)
void message(G4RunManager *runmanager)
ts_scorers example shows how to use global scorers.
Definition ts_scorers.cc:71

Applications | User Support | Publications | Collaboration