]> Creatis software - gdcm.git/blob - Example/exConvert3DplusT.cxx
//
[gdcm.git] / Example / exConvert3DplusT.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: exConvert3DplusT.cxx,v $
5   Language:  C++
6   Date:      $Date: 2006/07/19 09:03:24 $
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 //
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    // 'Study Instance UID'
181    // The user is allowed to create his own Study, 
182    //          keeping the same 'Study Instance UID' for various images
183    // The user may add images to a 'Manufacturer Study',
184    //          adding new Series to an already existing Study
185    std::string strStudyUID;
186    if ( !userDefinedStudy)
187       strStudyUID =  gdcm::Util::CreateUniqueUID();
188    else
189       strStudyUID = studyUID;
190
191
192    // 'Serie Instance UID'
193    // The user is allowed to create his own Series, 
194    // keeping the same 'Serie Instance UID' for various images
195    // The user shouldn't add any image to a 'Manufacturer Serie'
196    // but there is no way no to prevent him for doing that
197    
198    std::string strSerieUID; 
199    if ( !userDefinedSerie)   
200       strSerieUID =  gdcm::Util::CreateUniqueUID();
201    else      
202       strSerieUID = serieUID;
203
204    std::ostringstream str;
205   
206    size_t totalNumberOfPixels =  imageDimX*imageDimY * imagetteLineNumber*imagetteRowNumber;     
207    int16_t *imageTable = new int16_t[totalNumberOfPixels]; 
208    
209    //std::cout << " imageTable length " << totalNumberOfPixels;
210    memset(imageTable, 0, totalNumberOfPixels * imagePixelSize);
211              
212    int16_t **tabImageData = new int16_t *[nbOfImagesInVolume];
213    
214    gdcm::File *f;
215    gdcm::FileHelper *fh;
216    std::string fullFilename, lastFilename;
217    float zPositionComponent = 0.0;
218       
219    int imageNumber = 0;
220    
221    for( gdcm::DirListType::iterator it  = fileList.begin();
222                                  it != fileList.end();
223                                  ++it )
224    {
225       fullFilename = *it;
226       f = gdcm::File::New( );
227       f->SetLoadMode(loadMode);
228       f->SetFileName( it->c_str() );
229
230       if (verbose)
231          std::cout << "file [" << it->c_str() << "]" << std::endl;
232       if ( !f->Load() )
233       {
234          if (verbose)
235             std::cout << "fail to load [" << it->c_str() << "]" << std::endl;      
236          f->Delete();
237          continue;
238       }
239
240       // Load the pixels in RAM.    
241       
242       fh = gdcm::FileHelper::New(f); 
243       // Don't convert (Gray Pixels + LUT) into (RGB pixels) ?!?
244    
245       tabImageData[imageNumber] = (int16_t *)fh->GetImageDataRaw();
246       if (!tabImageData[imageNumber]) 
247       {
248          std::cout << "fail to read [" << it->c_str() << std::endl;
249          continue;
250       }
251       int16_t mini=32000;
252       int16_t maxi=-32000;
253        
254       if (imageNumber == nbOfImagesInVolume)
255       {        
256          for(imageNumber=0; imageNumber < nbOfImagesInVolume; imageNumber++)
257          {
258             int debMove = (imageNumber%imagetteRowNumber) * imageDimX 
259                 + (imageNumber/imagetteRowNumber) *imageDimX*imageDimY*imagetteRowNumber;
260
261             if (verbose)
262                std::cout << "imageNumber " << imageNumber << " debMove " << debMove << std::endl;
263       
264             for(int i=0; i<imageDimY; i++)
265             {
266                int debLigne = debMove + i*imagetteRowNumber*imageDimX;
267                //if (oververbose)
268                //   std::cout << "numLigne " << i << " debLigne " << debLigne 
269                //   << ": " << debMove << " + " << i << " * " << imagetteRowNumber*imageDimX << std::endl;
270                for (int j=0; j<imageDimX; j++)
271                {
272                   //std::cout << "j " << j << std::endl;
273   
274                   imageTable[debLigne + j] = *(tabImageData[imageNumber] + i*imageDimY + j);
275                   if (imageTable[debLigne + j] < 0) imageTable[debLigne + j]=0;
276                      if (imageTable[debLigne + j] > 100) imageTable[debLigne + j]=100;
277                      //std::cout << debLigne + j << " : " << imageTable[debLigne + j] << std::endl;
278   
279                      if (*(tabImageData[imageNumber] + i*imageDimY + j) < mini)
280                                         mini=*(tabImageData[imageNumber] + i*imageDimY + j);
281                      else if (*(tabImageData[imageNumber] + i*imageDimY + j) > maxi) 
282                                         maxi=*(tabImageData[imageNumber] + i*imageDimY + j); 
283              }
284           }   
285       }
286   //   if (oververbose)
287   //    std::cout << " mini = " << mini << " maxi = " << maxi << std::endl;
288   // just to check (actually, it's useless)       
289   /*
290      int16_t mini=32000;
291      int16_t maxi=-32000;
292      for (int k=0; k < totalNumberOfPixels; k++)
293      {
294         if (imageTable[k] < mini) mini=imageTable[k];
295         else if (imageTable[k] > maxi) maxi=imageTable[k]; 
296     }
297
298   // if (oververbose)
299   //    std::cout << " mini = " << mini << " maxi = " << maxi << std::endl;
300          /// \todo : FIXME what do I do ? Smallest/Largest Image Pixel Value is vr=US, Pixels are signed ?!?
301
302          str.str("");
303          str << mini;
304          fh->InsertEntryString(str.str(),0x0028,0x0106, "US"); // Smallest Image Pixel Value
305
306          str.str("");
307          str << maxi;
308          fh->InsertEntryString(str.str(),0x0028,0x0107, "US"); // Largest Image Pixel Value 
309 */
310          imageNumber = 0;
311
312     // write the imagette
313  
314          // Set the image size
315          str.str("");
316          str << imageDimX*imagetteRowNumber;
317          fh->InsertEntryString(str.str(),0x0028,0x0011, "US"); // Columns
318
319          str.str("");
320          str << imageDimY*imagetteLineNumber;
321          fh->InsertEntryString(str.str(),0x0028,0x0010, "US"); // Rows 
322
323          fh->InsertEntryString(strStudyUID,0x0020,0x000d,"UI");      
324          fh->InsertEntryString(strSerieUID,0x0020,0x000e,"UI");
325          fh->InsertEntryString(patName,0x0010,0x0010, "PN");   // Patient's Name
326
327          fh->SetImageData((uint8_t *)imageTable, totalNumberOfPixels * imagePixelSize);
328
329 // ==================================================================================================
330
331 // This is a dirty heuristics, but no other way :-(
332
333 // if Image Orientation (Patient) is not present
334 //    I create one, (as Axial) 
335 //    if Image Position (Patient) is not present
336 //       I create one, incrementing  zPositionComponent up by user supplied zSpacing
337 //    if Slice Location is not present 
338 //       I create one, as zPositionComponent
339 //
340 // Aware use is free to supply his own one !    
341
342 /*    
343          if (! f->CheckIfEntryExist(0x0020,0x0037) ) // 0020 0037 DS 6 Image Orientation (Patient)
344          {
345             fh->InsertEntryString("1.0\\0.0\\0.0\\0.0\\1.0\\0.0",0x0020,0x0037, "DS"); //[1\0\0\0\1\0] : Axial
346
347             char charImagePosition[256];
348      
349             sprintf(charImagePosition,"%f\\0.0\\0.0",zPositionComponent);
350             zPositionComponent += zSpacing;
351       
352             if (! f->CheckIfEntryExist(0x0020,0x0032) ) //0020 0032 DS 3 Image Position (Patient)
353                fh->InsertEntryString(charImagePosition,0x0020,0x0032, "DS"); 
354     
355             if (! f->CheckIfEntryExist(0x0020,0x1041) ) // 0020 0x1041 DS 1 Slice Location
356             {
357                sprintf(charImagePosition,"%f",zPositionComponent);
358                fh->InsertEntryString(charImagePosition,0x0020,0x1041, "DS");
359             }    
360          } 
361
362 */
363
364 // ==================================================================================================
365
366          fh->SetWriteTypeToDcmExplVR();               
367          fh->SetContentType(gdcm::UNMODIFIED_PIXELS_IMAGE);
368
369       
370          lastFilename =  gdcm::Util::GetName( fullFilename );
371          std::string fullWriteFilename = strDirNameout + gdcm::GDCM_FILESEPARATOR 
372                                        + lastFilename;
373          if (verbose)
374             std::cout << "Write : [" << fullWriteFilename << "]" << std::endl;
375          if (!fh->Write(fullWriteFilename))
376          {
377             std::cout << "Fail to write :[" << fullWriteFilename << "]"
378                       << std::endl;
379          }
380          //break;   
381       }  // end : 'write the imagette'
382       else
383       {
384          imageNumber++;
385       }
386       fh->Delete();
387       f->Delete();
388    }
389 }