]> Creatis software - gdcm.git/blob - Example/PhilipsToBrucker2.cxx
Fix some comments
[gdcm.git] / Example / PhilipsToBrucker2.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: PhilipsToBrucker2.cxx,v $
5   Language:  C++
6   Date:      $Date: 2006/03/17 14:36:37 $
7   Version:   $Revision: 1.20 $
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)PhilipsToBrucker2. Image Position (Patient)    ",
54    "           (0x0018, 0x1060) Trigger Time                                  ",
55    "           (0x0018, 0x1312) In-pl)ane 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    "                  [taggrid] [skel]                                        ",
77    "                  [input = {ACR|DCM}]                                     ", 
78    "                  [extent=image suffix (.IMA, .NEMA, .DCM, ...)]          ",
79    "                  [listonly] [split]                                      ",
80    "                  [noshadowseq][noshadow][noseq] [verbose] [debug]        ",
81    "                                                                          ",
82    " dirout : will be created if doesn't exist                                ",
83    " keep : if user wants to process a limited number of series               ",
84    "            he gives the list of 'SeriesNumber' (tag 0020|0011)           ",
85    " drop : if user wants to ignore a limited number of series                ",
86    "            he gives the list of 'SeriesNumber' (tag 0020|0011)           ",   
87    "        SeriesNumber are short enough to be human readable                ",
88    "        e.g : 1030,1035,1043                                              ", 
89    " taggrid : user knows all the images are 'grid' -ie : not 'col', not 'raw'",
90    " extent : DO NOT forget the leading '.' !                                 ",
91    " skel: name skeleton eg : patName_1.nema -> skel=patName_                 ",
92    " split: creates a tree-like structure of directories as :                 ",
93    "        - Patient                                                         ",
94    "        -- Serie                                                          ",
95    "        --- Position                                                      ",
96    "            Images are (sorted by Trigger Time /                          ",
97    "                     Encoding Direction (Row, Column)                     ",
98    " noshadowseq: user doesn't want to load Private Sequences                 ",
99    " noshadow : user doesn't want to load Private groups (odd number)         ",
100    " noseq    : user doesn't want to load Sequences                           ",
101    " verbose  : user wants to run the program in 'verbose mode'               ",
102    " debug    : *developer*  wants to run the program in 'debug mode'         ",
103    FINISH_USAGE
104
105    // ----- Initialize Arguments Manager ------
106       
107    gdcm::ArgMgr *am = new gdcm::ArgMgr(argc, argv);
108   
109    if (argc == 1 || am->ArgMgrDefined("usage")) 
110    {
111       am->ArgMgrUsage(usage); // Display 'usage'
112       delete am;
113       return 0;
114    }
115
116    const char *dirNamein;   
117    dirNamein  = am->ArgMgrGetString("dirin","."); 
118
119    const char *dirNameout;   
120    dirNameout  = am->ArgMgrGetString("dirout",".");  
121    
122    int loadMode = gdcm::LD_ALL;
123    if ( am->ArgMgrDefined("noshadowseq") )
124       loadMode |= gdcm::LD_NOSHADOWSEQ;
125    else 
126    {
127    if ( am->ArgMgrDefined("noshadow") )
128          loadMode |= gdcm::LD_NOSHADOW;
129       if ( am->ArgMgrDefined("noseq") )
130          loadMode |= gdcm::LD_NOSEQ;
131    }
132
133    if (am->ArgMgrDefined("debug"))
134       gdcm::Debug::DebugOn();
135       
136    bool verbose  = am->ArgMgrDefined("verbose");
137    bool split    = am->ArgMgrDefined("split");
138    bool listonly = am->ArgMgrDefined("listonly");
139          
140    int nbSeriesToKeep;
141    int *seriesToKeep = am->ArgMgrGetListOfInt("keep", &nbSeriesToKeep);
142    int nbSeriesToDrop;
143    int *seriesToDrop = am->ArgMgrGetListOfInt("drop", &nbSeriesToDrop);
144  
145    if ( nbSeriesToKeep!=0 && nbSeriesToDrop!=0)
146    {
147       std::cout << "KEEP and DROP are mutually exclusive !" << std::endl;
148       delete am;
149       return 0;         
150    }
151    
152    int taggrid = am->ArgMgrDefined("taggrid");
153    
154    int hasSkel = am->ArgMgrDefined("skel");
155    const char *skel;
156    if (hasSkel)
157       skel = am->ArgMgrGetString("skel");   
158       
159    const char *extent  = am->ArgMgrGetString("extent",".DCM");
160    
161    // if unused Param we give up
162    if ( am->ArgMgrPrintUnusedLabels() )
163    { 
164       am->ArgMgrUsage(usage);
165       delete am;
166       return 0;
167    }
168    delete am;  // we don't need Argument Manager any longer
169
170    // ----- Begin Processing -----
171    
172    if ( ! gdcm::DirList::IsDirectory(dirNamein) )
173    {
174       std::cout << "KO : [" << dirNamein << "] is not a Directory." << std::endl;
175       exit(0);
176    }
177    else
178    {
179       std::cout << "OK : [" << dirNamein << "] is a Directory." << std::endl;
180    }
181
182    std::string systemCommand;
183    
184    std::cout << "Check for output directory :[" << dirNameout << "]."
185              <<std::endl;
186    if ( ! gdcm::DirList::IsDirectory(dirNameout) )    // dirout not found
187    {
188       std::string strDirNameout(dirNameout);          // to please gcc 4
189       systemCommand = "mkdir " +strDirNameout;        // create it!
190       if (verbose)
191          std::cout << systemCommand << std::endl;
192       system (systemCommand.c_str());
193       if ( ! gdcm::DirList::IsDirectory(dirNameout) ) // be sure it worked
194       {
195           std::cout << "KO : not a dir : [" << dirNameout << "] (creation failure ?)" << std::endl;
196           exit(0);
197       }
198       else
199       {
200         std::cout << "Directory [" << dirNameout << "] created." << std::endl;
201       }
202    }
203    else
204    {
205        std::cout << "Output Directory [" << dirNameout << "] already exists; Used as is." << std::endl;
206    }
207     
208    std::string strDirNamein(dirNamein);
209    gdcm::DirList dirList(strDirNamein, true); // get recursively the list of files
210    
211    if (listonly)
212    {
213       std::cout << "------------List of found files ------------" << std::endl;
214       dirList.Print();
215    }
216    
217    gdcm::DirListType fileNames;
218    fileNames = dirList.GetFilenames();
219    gdcm::SerieHelper *s;              // Needed only to may use SerieHelper::AddSeriesDetail()
220    s = gdcm::SerieHelper::New();
221
222    std::string token = "%%%"; // Hope it's enough!
223 /*       
224    std::cout << "---------------Print Serie--------------" << std::endl; 
225    s->SetDirectory(dirNamein, true); // true : recursive exploration 
226    s->SetUseSeriesDetails(true);  
227    s->AddSeriesDetail(0x0018, 0x1312);   
228    s->Print();
229 */
230   
231    gdcm::File *f;
232    gdcm::FileHelper *fh;
233    std::vector<std::string> tokens;
234    std::vector<std::string> tokensForFileName;
235 /*   
236    std::cout << "---------------Print Unique Series identifiers---------"  
237              << std::endl;     
238    std::string uniqueSeriesIdentifier;
239  
240    for (gdcm::DirListType::iterator it) = fileNames.begin();  
241                                     it != fileNames.end();
242                                   ++it)
243    {
244       std::cout << "File Name : " << *it << std::endl;
245       f = gdcm::File::New();
246       f->SetLoadMode(gdcm::LD_ALL);
247       f->SetFileName( *it );
248       f->Load();
249         
250       uniqueSeriesIdentifier=s->CreateUniqueSeriesIdentifier(f);
251       std::cout << "                           [" <<
252                uniqueSeriesIdentifier  << "]" << std::endl;       
253       f->Delete();
254    }
255 */
256    
257    if (verbose)
258       std::cout << "------------------Print Break levels-----------------" << std::endl;
259
260    std::string userFileIdentifier; 
261    SortedFiles sf;
262
263    s->AddSeriesDetail(0x0010, 0x0010, false); // Patient's Name
264    s->AddSeriesDetail(0x0020, 0x000e, false); // Series Instance UID
265    s->AddSeriesDetail(0x0020, 0x0032, false); // Image Position (Patient)     
266    s->AddSeriesDetail(0x0018, 0x1060, true);  // Trigger Time (true: convert to keep numerical order)
267    s->AddSeriesDetail(0x0018, 0x1312, false); // In-plane Phase Encoding Direction 
268       
269    for (gdcm::DirListType::iterator it = fileNames.begin();  
270                                     it != fileNames.end();
271                                   ++it)
272    {
273       f = gdcm::File::New();
274       f->SetLoadMode(loadMode);
275       f->SetFileName( *it );
276       f->Load();
277       
278       // keep only requested Series
279       std::string strSeriesNumber;
280       int seriesNumber;
281       int j;
282       
283       bool keep = false;
284       if (nbSeriesToKeep != 0)
285       {     
286          strSeriesNumber = f->GetEntryString(0x0020, 0x0011 );
287          seriesNumber = atoi( strSeriesNumber.c_str() );
288          for (j=0;j<nbSeriesToKeep; j++)
289          {
290             if(seriesNumber == seriesToKeep[j])
291             {
292                keep = true;
293                break;
294             }
295          }
296          if ( !keep)
297          {
298             f->Delete();
299             continue;
300          } 
301       }
302       // drop all unrequested Series
303       bool drop = false;
304       if (nbSeriesToDrop != 0)
305       {     
306          strSeriesNumber = f->GetEntryString(0x0020, 0x0011 );
307          seriesNumber = atoi( strSeriesNumber.c_str() );
308          for (j=0;j<nbSeriesToDrop; j++)
309          {
310             if(seriesNumber == seriesToDrop[j])
311             { 
312                drop = true;
313                break;
314             }
315         }
316         if (drop)
317         {
318            f->Delete();
319            continue;
320         }
321       }      
322
323       userFileIdentifier=s->CreateUserDefinedFileIdentifier(f); 
324       tokens.clear();
325       gdcm::Util::Tokenize (userFileIdentifier, tokens, token); 
326    
327       int imageNum; // Within FileName
328       char newName[1024];
329       
330       if ( tokens[3] == "gdcmUnfound")  // sometimes Trigger Time is not found. CreateUserDefinedFileIdentifier is not aware of the pb.
331       {
332          ///this is a trick to build up a lexicographical compliant name :
333          ///     eg : fich001.ima vs fich100.ima as opposed to fich1.ima vs fich100.ima
334          std::string name = gdcm::Util::GetName( *it );
335          if (hasSkel)
336          {
337             gdcm::Util::Tokenize (name, tokensForFileName, skel);
338             imageNum = atoi ( tokensForFileName[0].c_str() );
339             // probabely we could write something much more complicated using C++ !
340             sprintf (newName, "%s%06d%s", skel, imageNum, extent);
341             tokens[3] = newName;
342             tokensForFileName.clear();    
343          }
344          else
345             tokens[3] = name;
346  
347  
348          userFileIdentifier = tokens[0] + token + tokens[1] + token + tokens[2] + token 
349                     + tokens[3] + token + tokens[4] + token;
350       }   
351       if (verbose) 
352          std::cout << "[" << userFileIdentifier  << "]" << std::endl;
353                
354       // storing in a map ensures automatic sorting !      
355       sf[userFileIdentifier] = f;
356    }
357       
358    std::string fullFilename, lastFilename;
359    std::string previousPatientName, currentPatientName;
360    std::string previousSerieInstanceUID, currentSerieInstanceUID;
361    std::string previousImagePosition, currentImagePosition;
362    std::string previousPhaseEncodingDirection, currentPhaseEncodingDirection;
363    std::string previousTriggerTime, currentTriggerTime;
364       
365    std::string writeDir, currentWriteDir;
366    std::string currentPatientWriteDir, currentSerieWriteDir, 
367                currentPositionWriteDir, currentPhaseEncodingDirectionWriteDir;
368
369    std::string fullWriteFilename;
370    std::string strExtent(extent); 
371            
372    writeDir = gdcm::Util::NormalizePath(dirNameout);     
373    SortedFiles::iterator it2;
374  
375    previousPatientName            = "";
376    previousSerieInstanceUID       = "";   
377    previousImagePosition          = "";
378    previousPhaseEncodingDirection = "";
379    previousTriggerTime            = "";
380    
381    int sliceIndex = 0; // Is incremented *at the beginning* of processing
382    int frameIndex = 1;
383    int flag       = 0;
384        
385    gdcm::File *currentFile;
386      
387    for (it2 = sf.begin() ; it2 != sf.end(); ++it2)
388    {  
389       currentFile = it2->second;
390        
391       fullFilename =  currentFile->GetFileName();
392       lastFilename =  gdcm::Util::GetName( fullFilename ); 
393       std::cout << "Rewrite [" <<lastFilename << "]" << std::endl;
394      
395       tokens.clear();
396       gdcm::Util::Tokenize (it2->first, tokens, token);
397       
398       currentPatientName            = tokens[0];
399       currentSerieInstanceUID       = tokens[1];
400       currentImagePosition          = tokens[2];
401       currentTriggerTime            = tokens[3];
402       currentPhaseEncodingDirection = tokens[4]; 
403
404       if ( currentImagePosition[0] == '-')
405           currentImagePosition[0] = 'M';
406       if ( currentImagePosition[0] == '+')
407           currentImagePosition[0] = 'P'; 
408       
409       if (previousPatientName != currentPatientName)
410       {
411          previousPatientName = currentPatientName;
412          if (verbose)   
413             std::cout << "==== new Patient  [" << currentPatientName  << "]" << std::endl;
414     
415          previousPatientName            = currentPatientName;
416          previousSerieInstanceUID       = ""; //currentSerieInstanceUID;
417          previousImagePosition          = ""; //currentImagePosition;
418          previousTriggerTime            = "";
419          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
420   
421          currentPatientWriteDir = writeDir + currentPatientName;
422          //if ( ! gdcm::DirList::IsDirectory(currentPatientWriteDir) )
423            {
424               systemCommand   = "mkdir " + currentPatientWriteDir;
425               if (verbose)
426                  std::cout << systemCommand << std::endl;
427               system ( systemCommand.c_str() );
428          }
429       }
430
431       if (previousSerieInstanceUID != currentSerieInstanceUID)
432       {        
433          if (verbose)   
434             std::cout << "==== === new Serie [" << currentSerieInstanceUID << "]"
435                       << std::endl;
436          if (split)
437          {
438              currentSerieWriteDir  = currentPatientWriteDir + gdcm::GDCM_FILESEPARATOR
439                              + currentSerieInstanceUID;
440              systemCommand   = "mkdir " + currentSerieWriteDir;  
441              system (systemCommand.c_str());
442          }
443          previousSerieInstanceUID       = currentSerieInstanceUID;
444          previousImagePosition          = ""; //currentImagePosition;
445          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
446       }
447
448       if (previousImagePosition != currentImagePosition)
449       {
450          frameIndex = 1;
451          flag = 0;        
452          if (verbose)   
453             std::cout << "=== === === new Position [" << currentImagePosition  << "]"
454                       << std::endl;
455          if (split)
456          {
457              currentPositionWriteDir  = currentSerieWriteDir + gdcm::GDCM_FILESEPARATOR
458                              + currentImagePosition;
459              systemCommand   = "mkdir " + currentPositionWriteDir;     
460              system (systemCommand.c_str()); 
461          }
462          previousImagePosition          = currentImagePosition;
463          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
464          if (split)
465             sliceIndex = 1; // only *one* slice in a given directory
466          else
467             sliceIndex += 1;
468       }      
469
470 // We don't split on Row/Column!
471 /*
472       if (previousPhaseEncodingDirection != currentPhaseEncodingDirection)
473       {        
474          if (verbose)   
475             std::cout << "==== === === === new PhaseEncodingDirection [" 
476                       << currentPhaseEncodingDirection  << "]" << std::endl;
477       
478          if (split)
479          {
480              currentPhaseEncodingDirectionWriteDir  = currentPositionWriteDir 
481                              + gdcm::GDCM_FILESEPARATOR
482                              + currentPhaseEncodingDirection;
483              systemCommand   = "mkdir " + currentPhaseEncodingDirectionWriteDir;     
484              system (systemCommand.c_str());     
485          }      
486     
487          previousPhaseEncodingDirection = currentPhaseEncodingDirection;
488       } 
489 */    
490       
491       if (verbose)
492          std::cout << "--- --- --- --- --- " << (it2->second)->GetFileName() 
493                    << std::endl;
494    
495       if ( gdcm::Debug::GetDebugFlag())
496          std::cout << "--- --- --- --- --- " << it2->first << "  " 
497                    << (it2->second)->GetFileName() << " " 
498                    << gdcm::Util::GetName( fullFilename ) << std::endl;           
499       
500       // Transform the image to be 'Brucker-Like'
501       // ----------------------------------------   
502     
503       // Deal with 0x0019, 0x1000 : 'FOV'
504       int nX = currentFile->GetXSize();
505       int nY = currentFile->GetYSize();
506       float pxSzX = currentFile->GetXSpacing();
507       float pxSzY = currentFile->GetYSpacing();
508       char fov[64];
509       sprintf(fov, "%f\\%f",nX*pxSzX, nY*pxSzY);
510       if (currentFile->IsVRCoherent(0x0019) == 1 )
511          currentFile->InsertEntryString(fov, 0x0019, 0x1000, "  ");
512       else     
513          currentFile->InsertEntryString(fov, 0x0019, 0x1000, "DS");
514
515      
516       // Deal with 0x0020, 0x0012 : 'SESSION INDEX'  (Acquisition Number)
517       std::string chSessionIndex;
518       // CLEANME
519       if (taggrid) 
520          chSessionIndex = "1";
521       else
522       {
523          if (currentPhaseEncodingDirection == "COL" || currentPhaseEncodingDirection == "COL " || currentPhaseEncodingDirection == " COL")
524             chSessionIndex = "1";
525          else if (currentPhaseEncodingDirection == "ROW" || currentPhaseEncodingDirection == "ROW "|| currentPhaseEncodingDirection == " ROW")
526             chSessionIndex = "2"; 
527          else
528          {
529             std::cout << "====================== PhaseEncodingDirection "
530                       << " neither COL nor ROW (?!?) : [ "
531                       << currentPhaseEncodingDirection << "]" << std::endl;
532             chSessionIndex = "1";
533          }
534       }
535        if (currentFile->IsVRCoherent(0x0020) == 1 )     
536          currentFile->InsertEntryString(chSessionIndex, 0x0020, 0x0012, "  ");
537        else
538          currentFile->InsertEntryString(chSessionIndex, 0x0020, 0x0012, "IS");
539  
540       // Deal with  0x0021, 0x1020 : 'SLICE INDEX'
541       char chSliceIndex[5];
542       sprintf(chSliceIndex, "%04d", sliceIndex);
543       std::string strChSliceIndex(chSliceIndex);
544        
545       // Deal with  0x0021, 0x1040 : 'FRAME INDEX' 
546       char chFrameIndex[5];
547       sprintf(chFrameIndex, "%04d", frameIndex);
548
549       std::string stringVR;       
550       if (currentFile->IsVRCoherent(0x0021) == 1 )
551          stringVR = "  ";
552       else
553         stringVR = "IS";
554   
555       currentFile->InsertEntryString(strChSliceIndex, 0x0021, 0x1020, stringVR);
556       currentFile->InsertEntryString(chFrameIndex, 0x0021, 0x1040, stringVR); 
557       
558       if (taggrid)
559          frameIndex++;
560       else     
561       {     
562          if (flag == 0)
563          {       
564             flag = 1;
565          }
566          else
567          {
568             frameIndex++;
569             flag = 0;
570          }
571       } 
572                  
573       if (split)
574       
575          //fullWriteFilename = currentPhaseEncodingDirectionWriteDir + gdcm::GDCM_FILESEPARATOR 
576          //                                + lastFilename + strExtent;      
577          fullWriteFilename = currentPositionWriteDir + gdcm::GDCM_FILESEPARATOR 
578                                          + lastFilename + strExtent; 
579       else
580          fullWriteFilename = currentPatientWriteDir + gdcm::GDCM_FILESEPARATOR 
581                                          + lastFilename + strExtent; 
582       
583       /*           
584       systemCommand  = "cp " + fullFilename + " " + fullWriteFilename;
585       std::cout << systemCommand << std::endl;
586       system (  systemCommand.c_str() );
587       */
588             
589       // Load the pixels in RAM.    
590       
591       fh = gdcm::FileHelper::New(currentFile);     
592       fh->GetImageDataRaw(); // Don't convert (Gray Pixels + LUT) into (RGB pixels) ?!?
593       fh->SetWriteTypeToDcmExplVR();
594       // We didn't modify pixels -> keep unchanged the following :
595       // 'Media Storage SOP Class UID' (0x0002,0x0002)
596       // 'SOP Class UID'               (0x0008,0x0016)
597       // 'Image Type'                  (0x0008,0x0008)
598       // 'Conversion Type'             (0x0008,0x0064)
599       fh->SetContentType(gdcm::UNMODIFIED_PIXELS_IMAGE);
600       if (!fh->Write(fullWriteFilename))
601       {
602          std::cout << "Fail to write :[" << fullWriteFilename << "]"
603                    << std::endl;
604       } 
605       fh->Delete();                
606    }
607  }