]> Creatis software - gdcm.git/blob - Example/ToInTag.cxx
Replace stupid name 'PhilipsToBrucker2' by 'ToInTag'
[gdcm.git] / Example / ToInTag.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: ToInTag.cxx,v $
5   Language:  C++
6   Date:      $Date: 2006/05/31 16:23:25 $
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 ToInTag :\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    "                  [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
279       std::string strSeriesNumber;
280       int seriesNumber;
281       int j;
282
283       // keep only requested Series      
284       bool keep = false;
285       if (nbSeriesToKeep != 0)
286       {     
287          strSeriesNumber = f->GetEntryString(0x0020, 0x0011 );
288          seriesNumber = atoi( strSeriesNumber.c_str() );
289          for (j=0;j<nbSeriesToKeep; j++)
290          {
291             if(seriesNumber == seriesToKeep[j])
292             {
293                keep = true;
294                break;
295             }
296          }
297          if ( !keep)
298          {
299             f->Delete();
300             continue;
301          } 
302       }
303       // drop all unrequested Series
304       bool drop = false;
305       if (nbSeriesToDrop != 0)
306       {     
307          strSeriesNumber = f->GetEntryString(0x0020, 0x0011 );
308          seriesNumber = atoi( strSeriesNumber.c_str() );
309          for (j=0;j<nbSeriesToDrop; j++)
310          {
311             if(seriesNumber == seriesToDrop[j])
312             { 
313                drop = true;
314                break;
315             }
316         }
317         if (drop)
318         {
319            f->Delete();
320            continue;
321         }
322       }      
323
324       userFileIdentifier=s->CreateUserDefinedFileIdentifier(f); 
325       tokens.clear();
326       gdcm::Util::Tokenize (userFileIdentifier, tokens, token); 
327    
328       int imageNum; // Within FileName
329       char newName[1024];
330       
331       if ( tokens[3] == "gdcmUnfound")  // sometimes Trigger Time is not found. CreateUserDefinedFileIdentifier is not aware of the pb.
332       {
333          ///this is a trick to build up a lexicographical compliant name :
334          ///     eg : fich001.ima vs fich100.ima as opposed to fich1.ima vs fich100.ima
335          std::string name = gdcm::Util::GetName( *it );
336          if (hasSkel)
337          {
338             gdcm::Util::Tokenize (name, tokensForFileName, skel);
339             imageNum = atoi ( tokensForFileName[0].c_str() );
340             // probabely we could write something much more complicated using C++ !
341             sprintf (newName, "%s%06d%s", skel, imageNum, extent);
342             tokens[3] = newName;
343             tokensForFileName.clear();    
344          }
345          else
346             tokens[3] = name;
347  
348  
349          userFileIdentifier = tokens[0] + token + tokens[1] + token + tokens[2] + token 
350                     + tokens[3] + token + tokens[4] + token;
351       }   
352       if (verbose) 
353          std::cout << "[" << userFileIdentifier  << "]" << std::endl;
354                
355       // storing in a map ensures automatic sorting !      
356       sf[userFileIdentifier] = f;
357    }
358       
359    std::string fullFilename, lastFilename;
360    std::string previousPatientName, currentPatientName;
361    std::string previousSerieInstanceUID, currentSerieInstanceUID;
362    std::string previousImagePosition, currentImagePosition;
363    std::string previousPhaseEncodingDirection, currentPhaseEncodingDirection;
364    std::string previousTriggerTime, currentTriggerTime;
365       
366    std::string writeDir, currentWriteDir;
367    std::string currentPatientWriteDir, currentSerieWriteDir, 
368                currentPositionWriteDir, currentPhaseEncodingDirectionWriteDir;
369
370    std::string fullWriteFilename;
371    std::string strExtent(extent); 
372            
373    writeDir = gdcm::Util::NormalizePath(dirNameout);     
374    SortedFiles::iterator it2;
375  
376    previousPatientName            = "";
377    previousSerieInstanceUID       = "";   
378    previousImagePosition          = "";
379    previousPhaseEncodingDirection = "";
380    previousTriggerTime            = "";
381    
382    int sliceIndex = 0; // Is incremented *at the beginning* of processing
383    int frameIndex = 1;
384    int flag       = 0;
385        
386    gdcm::File *currentFile;
387      
388    for (it2 = sf.begin() ; it2 != sf.end(); ++it2)
389    {  
390       currentFile = it2->second;
391        
392       fullFilename =  currentFile->GetFileName();
393       lastFilename =  gdcm::Util::GetName( fullFilename ); 
394       std::cout << "Rewrite [" <<lastFilename << "]" << std::endl;
395      
396       tokens.clear();
397       gdcm::Util::Tokenize (it2->first, tokens, token);
398       
399       currentPatientName            = tokens[0];
400       currentSerieInstanceUID       = tokens[1];
401       currentImagePosition          = tokens[2];
402       currentTriggerTime            = tokens[3];
403       currentPhaseEncodingDirection = tokens[4]; 
404
405       if ( currentImagePosition[0] == '-')
406           currentImagePosition[0] = 'M';
407       if ( currentImagePosition[0] == '+')
408           currentImagePosition[0] = 'P'; 
409       
410       if (previousPatientName != currentPatientName)
411       {
412          previousPatientName = currentPatientName;
413          if (verbose)   
414             std::cout << "==== new Patient  [" << currentPatientName  << "]" << std::endl;
415     
416          previousPatientName            = currentPatientName;
417          previousSerieInstanceUID       = ""; //currentSerieInstanceUID;
418          previousImagePosition          = ""; //currentImagePosition;
419          previousTriggerTime            = "";
420          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
421   
422          currentPatientWriteDir = writeDir + currentPatientName;
423          //if ( ! gdcm::DirList::IsDirectory(currentPatientWriteDir) )
424            {
425               systemCommand   = "mkdir " + currentPatientWriteDir;
426               if (verbose)
427                  std::cout << systemCommand << std::endl;
428               system ( systemCommand.c_str() );
429          }
430       }
431
432       if (previousSerieInstanceUID != currentSerieInstanceUID)
433       {        
434          if (verbose)   
435             std::cout << "==== === new Serie [" << currentSerieInstanceUID << "]"
436                       << std::endl;
437          if (split)
438          {
439              currentSerieWriteDir  = currentPatientWriteDir + gdcm::GDCM_FILESEPARATOR
440                              + currentSerieInstanceUID;
441              systemCommand   = "mkdir " + currentSerieWriteDir;  
442              system (systemCommand.c_str());
443          }
444          previousSerieInstanceUID       = currentSerieInstanceUID;
445          previousImagePosition          = ""; //currentImagePosition;
446          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
447       }
448
449       if (previousImagePosition != currentImagePosition)
450       {
451          frameIndex = 1;
452          flag = 0;        
453          if (verbose)   
454             std::cout << "=== === === new Position [" << currentImagePosition  << "]"
455                       << std::endl;
456          if (split)
457          {
458              currentPositionWriteDir  = currentSerieWriteDir + gdcm::GDCM_FILESEPARATOR
459                              + currentImagePosition;
460              systemCommand   = "mkdir " + currentPositionWriteDir;     
461              system (systemCommand.c_str()); 
462          }
463          previousImagePosition          = currentImagePosition;
464          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
465          if (split)
466             sliceIndex = 1; // only *one* slice in a given directory
467          else
468             sliceIndex += 1;
469       }      
470
471 // We don't split on Row/Column!
472 /*
473       if (previousPhaseEncodingDirection != currentPhaseEncodingDirection)
474       {        
475          if (verbose)   
476             std::cout << "==== === === === new PhaseEncodingDirection [" 
477                       << currentPhaseEncodingDirection  << "]" << std::endl;
478       
479          if (split)
480          {
481              currentPhaseEncodingDirectionWriteDir  = currentPositionWriteDir 
482                              + gdcm::GDCM_FILESEPARATOR
483                              + currentPhaseEncodingDirection;
484              systemCommand   = "mkdir " + currentPhaseEncodingDirectionWriteDir;     
485              system (systemCommand.c_str());     
486          }      
487     
488          previousPhaseEncodingDirection = currentPhaseEncodingDirection;
489       } 
490 */    
491       
492       if (verbose)
493          std::cout << "--- --- --- --- --- " << (it2->second)->GetFileName() 
494                    << std::endl;
495    
496       if ( gdcm::Debug::GetDebugFlag())
497          std::cout << "--- --- --- --- --- " << it2->first << "  " 
498                    << (it2->second)->GetFileName() << " " 
499                    << gdcm::Util::GetName( fullFilename ) << std::endl;           
500       
501       // Transform the image to be 'Brucker-Like'
502       // ----------------------------------------   
503     
504       // Deal with 0x0019, 0x1000 : 'FOV'
505       int nX = currentFile->GetXSize();
506       int nY = currentFile->GetYSize();
507       float pxSzX = currentFile->GetXSpacing();
508       float pxSzY = currentFile->GetYSpacing();
509       char fov[64];
510       sprintf(fov, "%f\\%f",nX*pxSzX, nY*pxSzY);
511       if (currentFile->IsVRCoherent(0x0019) == 1 )
512          currentFile->InsertEntryString(fov, 0x0019, 0x1000, "  ");
513       else     
514          currentFile->InsertEntryString(fov, 0x0019, 0x1000, "DS");
515
516      
517       // Deal with 0x0020, 0x0012 : 'SESSION INDEX'  (Acquisition Number)
518       std::string chSessionIndex;
519       // CLEANME
520       if (taggrid) 
521          chSessionIndex = "1";
522       else
523       {
524          if (currentPhaseEncodingDirection == "COL" || currentPhaseEncodingDirection == "COL " || currentPhaseEncodingDirection == " COL")
525             chSessionIndex = "1";
526          else if (currentPhaseEncodingDirection == "ROW" || currentPhaseEncodingDirection == "ROW "|| currentPhaseEncodingDirection == " ROW")
527             chSessionIndex = "2"; 
528          else
529          {
530             std::cout << "====================== PhaseEncodingDirection "
531                       << " neither COL nor ROW (?!?) : [ "
532                       << currentPhaseEncodingDirection << "]" << std::endl;
533             chSessionIndex = "1";
534          }
535       }
536        if (currentFile->IsVRCoherent(0x0020) == 1 )     
537          currentFile->InsertEntryString(chSessionIndex, 0x0020, 0x0012, "  ");
538        else
539          currentFile->InsertEntryString(chSessionIndex, 0x0020, 0x0012, "IS");
540  
541       // Deal with  0x0021, 0x1020 : 'SLICE INDEX'
542       char chSliceIndex[5];
543       sprintf(chSliceIndex, "%04d", sliceIndex);
544       std::string strChSliceIndex(chSliceIndex);
545        
546       // Deal with  0x0021, 0x1040 : 'FRAME INDEX' 
547       char chFrameIndex[5];
548       sprintf(chFrameIndex, "%04d", frameIndex);
549
550       std::string stringVR;       
551       if (currentFile->IsVRCoherent(0x0021) == 1 )
552          stringVR = "  ";
553       else
554         stringVR = "IS";
555   
556       currentFile->InsertEntryString(strChSliceIndex, 0x0021, 0x1020, stringVR);
557       currentFile->InsertEntryString(chFrameIndex,    0x0021, 0x1040, stringVR); 
558       
559       if (taggrid)
560          frameIndex++;
561       else     
562       {     
563          if (flag == 0)
564          {       
565             flag = 1;
566          }
567          else
568          {
569             frameIndex++;
570             flag = 0;
571          }
572       } 
573                  
574       if (split)
575       
576          //fullWriteFilename = currentPhaseEncodingDirectionWriteDir + gdcm::GDCM_FILESEPARATOR 
577          //                                + lastFilename + strExtent;      
578          fullWriteFilename = currentPositionWriteDir + gdcm::GDCM_FILESEPARATOR 
579                                          + lastFilename + strExtent; 
580       else
581          fullWriteFilename = currentPatientWriteDir + gdcm::GDCM_FILESEPARATOR 
582                                          + lastFilename + strExtent; 
583       
584       /*           
585       systemCommand  = "cp " + fullFilename + " " + fullWriteFilename;
586       std::cout << systemCommand << std::endl;
587       system (  systemCommand.c_str() );
588       */
589             
590       // Load the pixels in RAM.    
591       
592       fh = gdcm::FileHelper::New(currentFile);     
593       fh->GetImageDataRaw(); // Don't convert (Gray Pixels + LUT) into (RGB pixels) ?!?
594       fh->SetWriteTypeToDcmExplVR();
595       // We didn't modify pixels -> keep unchanged the following :
596       // 'Media Storage SOP Class UID' (0x0002,0x0002)
597       // 'SOP Class UID'               (0x0008,0x0016)
598       // 'Image Type'                  (0x0008,0x0008)
599       // 'Conversion Type'             (0x0008,0x0064)
600       fh->SetContentType(gdcm::UNMODIFIED_PIXELS_IMAGE);
601       if (!fh->Write(fullWriteFilename))
602       {
603          std::cout << "Fail to write :[" << fullWriteFilename << "]"
604                    << std::endl;
605       } 
606       fh->Delete();                
607    }
608  }