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