]> Creatis software - gdcm.git/blob - Example/PhilipsToBrucker2.cxx
PhilipsToBrucker2 allows splitting the files into a directory tree-like
[gdcm.git] / Example / PhilipsToBrucker2.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: PhilipsToBrucker2.cxx,v $
5   Language:  C++
6   Date:      $Date: 2006/01/17 15:58:30 $
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   *          - fills a single level Directory with *all* the files,
38   *            converted into a Brucker-like Dicom, Intags compliant
39   *          
40   */  
41
42 typedef std::map<std::string, gdcm::File*> SortedFiles;
43
44 int main(int argc, char *argv[]) 
45 {
46    START_USAGE(usage)
47    " \n PhilipsToBrucker :\n                                                  ",
48    " - explores recursively the given directory,                              ",
49    " - keeps the requested series/ drops the unrequested series               ",
50    " - orders the gdcm-readable found Files according to their                ",
51    "           (0x0010, 0x0010) Patient's Name                                ",
52    "           (0x0020, 0x000e) Series Instance UID                           ",
53    "           (0x0020, 0x0032) Image Position (Patient)                      ",
54    "           (0x0018, 0x1060) Trigger Time                                  ",
55    "           (0x0018, 0x1312) In-plane Phase Encoding Direction             ",
56    " - fills a single level (*) Directory with *all* the files,               ",
57    "           converted into a Brucker-like Dicom, InTags compliant          ",
58    "   (*) actually : creates as many directories as Patients                 ",
59    "                  -that shouldn't appear, but being carefull is better ! -",
60    " or                                                                       ",
61    " - fills a tree-like structure of directories as :                        ",
62    "        - Patient                                                         ",
63    "        -- Serie                                                          ",
64    "        --- Position                                                      ",
65    "            Images are (sorted by Trigger Time /                          ",
66    "                     Encoding Direction (Row, Column)                     ",
67    "      use :                                                               ",
68    "           0x0021, 0x1020 : 'SLICE INDEX'                                 ",
69    "           0x0021, 0x1040 : 'FRAME INDEX'                                 ",
70    "           0x0020, 0x0012 : 'SESSION INDEX'  (Acquisition Number)         ",
71    " usage:                                                                   ",
72    " PhilipsToBrucker dirin=rootDirectoryName                                 ",
73    "                  dirout=outputDirectoryName                              ",
74    "                  {  [keep= list of seriesNumber to process]              ",
75    "                   | [drop= list of seriesNumber to ignore] }             ",
76    "                  [extent=image suffix (.IMA, .NEMA, .DCM, ...)]          ",
77    "                  [listonly] [split]                                      ",
78    "                  [noshadowseq][noshadow][noseq] [verbose] [debug]        ",
79    "                                                                          ",
80    " dirout : will be created if doesn't exist                                ",
81    " keep : if user wants to process a limited number of series               ",
82    "            he gives the list of 'SeriesNumber' (tag 0020|0011)           ",
83    " drop : if user wants to ignore a limited number of series                ",
84    "            he gives the list of 'SeriesNumber' (tag 0020|0011)           ",   
85    "        SeriesNumber are short enough to be human readable                ",
86    "        e.g : 1030,1035,1043                                              ",
87    " extent : DO NOT forget the leading '.' !                                 ",
88    " split: creates a tree-like structure of directories as :                 ",
89    "        - Patient                                                         ",
90    "        -- Serie                                                          ",
91    "        --- Position                                                      ",
92    "        ---- Images (sorted by Trigger Time)                              ",
93    "        ----- Phase Encoding Direction (Row, Column)                      ",
94    " noshadowseq: user doesn't want to load Private Sequences                 ",
95    " noshadow : user doesn't want to load Private groups (odd number)         ",
96    " noseq    : user doesn't want to load Sequences                           ",
97    " verbose  : user wants to run the program in 'verbose mode'               ",
98    " debug    : *developer*  wants to run the program in 'debug mode'         ",
99    FINISH_USAGE
100
101    // ----- Initialize Arguments Manager ------
102       
103    gdcm::ArgMgr *am = new gdcm::ArgMgr(argc, argv);
104   
105    if (argc == 1 || am->ArgMgrDefined("usage")) 
106    {
107       am->ArgMgrUsage(usage); // Display 'usage'
108       delete am;
109       return 0;
110    }
111
112    char *dirNamein;   
113    dirNamein  = am->ArgMgrGetString("dirin",(char *)"."); 
114
115    char *dirNameout;   
116    dirNameout  = am->ArgMgrGetString("dirout",(char *)".");  
117    
118    int loadMode = gdcm::LD_ALL;
119    if ( am->ArgMgrDefined("noshadowseq") )
120       loadMode |= gdcm::LD_NOSHADOWSEQ;
121    else 
122    {
123    if ( am->ArgMgrDefined("noshadow") )
124          loadMode |= gdcm::LD_NOSHADOW;
125       if ( am->ArgMgrDefined("noseq") )
126          loadMode |= gdcm::LD_NOSEQ;
127    }
128
129    if (am->ArgMgrDefined("debug"))
130       gdcm::Debug::DebugOn();
131       
132    bool verbose  = am->ArgMgrDefined("verbose");
133    bool split    = am->ArgMgrDefined("split");
134    bool listonly = am->ArgMgrDefined("listonly");
135          
136    int nbSeriesToKeep;
137    int *seriesToKeep = am->ArgMgrGetListOfInt("keep", &nbSeriesToKeep);
138    int nbSeriesToDrop;
139    int *seriesToDrop = am->ArgMgrGetListOfInt("drop", &nbSeriesToDrop);
140  
141    if ( nbSeriesToKeep!=0 && nbSeriesToDrop!=0)
142    {
143       std::cout << "KEEP and DROP are mutually exclusive !" << std::endl;
144       delete am;
145       return 0;         
146    }
147    
148    char *extent  = am->ArgMgrGetString("extent",".DCM");
149         
150    // if unused Param we give up
151    if ( am->ArgMgrPrintUnusedLabels() )
152    { 
153       am->ArgMgrUsage(usage);
154       delete am;
155       return 0;
156    }
157    delete am;  // we don't need Argument Manager any longer
158
159    // ----- Begin Processing -----
160    
161    if ( ! gdcm::DirList::IsDirectory(dirNamein) )
162    {
163       std::cout << "KO : [" << dirNamein << "] is not a Directory." << std::endl;
164       exit(0);
165    }
166    else
167    {
168       std::cout << "OK : [" << dirNamein << "] is a Directory." << std::endl;
169    }
170
171    std::string systemCommand;
172    
173    std::cout << "Check for output directory :[" << dirNameout << "]."
174              <<std::endl;
175    if ( ! gdcm::DirList::IsDirectory(dirNameout) )    // dirout not found
176    {
177       std::string strDirNameout(dirNameout);          // to please gcc 4
178       systemCommand = "mkdir " +strDirNameout;        // create it!
179       if (verbose)
180          std::cout << systemCommand << std::endl;
181       system (systemCommand.c_str());
182       if ( ! gdcm::DirList::IsDirectory(dirNameout) ) // be sure it worked
183       {
184           std::cout << "KO : not a dir : [" << dirNameout << "] (creation failure ?)" << std::endl;
185           exit(0);
186       }
187       else
188       {
189         std::cout << "Directory [" << dirNameout << "] created." << std::endl;
190       }
191    }
192    else
193    {
194        std::cout << "Output Directory [" << dirNameout << "] already exists; Used as is." << std::endl;
195    }
196     
197    std::string strDirNamein(dirNamein);
198    gdcm::DirList dirList(strDirNamein, true); // get recursively the list of files
199    
200    if (listonly)
201    {
202       std::cout << "------------List of found files ------------" << std::endl;
203       dirList.Print();
204    }
205
206    gdcm::DirListType fileNames;
207    fileNames = dirList.GetFilenames();
208    gdcm::SerieHelper *s;              // Needed only to may use SerieHelper::AddSeriesDetail()
209    s = gdcm::SerieHelper::New();
210
211 /*       
212    std::cout << "---------------Print Serie--------------" << std::endl; 
213    s->SetDirectory(dirNamein, true); // true : recursive exploration 
214    s->SetUseSeriesDetails(true);  
215    s->AddSeriesDetail(0x0018, 0x1312);   
216    s->Print();
217 */
218   
219    gdcm::File *f;
220    gdcm::FileHelper *fh;
221 /*   
222    std::cout << "---------------Print Unique Series identifiers---------"  
223              << std::endl;     
224    std::string uniqueSeriesIdentifier;
225  
226    for (gdcm::DirListType::iterator it = fileNames.begin();  
227                                     it != fileNames.end();
228                                   ++it)
229    {
230       std::cout << "File Name : " << *it << std::endl;
231       f = gdcm::File::New();
232       f->SetLoadMode(gdcm::LD_ALL);
233       f->SetFileName( *it );
234       f->Load();
235         
236       uniqueSeriesIdentifier=s->CreateUniqueSeriesIdentifier(f);
237       std::cout << "                           [" <<
238                uniqueSeriesIdentifier  << "]" << std::endl;       
239       f->Delete();
240    }
241 */
242    
243    if (verbose)
244       std::cout << "------------------Print Break levels-----------------" << std::endl;
245
246    std::string userFileIdentifier; 
247    SortedFiles sf;
248
249    s->AddSeriesDetail(0x0010, 0x0010, false); // Patient's Name
250    s->AddSeriesDetail(0x0020, 0x000e, false); // Series Instance UID
251    s->AddSeriesDetail(0x0020, 0x0032, false); // Image Position (Patient)     
252    s->AddSeriesDetail(0x0018, 0x1060, true);  // Trigger Time (true: convert to keep numerical order)
253    s->AddSeriesDetail(0x0018, 0x1312, false); // In-plane Phase Encoding Direction 
254       
255    for (gdcm::DirListType::iterator it = fileNames.begin();  
256                                     it != fileNames.end();
257                                   ++it)
258    {
259       f = gdcm::File::New();
260       f->SetLoadMode(loadMode);
261       f->SetFileName( *it );
262       f->Load();
263       
264       // keep only requested Series
265       std::string strSeriesNumber;
266       int seriesNumber;
267       int j;
268       
269       bool keep = false;
270       if (nbSeriesToKeep != 0)
271       {     
272          strSeriesNumber = f->GetEntryString(0x0020, 0x0011 );
273          seriesNumber = atoi( strSeriesNumber.c_str() );
274          for (j=0;j<nbSeriesToKeep; j++)
275          {
276             if(seriesNumber == seriesToKeep[j])
277             {
278                keep = true;
279                break;
280             }
281          }
282          if ( !keep)
283          {
284             f->Delete();
285             continue;
286          } 
287       }
288       // drop all unrequested Series
289       bool drop = false;
290       if (nbSeriesToDrop != 0)
291       {     
292          strSeriesNumber = f->GetEntryString(0x0020, 0x0011 );
293          seriesNumber = atoi( strSeriesNumber.c_str() );
294          for (j=0;j<nbSeriesToDrop; j++)
295          {
296             if(seriesNumber == seriesToDrop[j])
297             { 
298                drop = true;
299                break;
300             }
301         }
302         if (drop)
303         {
304            f->Delete();
305            continue;
306         }
307       }      
308
309       userFileIdentifier=s->CreateUserDefinedFileIdentifier(f); 
310      // userFileIdentifier += "_";
311       //userFileIdentifier += *it;       
312       std::cout << "                           [" <<
313               userFileIdentifier  << "]" << std::endl;
314       
315       // storing in a map ensures automatic sorting !      
316       sf[userFileIdentifier] = f;
317    }
318       
319    std::vector<std::string> tokens;
320    std::string fullFilename, lastFilename;
321    std::string previousPatientName, currentPatientName;
322    std::string previousSerieInstanceUID, currentSerieInstanceUID;
323    std::string previousImagePosition, currentImagePosition;
324    std::string previousPhaseEncodingDirection, currentPhaseEncodingDirection;
325    std::string previousTriggerTime, currentTriggerTime;
326       
327    std::string writeDir, currentWriteDir;
328    std::string currentPatientWriteDir, currentSerieWriteDir, 
329                currentPositionWriteDir, currentPhaseEncodingDirectionWriteDir;
330
331    std::string fullWriteFilename;
332    std::string strExtent(extent); 
333            
334    writeDir = gdcm::Util::NormalizePath(dirNameout);     
335    SortedFiles::iterator it2;
336  
337    previousPatientName            = "";
338    previousSerieInstanceUID       = "";   
339    previousImagePosition          = "";
340    previousPhaseEncodingDirection = "";
341    previousTriggerTime            = "";
342    
343    int sliceIndex = 1;
344    int frameIndex = 1;
345    int flag       = 0;
346        
347    gdcm::File *currentFile;
348      
349    for (it2 = sf.begin() ; it2 != sf.end(); ++it2)
350    {  
351       currentFile = it2->second;
352        
353       fullFilename =  currentFile->GetFileName();
354       lastFilename =  gdcm::Util::GetName( fullFilename ); 
355       std::cout << "Try to write" <<lastFilename << std::endl;
356      
357       tokens.clear();
358       gdcm::Util::Tokenize (it2->first, tokens, "_");
359       
360       currentPatientName            = tokens[0];
361       currentSerieInstanceUID       = tokens[1];
362       currentImagePosition          = tokens[2];
363       currentTriggerTime            = tokens[3];
364       currentPhaseEncodingDirection = tokens[4];           
365       
366       if ( currentImagePosition[0] == '-')
367           currentImagePosition[0] = 'M';
368       if ( currentImagePosition[0] == '+')
369           currentImagePosition[0] = 'P'; 
370       
371       if (previousPatientName != currentPatientName)
372       {
373          previousPatientName = currentPatientName;
374          if (verbose)   
375             std::cout << "==== new Patient  [" << currentPatientName  << "]" << std::endl;
376     
377          previousPatientName            = currentPatientName;
378          previousSerieInstanceUID       = ""; //currentSerieInstanceUID;
379          previousImagePosition          = ""; //currentImagePosition;
380          previousTriggerTime            = "";
381          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
382   
383          currentPatientWriteDir = writeDir + currentPatientName;
384          //if ( ! gdcm::DirList::IsDirectory(currentPatientWriteDir) )
385            {
386               systemCommand   = "mkdir " + currentPatientWriteDir;
387               if (verbose)
388                  std::cout << systemCommand << std::endl;
389               system ( systemCommand.c_str() );
390          }
391       }
392
393       if (previousSerieInstanceUID != currentSerieInstanceUID)
394       {        
395          if (verbose)   
396             std::cout << "==== === new Serie [" << currentSerieInstanceUID << "]"
397                       << std::endl;
398          if (split)
399          {
400              currentSerieWriteDir  = currentPatientWriteDir + gdcm::GDCM_FILESEPARATOR
401                              + currentSerieInstanceUID;
402              systemCommand   = "mkdir " + currentSerieWriteDir;  
403              system (systemCommand.c_str());
404          }
405          previousSerieInstanceUID       = currentSerieInstanceUID;
406          previousImagePosition          = ""; //currentImagePosition;
407          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
408       }
409
410       if (previousImagePosition != currentImagePosition)
411       {
412       
413          flag = 0;        
414          if (verbose)   
415             std::cout << "=== === === new Position [" << currentImagePosition  << "]"
416                       << std::endl;
417          if (split)
418          {
419              currentPositionWriteDir  = currentSerieWriteDir + gdcm::GDCM_FILESEPARATOR
420                              + currentImagePosition;
421              systemCommand   = "mkdir " + currentPositionWriteDir;     
422              system (systemCommand.c_str()); 
423          }
424          previousImagePosition          = currentImagePosition;
425          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
426          sliceIndex += 1;
427       }      
428
429 // We don't split on Row/Column!
430 /*
431       if (previousPhaseEncodingDirection != currentPhaseEncodingDirection)
432       {        
433          if (verbose)   
434             std::cout << "==== === === === new PhaseEncodingDirection [" 
435                       << currentPhaseEncodingDirection  << "]" << std::endl;
436       
437          if (split)
438          {
439              currentPhaseEncodingDirectionWriteDir  = currentPositionWriteDir 
440                              + gdcm::GDCM_FILESEPARATOR
441                              + currentPhaseEncodingDirection;
442              systemCommand   = "mkdir " + currentPhaseEncodingDirectionWriteDir;     
443              system (systemCommand.c_str());     
444          }      
445     
446          previousPhaseEncodingDirection = currentPhaseEncodingDirection;
447       } 
448 */    
449       
450       if (verbose)
451          std::cout << "--- --- --- --- --- " << (it2->second)->GetFileName() 
452                    << std::endl;
453    
454       if ( gdcm::Debug::GetDebugFlag())
455          std::cout << "--- --- --- --- --- " << it2->first << "  " 
456                    << (it2->second)->GetFileName() << " " 
457                    << gdcm::Util::GetName( fullFilename ) << std::endl;           
458       
459       // Transform the image to be 'Brucker-Like'
460       // ----------------------------------------   
461     
462       // Deal with 0x0019, 0x1000 : 'FOV'
463       int nX = currentFile->GetXSize();
464       int nY = currentFile->GetYSize();
465       float pxSzX = currentFile->GetXSpacing();
466       float pxSzY = currentFile->GetYSpacing();
467       char fov[64];
468       sprintf(fov, "%f\\%f",nX*pxSzX, nY*pxSzY);
469       currentFile->InsertEntryString(fov, 0x0019, 0x1000, "DS");
470      
471       // Deal with 0x0020, 0x0012 : 'SESSION INDEX'  (Acquisition Number)
472       std::string chSessionIndex;
473       if (currentPhaseEncodingDirection == "ROW")
474          chSessionIndex = "1";
475       else
476          chSessionIndex = "2"; // suppose it's "COLUMN" !
477       currentFile->InsertEntryString(chSessionIndex, 0x0020, 0x0012, "IS");
478    
479       // Deal with  0x0021, 0x1020 : 'SLICE INDEX'
480       char chSliceIndex[5];
481       sprintf(chSliceIndex, "%04d", sliceIndex);
482       std::string strChSliceIndex(chSliceIndex);
483       currentFile->InsertEntryString(strChSliceIndex, 0x0021, 0x1020, "IS");
484        
485       // Deal with  0x0021, 0x1040 : 'FRAME INDEX' 
486       char chFrameIndex[5];
487       sprintf(chFrameIndex, "%04d", frameIndex);
488       currentFile->InsertEntryString(chFrameIndex, 0x0021, 0x1040, "IS"); 
489       
490       if (flag == 0)
491       {       
492          flag = 1;
493       }
494       else
495       {
496          frameIndex++;
497          flag = 0;
498       }
499                     
500       if (split)
501       
502          //fullWriteFilename = currentPhaseEncodingDirectionWriteDir + gdcm::GDCM_FILESEPARATOR 
503          //                                + lastFilename + strExtent;      
504          fullWriteFilename = currentPositionWriteDir + gdcm::GDCM_FILESEPARATOR 
505                                          + lastFilename + strExtent; 
506       else
507          fullWriteFilename = currentPatientWriteDir + gdcm::GDCM_FILESEPARATOR 
508                                          + lastFilename + strExtent; 
509       
510       /*           
511       systemCommand  = "cp " + fullFilename + " " + fullWriteFilename;
512       std::cout << systemCommand << std::endl;
513       system (  systemCommand.c_str() );
514       */
515             
516       // Load the pixels in RAM.    
517       
518       fh = gdcm::FileHelper::New(currentFile);     
519       fh->GetImageDataRaw(); // Don't convert (Gray Pixels + LUT) into (RGB pixels) ?!?
520       fh->SetWriteTypeToDcmExplVR();
521       if (!fh->Write(fullWriteFilename))
522       {
523          std::cout << "Fail to write :[" << fullWriteFilename << "]"
524                    << std::endl;
525       }
526       fh->gdcm::FileHelper::Delete();                
527    }
528  }