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>
16 //-----------------------------------------------------------------------------
17 // For full DICOMDIR description, see:
18 // PS 3.3-2003, pages 731-750
19 //-----------------------------------------------------------------------------
20 // Constructor / Destructor
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 passed an entry point
28 * and wants to explore recursively the directories
29 * - false if user passed an already built DICOMDIR file
31 * @param exception_on_error whether we want to throw an exception or not
33 gdcmDicomDir::gdcmDicomDir(const char *Name, bool parseDir,
34 bool exception_on_error):
35 gdcmParser(Name,exception_on_error,true) // true : enable SeQuences
37 // que l'on ai passe un root directory ou un DICOMDIR
38 // et quelle que soit la valeur de parseDir,
39 // on a lance gdcmParser
44 startMethodArgDelete= NULL;
45 progressMethodArgDelete=NULL;
46 endMethodArgDelete= NULL;
56 // Si on passe un root directory, on est assuré de n'avoir rien ramené
58 if( GetListEntry().begin()==GetListEntry().end() )
60 // Si, en plus, parseDir == false, ca devrait etre une erreur
61 dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : entry list empty");
65 dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : Parse directory and create the DicomDir");
74 * \ingroup gdcmDicomDir
75 * \brief Constructor : creates an empty gdcmDicomDir
76 * @param exception_on_error whether we want to throw an exception or not
78 gdcmDicomDir::gdcmDicomDir(bool exception_on_error):
79 gdcmParser(exception_on_error)
84 startMethodArgDelete= NULL;
85 progressMethodArgDelete=NULL;
86 endMethodArgDelete= NULL;
93 std::string pathBidon = ""; // Sorry, NULL not allowed ...
94 SetElement(pathBidon, GDCM_DICOMDIR_META, NULL); // Set the META elements
96 AddDicomDirMetaToEnd(listEntries.begin(),--listEntries.end());
101 * \ingroup gdcmDicomDir
102 * \brief Canonical destructor
104 gdcmDicomDir::~gdcmDicomDir()
106 SetStartMethod(NULL);
107 SetProgressMethod(NULL);
113 for(ListDicomDirPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
119 //-----------------------------------------------------------------------------
122 * \ingroup gdcmDicomDir
123 * \brief Canonical Printer
125 void gdcmDicomDir::Print(std::ostream &os)
129 metaElems->SetPrintLevel(printLevel);
130 metaElems->Print(os);
132 for(ListDicomDirPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
134 (*cc)->SetPrintLevel(printLevel);
139 //-----------------------------------------------------------------------------
142 * \ingroup gdcmDicomDir
143 * \brief This predicate, based on hopefully reasonable heuristics,
144 * decides whether or not the current gdcmParser was properly parsed
145 * and contains the mandatory information for being considered as
146 * a well formed and usable DicomDir.
147 * @return true when gdcmParser is the one of a reasonable DicomDir,
150 bool gdcmDicomDir::IsReadable(void)
152 if(!gdcmParser::IsReadable())
156 if(patients.size()<=0)
163 * \ingroup gdcmDicomDir
164 * \brief fills whole the structure
166 void gdcmDicomDir::ParseDirectory(void)
168 CreateDicomDirChainedList(GetPath());
173 * \ingroup gdcmDicomDir
174 * \brief Set the start method to call when the parsing of the directory starts
175 * @param method Method to call
176 * @param arg Argument to pass to the method
177 * \warning In python : the arg parameter isn't considered
179 void gdcmDicomDir::SetStartMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
181 if((startArg)&&(startMethodArgDelete))
182 startMethodArgDelete(startArg);
186 startMethodArgDelete=argDelete;
190 * \ingroup gdcmDicomDir
191 * \brief Set the method to delete the argument
192 * The argument is destroyed when the method is changed or when the class
194 * @param method Method to call to delete the argument
196 void gdcmDicomDir::SetStartMethodArgDelete(gdcmMethod *method)
198 startMethodArgDelete=method;
202 * \ingroup gdcmDicomDir
203 * \brief Set the progress method to call when the parsing of the directory progress
204 * @param method Method to call
205 * @param arg Argument to pass to the method
206 * \warning In python : the arg parameter isn't considered
208 void gdcmDicomDir::SetProgressMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
210 if((progressArg)&&(progressMethodArgDelete))
211 progressMethodArgDelete(progressArg);
213 progressMethod=method;
215 progressMethodArgDelete=argDelete;
219 * \ingroup gdcmDicomDir
220 * \brief Set the method to delete the argument
221 * The argument is destroyed when the method is changed or when the class
223 * @param method Method to call to delete the argument
225 void gdcmDicomDir::SetProgressMethodArgDelete(gdcmMethod *method)
227 progressMethodArgDelete=method;
231 * \ingroup gdcmDicomDir
232 * \brief Set the end method to call when the parsing of the directory ends
233 * @param method Method to call
234 * @param arg Argument to pass to the method
235 * \warning In python : the arg parameter isn't considered
237 void gdcmDicomDir::SetEndMethod(gdcmMethod *method,void *arg,gdcmMethod *argDelete)
239 if((endArg)&&(endMethodArgDelete))
240 endMethodArgDelete(endArg);
244 endMethodArgDelete=argDelete;
248 * \ingroup gdcmDicomDir
249 * \brief Set the method to delete the argument
250 * The argument is destroyed when the method is changed or when the class
252 * @param method Method to call to delete the argument
254 void gdcmDicomDir::SetEndMethodArgDelete(gdcmMethod *method)
256 endMethodArgDelete=method;
260 * \ingroup gdcmDicomDir
261 * \brief writes on disc a DICOMDIR
262 * \ warning does NOT add the missing elements in the header :
263 * it's up to the user doing it !
264 * \todo : to be re-written using the DICOMDIR tree-like structure
265 * *not* the chained list
266 * (does NOT exist if the DICOMDIR is user-forged !)
267 * @param fileName file to be written to
268 * @return false only when fail to open
271 bool gdcmDicomDir::Write(std::string fileName)
275 fp1=fopen(fileName.c_str(),"wb");
278 printf("Failed to open(write) File [%s] \n",fileName.c_str());
283 filePreamble=(char*)calloc(128,1);
284 fwrite(filePreamble,128,1,fp1);
285 fwrite("DICM",4,1,fp1);
287 //UpdateDirectoryRecordSequenceLength(); // a reecrire en utilisant
288 // la structure arborescente JPR
289 WriteDicomDirEntries(fp1);
297 * \ingroup gdcmParser
298 * \brief writes on disc according to the DICOMDIR format
299 * using the Chained List
300 * \todo a reecrire en utilisant la structure arborescente
301 * @param _fp already open file pointer
304 void gdcmDicomDir::WriteDicomDirEntries(FILE *_fp)
306 // TODO (?) tester les echecs en ecriture
307 // (apres chaque fwrite, dans le WriteEntry)
309 gdcmDicomDirMeta *ptrMeta;
310 ListDicomDirPatient::iterator itPatient;
311 ListDicomDirStudy::iterator itStudy;
312 ListDicomDirSerie::iterator itSerie;
313 ListDicomDirImage::iterator itImage;
316 ptrMeta= GetDicomDirMeta();
317 for(i=ptrMeta->debut();i!=ptrMeta->fin();++i) {
318 WriteEntry(*i,_fp,DICOMDIR);
321 itPatient = GetDicomDirPatients().begin();
322 while ( itPatient != GetDicomDirPatients().end() ) {
323 for(i=(*itPatient)->debut();i!=(*itPatient)->fin();++i) {
324 WriteEntry(*i,_fp,DICOMDIR);
326 itStudy = ((*itPatient)->GetDicomDirStudies()).begin();
327 while (itStudy != (*itPatient)->GetDicomDirStudies().end() ) {
328 for(i=(*itStudy)->debut();i!=(*itStudy)->fin();++i) {
329 WriteEntry(*i,_fp,DICOMDIR);
331 itSerie = ((*itStudy)->GetDicomDirSeries()).begin();
332 while (itSerie != (*itStudy)->GetDicomDirSeries().end() ) {
333 for(i=(*itSerie)->debut();i!=(*itSerie)->fin();++i) {
334 WriteEntry(*i,_fp,DICOMDIR);
336 itImage = ((*itSerie)->GetDicomDirImages()).begin();
337 while (itImage != (*itSerie)->GetDicomDirImages().end() ) {
338 for(i=(*itImage)->debut();i!=(*itImage)->fin();++i) {
339 WriteEntry(*i,_fp,DICOMDIR);
351 //-----------------------------------------------------------------------------
355 * \ingroup gdcmDicomDir
356 * \brief create a gdcmHeader-like chained list from a root Directory
357 * @param path entry point of the tree-like structure
359 void gdcmDicomDir::CreateDicomDirChainedList(std::string path)
363 gdcmDirList fileList(path,1); // gets recursively the file list
364 unsigned int count=0;
371 for(gdcmDirList::iterator it=fileList.begin();
372 it!=fileList.end(); ++it)
374 progress=(float)(count+1)/(float)fileList.size();
375 CallProgressMethod();
379 header=new gdcmHeader(it->c_str());
380 if(header->IsReadable())
381 list.push_back(header); // adds the file header to the chained list
387 // sorts Patient/Study/Serie/
388 std::sort(list.begin(),list.end(),gdcmDicomDir::HeaderLessThan);
390 std::string tmp=fileList.GetDirName();
391 //for each Header of the chained list, add/update the Patient/Study/Serie/Image info
392 SetElements(tmp,list);
398 * \ingroup gdcmDicomDir
399 * \brief adds a new Patient to a partially created DICOMDIR
401 gdcmDicomDirPatient * gdcmDicomDir::NewPatient(void) {
402 std::list<gdcmElement> elemList;
403 std::list<gdcmElement>::iterator it;
405 gdcmDictEntry *dictEntry;
406 gdcmHeaderEntry *entry;
408 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements();
409 std::list<gdcmHeaderEntry *>::iterator debInsertion, finInsertion, i,j;
412 debInsertion = metaElems->fin();
414 finInsertion=debInsertion;
416 // for all the DicomDirPatient Elements
417 for(it=elemList.begin();it!=elemList.end();++it)
421 dictEntry=GetPubDict()->GetDictEntryByNumber(tmpGr,tmpEl);
422 entry=new gdcmHeaderEntry(dictEntry);
423 entry->SetOffset(0); // just to avoid missprinting
424 entry->SetValue(it->value);
426 if(dictEntry->GetGroup()==0xfffe)
428 entry->SetLength(entry->GetValue().length());
430 else if( (dictEntry->GetVR()=="UL") || (dictEntry->GetVR()=="SL") )
434 else if( (dictEntry->GetVR()=="US") || (dictEntry->GetVR()=="SS") )
438 else if(dictEntry->GetVR()=="SQ")
440 entry->SetLength(0xffffffff);
444 entry->SetLength(entry->GetValue().length());
447 tagHT.insert( PairHT(entry->GetKey(),entry) ); // add in the (multimap) H Table
448 listEntries.insert(debInsertion ,entry); // en tete de liste des Patients
455 gdcmDicomDirPatient *p = new gdcmDicomDirPatient(i, --debInsertion,
456 &tagHT, &listEntries);
457 patients.push_front(p);
462 * \ingroup gdcmDicomDir
463 * \brief Get the DicomDir path
465 std::string gdcmDicomDir::GetPath(void)
467 std::string path=GetFileName();
469 int pos1=path.rfind("/");
470 int pos2=path.rfind("\\");
479 * \ingroup gdcmDicomDir
480 * \brief CallStartMethod
482 void gdcmDicomDir::CallStartMethod(void)
487 startMethod(startArg);
490 * \ingroup gdcmDicomDir
491 * \brief CallProgressMethod
493 void gdcmDicomDir::CallProgressMethod(void)
496 progressMethod(progressArg);
499 * \ingroup gdcmDicomDir
500 * \brief CallEndMethod
502 void gdcmDicomDir::CallEndMethod(void)
509 //-----------------------------------------------------------------------------
512 * \ingroup gdcmDicomDir
513 * \brief create a 'gdcmDicomDir' from a DICOMDIR gdcmHeader
515 void gdcmDicomDir::CreateDicomDir()
517 // The list is parsed.
518 // When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
519 // 1 - we save the beginning iterator
520 // 2 - we continue to parse
521 // 3 - we find an other tag
522 // + we create the object for the precedent tag
525 gdcmDicomDirType type=gdcmDicomDir::GDCM_DICOMDIR_META;
526 ListTag::iterator begin;
527 ListTag::iterator end, end2;
529 begin=listEntries.begin();
531 for(ListTag::iterator i=end;i !=listEntries.end();++i)
533 std::string v=(*i)->GetValue();
537 AddObjectToEnd(type,begin,--end2);
538 type=gdcmDicomDir::GDCM_DICOMDIR_PATIENT;
545 AddObjectToEnd(type,begin,--end2);
546 type=gdcmDicomDir::GDCM_DICOMDIR_STUDY;
553 AddObjectToEnd(type,begin,--end2);
554 type=gdcmDicomDir::GDCM_DICOMDIR_SERIE;
561 AddObjectToEnd(type,begin,--end2);
562 type=gdcmDicomDir::GDCM_DICOMDIR_IMAGE;
567 end=end2=listEntries.end();
569 AddObjectToEnd(type,begin,--end2);
572 * \ingroup gdcmDicomDir
573 * \brief AddObjectToEnd
578 void gdcmDicomDir::AddObjectToEnd(gdcmDicomDirType type,ListTag::iterator begin,ListTag::iterator end)
585 case gdcmDicomDir::GDCM_DICOMDIR_META:
586 AddDicomDirMetaToEnd(begin,end);
588 case gdcmDicomDir::GDCM_DICOMDIR_PATIENT:
589 AddDicomDirPatientToEnd(begin,end);
591 case gdcmDicomDir::GDCM_DICOMDIR_STUDY:
592 AddDicomDirStudyToEnd(begin,end);
594 case gdcmDicomDir::GDCM_DICOMDIR_SERIE:
595 AddDicomDirSerieToEnd(begin,end);
597 case gdcmDicomDir::GDCM_DICOMDIR_IMAGE:
598 AddDicomDirImageToEnd(begin,end);
604 * \ingroup gdcmDicomDir
605 * \brief Well ... Not realy to end, there is only one occurence
609 void gdcmDicomDir::AddDicomDirMetaToEnd(ListTag::iterator begin,ListTag::iterator end)
613 metaElems = new gdcmDicomDirMeta(begin,end,&tagHT,&listEntries);
617 * \ingroup gdcmDicomDir
618 * \brief AddDicomDirPatientToEnd
622 void gdcmDicomDir::AddDicomDirPatientToEnd(ListTag::iterator begin,ListTag::iterator end)
624 patients.push_back(new gdcmDicomDirPatient(begin,end,&tagHT, &listEntries));
628 * \ingroup gdcmDicomDir
629 * \brief AddDicomDirStudyToEnd
633 void gdcmDicomDir::AddDicomDirStudyToEnd(ListTag::iterator begin,ListTag::iterator end)
635 if(patients.size()>0)
637 ListDicomDirPatient::iterator itp=patients.end();
639 (*itp)->AddDicomDirStudy(new gdcmDicomDirStudy(begin,end,&tagHT, &listEntries));
643 * \ingroup gdcmDicomDir
644 * \brief AddDicomDirSerieToEnd
648 void gdcmDicomDir::AddDicomDirSerieToEnd(ListTag::iterator begin,ListTag::iterator end)
650 if(patients.size()>0)
652 ListDicomDirPatient::iterator itp=patients.end();
655 if((*itp)->GetDicomDirStudies().size()>0)
657 ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
659 (*itst)->AddDicomDirSerie(new gdcmDicomDirSerie(begin,end,&tagHT, &listEntries));
665 * \ingroup gdcmDicomDir
666 * \brief AddDicomDirImageToEnd
670 void gdcmDicomDir::AddDicomDirImageToEnd(ListTag::iterator begin,ListTag::iterator end)
672 if(patients.size()>0)
674 ListDicomDirPatient::iterator itp=patients.end();
677 if((*itp)->GetDicomDirStudies().size()>0)
679 ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
682 if((*itst)->GetDicomDirSeries().size()>0)
684 ListDicomDirSerie::iterator its=(*itst)->GetDicomDirSeries().end();
686 (*its)->AddDicomDirImage(new gdcmDicomDirImage(begin,end,&tagHT, &listEntries));
693 * \ingroup gdcmDicomDir
694 * \brief for each Header of the chained list, add/update the Patient/Study/Serie/Image info
698 void gdcmDicomDir::SetElements(std::string &path, ListHeader &list)
700 std::string patPrevName="", patPrevID="";
701 std::string studPrevInstanceUID="", studPrevID="";
702 std::string serPrevInstanceUID="", serPrevID="";
704 std::string patCurName, patCurID;
705 std::string studCurInstanceUID, studCurID;
706 std::string serCurInstanceUID, serCurID;
708 SetElement(path,GDCM_DICOMDIR_META,NULL);
710 ListTag::iterator debPat=listEntries.begin();
711 for(ListHeader::iterator it=list.begin();it!=list.end();++it)
713 // get the current file characteristics
714 patCurName= (*it)->GetEntryByNumber(0x0010,0x0010);
715 patCurID= (*it)->GetEntryByNumber(0x0010,0x0011);
716 studCurInstanceUID=(*it)->GetEntryByNumber(0x0020,0x000d);
717 studCurID= (*it)->GetEntryByNumber(0x0020,0x0010);
718 serCurInstanceUID= (*it)->GetEntryByNumber(0x0020,0x000e);
719 serCurID= (*it)->GetEntryByNumber(0x0020,0x0011);
721 if(patCurName!=patPrevName || patCurID!=patPrevID)
722 SetElement(path,GDCM_DICOMDIR_PATIENT,*it);
724 // if new Study Deal with 'STUDY' Elements
725 if(studCurInstanceUID!=studPrevInstanceUID || studCurID!=studPrevID)
726 SetElement(path,GDCM_DICOMDIR_STUDY,*it);
728 // if new Serie Deal with 'SERIE' Elements
729 if(serCurInstanceUID!=serPrevInstanceUID || serCurID!=serPrevID)
730 SetElement(path,GDCM_DICOMDIR_SERIE,*it);
732 // Always Deal with 'IMAGE' Elements
733 SetElement(path,GDCM_DICOMDIR_IMAGE,*it);
735 patPrevName= patCurName;
737 studPrevInstanceUID=studCurInstanceUID;
738 studPrevID= studCurID;
739 serPrevInstanceUID= serCurInstanceUID;
745 * \ingroup gdcmDicomDir
746 * \brief adds to the HTable and at the end of the Chained List
747 * the gdcmEntries (Dicom Elements) corresponding to the given type
748 * @param path file Path (only used when type = GDCM_DICOMDIR_IMAGE
749 * @param type gdcmObject type to create (GDCM_DICOMDIR_PATIENT, GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
750 * @param header gdcmHeader of the current file
752 void gdcmDicomDir::SetElement(std::string &path,gdcmDicomDirType type,gdcmHeader *header)
754 std::list<gdcmElement> elemList;
755 std::list<gdcmElement>::iterator it;
756 guint16 tmpGr, tmpEl;
757 gdcmDictEntry *dictEntry;
758 gdcmHeaderEntry *entry;
763 case GDCM_DICOMDIR_PATIENT:
764 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements();
766 case GDCM_DICOMDIR_STUDY:
767 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirStudyElements();
769 case GDCM_DICOMDIR_SERIE:
770 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirSerieElements();
772 case GDCM_DICOMDIR_IMAGE:
773 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirImageElements();
775 case GDCM_DICOMDIR_META:
776 elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirMetaElements();
782 for(it=elemList.begin();it!=elemList.end();++it)
787 dictEntry=GetPubDict()->GetDictEntryByNumber(tmpGr,tmpEl);
788 entry=new gdcmHeaderEntry(dictEntry);
789 entry->SetOffset(0); // just to avoid missprinting
792 val=header->GetEntryByNumber(tmpGr,tmpEl);
796 if(val==GDCM_UNFOUND)
798 if((tmpGr==0x0004) &&(tmpEl==0x1130) )
800 // TODO force the *end* File Name(remove path)
803 else if( (tmpGr==0x0004) && (tmpEl==0x1500) ) // Only used for image
805 if(header->GetFileName().substr(0,path.length())!=path)
807 dbg.Verbose(0, "gdcmDicomDir::SetElement : the base path of file name is incorrect");
808 val=header->GetFileName();
811 val=&(header->GetFileName()[path.length()]);
820 if (header->GetEntryLengthByNumber(tmpGr,tmpEl)== 0)
824 entry->SetValue(val);
828 if(dictEntry->GetGroup()==0xfffe)
830 entry->SetLength(entry->GetValue().length()); //JPR
832 else if( (dictEntry->GetVR()=="UL") || (dictEntry->GetVR()=="SL") )
836 else if( (dictEntry->GetVR()=="US") || (dictEntry->GetVR()=="SS") )
840 else if(dictEntry->GetVR()=="SQ")
842 entry->SetLength(0xffffffff);
846 entry->SetLength(entry->GetValue().length());
849 //AddHeaderEntry(entry); // both in H Table and in chained list
850 tagHT.insert( PairHT( entry->GetKey(),entry) );
851 listEntries.push_back(entry);
852 //wasUpdated = 1; // is private
856 bool gdcmDicomDir::HeaderLessThan(gdcmHeader *header1,gdcmHeader *header2)
858 return(*header1<*header2);
863 void gdcmDicomDir::UpdateDirectoryRecordSequenceLength() {
865 ListTag::iterator it;
868 for(it=listEntries.begin();it!=listEntries.end();++it) {
869 gr = (*it)->GetGroup();
870 el = (*it)->GetElement();
873 if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
874 offset += 4; // explicit VR AND OB, OW, SQ : 4 more bytes
876 offset += 2 + 2 + 4 + (*it)->GetLength();
878 offset += 4; // delimiters don't have a value.
881 bool res=SetEntryLengthByNumber(offset, 0x0004, 0x1220); // Hope there is no dupps.
885 //-----------------------------------------------------------------------------