2 //-----------------------------------------------------------------------------
3 #include "gdcmDicomDir.h"
4 #include "gdcmDicomDirStudy.h"
5 #include "gdcmDicomDirSerie.h"
6 #include "gdcmDicomDirImage.h"
7 #include "gdcmDirList.h"
13 #include <sys/types.h>
17 //-----------------------------------------------------------------------------
18 // For full DICOMDIR description, see:
19 // PS 3.3-2003, pages 731-750
20 //-----------------------------------------------------------------------------
21 // Constructor / Destructor
23 * \ingroup gdcmDicomDir
24 * \brief Constructor : Parses recursively the directory and creates the DicomDir
25 * \ or uses an already built DICOMDIR, depending on 'parseDir' value
26 * @param Name name of the root directory (parseDir = true)
27 * name of the DICOMDIR (parseDir = false)
28 * @param parseDir - true if user passed an entry point
29 * and wants to explore recursively the directories
30 * - false if user passed an already built DICOMDIR file
32 * @param exception_on_error whether we want to throw an exception or not
34 gdcmDicomDir::gdcmDicomDir(const char *Name, bool parseDir,
35 bool exception_on_error):
36 gdcmParser(Name,exception_on_error,true) // true : enable SeQuences
38 // que l'on ai passe un root directory ou un DICOMDIR
39 // et quelle que soit la valeur de parseDir,
40 // on a lance gdcmParser
42 cout << "---------------------------------------------- " << Name <<endl;
49 startMethodArgDelete= NULL;
50 progressMethodArgDelete=NULL;
51 endMethodArgDelete= NULL;
61 // gdcmParser already executed
62 // Si on a passe un root directory, on est assuré de n'avoir rien ramené
64 if( GetListEntry().begin()==GetListEntry().end() )
66 // Si, en plus, parseDir == false, ca devrait etre une erreur
67 dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : entry list empty");
69 if(strlen(Name)==1 && Name[0]=='.') { // user passed '.' as Name
70 // we get current directory name
71 char*dummy=(char*) malloc(1000); // TODO : check with Windoze
72 getcwd(dummy,(size_t)1000);
73 SetFileName(dummy); // will be converted into a string
74 free(dummy); // no longer needed
79 dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : Parse directory and create the DicomDir");
88 * \ingroup gdcmDicomDir
89 * \brief Constructor : creates an empty gdcmDicomDir
90 * @param exception_on_error whether we want to throw an exception or not
92 gdcmDicomDir::gdcmDicomDir(bool exception_on_error):
93 gdcmParser(exception_on_error)
98 startMethodArgDelete= NULL;
99 progressMethodArgDelete=NULL;
100 endMethodArgDelete= NULL;
107 std::string pathBidon = ""; // Sorry, NULL not allowed ...
108 SetElement(pathBidon, GDCM_DICOMDIR_META, NULL); // Set the META elements
110 AddDicomDirMetaToEnd(listEntries.begin(),--listEntries.end());
115 * \ingroup gdcmDicomDir
116 * \brief Canonical destructor
118 gdcmDicomDir::~gdcmDicomDir()
120 SetStartMethod(NULL);
121 SetProgressMethod(NULL);
127 for(ListDicomDirPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
133 //-----------------------------------------------------------------------------
136 * \ingroup gdcmDicomDir
137 * \brief Canonical Printer
139 void gdcmDicomDir::Print(std::ostream &os)
143 metaElems->SetPrintLevel(printLevel);
144 metaElems->Print(os);
146 for(ListDicomDirPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
148 (*cc)->SetPrintLevel(printLevel);
153 //-----------------------------------------------------------------------------
156 * \ingroup gdcmDicomDir
157 * \brief This predicate, based on hopefully reasonable heuristics,
158 * decides whether or not the current header was properly parsed
159 * and contains the mandatory information for being considered as
160 * a well formed and usable DicomDir.
161 * @return true when gdcmParser is the one of a reasonable DicomDir,
164 bool gdcmDicomDir::IsReadable(void)
166 if(!gdcmParser::IsReadable())
170 if(patients.size()<=0)
177 * \ingroup gdcmDicomDir
178 * \brief fills the whole structure, starting from a root Directory
180 void gdcmDicomDir::ParseDirectory(void)
182 CreateDicomDirChainedList(GetFileName());
187 * \ingroup gdcmDicomDir
188 * \brief Set the start method to call when the parsing of the directory starts
189 * @param method Method to call
190 * @param arg Argument to pass to the method
191 * \warning In python : the arg parameter isn't considered
193 void gdcmDicomDir::SetStartMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
195 if((startArg)&&(startMethodArgDelete))
196 startMethodArgDelete(startArg);
200 startMethodArgDelete=argDelete;
204 * \ingroup gdcmDicomDir
205 * \brief Set the method to delete the argument
206 * The argument is destroyed when the method is changed or when the
208 * @param method Method to call to delete the argument
210 void gdcmDicomDir::SetStartMethodArgDelete(gdcmMethod *method)
212 startMethodArgDelete=method;
216 * \ingroup gdcmDicomDir
217 * \brief Set the progress method to call when the parsing of the directory progress
218 * @param method Method to call
219 * @param arg Argument to pass to the method
220 * \warning In python : the arg parameter isn't considered
222 void gdcmDicomDir::SetProgressMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
224 if((progressArg)&&(progressMethodArgDelete))
225 progressMethodArgDelete(progressArg);
227 progressMethod=method;
229 progressMethodArgDelete=argDelete;
233 * \ingroup gdcmDicomDir
234 * \brief Set the method to delete the argument
235 * The argument is destroyed when the method is changed or when the
237 * @param method Method to call to delete the argument
239 void gdcmDicomDir::SetProgressMethodArgDelete(gdcmMethod *method)
241 progressMethodArgDelete=method;
245 * \ingroup gdcmDicomDir
246 * \brief Set the end method to call when the parsing of the directory ends
247 * @param method Method to call
248 * @param arg Argument to pass to the method
249 * \warning In python : the arg parameter isn't considered
251 void gdcmDicomDir::SetEndMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
253 if((endArg)&&(endMethodArgDelete))
254 endMethodArgDelete(endArg);
258 endMethodArgDelete=argDelete;
262 * \ingroup gdcmDicomDir
263 * \brief Set the method to delete the argument
264 * The argument is destroyed when the method is changed or when the class
266 * @param method Method to call to delete the argument
268 void gdcmDicomDir::SetEndMethodArgDelete(gdcmMethod *method)
270 endMethodArgDelete=method;
274 * \ingroup gdcmDicomDir
275 * \brief writes on disc a DICOMDIR
276 * \ warning does NOT add the missing elements in the header :
277 * it's up to the user doing it !
278 * \todo : to be re-written using the DICOMDIR tree-like structure
279 * *not* the chained list
280 * (does NOT exist if the DICOMDIR is user-forged !)
281 * @param fileName file to be written to
282 * @return false only when fail to open
285 bool gdcmDicomDir::Write(std::string fileName)
289 fp1=fopen(fileName.c_str(),"wb");
292 printf("Failed to open(write) File [%s] \n",fileName.c_str());
297 filePreamble=(char*)calloc(128,1);
298 fwrite(filePreamble,128,1,fp1);
299 fwrite("DICM",4,1,fp1);
301 //UpdateDirectoryRecordSequenceLength(); // a reecrire en utilisant
302 // la structure arborescente JPR
303 WriteDicomDirEntries(fp1);
310 * \ingroup gdcmParser
311 * \brief writes on disc according to the DICOMDIR format
312 * using the tree-like structure
313 * @param _fp already open file pointer
316 void gdcmDicomDir::WriteDicomDirEntries(FILE *_fp)
318 // TODO (?) tester les echecs en ecriture
319 // (apres chaque fwrite, dans le WriteEntry)
321 gdcmDicomDirMeta *ptrMeta;
322 ListDicomDirPatient::iterator itPatient;
323 ListDicomDirStudy::iterator itStudy;
324 ListDicomDirSerie::iterator itSerie;
325 ListDicomDirImage::iterator itImage;
328 ptrMeta= GetDicomDirMeta();
329 for(i=ptrMeta->debut();i!=ptrMeta->fin();++i) {
330 WriteEntry(*i,_fp,DICOMDIR);
333 itPatient = GetDicomDirPatients().begin();
334 while ( itPatient != GetDicomDirPatients().end() ) {
335 for(i=(*itPatient)->debut();i!=(*itPatient)->fin();++i) {
336 WriteEntry(*i,_fp,DICOMDIR);
338 itStudy = ((*itPatient)->GetDicomDirStudies()).begin();
339 while (itStudy != (*itPatient)->GetDicomDirStudies().end() ) {
340 for(i=(*itStudy)->debut();i!=(*itStudy)->fin();++i) {
341 WriteEntry(*i,_fp,DICOMDIR);
343 itSerie = ((*itStudy)->GetDicomDirSeries()).begin();
344 while (itSerie != (*itStudy)->GetDicomDirSeries().end() ) {
345 for(i=(*itSerie)->debut();i!=(*itSerie)->fin();++i) {
346 WriteEntry(*i,_fp,DICOMDIR);
348 itImage = ((*itSerie)->GetDicomDirImages()).begin();
349 while (itImage != (*itSerie)->GetDicomDirImages().end() ) {
350 for(i=(*itImage)->debut();i!=(*itImage)->fin();++i) {
351 WriteEntry(*i,_fp,DICOMDIR);
363 //-----------------------------------------------------------------------------
367 * \ingroup gdcmDicomDir
368 * \brief create a gdcmHeader-like chained list from a root Directory
369 * @param path entry point of the tree-like structure
371 void gdcmDicomDir::CreateDicomDirChainedList(std::string path)
375 gdcmDirList fileList(path,1); // gets recursively the file list
376 unsigned int count=0;
383 for(gdcmDirList::iterator it=fileList.begin();
387 progress=(float)(count+1)/(float)fileList.size();
388 CallProgressMethod();
392 header=new gdcmHeader(it->c_str());
393 if(header->IsReadable())
394 list.push_back(header); // adds the file header to the chained list
400 // sorts Patient/Study/Serie/
401 std::sort(list.begin(),list.end(),gdcmDicomDir::HeaderLessThan);
403 std::string tmp=fileList.GetDirName();
404 //for each Header of the chained list, add/update the Patient/Study/Serie/Image info
405 SetElements(tmp,list);
411 * \ingroup gdcmDicomDir
412 * \brief adds a new Patient to a partially created DICOMDIR
414 gdcmDicomDirPatient * gdcmDicomDir::NewPatient(void) {
415 std::list<gdcmElement> elemList;
416 std::list<gdcmElement>::iterator it;
418 gdcmDictEntry *dictEntry;
419 gdcmHeaderEntry *entry;
421 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements();
422 std::list<gdcmHeaderEntry *>::iterator debInsertion, finInsertion, i,j;
424 debInsertion = metaElems->fin();
426 finInsertion=debInsertion;
428 // for all the DicomDirPatient Elements
429 for(it=elemList.begin();it!=elemList.end();++it)
433 dictEntry=GetPubDict()->GetDictEntryByNumber(tmpGr,tmpEl);
434 entry=new gdcmHeaderEntry(dictEntry);
435 entry->SetOffset(0); // just to avoid missprinting
436 entry->SetValue(it->value);
438 if(dictEntry->GetGroup()==0xfffe)
440 entry->SetLength(entry->GetValue().length());
442 else if( (dictEntry->GetVR()=="UL") || (dictEntry->GetVR()=="SL") )
446 else if( (dictEntry->GetVR()=="US") || (dictEntry->GetVR()=="SS") )
450 else if(dictEntry->GetVR()=="SQ")
452 entry->SetLength(0xffffffff);
456 entry->SetLength(entry->GetValue().length());
459 tagHT.insert( PairHT(entry->GetKey(),entry) ); // add in the (multimap) H Table
460 listEntries.insert(debInsertion ,entry); // en tete de liste des Patients
467 gdcmDicomDirPatient *p = new gdcmDicomDirPatient(i, --debInsertion,
468 &tagHT, &listEntries);
469 patients.push_front(p);
474 * \ingroup gdcmDicomDir
475 * \brief CallStartMethod
477 void gdcmDicomDir::CallStartMethod(void)
482 startMethod(startArg);
485 * \ingroup gdcmDicomDir
486 * \brief CallProgressMethod
488 void gdcmDicomDir::CallProgressMethod(void)
491 progressMethod(progressArg);
494 * \ingroup gdcmDicomDir
495 * \brief CallEndMethod
497 void gdcmDicomDir::CallEndMethod(void)
504 //-----------------------------------------------------------------------------
507 * \ingroup gdcmDicomDir
508 * \brief create a 'gdcmDicomDir' from a DICOMDIR gdcmHeader
510 void gdcmDicomDir::CreateDicomDir()
512 // The list is parsed.
513 // When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
514 // 1 - we save the beginning iterator
515 // 2 - we continue to parse
516 // 3 - we find an other tag
517 // + we create the object for the precedent tag
520 gdcmDicomDirType type=gdcmDicomDir::GDCM_DICOMDIR_META;
521 ListTag::iterator begin;
522 ListTag::iterator end, end2;
524 begin=listEntries.begin();
526 for(ListTag::iterator i=end;i !=listEntries.end();++i)
528 std::string v=(*i)->GetValue();
532 AddObjectToEnd(type,begin,--end2);
533 type=gdcmDicomDir::GDCM_DICOMDIR_PATIENT;
540 AddObjectToEnd(type,begin,--end2);
541 type=gdcmDicomDir::GDCM_DICOMDIR_STUDY;
548 AddObjectToEnd(type,begin,--end2);
549 type=gdcmDicomDir::GDCM_DICOMDIR_SERIE;
556 AddObjectToEnd(type,begin,--end2);
557 type=gdcmDicomDir::GDCM_DICOMDIR_IMAGE;
562 end=end2=listEntries.end();
564 AddObjectToEnd(type,begin,--end2);
567 * \ingroup gdcmDicomDir
568 * \brief AddObjectToEnd
573 void gdcmDicomDir::AddObjectToEnd(gdcmDicomDirType type,ListTag::iterator begin,ListTag::iterator end)
580 case gdcmDicomDir::GDCM_DICOMDIR_META:
581 AddDicomDirMetaToEnd(begin,end);
583 case gdcmDicomDir::GDCM_DICOMDIR_PATIENT:
584 AddDicomDirPatientToEnd(begin,end);
586 case gdcmDicomDir::GDCM_DICOMDIR_STUDY:
587 AddDicomDirStudyToEnd(begin,end);
589 case gdcmDicomDir::GDCM_DICOMDIR_SERIE:
590 AddDicomDirSerieToEnd(begin,end);
592 case gdcmDicomDir::GDCM_DICOMDIR_IMAGE:
593 AddDicomDirImageToEnd(begin,end);
599 * \ingroup gdcmDicomDir
600 * \brief Well ... Not realy to end, there is only one occurence
604 void gdcmDicomDir::AddDicomDirMetaToEnd(ListTag::iterator begin,ListTag::iterator end)
608 metaElems = new gdcmDicomDirMeta(begin,end,&tagHT,&listEntries);
612 * \ingroup gdcmDicomDir
613 * \brief AddDicomDirPatientToEnd
617 void gdcmDicomDir::AddDicomDirPatientToEnd(ListTag::iterator begin,ListTag::iterator end)
619 patients.push_back(new gdcmDicomDirPatient(begin,end,&tagHT, &listEntries));
623 * \ingroup gdcmDicomDir
624 * \brief AddDicomDirStudyToEnd
628 void gdcmDicomDir::AddDicomDirStudyToEnd(ListTag::iterator begin,ListTag::iterator end)
630 if(patients.size()>0)
632 ListDicomDirPatient::iterator itp=patients.end();
634 (*itp)->AddDicomDirStudy(new gdcmDicomDirStudy(begin,end,&tagHT, &listEntries));
638 * \ingroup gdcmDicomDir
639 * \brief AddDicomDirSerieToEnd
643 void gdcmDicomDir::AddDicomDirSerieToEnd(ListTag::iterator begin,ListTag::iterator end)
645 if(patients.size()>0)
647 ListDicomDirPatient::iterator itp=patients.end();
650 if((*itp)->GetDicomDirStudies().size()>0)
652 ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
654 (*itst)->AddDicomDirSerie(new gdcmDicomDirSerie(begin,end,&tagHT, &listEntries));
660 * \ingroup gdcmDicomDir
661 * \brief AddDicomDirImageToEnd
665 void gdcmDicomDir::AddDicomDirImageToEnd(ListTag::iterator begin,ListTag::iterator end)
667 if(patients.size()>0)
669 ListDicomDirPatient::iterator itp=patients.end();
672 if((*itp)->GetDicomDirStudies().size()>0)
674 ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
677 if((*itst)->GetDicomDirSeries().size()>0)
679 ListDicomDirSerie::iterator its=(*itst)->GetDicomDirSeries().end();
681 (*its)->AddDicomDirImage(new gdcmDicomDirImage(begin,end,&tagHT, &listEntries));
688 * \ingroup gdcmDicomDir
689 * \brief for each Header of the chained list, add/update the Patient/Study/Serie/Image info
690 * @param path path of the root directory
691 * @param list chained list of Headers
693 void gdcmDicomDir::SetElements(std::string &path, ListHeader &list)
695 std::string patPrevName="", patPrevID="";
696 std::string studPrevInstanceUID="", studPrevID="";
697 std::string serPrevInstanceUID="", serPrevID="";
699 std::string patCurName, patCurID;
700 std::string studCurInstanceUID, studCurID;
701 std::string serCurInstanceUID, serCurID;
703 SetElement(path,GDCM_DICOMDIR_META,NULL);
705 ListTag::iterator debPat=listEntries.begin();
706 for(ListHeader::iterator it=list.begin();it!=list.end();++it)
708 // get the current file characteristics
709 patCurName= (*it)->GetEntryByNumber(0x0010,0x0010);
710 patCurID= (*it)->GetEntryByNumber(0x0010,0x0011);
711 studCurInstanceUID=(*it)->GetEntryByNumber(0x0020,0x000d);
712 studCurID= (*it)->GetEntryByNumber(0x0020,0x0010);
713 serCurInstanceUID= (*it)->GetEntryByNumber(0x0020,0x000e);
714 serCurID= (*it)->GetEntryByNumber(0x0020,0x0011);
716 if(patCurName!=patPrevName || patCurID!=patPrevID)
717 SetElement(path,GDCM_DICOMDIR_PATIENT,*it);
719 // if new Study Deal with 'STUDY' Elements
720 if(studCurInstanceUID!=studPrevInstanceUID || studCurID!=studPrevID)
721 SetElement(path,GDCM_DICOMDIR_STUDY,*it);
723 // if new Serie Deal with 'SERIE' Elements
724 if(serCurInstanceUID!=serPrevInstanceUID || serCurID!=serPrevID)
725 SetElement(path,GDCM_DICOMDIR_SERIE,*it);
727 // Always Deal with 'IMAGE' Elements
728 SetElement(path,GDCM_DICOMDIR_IMAGE,*it);
730 patPrevName= patCurName;
732 studPrevInstanceUID=studCurInstanceUID;
733 studPrevID= studCurID;
734 serPrevInstanceUID= serCurInstanceUID;
740 * \ingroup gdcmDicomDir
741 * \brief adds to the HTable and at the end of the Chained List
742 * the gdcmEntries (Dicom Elements) corresponding to the given type
743 * @param path full path file name(only used when type = GDCM_DICOMDIR_IMAGE
744 * @param type gdcmObject type to create (GDCM_DICOMDIR_PATIENT, GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
745 * @param header gdcmHeader of the current file
747 void gdcmDicomDir::SetElement(std::string &path,gdcmDicomDirType type,gdcmHeader *header)
749 std::list<gdcmElement> elemList;
750 std::list<gdcmElement>::iterator it;
751 guint16 tmpGr, tmpEl;
752 gdcmDictEntry *dictEntry;
753 gdcmHeaderEntry *entry;
758 case GDCM_DICOMDIR_PATIENT:
759 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements();
761 case GDCM_DICOMDIR_STUDY:
762 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirStudyElements();
764 case GDCM_DICOMDIR_SERIE:
765 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirSerieElements();
767 case GDCM_DICOMDIR_IMAGE:
768 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirImageElements();
770 case GDCM_DICOMDIR_META:
771 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirMetaElements();
777 for(it=elemList.begin();it!=elemList.end();++it)
781 dictEntry=GetPubDict()->GetDictEntryByNumber(tmpGr,tmpEl);
782 entry=new gdcmHeaderEntry(dictEntry);
783 entry->SetOffset(0); // just to avoid missprinting
786 val=header->GetEntryByNumber(tmpGr,tmpEl);
790 if(val==GDCM_UNFOUND)
792 if((tmpGr==0x0004) &&(tmpEl==0x1130) ) // File-set ID
794 // force to the *end* File Name
797 else if( (tmpGr==0x0004) && (tmpEl==0x1500) ) // Only used for image
799 if(header->GetFileName().substr(0,path.length())!=path)
801 dbg.Verbose(0, "gdcmDicomDir::SetElement : the base path of file name is incorrect");
802 val=header->GetFileName();
805 val=&(header->GetFileName().c_str()[path.length()]);
815 if (header->GetEntryLengthByNumber(tmpGr,tmpEl)== 0)
819 entry->SetValue(val);
823 if(dictEntry->GetGroup()==0xfffe)
825 entry->SetLength(entry->GetValue().length());
827 else if( (dictEntry->GetVR()=="UL") || (dictEntry->GetVR()=="SL") )
831 else if( (dictEntry->GetVR()=="US") || (dictEntry->GetVR()=="SS") )
835 else if(dictEntry->GetVR()=="SQ")
837 entry->SetLength(0xffffffff);
841 entry->SetLength(entry->GetValue().length());
844 //AddHeaderEntry(entry); // both in H Table and in chained list
845 tagHT.insert( PairHT( entry->GetKey(),entry) );
846 listEntries.push_back(entry);
847 //wasUpdated = 1; // is private
851 bool gdcmDicomDir::HeaderLessThan(gdcmHeader *header1,gdcmHeader *header2)
853 return(*header1<*header2);
858 void gdcmDicomDir::UpdateDirectoryRecordSequenceLength() {
860 ListTag::iterator it;
863 for(it=listEntries.begin();it!=listEntries.end();++it) {
864 gr = (*it)->GetGroup();
865 el = (*it)->GetElement();
868 if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
869 offset += 4; // explicit VR AND OB, OW, SQ : 4 more bytes
871 offset += 2 + 2 + 4 + (*it)->GetLength();
873 offset += 4; // delimiters don't have a value.
876 bool res=SetEntryLengthByNumber(offset, 0x0004, 0x1220); // Hope there is no dupps.
880 //-----------------------------------------------------------------------------