]> Creatis software - gdcm.git/blob - Example/ToInTag.cxx
avoid warnings
[gdcm.git] / Example / ToInTag.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: ToInTag.cxx,v $
5   Language:  C++
6   Date:      $Date: 2006/06/01 10:33:13 $
7   Version:   $Revision: 1.2 $
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    int verbose  = am->ArgMgrDefined("verbose");
137    int split    = am->ArgMgrDefined("split");
138    int 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       return 0;
176
177    }
178    else
179    {
180       std::cout << "OK : [" << dirNamein << "] is a Directory." << std::endl;
181    }
182
183    std::string systemCommand;
184    
185    std::cout << "Check for output directory :[" << dirNameout << "]."
186              <<std::endl;
187    if ( ! gdcm::DirList::IsDirectory(dirNameout) )    // dirout not found
188    {
189       std::string strDirNameout(dirNameout);          // to please gcc 4
190       systemCommand = "mkdir " +strDirNameout;        // create it!
191       if (verbose)
192          std::cout << systemCommand << std::endl;
193       system (systemCommand.c_str());
194       if ( ! gdcm::DirList::IsDirectory(dirNameout) ) // be sure it worked
195       {
196           std::cout << "KO : not a dir : [" << dirNameout << "] (creation failure ?)" << std::endl;
197       return 0;
198
199       }
200       else
201       {
202         std::cout << "Directory [" << dirNameout << "] created." << std::endl;
203       }
204    }
205    else
206    {
207        std::cout << "Output Directory [" << dirNameout << "] already exists; Used as is." << std::endl;
208    }
209     
210    std::string strDirNamein(dirNamein);
211    gdcm::DirList dirList(strDirNamein, true); // get recursively the list of files
212    
213    if (listonly)
214    {
215       std::cout << "------------List of found files ------------" << std::endl;
216       dirList.Print();
217    }
218    
219    gdcm::DirListType fileNames;
220    fileNames = dirList.GetFilenames();
221    gdcm::SerieHelper *s;              // Needed only to may use SerieHelper::AddSeriesDetail()
222    s = gdcm::SerieHelper::New();
223
224    std::string token = "%%%"; // Hope it's enough!
225 /*       
226    std::cout << "---------------Print Serie--------------" << std::endl; 
227    s->SetDirectory(dirNamein, true); // true : recursive exploration 
228    s->SetUseSeriesDetails(true);  
229    s->AddSeriesDetail(0x0018, 0x1312);   
230    s->Print();
231 */
232   
233    gdcm::File *f;
234    gdcm::FileHelper *fh;
235    std::vector<std::string> tokens;
236    std::vector<std::string> tokensForFileName;
237 /*   
238    std::cout << "---------------Print Unique Series identifiers---------"  
239              << std::endl;     
240    std::string uniqueSeriesIdentifier;
241  
242    for (gdcm::DirListType::iterator it) = fileNames.begin();  
243                                     it != fileNames.end();
244                                   ++it)
245    {
246       std::cout << "File Name : " << *it << std::endl;
247       f = gdcm::File::New();
248       f->SetLoadMode(gdcm::LD_ALL);
249       f->SetFileName( *it );
250       f->Load();
251         
252       uniqueSeriesIdentifier=s->CreateUniqueSeriesIdentifier(f);
253       std::cout << "                           [" <<
254                uniqueSeriesIdentifier  << "]" << std::endl;       
255       f->Delete();
256    }
257 */
258    
259    if (verbose)
260       std::cout << "------------------Print Break levels-----------------" << std::endl;
261
262    std::string userFileIdentifier; 
263    SortedFiles sf;
264
265    s->AddSeriesDetail(0x0010, 0x0010, false); // Patient's Name
266    s->AddSeriesDetail(0x0020, 0x000e, false); // Series Instance UID
267    s->AddSeriesDetail(0x0020, 0x0032, false); // Image Position (Patient)     
268    s->AddSeriesDetail(0x0018, 0x1060, true);  // Trigger Time (true: convert to keep numerical order)
269    s->AddSeriesDetail(0x0018, 0x1312, false); // In-plane Phase Encoding Direction 
270       
271    for (gdcm::DirListType::iterator it = fileNames.begin();  
272                                     it != fileNames.end();
273                                   ++it)
274    {
275       f = gdcm::File::New();
276       f->SetLoadMode(loadMode);
277       f->SetFileName( *it );
278       f->Load();
279       
280
281       std::string strSeriesNumber;
282       int seriesNumber;
283       int j;
284
285       // keep only requested Series      
286       bool keep = false;
287       if (nbSeriesToKeep != 0)
288       {     
289          strSeriesNumber = f->GetEntryString(0x0020, 0x0011 );
290          seriesNumber = atoi( strSeriesNumber.c_str() );
291          for (j=0;j<nbSeriesToKeep; j++)
292          {
293             if(seriesNumber == seriesToKeep[j])
294             {
295                keep = true;
296                break;
297             }
298          }
299          if ( !keep)
300          {
301             f->Delete();
302             continue;
303          } 
304       }
305       // drop all unrequested Series
306       bool drop = false;
307       if (nbSeriesToDrop != 0)
308       {     
309          strSeriesNumber = f->GetEntryString(0x0020, 0x0011 );
310          seriesNumber = atoi( strSeriesNumber.c_str() );
311          for (j=0;j<nbSeriesToDrop; j++)
312          {
313             if(seriesNumber == seriesToDrop[j])
314             { 
315                drop = true;
316                break;
317             }
318         }
319         if (drop)
320         {
321            f->Delete();
322            continue;
323         }
324       }      
325
326       userFileIdentifier=s->CreateUserDefinedFileIdentifier(f); 
327       tokens.clear();
328       gdcm::Util::Tokenize (userFileIdentifier, tokens, token); 
329    
330       int imageNum; // Within FileName
331       char newName[1024];
332       
333       if ( tokens[3] == "gdcmUnfound")  // sometimes Trigger Time is not found. CreateUserDefinedFileIdentifier is not aware of the pb.
334       {
335          ///this is a trick to build up a lexicographical compliant name :
336          ///     eg : fich001.ima vs fich100.ima as opposed to fich1.ima vs fich100.ima
337          std::string name = gdcm::Util::GetName( *it );
338          if (hasSkel)
339          {
340             gdcm::Util::Tokenize (name, tokensForFileName, skel);
341             imageNum = atoi ( tokensForFileName[0].c_str() );
342             // probabely we could write something much more complicated using C++ !
343             sprintf (newName, "%s%06d%s", skel, imageNum, extent);
344             tokens[3] = newName;
345             tokensForFileName.clear();    
346          }
347          else
348             tokens[3] = name;
349  
350  
351          userFileIdentifier = tokens[0] + token + tokens[1] + token + tokens[2] + token 
352                     + tokens[3] + token + tokens[4] + token;
353       }   
354       if (verbose) 
355          std::cout << "[" << userFileIdentifier  << "]" << std::endl;
356                
357       // storing in a map ensures automatic sorting !      
358       sf[userFileIdentifier] = f;
359    }
360       
361    std::string fullFilename, lastFilename;
362    std::string previousPatientName, currentPatientName;
363    std::string previousSerieInstanceUID, currentSerieInstanceUID;
364    std::string previousImagePosition, currentImagePosition;
365    std::string previousPhaseEncodingDirection, currentPhaseEncodingDirection;
366    std::string previousTriggerTime, currentTriggerTime;
367       
368    std::string writeDir, currentWriteDir;
369    std::string currentPatientWriteDir, currentSerieWriteDir, 
370                currentPositionWriteDir, currentPhaseEncodingDirectionWriteDir;
371
372    std::string fullWriteFilename;
373    std::string strExtent(extent); 
374            
375    writeDir = gdcm::Util::NormalizePath(dirNameout);     
376    SortedFiles::iterator it2;
377  
378    previousPatientName            = "";
379    previousSerieInstanceUID       = "";   
380    previousImagePosition          = "";
381    previousPhaseEncodingDirection = "";
382    previousTriggerTime            = "";
383    
384    int sliceIndex = 0; // Is incremented *at the beginning* of processing
385    int frameIndex = 1;
386    int flag       = 0;
387        
388    gdcm::File *currentFile;
389      
390    for (it2 = sf.begin() ; it2 != sf.end(); ++it2)
391    {  
392       currentFile = it2->second;
393        
394       fullFilename =  currentFile->GetFileName();
395       lastFilename =  gdcm::Util::GetName( fullFilename ); 
396       std::cout << "Rewrite [" <<lastFilename << "]" << std::endl;
397      
398       tokens.clear();
399       gdcm::Util::Tokenize (it2->first, tokens, token);
400       
401       currentPatientName            = tokens[0];
402       currentSerieInstanceUID       = tokens[1];
403       currentImagePosition          = tokens[2];
404       currentTriggerTime            = tokens[3];
405       currentPhaseEncodingDirection = tokens[4]; 
406
407       if ( currentImagePosition[0] == '-')
408           currentImagePosition[0] = 'M';
409       if ( currentImagePosition[0] == '+')
410           currentImagePosition[0] = 'P'; 
411       
412       if (previousPatientName != currentPatientName)
413       {
414          previousPatientName = currentPatientName;
415          if (verbose)   
416             std::cout << "==== new Patient  [" << currentPatientName  << "]" << std::endl;
417     
418          previousPatientName            = currentPatientName;
419          previousSerieInstanceUID       = ""; //currentSerieInstanceUID;
420          previousImagePosition          = ""; //currentImagePosition;
421          previousTriggerTime            = "";
422          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
423   
424          currentPatientWriteDir = writeDir + currentPatientName;
425          //if ( ! gdcm::DirList::IsDirectory(currentPatientWriteDir) )
426            {
427               systemCommand   = "mkdir " + currentPatientWriteDir;
428               if (verbose)
429                  std::cout << systemCommand << std::endl;
430               system ( systemCommand.c_str() );
431          }
432       }
433
434       if (previousSerieInstanceUID != currentSerieInstanceUID)
435       {        
436          if (verbose)   
437             std::cout << "==== === new Serie [" << currentSerieInstanceUID << "]"
438                       << std::endl;
439          if (split)
440          {
441              currentSerieWriteDir  = currentPatientWriteDir + gdcm::GDCM_FILESEPARATOR
442                              + currentSerieInstanceUID;
443              systemCommand   = "mkdir " + currentSerieWriteDir;  
444              system (systemCommand.c_str());
445          }
446          previousSerieInstanceUID       = currentSerieInstanceUID;
447          previousImagePosition          = ""; //currentImagePosition;
448          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
449       }
450
451       if (previousImagePosition != currentImagePosition)
452       {
453          frameIndex = 1;
454          flag = 0;        
455          if (verbose)   
456             std::cout << "=== === === new Position [" << currentImagePosition  << "]"
457                       << std::endl;
458          if (split)
459          {
460              currentPositionWriteDir  = currentSerieWriteDir + gdcm::GDCM_FILESEPARATOR
461                              + currentImagePosition;
462              systemCommand   = "mkdir " + currentPositionWriteDir;     
463              system (systemCommand.c_str()); 
464          }
465          previousImagePosition          = currentImagePosition;
466          previousPhaseEncodingDirection = ""; //currentPhaseEncodingDirection;
467          if (split)
468             sliceIndex = 1; // only *one* slice in a given directory
469          else
470             sliceIndex += 1;
471       }      
472
473 // We don't split on Row/Column!
474 /*
475       if (previousPhaseEncodingDirection != currentPhaseEncodingDirection)
476       {        
477          if (verbose)   
478             std::cout << "==== === === === new PhaseEncodingDirection [" 
479                       << currentPhaseEncodingDirection  << "]" << std::endl;
480       
481          if (split)
482          {
483              currentPhaseEncodingDirectionWriteDir  = currentPositionWriteDir 
484                              + gdcm::GDCM_FILESEPARATOR
485                              + currentPhaseEncodingDirection;
486              systemCommand   = "mkdir " + currentPhaseEncodingDirectionWriteDir;     
487              system (systemCommand.c_str());     
488          }      
489     
490          previousPhaseEncodingDirection = currentPhaseEncodingDirection;
491       } 
492 */    
493       
494       if (verbose)
495          std::cout << "--- --- --- --- --- " << (it2->second)->GetFileName() 
496                    << std::endl;
497    
498       if ( gdcm::Debug::GetDebugFlag())
499          std::cout << "--- --- --- --- --- " << it2->first << "  " 
500                    << (it2->second)->GetFileName() << " " 
501                    << gdcm::Util::GetName( fullFilename ) << std::endl;           
502       
503       // Transform the image to be 'Brucker-Like'
504       // ----------------------------------------   
505     
506       // Deal with 0x0019, 0x1000 : 'FOV'
507       int nX = currentFile->GetXSize();
508       int nY = currentFile->GetYSize();
509       float pxSzX = currentFile->GetXSpacing();
510       float pxSzY = currentFile->GetYSpacing();
511       char fov[64];
512       sprintf(fov, "%f\\%f",nX*pxSzX, nY*pxSzY);
513       if (currentFile->IsVRCoherent(0x0019) == 1 )
514          currentFile->InsertEntryString(fov, 0x0019, 0x1000, "  ");
515       else     
516          currentFile->InsertEntryString(fov, 0x0019, 0x1000, "DS");
517
518      
519       // Deal with 0x0020, 0x0012 : 'SESSION INDEX'  (Acquisition Number)
520       std::string chSessionIndex;
521       // CLEANME
522       if (taggrid) 
523          chSessionIndex = "1";
524       else
525       {
526          if (currentPhaseEncodingDirection == "COL" || currentPhaseEncodingDirection == "COL " || currentPhaseEncodingDirection == " COL")
527             chSessionIndex = "1";
528          else if (currentPhaseEncodingDirection == "ROW" || currentPhaseEncodingDirection == "ROW "|| currentPhaseEncodingDirection == " ROW")
529             chSessionIndex = "2"; 
530          else
531          {
532             std::cout << "====================== PhaseEncodingDirection "
533                       << " neither COL nor ROW (?!?) : [ "
534                       << currentPhaseEncodingDirection << "]" << std::endl;
535             chSessionIndex = "1";
536          }
537       }
538        if (currentFile->IsVRCoherent(0x0020) == 1 )     
539          currentFile->InsertEntryString(chSessionIndex, 0x0020, 0x0012, "  ");
540        else
541          currentFile->InsertEntryString(chSessionIndex, 0x0020, 0x0012, "IS");
542  
543       // Deal with  0x0021, 0x1020 : 'SLICE INDEX'
544       char chSliceIndex[5];
545       sprintf(chSliceIndex, "%04d", sliceIndex);
546       std::string strChSliceIndex(chSliceIndex);
547        
548       // Deal with  0x0021, 0x1040 : 'FRAME INDEX' 
549       char chFrameIndex[5];
550       sprintf(chFrameIndex, "%04d", frameIndex);
551
552       std::string stringVR;       
553       if (currentFile->IsVRCoherent(0x0021) == 1 )
554          stringVR = "  ";
555       else
556         stringVR = "IS";
557   
558       currentFile->InsertEntryString(strChSliceIndex, 0x0021, 0x1020, stringVR);
559       currentFile->InsertEntryString(chFrameIndex,    0x0021, 0x1040, stringVR); 
560       
561       if (taggrid)
562          frameIndex++;
563       else     
564       {     
565          if (flag == 0)
566          {       
567             flag = 1;
568          }
569          else
570          {
571             frameIndex++;
572             flag = 0;
573          }
574       } 
575                  
576       if (split)
577       
578          //fullWriteFilename = currentPhaseEncodingDirectionWriteDir + gdcm::GDCM_FILESEPARATOR 
579          //                                + lastFilename + strExtent;      
580          fullWriteFilename = currentPositionWriteDir + gdcm::GDCM_FILESEPARATOR 
581                                          + lastFilename + strExtent; 
582       else
583          fullWriteFilename = currentPatientWriteDir + gdcm::GDCM_FILESEPARATOR 
584                                          + lastFilename + strExtent; 
585       
586       /*           
587       systemCommand  = "cp " + fullFilename + " " + fullWriteFilename;
588       std::cout << systemCommand << std::endl;
589       system (  systemCommand.c_str() );
590       */
591             
592       // Load the pixels in RAM.    
593       
594       fh = gdcm::FileHelper::New(currentFile);     
595       fh->GetImageDataRaw(); // Don't convert (Gray Pixels + LUT) into (RGB pixels) ?!?
596       fh->SetWriteTypeToDcmExplVR();
597       // We didn't modify pixels -> keep unchanged the following :
598       // 'Media Storage SOP Class UID' (0x0002,0x0002)
599       // 'SOP Class UID'               (0x0008,0x0016)
600       // 'Image Type'                  (0x0008,0x0008)
601       // 'Conversion Type'             (0x0008,0x0064)
602       fh->SetContentType(gdcm::UNMODIFIED_PIXELS_IMAGE);
603       if (!fh->Write(fullWriteFilename))
604       {
605          std::cout << "Fail to write :[" << fullWriteFilename << "]"
606                    << std::endl;
607       } 
608       fh->Delete();                
609    }
610    return 0;
611  }