]> Creatis software - gdcm.git/blob - Example/SplitIntoDirectories.cxx
This one should be usefull in order not to 'read' to many headers, in
[gdcm.git] / Example / SplitIntoDirectories.cxx
1 /*=========================================================================
2
3   Program:   gdcm
4   Module:    $RCSfile: SplitIntoDirectories.cxx,v $
5   Language:  C++
6   Date:      $Date: 2007/09/26 08:14:27 $
7   Version:   $Revision: 1.1 $
8                                                                                 
9   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10   l'Image). All rights reserved. See Doc/License.txt or
11   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
12                  
13      This software is distributed WITHOUT ANY WARRANTY; without even
14      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15      PURPOSE.  See the above copyright notices for more information.
16                                                                                 
17 =========================================================================*/
18 #include "gdcmDocEntry.h"
19 #include "gdcmDicomDir.h"
20 #include "gdcmDicomDirPatient.h"
21 #include "gdcmFile.h"
22 #include "gdcmFileHelper.h"
23 #include "gdcmDirList.h"
24 #include "gdcmDebug.h"
25 #include "gdcmArgMgr.h"
26 #include "gdcmUtil.h"
27 #include "gdcmSerieHelper.h"
28
29 #include <iostream>
30
31 /**
32   * \brief   
33   *          - explores recursively the given directory
34   *          - keeps the requested series
35   *          - orders the gdcm-readable found Files
36   *            according to their Patient/Study/Serie/Image characteristics
37   */  
38
39 typedef std::map<std::string, GDCM_NAME_SPACE::File*> SortedFiles;
40
41 int main(int argc, char *argv[]) 
42 {
43    START_USAGE(usage)
44    " \n SplitIntoDirectories :\n                                              ",
45    " - explores recursively the given directory,                              ",
46    " - keeps the requested series / drops the unrequested series              ",
47    " - orders the gdcm-readable found Files according to their                ",
48    "           (0x0010, 0x0010) Patient's Name                                ",
49    "           (0x0020, 0x000d) Study Instance UID                            ",   
50    "           (0x0020, 0x000e) Series Instance UID                           ",
51    " - fills a tree-like structure of directories as :                        ",
52    "        - Patient                                                         ",
53    "        -- Study                                                          ",
54    "        --- Serie                                                         ",
55    "                                                                          ",
56    " usage:                                                                   ",
57    " -----                                                                    ",
58    " SplitIntoDirectories                                                     ",
59    "                  dirin=rootDirectoryName                                 ",
60    "                  dirout=outputDirectoryName                              ",
61    "                  {  [keep= list of seriesNumber to process]              ",
62    "                   | [drop= list of seriesNumber to ignore] }             ",
63    "                  [listonly]                                              ",
64    "                  [noshadowseq][noshadow][noseq] [verbose] [debug]        ",
65    "                                                                          ",
66    " dirout : will be created if doesn't exist                                ",
67    " keep : if user wants to process a limited number of series               ",
68    "            he gives the list of 'SeriesNumber' (tag 0020|0011)           ",
69    " drop : if user wants to ignore a limited number of series                ",
70    "            he gives the list of 'SeriesNumber' (tag 0020|0011)           ",
71    "        SeriesNumber are short enough to be human readable                ",
72    "        e.g : 1030,1035,1043                                              ", 
73    " noshadowseq: user doesn't want to load Private Sequences                 ",
74    " noshadow : user doesn't want to load Private groups (odd number)         ",
75    " noseq    : user doesn't want to load Sequences                           ",
76    " verbose  : user wants to run the program in 'verbose mode'               ",
77    " debug    : *developer*  wants to run the program in 'debug mode'         ",
78    FINISH_USAGE
79
80
81    enum Index
82    {
83       IND_PatientName,
84       IND_StudyInstanceUID,
85       IND_SerieInstanceUID
86    };
87       
88    std::cout << "... inside " << argv[0] << std::endl;
89    
90    // ----- Initialize Arguments Manager ------
91       
92    GDCM_NAME_SPACE::ArgMgr *am = new GDCM_NAME_SPACE::ArgMgr(argc, argv);
93   
94    if (argc == 1 || am->ArgMgrDefined("usage")) 
95    {
96       am->ArgMgrUsage(usage); // Display 'usage'
97       delete am;
98       return 0;
99    }
100
101    const char *dirNamein;   
102    dirNamein  = am->ArgMgrGetString("dirin","."); 
103
104    const char *dirNameout;   
105    dirNameout  = am->ArgMgrGetString("dirout",".");  
106    
107    int loadMode = GDCM_NAME_SPACE::LD_ALL;
108    if ( am->ArgMgrDefined("noshadowseq") )
109       loadMode |= GDCM_NAME_SPACE::LD_NOSHADOWSEQ;
110    else 
111    {
112    if ( am->ArgMgrDefined("noshadow") )
113          loadMode |= GDCM_NAME_SPACE::LD_NOSHADOW;
114       if ( am->ArgMgrDefined("noseq") )
115          loadMode |= GDCM_NAME_SPACE::LD_NOSEQ;
116    }
117
118    if (am->ArgMgrDefined("debug"))
119       GDCM_NAME_SPACE::Debug::DebugOn();
120
121    bool verbose  = ( 0 != am->ArgMgrDefined("verbose") );
122    bool listonly = ( 0 != am->ArgMgrDefined("listonly") );
123            
124    int nbSeriesToKeep;
125    int *seriesToKeep = am->ArgMgrGetListOfInt("keep", &nbSeriesToKeep);
126    int nbSeriesToDrop;
127    int *seriesToDrop = am->ArgMgrGetListOfInt("drop", &nbSeriesToDrop);
128  
129    if ( nbSeriesToKeep!=0 && nbSeriesToDrop!=0)
130    {
131       std::cout << "KEEP and DROP are mutually exclusive !" << std::endl;
132       delete am;
133       return 0;         
134    }
135
136    int hasSkel = am->ArgMgrDefined("skel");
137    const char *skel;
138    if (hasSkel)
139       skel = am->ArgMgrGetString("skel");   
140
141    const char *input   = am->ArgMgrGetString("input","DCM");
142    
143    // if unused Param we give up
144    if ( am->ArgMgrPrintUnusedLabels() )
145    { 
146       am->ArgMgrUsage(usage);
147       delete am;
148       return 0;
149    }
150    delete am;  // we don't need Argument Manager any longer
151
152    // ----- Begin Processing -----
153    
154      
155    // --> Check supposed-to-be-directory names
156    
157    if ( ! GDCM_NAME_SPACE::DirList::IsDirectory(dirNamein) )
158    {
159       std::cout << "KO : [" << dirNamein << "] is not a Directory."
160                 << std::endl;
161       return 0;
162
163    }
164    else
165    {
166       std::cout << "OK : [" << dirNamein << "] is a Directory." << std::endl;
167    }
168
169    std::string systemCommand;
170    
171    std::cout << "Check for output directory :[" << dirNameout << "]."
172              <<std::endl;
173    if ( ! GDCM_NAME_SPACE::DirList::IsDirectory(dirNameout) )    // dirout not found
174    {
175       std::string strDirNameout(dirNameout);          // to please gcc 4
176       systemCommand = "mkdir " +strDirNameout;        // create it!
177       if (verbose)
178          std::cout << systemCommand << std::endl;
179       system (systemCommand.c_str());
180       if ( ! GDCM_NAME_SPACE::DirList::IsDirectory(dirNameout) ) // be sure it worked
181       {
182           std::cout << "KO : not a dir : [" << dirNameout << "] (creation failure ?)" 
183                     << std::endl;
184       return 0;
185
186       }
187       else
188       {
189         std::cout << "Directory [" << dirNameout << "] created." << std::endl;
190       }
191    }
192    else
193    {
194        std::cout << "Output Directory [" << dirNameout 
195                  << "] already exists; Used as is."
196                  << std::endl;
197    }
198    // --> End of checking supposed-to-be-directory names
199        
200    std::string strDirNamein(dirNamein);
201    // true ; get recursively the list of files
202    GDCM_NAME_SPACE::DirList dirList(strDirNamein, true); 
203    
204    if (listonly)
205    {
206       std::cout << "------------List of found files ------------" << std::endl;
207       dirList.Print();
208       std::cout << std::endl;
209    }
210
211
212 // ======================================= The job starts here =========================
213    
214    GDCM_NAME_SPACE::DirListType fileNames;
215    fileNames = dirList.GetFilenames();
216
217    GDCM_NAME_SPACE::SerieHelper *s;     // Needed to use SerieHelper::AddSeriesDetail()
218    s = GDCM_NAME_SPACE::SerieHelper::New();
219
220    std::string token = "%%%"; // Hope it's enough!
221   
222    GDCM_NAME_SPACE::File *f;
223    std::vector<std::string> tokens;
224    std::vector<std::string> tokensForFileName;
225    
226    if (verbose)
227       std::cout << "------------------Print Break levels-----------------" << std::endl;
228
229    std::string userFileIdentifier;
230    SortedFiles sf;
231  
232    s->AddSeriesDetail(0x0010, 0x0010, false); // Patient's Name (false : no convert)
233    
234    // You may prefer 0020 0010  Study ID
235    // use :
236    // s->AddSeriesDetail(0x0020, 0x0010, true); 
237    // Avoid using 0008 0020 Study Date, 
238    // since you may have more than one study, for a given Patient, at a given Date!
239    // or the field may be empty!   
240    s->AddSeriesDetail(0x0020, 0x000d, false); // Study Instance UID (false : no convert)
241
242
243    // You may prefer 0020 0011 Series Number
244    // use :
245    // s->AddSeriesDetail(0x0020, 0x0011, true);    
246    s->AddSeriesDetail(0x0020, 0x000e, false); // Series Instance UID (false : no convert)
247    
248    // Feel free to add more fields, if they can help a suitable (for you)
249    // image sorting
250
251 // Loop on all the gdcm-readable files
252    for (GDCM_NAME_SPACE::DirListType::iterator it = fileNames.begin();
253                                     it != fileNames.end();
254                                   ++it)
255    {
256       f = GDCM_NAME_SPACE::File::New();
257       f->SetLoadMode(loadMode);
258       f->SetFileName( *it );
259       f->Load();
260
261       std::string strSeriesNumber;
262       int seriesNumber;
263       int j;
264
265       // keep only requested Series
266       bool keep = false;
267       if (nbSeriesToKeep != 0)
268       {
269          strSeriesNumber = f->GetEntryString(0x0020, 0x0011 );
270          seriesNumber = atoi( strSeriesNumber.c_str() );
271          for (j=0; j<nbSeriesToKeep; j++)
272          {
273             if(seriesNumber == seriesToKeep[j])
274             {
275                keep = true;
276                break;
277             }
278          }
279          if ( !keep)
280          {
281             f->Delete();
282             continue;
283          }
284       }
285       // drop all unrequested Series
286       bool drop = false;
287       if (nbSeriesToDrop != 0)
288       {     
289          strSeriesNumber = f->GetEntryString(0x0020, 0x0011 );
290          seriesNumber = atoi( strSeriesNumber.c_str() );
291          for (j=0;j<nbSeriesToDrop; j++)
292          {
293             if(seriesNumber == seriesToDrop[j])
294             { 
295                drop = true;
296                break;
297             }
298         }
299         if (drop)
300         {
301            f->Delete();
302            continue;
303         }
304       } 
305
306       userFileIdentifier=s->CreateUserDefinedFileIdentifier(f); 
307       tokens.clear();
308       GDCM_NAME_SPACE::Util::Tokenize (userFileIdentifier, tokens, token); 
309    
310       int imageNum; // Within FileName
311       char newName[1024];
312       
313  
314          // Patient's Name
315          // Study Instance UID 
316          // Series Instance UID
317
318       userFileIdentifier = tokens[IND_PatientName]      + token +
319                            tokens[IND_StudyInstanceUID] + token + 
320                            tokens[IND_SerieInstanceUID] + token;
321          
322       if (verbose) 
323          std::cout << "[" << userFileIdentifier  << "] : " << *it << std::endl;
324                
325       // storing in a map ensures automatic sorting !      
326       sf[userFileIdentifier] = f;
327    }
328    
329    if (verbose)
330       std::cout << "  " << std::endl;
331       
332    std::string fullFilename, lastFilename;
333    std::string previousPatientName, currentPatientName;
334    std::string previousStudyInstanceUID, currentStudyInstanceUID;   
335    std::string previousSerieInstanceUID, currentSerieInstanceUID;
336    
337       
338    std::string writeDir, currentWriteDir;
339    std::string currentPatientWriteDir;
340    std::string currentStudyWriteDir;    
341    std::string currentSerieWriteDir; 
342
343    std::string fullWriteFilename;
344            
345    writeDir = GDCM_NAME_SPACE::Util::NormalizePath(dirNameout);     
346    SortedFiles::iterator it2;
347  
348    previousPatientName            = "";
349    previousStudyInstanceUID       = "";    
350    previousSerieInstanceUID       = "";   
351        
352    GDCM_NAME_SPACE::File *currentFile;
353      
354    for (it2 = sf.begin() ; it2 != sf.end(); ++it2)
355    {  
356       currentFile = it2->second;
357        
358       fullFilename =  currentFile->GetFileName();
359       lastFilename =  GDCM_NAME_SPACE::Util::GetName( fullFilename );
360       if (verbose) 
361       std::cout <<" ------------------------------------------------------------------------------" 
362                 << std::endl << " Deal with [" << it2->first << "] : [" <<fullFilename << "]" 
363                 << std::endl;
364      
365       tokens.clear();
366       GDCM_NAME_SPACE::Util::Tokenize (it2->first, tokens, token);
367       
368       currentPatientName            = tokens[IND_PatientName];
369       currentStudyInstanceUID       = tokens[IND_StudyInstanceUID];      
370       currentSerieInstanceUID       = tokens[IND_SerieInstanceUID];
371      
372       if (previousPatientName != currentPatientName)
373       {  
374          previousPatientName = currentPatientName;
375          if (verbose)   
376             std::cout << "==== new Patient  [" << currentPatientName  << "]" << std::endl;
377     
378          previousPatientName            = currentPatientName;
379          previousStudyInstanceUID       = ""; 
380          previousSerieInstanceUID       = "";
381   
382          currentPatientWriteDir = writeDir + currentPatientName;
383
384          systemCommand   = "mkdir " + currentPatientWriteDir;
385          if (verbose)
386             std::cout << systemCommand << std::endl;
387    
388          system ( systemCommand.c_str() );
389       }
390       
391       if (previousStudyInstanceUID != currentStudyInstanceUID)
392       {        
393          if (verbose)   
394             std::cout << "==== === new Study [" << currentStudyInstanceUID << "]"
395                       << std::endl;      
396
397          currentStudyWriteDir  = currentPatientWriteDir + GDCM_NAME_SPACE::GDCM_FILESEPARATOR
398                              + currentStudyInstanceUID;
399          systemCommand   = "mkdir " + currentStudyWriteDir;  
400          system (systemCommand.c_str());
401
402          previousStudyInstanceUID       = currentStudyInstanceUID;
403       }  
404       
405       if (previousSerieInstanceUID != currentSerieInstanceUID)
406       {        
407          if (verbose)   
408             std::cout << "=== ==== === new Serie [" << currentSerieInstanceUID << "]"
409                       << std::endl;      
410
411          currentSerieWriteDir  = currentStudyWriteDir + GDCM_NAME_SPACE::GDCM_FILESEPARATOR
412                              + currentSerieInstanceUID;
413          systemCommand   = "mkdir " + currentSerieWriteDir;  
414          system (systemCommand.c_str());
415
416          previousSerieInstanceUID       = currentSerieInstanceUID;
417       }            
418    
419       if ( GDCM_NAME_SPACE::Debug::GetDebugFlag())
420          std::cout << "--- --- --- --- --- " << it2->first << "  " 
421                    << (it2->second)->GetFileName() << " " 
422                    << GDCM_NAME_SPACE::Util::GetName( fullFilename ) << std::endl;
423  
424       // If you want to create file names of your own, here is the place!
425       // Just replace 'lastFilename' by anything that's better for you.               
426       fullWriteFilename = currentSerieWriteDir + GDCM_NAME_SPACE::GDCM_FILESEPARATOR 
427                                          + lastFilename; 
428
429       systemCommand   = "cp " + fullFilename + " " + fullWriteFilename;
430       system ( systemCommand.c_str());          
431
432    }
433    return 0;
434  }
435