]> Creatis software - gdcm.git/blob - Example/exConvert3DplusT.cxx
Sorting the file list on 'file name' should work on any platform!
[gdcm.git] / Example / exConvert3DplusT.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: exConvert3DplusT.cxx,v $
5   Language:  C++
6   Date:      $Date: 2006/09/01 13:25:27 $
7   Version:   $Revision: 1.3 $
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 //
19 // This program is used to convert huge amounts of functionnal (3D + T) MR images
20 // into 'image tiles' (one title per volume) processable by clinical softwares
21 // 
22 #include "gdcmFile.h"
23 #include "gdcmFileHelper.h"
24 #include "gdcmCommon.h"
25 #include "gdcmDebug.h"
26 #include "gdcmDirList.h"
27 #include "gdcmUtil.h"
28
29 #include "gdcmArgMgr.h"
30
31 #include <iostream>
32 #include <sstream>
33 #include <string.h> // for memset
34
35 int main(int argc, char *argv[])
36 {   
37    START_USAGE(usage)
38    "\n exConvert3plusT :\n                                                    ",
39    " Converts the Dicom files inside a single-level Directory                 ",
40    " into a '3D + T' image set                                                ",
41    " usage: exConvert3plusT                                                   ",
42    "              dirin=inputDirectoryName                                    ",
43    "              dirout=outputDirectoryName                                  ",
44    "              [studyUID = ] [patName = ]                                  ",
45    "              [ { [noshadowseq] | [noshadow][noseq] } ]  [debug] [verbose]",
46    "                                                                          ",
47    "       dirin : single-level Directory containing the images               ",
48    "                                        (no recursive parsing)            ",
49    "       dirout : will be created if doesn't exist                          ",
50    "                                                                          ",
51    "       [imdimx = ] [imdimy = ] [imgline = ] [imgcol = ]                   ",
52    "       [pixelsize = ] [imagesinvolume = ]                                 ",
53    "                                                                          ",
54    "       studyUID   : *aware* user wants to add the serie                   ",
55    "                                             to an already existing study ",   
56    "       imdimx,imdimy : 'elementary image' size (default : 64)             ",
57    "                        used to reject erroneous images                   ",
58    "       imgline, imgcol : sizes of the 'image tile' defaulted as 6x6       ",
59    "       pixelsize : in bytes; defaulted as 2 (uint16_t)                    ",
60    "                        used to reject erroneous images                   ",
61    "       imagesinvolume : we have no way to guess it!                       ",
62    "                        defaulted as imgline*imgcol                       ",
63    "       noshadowseq: user doesn't want to load Private Sequences           ",
64    "       noshadow   : user doesn't want to load Private groups (odd number) ",
65    "       noseq      : user doesn't want to load Sequences                   ",
66    "       verbose    : user wants to run the program in 'verbose mode'       ",
67    "       debug      : developper wants to run the program in 'debug mode'   ",
68    FINISH_USAGE
69    
70    
71      // ----- Initialize Arguments Manager ------
72   
73    gdcm::ArgMgr *am = new gdcm::ArgMgr(argc, argv);
74   
75    if (am->ArgMgrDefined("usage") || argc == 1) 
76    {
77       am->ArgMgrUsage(usage); // Display 'usage'
78       delete am;
79       return 0;
80    }
81
82    if (am->ArgMgrDefined("debug"))
83       gdcm::Debug::DebugOn(); 
84    int verbose  = am->ArgMgrDefined("verbose");
85    int oververbose  = am->ArgMgrDefined("oververbose");
86       
87    std::string patName = am->ArgMgrGetString("patname", "g^PatientName");
88    //float zSpacing = am->ArgMgrGetFloat("zSpacing", 1.0);
89       
90    const char *dirIn  = am->ArgMgrGetString("dirin");
91    const char *dirOut  = am->ArgMgrGetString("dirout");
92    
93    bool userDefinedStudy = am->ArgMgrDefined("studyUID");
94    const char *studyUID  = am->ArgMgrGetString("studyUID");
95
96 // not described *on purpose* in the Usage ! 
97    bool userDefinedSerie = am->ArgMgrDefined("serieUID");   
98    const char *serieUID  = am->ArgMgrGetString("serieUID");  
99    
100    
101    int imageDimX          = am->ArgMgrGetInt("imdimx",64);
102    int imageDimY          = am->ArgMgrGetInt("imdimy",64); 
103    int imagePixelSize     = am->ArgMgrGetInt("pixelsize", 2);
104    int imagetteLineNumber = am->ArgMgrGetInt("imgline",6);
105    int imagetteRowNumber  = am->ArgMgrGetInt("imgcol",6);
106    int nbOfImagesInVolume = am->ArgMgrGetInt("imagesinvolume",
107                                          imagetteLineNumber*imagetteRowNumber);
108  
109    int loadMode = gdcm::LD_ALL;
110    if ( am->ArgMgrDefined("noshadowseq") )
111       loadMode |= gdcm::LD_NOSHADOWSEQ;
112    else 
113    {
114    if ( am->ArgMgrDefined("noshadow") )
115          loadMode |= gdcm::LD_NOSHADOW;
116       if ( am->ArgMgrDefined("noseq") )
117          loadMode |= gdcm::LD_NOSEQ;
118    }
119         
120    /* if unused Param we give up */
121    if ( am->ArgMgrPrintUnusedLabels() )
122    {
123       am->ArgMgrUsage(usage);
124       delete am;
125       return 0;
126    }
127
128    delete am;  // ------ we don't need Arguments Manager any longer ------
129    
130  // ====== Deal with a (single level, single Patient) Directory ======
131  
132  
133    //std::cout << "dirIn [" << dirIn << "]" << std::endl;
134    if ( ! gdcm::DirList::IsDirectory(dirIn) )
135    {
136       std::cout << "KO : [" << dirIn << "] is not a Directory." << std::endl;
137       return 0;
138
139    }
140    else
141    {
142       if (verbose)     
143          std::cout << "OK : [" << dirIn << "] is a Directory." << std::endl;
144    }   
145   
146    std::string systemCommand;
147    std::string strDirNameout(dirOut);          // to please gcc 4 
148    if (verbose)
149       std::cout << "Check for output directory :[" << dirOut << "]."
150              <<std::endl;
151    if ( ! gdcm::DirList::IsDirectory(dirOut) )    // dirout not found
152    {
153       systemCommand = "mkdir " +strDirNameout;        // create it!
154       if (verbose)
155          std::cout << systemCommand << std::endl;
156       system (systemCommand.c_str());
157       if ( ! gdcm::DirList::IsDirectory(dirOut) ) // be sure it worked
158       {
159           std::cout << "KO : not a dir : [" << dirOut 
160                     << "] (creation failure ?)" << std::endl;
161       return 0;
162
163       }
164       else
165       {
166         if (verbose)
167            std::cout << "Directory [" << dirOut << "] created." << std::endl;
168       }
169    }
170    else
171    {
172       if (verbose)
173          std::cout << "Output Directory [" << dirOut 
174                    << "] already exists; Used as is." << std::endl;
175    }
176
177    gdcm::DirList dirList(dirIn,false); // gets (at single level) the file list
178    gdcm::DirListType fileList = dirList.GetFilenames();
179    
180    // hope sorting on the filename is enough!
181    // anyway, *no* filed is available to perform anything more clever.
182       
183    std::sort(fileList.begin(), fileList.end() );
184
185    // 'Study Instance UID'
186    // The user is allowed to create his own Study, 
187    //          keeping the same 'Study Instance UID' for various images
188    // The user may add images to a 'Manufacturer Study',
189    //          adding new Series to an already existing Study
190    std::string strStudyUID;
191    if ( !userDefinedStudy)
192       strStudyUID =  gdcm::Util::CreateUniqueUID();
193    else
194       strStudyUID = studyUID;
195
196    // 'Serie Instance UID'
197    // The user is allowed to create his own Series, 
198    // keeping the same 'Serie Instance UID' for various images
199    // The user shouldn't add any image to a 'Manufacturer Serie'
200    // but there is no way no to prevent him for doing that
201    
202    std::string strSerieUID; 
203    if ( !userDefinedSerie)   
204       strSerieUID =  gdcm::Util::CreateUniqueUID();
205    else      
206       strSerieUID = serieUID;
207
208    std::ostringstream str;
209   
210    size_t totalNumberOfPixels =  imageDimX*imageDimY * imagetteLineNumber*imagetteRowNumber;     
211    int16_t *imageTable = new int16_t[totalNumberOfPixels]; 
212    
213    //std::cout << " imageTable length " << totalNumberOfPixels;
214    memset(imageTable, 0, totalNumberOfPixels * imagePixelSize);
215              
216    int16_t **tabImageData = new int16_t *[nbOfImagesInVolume];
217    
218    gdcm::File *f;
219    gdcm::FileHelper *fh;
220    std::string fullFilename, lastFilename;
221    float zPositionComponent = 0.0;
222       
223    int imageNumber = 0;
224    
225    for( gdcm::DirListType::iterator it  = fileList.begin();
226                                  it != fileList.end();
227                                  ++it )
228    {
229       fullFilename = *it;
230       f = gdcm::File::New( );
231       f->SetLoadMode(loadMode);
232       f->SetFileName( it->c_str() );
233
234       if (verbose)
235          std::cout << "file [" << it->c_str() << "], as imageNumber : " << imageNumber << std::endl;
236  
237       if ( !f->Load() )
238       {
239          if (verbose)
240             std::cout << "fail to load [" << it->c_str() << "]" << std::endl;      
241          f->Delete();
242          continue;
243       }
244
245       // Load the pixels in RAM.    
246       
247       fh = gdcm::FileHelper::New(f); 
248       // Don't convert (Gray Pixels + LUT) into (RGB pixels) ?!?
249    
250       tabImageData[imageNumber] = (int16_t *)fh->GetImageDataRaw();
251       
252       if (!tabImageData[imageNumber]) 
253       {
254          std::cout << "fail to read [" << it->c_str() << std::endl;
255          continue;
256       }
257       int16_t mini=32000;
258       int16_t maxi=-32000;
259        
260       if (imageNumber == nbOfImagesInVolume-1)
261       {        
262          for(imageNumber=0; imageNumber < nbOfImagesInVolume; imageNumber++)
263          {
264             int debMove = (imageNumber%imagetteRowNumber) * imageDimX 
265                 + (imageNumber/imagetteRowNumber) *imageDimX*imageDimY*imagetteRowNumber;
266
267             if (verbose)
268                std::cout << "imageNumber " << imageNumber << " debMove " << debMove << std::endl;
269       
270             for(int i=0; i<imageDimY; i++)
271             {
272                int debLigne = debMove + i*imagetteRowNumber*imageDimX;
273                //if (oververbose)
274                //   std::cout << "numLigne " << i << " debLigne " << debLigne 
275                //   << ": " << debMove << " + " << i << " * " << imagetteRowNumber*imageDimX << std::endl;
276                for (int j=0; j<imageDimX; j++)
277                {
278                   //std::cout << "j " << j << std::endl;
279   
280                   imageTable[debLigne + j] = *(tabImageData[imageNumber] + i*imageDimY + j);
281                   if (imageTable[debLigne + j] < 0) imageTable[debLigne + j]=0;
282                      //std::cout << debLigne + j << " : " << imageTable[debLigne + j] << std::endl;
283   
284                      if (*(tabImageData[imageNumber] + i*imageDimY + j) < mini)
285                                         mini=*(tabImageData[imageNumber] + i*imageDimY + j);
286                      else if (*(tabImageData[imageNumber] + i*imageDimY + j) > maxi) 
287                                         maxi=*(tabImageData[imageNumber] + i*imageDimY + j); 
288              }
289           }   
290       }
291   //   if (oververbose)
292   //    std::cout << " mini = " << mini << " maxi = " << maxi << std::endl;
293   // just to check (actually, it's useless)       
294   /*
295      int16_t mini=32000;
296      int16_t maxi=-32000;
297      for (int k=0; k < totalNumberOfPixels; k++)
298      {
299         if (imageTable[k] < mini) mini=imageTable[k];
300         else if (imageTable[k] > maxi) maxi=imageTable[k]; 
301     }
302
303   // if (oververbose)
304   //    std::cout << " mini = " << mini << " maxi = " << maxi << std::endl;
305          /// \todo : FIXME what do I do ? Smallest/Largest Image Pixel Value is vr=US, Pixels are signed ?!?
306
307          str.str("");
308          str << mini;
309          fh->InsertEntryString(str.str(),0x0028,0x0106, "US"); // Smallest Image Pixel Value
310
311          str.str("");
312          str << maxi;
313          fh->InsertEntryString(str.str(),0x0028,0x0107, "US"); // Largest Image Pixel Value 
314 */
315          imageNumber = 0;
316
317     // write the imagette
318  
319          // Set the image size
320          str.str("");
321          str << imageDimX*imagetteRowNumber;
322          fh->InsertEntryString(str.str(),0x0028,0x0011, "US"); // Columns
323
324          str.str("");
325          str << imageDimY*imagetteLineNumber;
326          fh->InsertEntryString(str.str(),0x0028,0x0010, "US"); // Rows 
327
328          fh->InsertEntryString(strStudyUID,0x0020,0x000d,"UI");      
329          fh->InsertEntryString(strSerieUID,0x0020,0x000e,"UI");
330          fh->InsertEntryString(patName,0x0010,0x0010, "PN");   // Patient's Name
331
332          fh->SetImageData((uint8_t *)imageTable, totalNumberOfPixels * imagePixelSize);
333
334 // ==================================================================================================
335
336 // This is a dirty heuristics, but no other way :-(
337
338 // if Image Orientation (Patient) is not present
339 //    I create one, (as Axial) 
340 //    if Image Position (Patient) is not present
341 //       I create one, incrementing  zPositionComponent up by user supplied zSpacing
342 //    if Slice Location is not present 
343 //       I create one, as zPositionComponent
344 //
345 // Aware use is free to supply his own one !    
346
347 /*    
348          if (! f->CheckIfEntryExist(0x0020,0x0037) ) // 0020 0037 DS 6 Image Orientation (Patient)
349          {
350             fh->InsertEntryString("1.0\\0.0\\0.0\\0.0\\1.0\\0.0",0x0020,0x0037, "DS"); //[1\0\0\0\1\0] : Axial
351
352             char charImagePosition[256];
353      
354             sprintf(charImagePosition,"%f\\0.0\\0.0",zPositionComponent);
355             zPositionComponent += zSpacing;
356       
357             if (! f->CheckIfEntryExist(0x0020,0x0032) ) //0020 0032 DS 3 Image Position (Patient)
358                fh->InsertEntryString(charImagePosition,0x0020,0x0032, "DS"); 
359     
360             if (! f->CheckIfEntryExist(0x0020,0x1041) ) // 0020 0x1041 DS 1 Slice Location
361             {
362                sprintf(charImagePosition,"%f",zPositionComponent);
363                fh->InsertEntryString(charImagePosition,0x0020,0x1041, "DS");
364             }    
365          } 
366 */
367
368 // ==================================================================================================
369
370          fh->SetWriteTypeToDcmExplVR();               
371          fh->SetContentType(gdcm::UNMODIFIED_PIXELS_IMAGE);
372
373       
374          lastFilename =  gdcm::Util::GetName( fullFilename );
375          std::string fullWriteFilename = strDirNameout + gdcm::GDCM_FILESEPARATOR 
376                                        + lastFilename;
377          if (verbose)
378             std::cout << "Write : [" << fullWriteFilename << "]" << std::endl;
379          if (!fh->Write(fullWriteFilename))
380          {
381             std::cout << "Fail to write :[" << fullWriteFilename << "]"
382                       << std::endl;
383          }
384          //break;   
385       }  // end : 'write the imagette'
386       else
387       {
388          imageNumber++;
389       }
390      // FIXME : delete just in time!
391      // fh->Delete();
392      // f->Delete();
393    }
394 }