]> Creatis software - gdcm.git/blob - Example/exConvert3DplusT.cxx
Avoid warning
[gdcm.git] / Example / exConvert3DplusT.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: exConvert3DplusT.cxx,v $
5   Language:  C++
6   Date:      $Date: 2007/06/21 15:06:13 $
7   Version:   $Revision: 1.5 $
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_NAME_SPACE::ArgMgr *am = new GDCM_NAME_SPACE::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_NAME_SPACE::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_NAME_SPACE::LD_ALL;
110    if ( am->ArgMgrDefined("noshadowseq") )
111       loadMode |= GDCM_NAME_SPACE::LD_NOSHADOWSEQ;
112    else 
113    {
114    if ( am->ArgMgrDefined("noshadow") )
115          loadMode |= GDCM_NAME_SPACE::LD_NOSHADOW;
116       if ( am->ArgMgrDefined("noseq") )
117          loadMode |= GDCM_NAME_SPACE::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_NAME_SPACE::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_NAME_SPACE::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_NAME_SPACE::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_NAME_SPACE::DirList dirList(dirIn,false); // gets (at single level) the file list
178    GDCM_NAME_SPACE::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_NAME_SPACE::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_NAME_SPACE::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    memset(imageTable, 0, totalNumberOfPixels * imagePixelSize);
214              
215    int16_t **tabImageData = new int16_t *[nbOfImagesInVolume];   
216    GDCM_NAME_SPACE::File **f         = new GDCM_NAME_SPACE::File *[nbOfImagesInVolume];
217    GDCM_NAME_SPACE::FileHelper **fh  = new GDCM_NAME_SPACE::FileHelper *[nbOfImagesInVolume];
218    
219    std::string fullFilename, lastFilename;
220    float zPositionComponent = 0.0;
221       
222    int imageNumber = 0;
223    
224    for( GDCM_NAME_SPACE::DirListType::iterator it  = fileList.begin();
225                                  it != fileList.end();
226                                  ++it )
227    {
228       fullFilename = *it;
229       f[imageNumber] = GDCM_NAME_SPACE::File::New( );
230       f[imageNumber]->SetLoadMode(loadMode);
231       f[imageNumber]->SetFileName( it->c_str() );
232
233       if (verbose)
234          std::cout << "file [" << it->c_str() << "], as imageNumber : " << imageNumber << std::endl;
235  
236       if ( !f[imageNumber]->Load() )
237       {
238          if (verbose)
239             std::cout << "fail to load [" << it->c_str() << "]" << std::endl;      
240          f[imageNumber]->Delete();
241          continue;
242       }
243
244       // Load the pixels in RAM.    
245       
246       fh[imageNumber] = GDCM_NAME_SPACE::FileHelper::New(f[imageNumber]); 
247       // Don't convert (Gray Pixels + LUT) into (RGB pixels) ?!?
248    
249       tabImageData[imageNumber] = (int16_t *)fh[imageNumber]->GetImageDataRaw();
250       
251       if (!tabImageData[imageNumber]) 
252       {
253          std::cout << "fail to read [" << it->c_str() << std::endl;
254          continue;
255       }
256       int16_t mini=32000;
257       int16_t maxi=-32000;
258        
259       if (imageNumber == nbOfImagesInVolume-1)
260       {        
261          for(imageNumber=0; imageNumber < nbOfImagesInVolume; imageNumber++)
262          {
263             int debMove = (imageNumber%imagetteRowNumber) * imageDimX 
264                 + (imageNumber/imagetteRowNumber) *imageDimX*imageDimY*imagetteRowNumber;
265
266             if (verbose)
267                std::cout << "imageNumber " << imageNumber << " debMove " << debMove << std::endl;
268       
269             for(int i=0; i<imageDimY; i++)
270             {
271                int debLigne = debMove + i*imagetteRowNumber*imageDimX;
272                //if (oververbose)
273                //   std::cout << "numLigne " << i << " debLigne " << debLigne 
274                //   << ": " << debMove << " + " << i << " * " << imagetteRowNumber*imageDimX << std::endl;
275                for (int j=0; j<imageDimX; j++)
276                {
277                   //std::cout << "j " << j << std::endl;
278   
279                   imageTable[debLigne + j] = *(tabImageData[imageNumber] + i*imageDimY + j);
280                   if (imageTable[debLigne + j] < 0) imageTable[debLigne + j]=0;
281                      //std::cout << debLigne + j << " : " << imageTable[debLigne + j] << std::endl;
282   
283                      if (*(tabImageData[imageNumber] + i*imageDimY + j) < mini)
284                                         mini=*(tabImageData[imageNumber] + i*imageDimY + j);
285                      else if (*(tabImageData[imageNumber] + i*imageDimY + j) > maxi) 
286                                         maxi=*(tabImageData[imageNumber] + i*imageDimY + j); 
287              }
288           }   
289       }
290   //   if (oververbose)
291   //    std::cout << " mini = " << mini << " maxi = " << maxi << std::endl;
292   // just to check (actually, it's useless)       
293   /*
294      int16_t mini=32000;
295      int16_t maxi=-32000;
296      for (int k=0; k < totalNumberOfPixels; k++)
297      {
298         if (imageTable[k] < mini) mini=imageTable[k];
299         else if (imageTable[k] > maxi) maxi=imageTable[k]; 
300     }
301
302   // if (oververbose)
303   //    std::cout << " mini = " << mini << " maxi = " << maxi << std::endl;
304          /// \todo : FIXME what do I do ? Smallest/Largest Image Pixel Value is vr=US, Pixels are signed ?!?
305
306          str.str("");
307          str << mini;
308          fh->InsertEntryString(str.str(),0x0028,0x0106, "US"); // Smallest Image Pixel Value
309
310          str.str("");
311          str << maxi;
312          fh->InsertEntryString(str.str(),0x0028,0x0107, "US"); // Largest Image Pixel Value 
313 */
314          imageNumber = 0;
315
316     // write the imagette
317  
318          // Set the image size
319          str.str("");
320          str << imageDimX*imagetteRowNumber;
321          fh[imageNumber]->InsertEntryString(str.str(),0x0028,0x0011, "US"); // Columns
322
323          str.str("");
324          str << imageDimY*imagetteLineNumber;
325          fh[imageNumber]->InsertEntryString(str.str(),0x0028,0x0010, "US"); // Rows 
326
327          fh[imageNumber]->InsertEntryString(strStudyUID,0x0020,0x000d,"UI");      
328          fh[imageNumber]->InsertEntryString(strSerieUID,0x0020,0x000e,"UI");
329          fh[imageNumber]->InsertEntryString(patName,0x0010,0x0010, "PN");   // Patient's Name
330
331          fh[imageNumber]->SetImageData((uint8_t *)imageTable, totalNumberOfPixels * imagePixelSize);
332
333 // ==================================================================================================
334
335 // This is a dirty heuristics, but no other way :-(
336
337 // if Image Orientation (Patient) is not present
338 //    I create one, (as Axial) 
339 //    if Image Position (Patient) is not present
340 //       I create one, incrementing  zPositionComponent up by user supplied zSpacing
341 //    if Slice Location is not present 
342 //       I create one, as zPositionComponent
343 //
344 // Aware use is free to supply his own one !    
345
346 /*    
347          if (! f[imageNumber]->CheckIfEntryExist(0x0020,0x0037) ) // 0020 0037 DS 6 Image Orientation (Patient)
348          {
349             fh[imageNumber]->InsertEntryString("1.0\\0.0\\0.0\\0.0\\1.0\\0.0",0x0020,0x0037, "DS"); //[1\0\0\0\1\0] : Axial
350
351             char charImagePosition[256];
352      
353             sprintf(charImagePosition,"%f\\0.0\\0.0",zPositionComponent);
354             zPositionComponent += zSpacing;
355       
356             if (! f[imageNumber]->CheckIfEntryExist(0x0020,0x0032) ) //0020 0032 DS 3 Image Position (Patient)
357                fh[imageNumber]->InsertEntryString(charImagePosition,0x0020,0x0032, "DS"); 
358     
359             if (! f[imageNumber]->CheckIfEntryExist(0x0020,0x1041) ) // 0020 0x1041 DS 1 Slice Location
360             {
361                sprintf(charImagePosition,"%f",zPositionComponent);
362                fh[imageNumber]->InsertEntryString(charImagePosition,0x0020,0x1041, "DS");
363             }    
364          } 
365 */
366
367 // ==================================================================================================
368
369          fh[imageNumber]->SetWriteTypeToDcmExplVR();               
370          fh[imageNumber]->SetContentType(GDCM_NAME_SPACE::UNMODIFIED_PIXELS_IMAGE);
371
372       
373          lastFilename =  GDCM_NAME_SPACE::Util::GetName( fullFilename );
374          std::string fullWriteFilename = strDirNameout + GDCM_NAME_SPACE::GDCM_FILESEPARATOR 
375                                        + lastFilename;
376          if (verbose)
377             std::cout << "Write : [" << fullWriteFilename << "]" << std::endl;
378     
379          if (!fh[imageNumber]->Write(fullWriteFilename))
380          {
381             std::cout << "Fail to write :[" << fullWriteFilename << "]"
382                       << std::endl;
383          }
384  
385          for(int k=0; k < nbOfImagesInVolume; k++)
386          {
387             fh[k]->Delete();
388             f[k]->Delete();
389          }
390
391       }  // end : 'write the imagette'
392       
393       else // start a new 'volume'
394       {
395          imageNumber++;
396       }
397
398    }
399 }