]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
Replace confusing names gdcmImage, etc by gdcmDicomDirImage etc, to avoid
[gdcm.git] / src / gdcmDicomDir.cxx
1 // gdcmDicomDir.cxx
2 //-----------------------------------------------------------------------------
3 #include "gdcmDicomDir.h"
4 #include "gdcmDicomDirStudy.h"
5 #include "gdcmDicomDirSerie.h"
6 #include "gdcmDicomDirImage.h"
7 #include "gdcmDirList.h"
8 #include "gdcmUtil.h"
9
10 #include <string>
11 #include <algorithm>
12
13 #include <sys/types.h>
14 #include <errno.h>
15
16 //-----------------------------------------------------------------------------
17 //  For full DICOMDIR description, see:
18 //  PS 3.3-2003, pages 731-750
19 //-----------------------------------------------------------------------------
20 // Constructor / Destructor
21 /*
22  * \ingroup gdcmDicomDir
23  * \brief   Constructor : Parses recursively the directory and creates the DicomDir
24  * \                      or uses an already built DICOMDIR, depending on 'parseDir' value
25  * @param   Name          name of the root directory (parseDir = true)
26  *                        name of the DICOMDIR       (parseDir = false)
27  * @param   parseDir      true if user wants to explore the directories
28  * @param   exception_on_error
29  */
30 gdcmDicomDir::gdcmDicomDir(const char *Name, bool parseDir,
31                            bool exception_on_error):
32    gdcmParser(Name,exception_on_error,true)
33 {
34    startMethod=            NULL;
35    progressMethod=         NULL;
36    endMethod=              NULL;
37    startMethodArgDelete=   NULL;
38    progressMethodArgDelete=NULL;
39    endMethodArgDelete=     NULL;
40    startArg=               NULL;
41    progressArg=            NULL;
42    endArg=                 NULL;
43
44    progress=0.0;
45    abort=false;
46
47    metaElems=NULL;
48
49    if( GetListEntry().begin()==GetListEntry().end() ) 
50    {
51       dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : entry list empty");
52
53       if(parseDir)
54       {
55          dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : Parse directory and create the DicomDir");
56          ParseDirectory();
57       }
58    }
59    else
60       CreateDicomDir();
61 }
62
63 /*
64  * \ingroup gdcmDicomDir
65  * \brief  Canonical destructor 
66  */
67 gdcmDicomDir::~gdcmDicomDir() 
68 {
69    SetStartMethod(NULL);
70    SetProgressMethod(NULL);
71    SetEndMethod(NULL);
72
73    if(metaElems)
74       delete metaElems;
75    
76    for(ListDicomDirPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
77    {
78       delete *cc;
79    }
80 }
81
82 //-----------------------------------------------------------------------------
83 // Print
84 /*
85  * \ingroup gdcmDicomDir
86  * \brief  Canonical Printer 
87  */
88 void gdcmDicomDir::Print(std::ostream &os)
89 {
90    if(metaElems)
91    {
92       metaElems->SetPrintLevel(printLevel);
93       metaElems->Print(os);   
94    }
95    
96    for(ListDicomDirPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
97    {
98      (*cc)->SetPrintLevel(printLevel);
99      (*cc)->Print(os);
100    }
101 }
102
103 //-----------------------------------------------------------------------------
104 // Public
105 /*
106  * \ingroup gdcmDicomDir
107  * \brief  This predicate, based on hopefully reasonable heuristics,
108  *         decides whether or not the current gdcmParser was properly parsed
109  *         and contains the mandatory information for being considered as
110  *         a well formed and usable DicomDir.
111  * @return true when gdcmParser is the one of a reasonable DicomDir,
112  *         false otherwise. 
113  */
114 bool gdcmDicomDir::IsReadable(void)
115 {
116    if(!gdcmParser::IsReadable())
117       return(false);
118    if(!metaElems)
119       return(false);
120    if(patients.size()<=0)
121       return(false);
122
123    return(true);
124 }
125
126 /*
127  * \ingroup gdcmDicomDir
128  * \brief  fills whole the structure
129  */
130 void gdcmDicomDir::ParseDirectory(void)
131 {
132    NewDicomDir(GetPath());
133    CreateDicomDir();
134 }
135
136 /*
137  * \ingroup gdcmDicomDir
138  * \brief   Set the start method to call when the parsing of the directory starts
139  * @param   method Method to call
140  * @param   arg    Argument to pass to the method
141  * \warning In python : the arg parameter isn't considered
142  */
143 void gdcmDicomDir::SetStartMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
144 {
145    if((startArg)&&(startMethodArgDelete))
146       startMethodArgDelete(startArg);
147
148    startMethod=method;
149    startArg=arg;
150    startMethodArgDelete=argDelete;
151 }
152
153 /*
154  * \ingroup gdcmDicomDir
155  * \brief   Set the method to delete the argument
156  *          The argument is destroyed when the method is changed or when the class
157  *          is destroyed
158  * @param   method Method to call to delete the argument
159  */
160 void gdcmDicomDir::SetStartMethodArgDelete(gdcmMethod *method) 
161 {
162    startMethodArgDelete=method;
163 }
164
165 /*
166  * \ingroup gdcmDicomDir
167  * \brief   Set the progress method to call when the parsing of the directory progress
168  * @param   method Method to call
169  * @param   arg    Argument to pass to the method
170  * \warning In python : the arg parameter isn't considered
171  */
172 void gdcmDicomDir::SetProgressMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
173 {
174    if((progressArg)&&(progressMethodArgDelete))
175       progressMethodArgDelete(progressArg);
176
177    progressMethod=method;
178    progressArg=arg;
179    progressMethodArgDelete=argDelete;
180 }
181
182 /*
183  * \ingroup gdcmDicomDir
184  * \brief   Set the method to delete the argument
185  *          The argument is destroyed when the method is changed or when the class
186  *          is destroyed
187  * @param   method Method to call to delete the argument
188  */
189 void gdcmDicomDir::SetProgressMethodArgDelete(gdcmMethod *method)
190 {
191    progressMethodArgDelete=method;
192 }
193
194 /*
195  * \ingroup gdcmDicomDir
196  * \brief   Set the end method to call when the parsing of the directory ends
197  * @param   method Method to call
198  * @param   arg    Argument to pass to the method
199  * \warning In python : the arg parameter isn't considered
200  */
201 void gdcmDicomDir::SetEndMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
202 {
203    if((endArg)&&(endMethodArgDelete))
204       endMethodArgDelete(endArg);
205
206    endMethod=method;
207    endArg=arg;
208    endMethodArgDelete=argDelete;
209 }
210
211 /*
212  * \ingroup gdcmDicomDir
213  * \brief   Set the method to delete the argument
214  *          The argument is destroyed when the method is changed or when the class
215  *          is destroyed
216  * @param   method Method to call to delete the argument
217  */
218 void gdcmDicomDir::SetEndMethodArgDelete(gdcmMethod *method)
219 {
220    endMethodArgDelete=method;
221 }
222
223 /**
224  * \ingroup gdcmDicomDir
225  * \brief   writes on disc a DICOMDIR
226  * \ warning does NOT add the missing elements in the header :
227  *           it's up to the user doing it !
228  * @param  fileName file to be written to 
229  * @return false only when fail to open
230  */
231 bool gdcmDicomDir::Write(std::string fileName) 
232 {
233    FILE * fp1;
234
235    fp1=fopen(fileName.c_str(),"wb");
236    if(fp1==NULL) 
237    {
238       printf("Failed to open(write) File [%s] \n",fileName.c_str());
239       return(false);
240    }
241
242    char * filePreamble;
243    filePreamble=(char*)calloc(128,1);
244    fwrite(filePreamble,128,1,fp1);
245    fwrite("DICM",4,1,fp1);
246    free(filePreamble);        
247    UpdateDirectoryRecordSequenceLength();
248    WriteEntries(fp1,DICOMDIR);
249
250    fclose(fp1);
251
252    return true;
253 }
254
255 //-----------------------------------------------------------------------------
256 // Protected
257 /*
258  * \ingroup gdcmDicomDir
259  * \brief create a gdcmDicomDir from a root Directory 
260  * @param path entry point of the stree-like structure
261  */
262 void gdcmDicomDir::NewDicomDir(std::string path)
263 {
264    CallStartMethod();
265
266    gdcmDirList fileList(path,1);
267    unsigned int count=0;
268    ListHeader list;
269    gdcmHeader *header;
270
271    listEntries.clear();
272    patients.clear();
273
274    for(gdcmDirList::iterator it=fileList.begin(); 
275        it!=fileList.end(); ++it) 
276    {
277       progress=(float)(count+1)/(float)fileList.size();
278       CallProgressMethod();
279       if(abort)
280          break;
281
282       header=new gdcmHeader(it->c_str());
283       if(header->IsReadable())
284          list.push_back(header);
285       else
286          delete header;
287
288       count++;
289    }
290
291    std::sort(list.begin(),list.end(),gdcmDicomDir::HeaderLessThan);
292
293    std::string tmp=fileList.GetDirName();
294    SetElements(tmp,list);
295
296    CallEndMethod();
297 }
298
299 /*
300  * \ingroup gdcmDicomDir
301  * \brief   Get the DicomDir path
302  * @param   
303  */
304 std::string gdcmDicomDir::GetPath(void)
305 {
306    std::string path=GetFileName();
307
308    int pos1=path.rfind("/");
309    int pos2=path.rfind("\\");
310    if(pos1>pos2)
311       path.resize(pos1);
312    else
313       path.resize(pos2);
314
315    return(path);
316 }
317
318 void gdcmDicomDir::CallStartMethod(void)
319 {
320    progress=0.0f;
321    abort=false;
322    if(startMethod)
323       startMethod(startArg);
324 }
325
326 void gdcmDicomDir::CallProgressMethod(void)
327 {
328    if(progressMethod)
329       progressMethod(progressArg);
330 }
331
332 void gdcmDicomDir::CallEndMethod(void)
333 {
334    progress=1.0f;
335    if(endMethod)
336       endMethod(endArg);
337 }
338
339 //-----------------------------------------------------------------------------
340 // Private
341 /*
342  * \ingroup gdcmDicomDir
343  * \brief create a 'gdcmDicomDir' from a DICOMDIR gdcmHeader 
344  */
345 void gdcmDicomDir::CreateDicomDir()
346 {
347    // The list is parsed. When a tag is found :
348    //  1 - we save the beginning iterator
349    //  2 - we continue to parse
350    //  3 - we find an other tag
351    //       + we create the object for the precedent tag
352    //       + loop to 1 -
353
354    gdcmDicomDirType type=gdcmDicomDir::GDCM_DICOMDIR_META;
355    ListTag::iterator begin;
356    ListTag::iterator end;
357
358    begin=listEntries.begin();
359    end=begin;
360    for(ListTag::iterator i=end;i !=listEntries.end();++i) 
361    {
362       std::string v=(*i)->GetValue();
363       if(v=="PATIENT ") 
364       {
365          end=i;
366          AddObjectToEnd(type,begin,end);
367
368          type=gdcmDicomDir::GDCM_DICOMDIR_PATIENT;
369          begin=end;
370       } 
371
372       if(v=="STUDY ")
373       {
374          end=i;
375          AddObjectToEnd(type,begin,end);
376
377          type=gdcmDicomDir::GDCM_DICOMDIR_STUDY;
378          begin=end;
379       }
380
381       if(v=="SERIES") 
382       {
383          end=i;
384          AddObjectToEnd(type,begin,end);
385
386          type=gdcmDicomDir::GDCM_DICOMDIR_SERIE;
387          begin=end;
388       }
389
390       if(v=="IMAGE ") 
391       {
392          end=i;
393          AddObjectToEnd(type,begin,end);
394
395          type=gdcmDicomDir::GDCM_DICOMDIR_IMAGE;
396          begin=end;
397       }
398    }
399
400    end=GetListEntry().end();
401    if(begin!=end)
402       AddObjectToEnd(type,begin,end);
403 }
404 /*
405  * \ingroup gdcmDicomDir
406  * \brief   
407  * @param   type
408  * @param   begin
409  * @param   end
410  */
411 void gdcmDicomDir::AddObjectToEnd(gdcmDicomDirType type,ListTag::iterator begin,ListTag::iterator end)
412 {
413    if(begin==end)
414       return;
415
416    switch(type)
417    {
418       case gdcmDicomDir::GDCM_DICOMDIR_META:
419          AddDicomDirMetaToEnd(begin,end);
420          break;      
421       case gdcmDicomDir::GDCM_DICOMDIR_PATIENT:
422          AddDicomDirPatientToEnd(begin,end);
423          break;
424       case gdcmDicomDir::GDCM_DICOMDIR_STUDY:
425          AddDicomDirStudyToEnd(begin,end);
426          break;
427       case gdcmDicomDir::GDCM_DICOMDIR_SERIE:
428          AddDicomDirSerieToEnd(begin,end);
429          break;
430       case gdcmDicomDir::GDCM_DICOMDIR_IMAGE:
431          AddDicomDirImageToEnd(begin,end);
432          break;
433    }
434 }
435
436 /*
437  * \ingroup gdcmDicomDir
438  * \brief Well ... Not realy to end, there is only one occurence  
439  * @param   begin
440  * @param   end
441 */
442 void gdcmDicomDir::AddDicomDirMetaToEnd(ListTag::iterator begin,ListTag::iterator end)
443 {
444    if(metaElems)
445       delete metaElems;
446    metaElems = new gdcmDicomDirMeta(begin,end);
447 }
448
449 /*
450  * \ingroup gdcmDicomDir
451  * \brief   
452  * @param   begin
453  * @param   end
454 */
455 void gdcmDicomDir::AddDicomDirPatientToEnd(ListTag::iterator begin,ListTag::iterator end)
456 {
457    patients.push_back(new gdcmDicomDirPatient(begin,end));
458 }
459
460 /*
461  * \ingroup gdcmDicomDir
462  * \brief   
463  * @param   begin
464  * @param   end
465  */
466  void gdcmDicomDir::AddDicomDirStudyToEnd(ListTag::iterator begin,ListTag::iterator end)
467 {
468    if(patients.size()>0)
469    {
470       ListDicomDirPatient::iterator itp=patients.end();
471       itp--;
472      (*itp)->AddDicomDirStudy(new gdcmDicomDirStudy(begin,end));
473    }
474 }
475 /*
476  * \ingroup gdcmDicomDir
477  * \brief   
478  * @param   begin
479  * @param   end
480  */
481 void gdcmDicomDir::AddDicomDirSerieToEnd(ListTag::iterator begin,ListTag::iterator end)
482 {
483    if(patients.size()>0)
484    {
485       ListDicomDirPatient::iterator itp=patients.end();
486       itp--;
487
488       if((*itp)->GetDicomDirStudies().size()>0)
489       {
490          ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
491          itst--;
492         (*itst)->AddDicomDirSerie(new gdcmDicomDirSerie(begin,end));
493       }
494    }
495 }
496
497 /*
498  * \ingroup gdcmDicomDir
499  * @param   begin
500  * @param   end
501  * @param   
502  */
503  void gdcmDicomDir::AddDicomDirImageToEnd(ListTag::iterator begin,ListTag::iterator end)
504 {
505    if(patients.size()>0)
506    {
507       ListDicomDirPatient::iterator itp=patients.end();
508       itp--;
509
510       if((*itp)->GetDicomDirStudies().size()>0)
511       {
512          ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
513          itst--;
514
515          if((*itst)->GetDicomDirSeries().size()>0)
516          {
517             ListDicomDirSerie::iterator its=(*itst)->GetDicomDirSeries().end();
518             its--;
519            (*its)->AddDicomDirImage(new gdcmDicomDirImage(begin,end));
520          }
521       }
522    }
523 }
524
525 /*
526  * \ingroup gdcmDicomDir
527  * \brief   
528  * @param   path
529  * @param   list
530  */
531 void gdcmDicomDir::SetElements(std::string &path,ListHeader &list)
532 {
533    std::string patPrevName="",         patPrevID="";
534    std::string studPrevInstanceUID="", studPrevID="";
535    std::string serPrevInstanceUID="",  serPrevID="";
536
537    std::string patCurName,         patCurID;
538    std::string studCurInstanceUID, studCurID;
539    std::string serCurInstanceUID,  serCurID;
540
541    SetElement(path,GDCM_DICOMDIR_NONE,NULL);
542
543    ListTag::iterator debPat=listEntries.begin();
544    for(ListHeader::iterator it=list.begin();it!=list.end();++it) 
545    {
546       // get the current file characteristics
547       patCurName=        (*it)->GetEntryByNumber(0x0010,0x0010); 
548       patCurID=          (*it)->GetEntryByNumber(0x0010,0x0011); 
549       studCurInstanceUID=(*it)->GetEntryByNumber(0x0020,0x000d);            
550       studCurID=         (*it)->GetEntryByNumber(0x0020,0x0010);            
551       serCurInstanceUID= (*it)->GetEntryByNumber(0x0020,0x000e);            
552       serCurID=          (*it)->GetEntryByNumber(0x0020,0x0011);
553
554       if(patCurName!=patPrevName || patCurID!=patPrevID) 
555          SetElement(path,GDCM_DICOMDIR_PATIENT,*it);
556
557       // if new Study Deal with 'STUDY' Elements   
558       if(studCurInstanceUID!=studPrevInstanceUID || studCurID!=studPrevID) 
559          SetElement(path,GDCM_DICOMDIR_STUDY,*it);
560
561       // if new Serie Deal with 'SERIE' Elements   
562       if(serCurInstanceUID!=serPrevInstanceUID || serCurID!=serPrevID) 
563          SetElement(path,GDCM_DICOMDIR_SERIE,*it);
564       
565       // Always Deal with 'IMAGE' Elements  
566       SetElement(path,GDCM_DICOMDIR_IMAGE,*it);
567
568       patPrevName=        patCurName;
569       patPrevID=          patCurID;
570       studPrevInstanceUID=studCurInstanceUID;
571       studPrevID=         studCurID;
572       serPrevInstanceUID= serCurInstanceUID;
573       serPrevID=          serCurID;
574    }
575 }
576
577 /*
578  * \ingroup gdcmDicomDir
579  * \brief   
580  * @param   path
581  * @param   type
582  * @param   header
583  */
584 void gdcmDicomDir::SetElement(std::string &path,gdcmDicomDirType type,gdcmHeader *header)
585 {
586    std::list<gdcmElement> elemList;
587    std::list<gdcmElement>::iterator it;
588    guint16 tmpGr, tmpEl;
589    gdcmDictEntry *dictEntry;
590    gdcmHeaderEntry *entry;
591    std::string val;
592
593    switch(type)
594    {
595       case GDCM_DICOMDIR_PATIENT:
596          elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements();
597          break;
598       case GDCM_DICOMDIR_STUDY:
599          elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirStudyElements();
600          break;
601       case GDCM_DICOMDIR_SERIE:
602          elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirSerieElements();
603          break;
604       case GDCM_DICOMDIR_IMAGE:
605          elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirImageElements();
606          break;
607       case GDCM_DICOMDIR_NONE:
608          elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirMetaElements();
609          break;
610       default:
611          return;
612    }
613
614    for(it=elemList.begin();it!=elemList.end();++it)
615    {
616       tmpGr=it->group;
617       tmpEl=it->elem;
618
619       dictEntry=GetPubDict()->GetDictEntryByNumber(tmpGr,tmpEl);
620       entry=new gdcmHeaderEntry(dictEntry);
621       entry->SetOffset(0); // just to avoid missprinting
622
623       if(header)
624          val=header->GetEntryByNumber(tmpGr,tmpEl);
625       else
626          val=GDCM_UNFOUND;
627
628       if(val==GDCM_UNFOUND) 
629       {
630          if((tmpGr==0x0004) &&(tmpEl==0x1130) )
631          {
632             // TODO force the *end* File Name(remove path)
633             val=path;
634          }
635          else if( (tmpGr==0x0004) && (tmpEl==0x1500) ) // Only used for image
636          {
637             if(header->GetFileName().substr(0,path.length())!=path)
638             {
639                dbg.Verbose(0, "gdcmDicomDir::SetElement : the base path of file name is incorrect");
640                val=header->GetFileName();
641             }
642             else
643                val=&(header->GetFileName()[path.length()]);
644          }
645          else
646          {
647             val=it->value;
648          }
649       } 
650       else
651       {
652          if (header->GetEntryLengthByNumber(tmpGr,tmpEl)== 0)
653             val=val=it->value;
654       }
655             
656       entry->SetValue(val);
657
658       if(dictEntry)
659       {
660          if(dictEntry->GetGroup()==0xfffe) 
661          {
662             entry->SetLength(entry->GetValue().length()); //JPR          
663          }
664          else if( (dictEntry->GetVR()=="UL") || (dictEntry->GetVR()=="SL") ) 
665          {
666             entry->SetLength(4);
667          } 
668          else if( (dictEntry->GetVR()=="US") || (dictEntry->GetVR()=="SS") ) 
669          {
670             entry->SetLength(2); 
671          } 
672          else if(dictEntry->GetVR()=="SQ") 
673          {
674             entry->SetLength(0xffffffff);
675          }
676          else
677          {
678             entry->SetLength(entry->GetValue().length());        
679          }
680       }
681       //listEntries.push_back(entry);
682       AddHeaderEntry(entry);    
683    }     
684 }
685
686 bool gdcmDicomDir::HeaderLessThan(gdcmHeader *header1,gdcmHeader *header2)
687 {
688    return(*header1<*header2);
689 }
690
691
692
693 void gdcmDicomDir::UpdateDirectoryRecordSequenceLength() {
694    int offset = 0;
695    ListTag::iterator it;
696    guint16 gr, el;
697    string vr;
698    for(it=listEntries.begin();it!=listEntries.end();++it) {
699       gr = (*it)->GetGroup();
700       el = (*it)->GetElement();
701       vr = (*it)->GetVR();      
702       if (gr !=0xfffe) {
703          if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {    
704             offset +=  4; // explicit VR AND OB, OW, SQ : 4 more bytes
705          }         
706          offset += 2 + 2 + 4 + (*it)->GetLength(); 
707       } else {
708          offset +=  4; // delimiters don't have a value.     
709       }            
710    }   
711    bool res=SetEntryLengthByNumber(offset, 0x0004, 0x1220); // Hope there is no dupps.
712    return;
713 }
714
715 //-----------------------------------------------------------------------------