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