1 /*=========================================================================
4 Module: $RCSfile: exConvert3DplusT.cxx,v $
6 Date: $Date: 2008/02/13 19:02:39 $
7 Version: $Revision: 1.6 $
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.
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 =========================================================================*/
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
22 #include "gdcmFileHelper.h"
23 #include "gdcmCommon.h"
24 #include "gdcmDebug.h"
25 #include "gdcmDirList.h"
28 #include "gdcmArgMgr.h"
32 #include <string.h> // for memset
34 int main(int argc, char *argv[])
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]",
46 " dirin : single-level Directory containing the images ",
47 " (no recursive parsing) ",
48 " dirout : will be created if doesn't exist ",
50 " [imdimx = ] [imdimy = ] [imgline = ] [imgcol = ] ",
51 " [pixelsize = ] [imagesinvolume = ] ",
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' ",
68 // ----- Initialize Arguments Manager ------
69 GDCM_NAME_SPACE::ArgMgr *am = new GDCM_NAME_SPACE::ArgMgr(argc, argv);
70 if (am->ArgMgrDefined("usage") || argc == 1)
72 am->ArgMgrUsage(usage); // Display 'usage'
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);
84 const char *dirIn = am->ArgMgrGetString("dirin");
85 const char *dirOut = am->ArgMgrGetString("dirout");
87 bool userDefinedStudy = am->ArgMgrDefined("studyUID");
88 const char *studyUID = am->ArgMgrGetString("studyUID");
90 // not described *on purpose* in the Usage !
91 bool userDefinedSerie = am->ArgMgrDefined("serieUID");
92 const char *serieUID = am->ArgMgrGetString("serieUID");
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);
103 int loadMode = GDCM_NAME_SPACE::LD_ALL;
104 if ( am->ArgMgrDefined("noshadowseq") )
105 loadMode |= GDCM_NAME_SPACE::LD_NOSHADOWSEQ;
108 if ( am->ArgMgrDefined("noshadow") )
109 loadMode |= GDCM_NAME_SPACE::LD_NOSHADOW;
110 if ( am->ArgMgrDefined("noseq") )
111 loadMode |= GDCM_NAME_SPACE::LD_NOSEQ;
114 /* if unused Param we give up */
115 if ( am->ArgMgrPrintUnusedLabels() )
117 am->ArgMgrUsage(usage);
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) )
127 std::cout << "KO : [" << dirIn << "] is not a Directory." << std::endl;
133 std::cout << "OK : [" << dirIn << "] is a Directory." << std::endl;
136 std::string systemCommand;
137 std::string strDirNameout(dirOut); // to please gcc 4
139 std::cout << "Check for output directory :[" << dirOut << "]."
141 if ( ! GDCM_NAME_SPACE::DirList::IsDirectory(dirOut) ) // dirout not found
143 systemCommand = "mkdir " +strDirNameout; // create it!
145 std::cout << systemCommand << std::endl;
146 system (systemCommand.c_str());
147 if ( ! GDCM_NAME_SPACE::DirList::IsDirectory(dirOut) ) // be sure it worked
149 std::cout << "KO : not a dir : [" << dirOut
150 << "] (creation failure ?)" << std::endl;
156 std::cout << "Directory [" << dirOut << "] created." << std::endl;
162 std::cout << "Output Directory [" << dirOut
163 << "] already exists; Used as is." << std::endl;
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() );
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();
181 strStudyUID = studyUID;
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();
192 strSerieUID = serieUID;
194 std::ostringstream str;
195 size_t totalNumberOfPixels = imageDimX*imageDimY * imagetteLineNumber*imagetteRowNumber;
196 int16_t *imageTable = new int16_t[totalNumberOfPixels];
198 memset(imageTable, 0, totalNumberOfPixels * imagePixelSize);
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];
204 std::string fullFilename, lastFilename;
205 float zPositionComponent = 0.0;
207 for( GDCM_NAME_SPACE::DirListType::iterator it = fileList.begin();
208 it != fileList.end();
212 f[imageNumber] = GDCM_NAME_SPACE::File::New( );
213 f[imageNumber]->SetLoadMode(loadMode);
214 f[imageNumber]->SetFileName( it->c_str() );
217 std::cout << "file [" << it->c_str() << "], as imageNumber : " << imageNumber << std::endl;
219 if ( !f[imageNumber]->Load() )
222 std::cout << "fail to load [" << it->c_str() << "]" << std::endl;
223 f[imageNumber]->Delete();
227 // Load the pixels in RAM.
229 fh[imageNumber] = GDCM_NAME_SPACE::FileHelper::New(f[imageNumber]);
230 // Don't convert (Gray Pixels + LUT) into (RGB pixels) ?!?
232 tabImageData[imageNumber] = (int16_t *)fh[imageNumber]->GetImageDataRaw();
233 if (!tabImageData[imageNumber])
235 std::cout << "fail to read [" << it->c_str() << std::endl;
240 if (imageNumber == nbOfImagesInVolume-1)
242 for(imageNumber=0; imageNumber < nbOfImagesInVolume; imageNumber++)
244 int debMove = (imageNumber%imagetteRowNumber) * imageDimX
245 + (imageNumber/imagetteRowNumber) *imageDimX*imageDimY*imagetteRowNumber;
248 std::cout << "imageNumber " << imageNumber << " debMove " << debMove << std::endl;
249 for(int i=0; i<imageDimY; i++)
251 int debLigne = debMove + i*imagetteRowNumber*imageDimX;
253 // std::cout << "numLigne " << i << " debLigne " << debLigne
254 // << ": " << debMove << " + " << i << " * " << imagetteRowNumber*imageDimX << std::endl;
255 for (int j=0; j<imageDimX; j++)
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);
269 // std::cout << " mini = " << mini << " maxi = " << maxi << std::endl;
270 // just to check (actually, it's useless)
274 for (int k=0; k < totalNumberOfPixels; k++)
276 if (imageTable[k] < mini) mini=imageTable[k];
277 else if (imageTable[k] > maxi) maxi=imageTable[k];
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 ?!?
286 fh->InsertEntryString(str.str(),0x0028,0x0106, "US"); // Smallest Image Pixel Value
290 fh->InsertEntryString(str.str(),0x0028,0x0107, "US"); // Largest Image Pixel Value
294 // write the imagette
296 // Set the image size
298 str << imageDimX*imagetteRowNumber;
299 fh[imageNumber]->InsertEntryString(str.str(),0x0028,0x0011, "US"); // Columns
302 str << imageDimY*imagetteLineNumber;
303 fh[imageNumber]->InsertEntryString(str.str(),0x0028,0x0010, "US"); // Rows
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
309 fh[imageNumber]->SetImageData((uint8_t *)imageTable, totalNumberOfPixels * imagePixelSize);
311 // ==================================================================================================
313 // This is a dirty heuristics, but no other way :-(
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
322 // Aware user is free to supply his own one !
325 if (! f[imageNumber]->CheckIfEntryExist(0x0020,0x0037) ) // 0020 0037 DS 6 Image Orientation (Patient)
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
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
336 sprintf(charImagePosition,"%f",zPositionComponent);
337 fh[imageNumber]->InsertEntryString(charImagePosition,0x0020,0x1041, "DS");
342 // ==================================================================================================
344 fh[imageNumber]->SetWriteTypeToDcmExplVR();
345 fh[imageNumber]->SetContentType(GDCM_NAME_SPACE::UNMODIFIED_PIXELS_IMAGE);
347 lastFilename = GDCM_NAME_SPACE::Util::GetName( fullFilename );
348 std::string fullWriteFilename = strDirNameout + GDCM_NAME_SPACE::GDCM_FILESEPARATOR
351 std::cout << "Write : [" << fullWriteFilename << "]" << std::endl;
352 if (!fh[imageNumber]->Write(fullWriteFilename))
354 std::cout << "Fail to write :[" << fullWriteFilename << "]"
358 for(int k=0; k < nbOfImagesInVolume; k++)
364 } // end : 'write the imagette'
365 else // start a new 'volume'