]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
* gdcmDirList : to parse a hard drive directory in recursive (or not)
[gdcm.git] / src / gdcmDicomDir.cxx
1 // gdcmDicomDir.cxx
2 //-----------------------------------------------------------------------------
3 #include "gdcmDicomDir.h"
4 #include "gdcmStudy.h"
5 #include "gdcmSerie.h"
6 #include "gdcmImage.h"
7 #include "gdcmDirList.h"
8 #include "gdcmUtil.h"
9
10 #include <string>
11
12 #include <sys/types.h>
13 #include <errno.h>
14
15 //-----------------------------------------------------------------------------
16 class ELEMENTS 
17 {
18 public :
19    ELEMENTS(unsigned short int _group,unsigned short int _elem,std::string _value)
20       {group=_group;elem=_elem;value=_value;}
21
22         unsigned short int group;
23         unsigned short int elem;
24         std::string value;
25 };
26
27 ELEMENTS metaElem[]={
28        //Meta Group Length : to be computed later
29    ELEMENTS(0x0002,0x0000,"12345"),
30       //File Meta Information Version
31    ELEMENTS(0x0002,0x0001,"\2\0\0\0"),
32       //Media Stored SOP Class UID i.e. : 'Media Storage Directory Storage'
33    ELEMENTS(0x0002,0x0002,"1.2.840.10008.1.3.10"),
34       //Media Stored SOP Instance UID  : may be forged later 
35    ELEMENTS(0x0002,0x0003,""), 
36       //Transfer Syntax UID i.e. : Explicit VR - Little Endian
37    ELEMENTS(0x0002,0x0010,"1.2.840.10008.1.2.1"), 
38       //Implementation Class UID : may be forged later 
39    ELEMENTS(0x0002,0x0012,""), 
40       //Implementation Version Name  
41    ELEMENTS(0x0002,0x0013,"gdcmLib"),
42       //File-set ID  :        
43    ELEMENTS(0x0004,0x1130,""),
44       //Offset of the first dir of root dir entity : to be computed later   
45    ELEMENTS(0x0004,0x1200,"0"),
46       //Offset of the last dir of root dir entity : to be computed later   
47    ELEMENTS(0x0004,0x1202,"0"),
48       //File-set consistency flag   
49    ELEMENTS(0x0004,0x1212,"0"),
50        //Directory record sequence : *length* to be set later 
51    ELEMENTS(0x0004,0x1220,"0"),           
52    ELEMENTS(0xffff,0xffff,"") 
53 } ;
54
55 ELEMENTS patientElem[]={
56    ELEMENTS(0xfffe,0xe000,"0"),
57       // Offset of next directory record : to be computed later
58    ELEMENTS(0x0004,0x1400,"0"),
59       // Record in use flag : 65535(?)
60    ELEMENTS(0x0004,0x1410,"65535"), 
61       // Offset of referenced lower-level dir entity : to be computed later   
62    ELEMENTS(0x0004,0x1420,"0"),
63       // Directory Record Type      
64    ELEMENTS(0x0004,0x1430,"PATIENT "), // don't remove trailing space !
65
66       // Specific Character Set
67    ELEMENTS(0x0008,0x0005,"ISO_IR 100"),
68       // Patient's Name 
69    ELEMENTS(0x0010,0x0010,""),
70       // Patient ID 
71    ELEMENTS(0x0010,0x0020,""),
72       // Patient's Birthdate
73    ELEMENTS(0x0010,0x0030,""), 
74       // Patient's Sex
75    ELEMENTS(0x0010,0x0040,""),    
76    ELEMENTS(0xffff,0xffff,"") 
77  }; 
78
79 ELEMENTS studyElem[]={  
80    ELEMENTS(0xfffe,0xe000,"0"),
81       // Offset of next directory record : to be computed later
82    ELEMENTS(0x0004,0x1400,"0"),
83       // Record in use flag : 65535(?)
84    ELEMENTS(0x0004,0x1410,"65535"), 
85       // Offset of referenced lower-level dir entity : to be computed later   
86    ELEMENTS(0x0004,0x1420,"0"),   
87       // Directory Record Type      
88    ELEMENTS(0x0004,0x1430,"STUDY "), // don't remove trailing space !   
89
90       // Specific Character Set
91    ELEMENTS(0x0008,0x0005,"ISO_IR 100"), 
92       // Study Date 
93    ELEMENTS(0x0008,0x0020,""),
94       // Study Time
95    ELEMENTS(0x0008,0x0030,""),
96       // Accession Number
97    ELEMENTS(0x0008,0x0050,""), 
98       // Study Description
99    ELEMENTS(0x0008,0x1030,""), 
100       // Study Instance UID : may be forged later
101    ELEMENTS(0x0020,0x000d,""), 
102       // Study ID : may be forged later
103    ELEMENTS(0x0020,0x0010,""),                   
104    ELEMENTS(0xffff,0xffff,"") 
105 }; 
106
107
108 ELEMENTS serieElem[]={  
109    ELEMENTS(0xfffe,0xe000,"0"),
110       // Offset of next directory record : to be computed later
111    ELEMENTS(0x0004,0x1400,"0"),
112       // Record in use flag : 65535(?)
113    ELEMENTS(0x0004,0x1410,"65535"), 
114       // Offset of referenced lower-level dir entity : to be computed later   
115    ELEMENTS(0x0004,0x1420,"0"),   
116       // Directory Record Type      
117    ELEMENTS(0x0004,0x1430,"SERIES"), // don't add trailing space !   
118
119       // Specific Character Set
120    ELEMENTS(0x0008,0x0005,"ISO_IR 100"), 
121       // Series Date
122    ELEMENTS(0x0008,0x0021,""),
123       // Series Time
124    ELEMENTS(0x0008,0x0031,""),
125       // Modality
126    ELEMENTS(0x0008,0x0060,""), 
127       // Institution Name  : may be forged later
128    ELEMENTS(0x0008,0x0080,""), 
129       // Institution Address : may be forged later
130    ELEMENTS(0x0008,0x0081,""), 
131       // Series Description :  may be forged later
132    ELEMENTS(0x0008,0x103e,""),
133       // Series Instance UID : may be forged later
134    ELEMENTS(0x0020,0x000e,""),   
135       // Series Number : may be forged later
136    ELEMENTS(0x0020,0x0011,"0"),                         
137    ELEMENTS(0xffff,0xffff,"") 
138 }; 
139
140 ELEMENTS imageElem[]={  
141    ELEMENTS(0xfffe,0xe000,"0"),
142       // Offset of next directory record : to be computed later
143    ELEMENTS(0x0004,0x1400,"0"),
144       // Record in use flag : 65535(?)
145    ELEMENTS(0x0004,0x1410,"65535"), 
146       // Offset of referenced lower-level dir entity : to be computed later   
147    ELEMENTS(0x0004,0x1420,"0"),   
148       // Directory Record Type      
149    ELEMENTS(0x0004,0x1430,"IMAGE "), // don't remove trailing space ! 
150
151       // Referenced File ID : to be set later(relative File Name)
152    ELEMENTS(0x0004,0x1500,""),
153       // Referenced SOP Class UID in File : may be forged later
154    ELEMENTS(0x0004,0x1510,""),
155       // Referenced SOP Class UID in File :  may be forged later
156    ELEMENTS(0x0004,0x1511,""),
157       // Referenced Transfer Syntax in File
158    ELEMENTS(0x0004,0x1512,""),      
159       // Specific Character Set
160    ELEMENTS(0x0008,0x0005,"ISO_IR 100"), 
161       // Image Type
162    ELEMENTS(0x0008,0x0008,""), 
163       // SOP Class UID : to be set/forged later
164    ELEMENTS(0x0008,0x0016,""),
165       // SOP Instance UID : to be set/forged later
166    ELEMENTS(0x0008,0x0018,""),    
167       // Content Date
168    ELEMENTS(0x0008,0x0023,""),
169       // Content Time
170    ELEMENTS(0x0008,0x0033,""),      
171       // Referenced Image Sequence : to be set/forged later
172    ELEMENTS(0x0008,0x1040,""), 
173    ELEMENTS(0xfffe,0xe000,"0"),
174       // Referenced SOP Class UID : to be set/forged later
175    ELEMENTS(0x0008,0x1150,""), 
176       // Referenced SOP Instance UID : to be set/forged later
177    ELEMENTS(0x0008,0x1155,""),      
178       // Image Number 
179    ELEMENTS(0x0020,0x0013,"0"),
180       // Image Position Patient 
181    ELEMENTS(0x0020,0x0032,"0"),   
182       // Image Orientation(Patient)
183    ELEMENTS(0x0020,0x0037,"0"),   
184       // Frame of Reference UID
185    ELEMENTS(0x0020,0x0052,"0"), 
186       // Rows
187    ELEMENTS(0x0028,0x0010,"0"),   
188       // Columns
189    ELEMENTS(0x0028,0x0011,"0"),
190       // Pixel Spacing
191    ELEMENTS(0x0028,0x0030,"0"),   
192       // Calibration Image
193    ELEMENTS(0x0050,0x0004,"0"),                                    
194    ELEMENTS(0xffff,0xffff,"") 
195 }; 
196
197 //-----------------------------------------------------------------------------
198 // Constructor / Destructor
199 /*
200  * \ingroup gdcmDicomDir
201  * \brief   
202  * @param   Filename
203  * @param   exception_on_error
204  */
205 gdcmDicomDir::gdcmDicomDir(const char *FileName,
206                            bool exception_on_error):
207    gdcmParser(FileName,exception_on_error,true)
208 {
209    if( GetListEntry().begin()==GetListEntry().end() ) 
210    {
211       dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : entry list empty");
212       dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : Parse directory and create the DicomDir");
213
214       std::string path=FileName;
215       std::string file;
216
217       int pos1=path.rfind("/");
218       int pos2=path.rfind("\\");
219       if(pos1>pos2)
220          path.resize(pos1);
221       else
222          path.resize(pos2);
223       NewDicomDir(path);
224    }
225
226    CreateDicomDir();
227 }
228
229 /*
230  * \ingroup gdcmDicomDir
231  * \brief   
232  * @param   exception_on_error
233  */
234 gdcmDicomDir::gdcmDicomDir(ListTag *l,
235                            bool exception_on_error):                           
236    gdcmParser(exception_on_error )  
237 {    
238    listEntries=*l;
239    CreateDicomDir();
240 }
241
242 /*
243  * \ingroup gdcmDicomDir
244  * \brief  Canonical destructor 
245  */
246 gdcmDicomDir::~gdcmDicomDir() 
247 {
248    for(ListPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
249    {
250       delete *cc;
251    }
252 }
253
254 //-----------------------------------------------------------------------------
255 // Print
256 void gdcmDicomDir::Print(std::ostream &os)
257 {
258    for(ListPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
259    {
260      (*cc)->SetPrintLevel(printLevel);
261      (*cc)->Print(os);
262    }
263 }
264
265 //-----------------------------------------------------------------------------
266 // Public
267 /**
268  * \ingroup gdcmDicomDir
269  * \brief   writes on disc a DICOMDIR
270  * \ warning does NOT add the missing elements in the header :
271  * \         it's up to the user doing it !
272  * @param  fileName file to be written to 
273  * @return
274  */
275 bool gdcmDicomDir::Write(std::string fileName) 
276 {
277    FILE * fp1;
278
279    fp1=fopen(fileName.c_str(),"wb");
280    if(fp1==NULL) 
281    {
282       printf("Failed to open(write) File [%s] \n",fileName.c_str());
283       return(false);
284    }
285
286    char * filePreamble;
287    filePreamble=(char*)calloc(128,1);
288    fwrite(filePreamble,128,1,fp1);
289    fwrite("DICM",4,1,fp1);
290    free(filePreamble);
291
292    WriteEntries(DICOMDIR,fp1);
293
294    fclose(fp1);
295
296    return true;
297 }
298
299 //-----------------------------------------------------------------------------
300 // Protected
301 /*
302  * \ingroup gdcmDicomDir
303  * \brief   
304  * @param   
305  */
306 void gdcmDicomDir::NewDicomDir(std::string path)
307 {
308    gdcmDirList fileList(path,1);
309    ListHeader list;
310    gdcmHeader *header;
311
312    listEntries.clear();
313
314    for(gdcmDirList::iterator it=fileList.begin(); 
315        it!=fileList.end(); ++it) 
316    {
317 //      std::cout<<*it<<std::endl;
318       header=new gdcmHeader(it->c_str());
319       if(header->IsReadable())
320          list.push_back(header);
321       else
322          delete header;
323    }
324
325    SetElements(path,list);
326 }
327
328 //-----------------------------------------------------------------------------
329 // Private
330 /*
331  * \ingroup gdcmDicomDir
332  * \brief   
333  * @param   
334  */
335 void gdcmDicomDir::CreateDicomDir()
336 {
337    // The list is parsed. When a tag is found :
338    //  1 - we save the beginning iterator
339    //  2 - we continue to parse
340    //  3 - we find an other tag
341    //       + we create the object for the precedent tag
342    //       + loop to 1 -
343
344    gdcmDicomDirType type=gdcmDicomDir::GDCM_NONE;
345    ListTag::iterator begin;
346    ListTag::iterator end;
347
348    begin=listEntries.begin();
349    end=begin;
350    for(ListTag::iterator i=listEntries.begin();i !=listEntries.end();++i) 
351    {
352       // std::cout << std::hex <<(*i)->GetGroup() << 
353       //                  " " <<(*i)->GetElement() << endl;
354
355       std::string v=(*i)->GetValue();
356       if(v=="PATIENT ") 
357       {
358        //  std::cout<<"PATIENT"<<std::endl;
359          end=i;
360          AddObjectToEnd(type,begin,end);
361
362          type=gdcmDicomDir::GDCM_PATIENT;
363          begin=end;
364       } 
365
366       if(v=="STUDY ")
367       {
368        //  std::cout<<"STUDY"<<std::endl;
369          end=i;
370          AddObjectToEnd(type,begin,end);
371
372          type=gdcmDicomDir::GDCM_STUDY;
373          begin=end;
374       }
375
376       if(v=="SERIES") 
377       {
378        //  std::cout<<"SERIES"<<std::endl;
379          end=i;
380          AddObjectToEnd(type,begin,end);
381
382          type=gdcmDicomDir::GDCM_SERIE;
383          begin=end;
384       }
385
386       if(v=="IMAGE ") 
387       {
388        //  std::cout<<"IMAGE"<<std::endl;
389          end=i;
390          AddObjectToEnd(type,begin,end);
391
392          type=gdcmDicomDir::GDCM_IMAGE;
393          begin=end;
394       }
395    }
396
397    end=GetListEntry().end();
398    AddObjectToEnd(type,begin,end);
399 }
400 /*
401  * \ingroup gdcmDicomDir
402  * \brief   
403  * @param   
404  */
405 void gdcmDicomDir::AddObjectToEnd(gdcmDicomDirType type,ListTag::iterator begin,ListTag::iterator end)
406 {
407    if(begin==end)
408       return;
409
410    switch(type)
411    {
412       case gdcmDicomDir::GDCM_PATIENT:
413          AddPatientToEnd(begin,end);
414          break;
415       case gdcmDicomDir::GDCM_STUDY:
416          AddStudyToEnd(begin,end);
417          break;
418       case gdcmDicomDir::GDCM_SERIE:
419          AddSerieToEnd(begin,end);
420          break;
421       case gdcmDicomDir::GDCM_IMAGE:
422          AddImageToEnd(begin,end);
423          break;
424    }
425 }
426
427 /*
428  * \ingroup gdcmDicomDir
429  * \brief   
430  * @param   
431  */
432 void gdcmDicomDir::AddPatientToEnd(ListTag::iterator begin,ListTag::iterator end)
433 {
434    patients.push_back(new gdcmPatient(begin,end));
435 }
436
437 /*
438  * \ingroup gdcmDicomDir
439  * \brief   
440  * @param   
441  */
442  void gdcmDicomDir::AddStudyToEnd(ListTag::iterator begin,ListTag::iterator end)
443 {
444    if(patients.size()>0)
445    {
446       ListPatient::iterator itp=patients.end();
447       itp--;
448      (*itp)->AddStudy(new gdcmStudy(begin,end));
449    }
450 }
451 /*
452  * \ingroup gdcmDicomDir
453  * \brief   
454  * @param   
455  */
456 void gdcmDicomDir::AddSerieToEnd(ListTag::iterator begin,ListTag::iterator end)
457 {
458    if(patients.size()>0)
459    {
460       ListPatient::iterator itp=patients.end();
461       itp--;
462
463       if((*itp)->GetStudies().size()>0)
464       {
465          ListStudy::iterator itst=(*itp)->GetStudies().end();
466          itst--;
467         (*itst)->AddSerie(new gdcmSerie(begin,end));
468       }
469    }
470 }
471
472 /*
473  * \ingroup gdcmDicomDir
474  * \brief   
475  * @param   
476  */
477  void gdcmDicomDir::AddImageToEnd(ListTag::iterator begin,ListTag::iterator end)
478 {
479    if(patients.size()>0)
480    {
481       ListPatient::iterator itp=patients.end();
482       itp--;
483
484       if((*itp)->GetStudies().size()>0)
485       {
486          ListStudy::iterator itst=(*itp)->GetStudies().end();
487          itst--;
488
489          if((*itst)->GetSeries().size()>0)
490          {
491             ListSerie::iterator its=(*itst)->GetSeries().end();
492             its--;
493            (*its)->AddImage(new gdcmImage(begin,end));
494          }
495       }
496    }
497 }
498
499 /*
500  * \ingroup gdcmDicomDir
501  * \brief   
502  * @param   
503  */
504 void gdcmDicomDir::SetElements(std::string &path,ListHeader &list)
505 {
506    std::string patPrevName="", patPrevID="";
507    std::string studPrevInstanceUID="", studPrevID="";
508    std::string serPrevInstanceUID="", serPrevID="";
509
510    std::string patCurName, patCurID;
511    std::string studCurInstanceUID, studCurID;
512    std::string serCurInstanceUID, serCurID;
513
514    SetElement(path,GDCM_NONE,NULL);
515
516    ListTag::iterator debPat=listEntries.begin();
517    for(ListHeader::iterator it=list.begin();it!=list.end();++it) 
518    {
519      // get the current file characteristics
520       patCurName=(*it)->GetEntryByNumber(0x0010,0x0010); 
521       patCurID=(*it)->GetEntryByNumber(0x0010,0x0011); 
522       studCurInstanceUID=(*it)->GetEntryByNumber(0x0020,0x000d);            
523       studCurID=(*it)->GetEntryByNumber(0x0020,0x0010);            
524       serCurInstanceUID=(*it)->GetEntryByNumber(0x0020,0x000e);            
525       serCurID=(*it)->GetEntryByNumber(0x0020,0x0011);
526
527       if(patCurName!=patPrevName || patCurID!=patPrevID) 
528          SetElement(path,GDCM_PATIENT,*it);
529
530       // if new Study Deal with 'STUDY' Elements   
531       if(studCurInstanceUID!=studPrevInstanceUID || studCurID!=studPrevID) 
532          SetElement(path,GDCM_STUDY,*it);
533
534       // if new Serie Deal with 'SERIE' Elements   
535       if(serCurInstanceUID!=serPrevInstanceUID || serCurID!=serPrevID) 
536       {
537          SetElement(path,GDCM_SERIE,*it);
538       } 
539       
540       // Always Deal with 'IMAGE' Elements  
541       SetElement(path,GDCM_IMAGE,*it);
542
543       patPrevName=patCurName;
544       patPrevID=patCurID;
545       studPrevInstanceUID=studCurInstanceUID;
546       studPrevID=studCurID;
547       serPrevInstanceUID=serCurInstanceUID;
548       serPrevID=serCurID;
549    }
550 }
551
552 /*
553  * \ingroup gdcmDicomDir
554  * \brief   
555  * @param   
556  */
557 void gdcmDicomDir::SetElement(std::string &path,gdcmDicomDirType type,gdcmHeader *header)
558 {
559    ELEMENTS *elemList;
560    guint16 tmpGr, tmpEl;
561    gdcmDictEntry *dictEntry;
562    gdcmHeaderEntry *entry;
563    std::string val;
564
565    switch(type)
566    {
567       case GDCM_PATIENT:
568          elemList=patientElem;
569          break;
570       case GDCM_STUDY:
571          elemList=studyElem;
572          break;
573       case GDCM_SERIE:
574          elemList=serieElem;
575          break;
576       case GDCM_IMAGE:
577          elemList=imageElem;
578          break;
579       case GDCM_NONE:
580          elemList=metaElem;
581          break;
582       default:
583          return;
584    }
585
586    for(int i=0;;i++)
587    {
588       tmpGr=elemList[i].group;
589       tmpEl=elemList[i].elem;
590       if(tmpGr==0xffff) 
591          break;
592
593       dictEntry=GetPubDict()->GetDictEntryByNumber(tmpGr,tmpEl);
594
595       entry=new gdcmHeaderEntry(dictEntry);
596       entry->SetOffset(0); // just to avoid missprinting
597
598       if(header)
599          val=header->GetEntryByNumber(tmpGr,tmpEl);
600       else
601          val=GDCM_UNFOUND;
602
603       if(val==GDCM_UNFOUND) 
604       {
605          if((tmpGr==0x0004) &&(tmpEl==0x1130) )
606          {
607             // TODO force the *end* File Name(remove path)
608             val=path;
609          }
610          else if( (tmpGr==0x0004) && (tmpEl==0x1500) ) // Only used for image
611          {
612             if(header->GetFileName().substr(0,path.length())!=path)
613             {
614                dbg.Verbose(0, "gdcmDicomDir::SetElement : the base path of file name is incorrect");
615                val=header->GetFileName();
616             }
617             else
618                val=&(header->GetFileName()[path.length()]);
619          }
620          else
621          {
622             val=elemList[i].value;
623          }
624       }
625       entry->SetValue(val);
626
627       if(dictEntry)
628       {
629          if( (dictEntry->GetVR()=="UL") || (dictEntry->GetVR()=="SL") ) 
630          {
631             entry->SetLength(4);
632          } 
633          else if( (dictEntry->GetVR()=="US") || (dictEntry->GetVR()=="SS") ) 
634          {
635             entry->SetLength(2); 
636          } 
637          else if(dictEntry->GetVR()=="SQ") 
638          {
639             entry->SetLength(0xffffffff);
640          }
641          else
642          {
643             entry->SetLength(entry->GetValue().length());        
644          }
645       }
646
647       listEntries.push_back(entry);
648    }     
649 }
650
651 //-----------------------------------------------------------------------------