]> Creatis software - gdcm.git/blob - Example/RawToDicom.cxx
Add some explanations
[gdcm.git] / Example / RawToDicom.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: RawToDicom.cxx,v $
5   Language:  C++
6   Date:      $Date: 2009/01/19 17:05:13 $
7   Version:   $Revision: 1.17 $
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 /**
20  * Writes a Dicom file from a Raw File
21  * User has to supply parameters. 
22  * 
23  */
24 #include "gdcmFile.h"
25 #include "gdcmFileHelper.h"
26 #include "gdcmDebug.h"
27 #include "gdcmUtil.h"
28 #include "gdcmArgMgr.h"
29
30 #include <iostream>
31 #include <sstream>
32
33 typedef char               * PS8;
34 typedef unsigned char      * PU8;
35 typedef short int          * PS16;
36 typedef unsigned short int * PU16;
37 typedef int                * PS32;
38 typedef unsigned int       * PU32;
39 typedef float              * PF32;
40 typedef double             * PD64;
41
42 #define CRR(t1,t2)   { for(int l=0;l<nX*nY*nZ*samplesPerPixel;l++)              \
43                                *((t2)planePixelsOut + l) = *(((t1)pixels)+ l);\
44                      }
45     
46 #define CFR(PPt)  switch ( pixelTypeOutCode ) {     \
47           case -8   : CRR(PPt,PS8);  break;         \
48           case 8    : CRR(PPt,PU8);  break;         \
49           case -16  : CRR(PPt,PS16); break;         \
50           case 16   : CRR(PPt,PU16); break;         \
51           case -32  : CRR(PPt,PS32); break;         \
52           case 32   : CRR(PPt,PU32); break;         \
53           case 33   : CRR(PPt,PF32); break;         \
54           case 64   : CRR(PPt,PD64); break;         \
55           }
56
57
58 void ConvertSwapZone(int pixelSize, void *Raw, size_t RawSize);
59
60 void ConvertSwapZone(int pixelSize, void *Raw, size_t RawSize)
61 {
62    unsigned int i;    
63    if ( pixelSize == 2 )
64    {
65       uint16_t *im16 = (uint16_t*)Raw;
66       for( i = 0; i < RawSize / 2; i++ )
67       {
68          im16[i]= (im16[i] >> 8) | (im16[i] << 8 );
69       }     
70    }
71    else if ( pixelSize == 4 )
72    {
73       uint32_t s32;
74       uint16_t high;
75       uint16_t low;
76       uint32_t *im32 = (uint32_t*)Raw;
77
78       for( i = 0; i < RawSize / 4; i++ )
79       {
80          low     = im32[i] & 0x0000ffff; // 3412
81          high    = im32[i] >> 16;
82          s32     = low;
83          im32[i] = ( s32 << 16 ) | high;
84       }
85       
86    }
87 }
88
89 int main(int argc, char *argv[])
90 {
91    START_USAGE(usage)
92    " \n RawToDicom : \n                                                       ",
93    " Writes a Dicom file from a Raw File                                      ",
94    " usage: RawToDicom filein=inputFileName                                   ",
95    "                   fileout=outputFileName                                 ",
96    "                   rows=nb of Rows                                        ",
97    "                   lines=nb of Lines,                                     ",
98    "                   [frames = nb of Frames] //defaulted to 1               ",
99    "                   pixeltype={8U|8S|16U|16S|32U|32S|32F|64D}              ",
100    "                   pixeltypeout={8U|8S|16U|16S|32U|32S}                   ",
101    "                   [{b|l}] b:BigEndian,l:LittleEndian default : l         ",
102    "                   [samples = {1|3}}       //(1:Gray,3:RGB) defaulted to 1",
103    "                   [monochrome1]                                          ",
104    "                   [studyid = ] [patientname = Patient's name]            ",
105    "                   [debug]                                                ",
106    "                                                                          ",
107    "  monochrome1 = user wants MONOCHROME1 photom. interp. (0=white)          ",
108    "  studyUID   : *aware* user wants to add the serie                        ",
109    "                                             to an already existing study ",
110    "  debug      : developper wants to run the program in 'debug mode'        ",
111    FINISH_USAGE
112    
113
114    // ------------ Initialize Arguments Manager ----------------  
115    GDCM_NAME_SPACE::ArgMgr *am= new GDCM_NAME_SPACE::ArgMgr(argc, argv);
116   
117    if (argc == 1 || am->ArgMgrDefined("usage") )
118    {
119       am->ArgMgrUsage(usage); // Display 'usage'
120       delete am;
121       return 1;
122    }
123
124    const char *inputFileName  = am->ArgMgrGetString("filein");
125    const char *outputFileName = am->ArgMgrGetString("fileout");
126    
127    const char *patientName = am->ArgMgrGetString("patientname", "g^Fantomas");
128    
129    int nX = am->ArgMgrWantInt("rows", usage);
130    int nY = am->ArgMgrWantInt("lines", usage);
131    int nZ = am->ArgMgrGetInt("frames", 1);
132    int samplesPerPixel = am->ArgMgrGetInt("samples", 1);
133    
134    int b = am->ArgMgrDefined("b");
135    int l = am->ArgMgrDefined("l");
136       
137    char *pixelType = am->ArgMgrWantString("pixeltype", usage);
138    const char *pixelTypeOut = am->ArgMgrGetString("pixeltypeout", pixelType);   
139
140    bool monochrome1 = ( 0 != am->ArgMgrDefined("monochrome1") );
141    
142    if (am->ArgMgrDefined("debug"))
143       GDCM_NAME_SPACE::Debug::DebugOn();
144
145    bool userDefinedStudy = ( 0 != am->ArgMgrDefined("studyUID") );
146    const char *studyUID;
147    if (userDefinedStudy)
148       studyUID  = am->ArgMgrGetString("studyUID");  
149
150    // not described *on purpose* in the Usage !
151    bool userDefinedSerie = ( 0 != am->ArgMgrDefined("serieUID") );       
152    const char *serieUID;
153    if(userDefinedSerie)
154       serieUID = am->ArgMgrGetString("serieUID");
155
156    /* if unused Param we give up */
157    if ( am->ArgMgrPrintUnusedLabels() )
158    {
159       am->ArgMgrUsage(usage);
160       delete am;
161       return 1;
162    } 
163
164    delete am;  // we don't need Argument Manager any longer
165
166    // ----------- End Arguments Manager ---------
167    
168  /// \TODO Deal with all the images of a directory
169   
170  // Read the Raw file  
171    std::ifstream *Fp = new std::ifstream(inputFileName, std::ios::in | std::ios::binary);
172    if ( ! *Fp )
173    {   
174       std::cout << "Cannot open file: " << inputFileName;
175       delete Fp;
176       Fp = 0;
177       return 0;
178    }  
179
180    bool bigEndian = GDCM_NAME_SPACE::Util::IsCurrentProcessorBigEndian();
181
182    std::string strPixelType(pixelType);
183    int pixelSign;
184    int pixelSize;
185    int pixelTypeCode; // for the switch case
186    
187    if (strPixelType == "8S")
188    {
189       pixelSize = 1;
190       pixelSign = 1;
191       pixelTypeCode = -8;
192    }
193    else if (strPixelType == "8U")
194    {
195       pixelSize = 1;
196       pixelSign = 0;
197       pixelTypeCode = 8;
198    }
199    else if (strPixelType == "16S")
200    {
201       pixelSize = 2;
202       pixelSign = 1;
203       pixelTypeCode = -16;
204    }   
205    else if (strPixelType == "16U")
206    {
207       pixelSize = 2;
208       pixelSign = 0;
209       pixelTypeCode = 16;
210    }      
211    else if (strPixelType == "32S")
212    {
213       pixelSize = 4;
214       pixelSign = 1;
215       pixelTypeCode = -32;
216    }   
217    else if (strPixelType == "32U")
218    {
219       pixelSize = 4;
220       pixelSign = 0;
221       pixelTypeCode = 32;
222    }
223    else if (strPixelType == "32F")
224    {
225       pixelSize = 4;
226       pixelSign = 0;
227       pixelTypeCode = 33;
228    }
229
230    else if (strPixelType == "64D")
231    {
232       pixelSize = 8;
233       pixelSign = 0;
234       pixelTypeCode = 64;
235    }
236    else
237    {
238       std::cout << "Wrong 'pixeltype' (" << strPixelType << ")" << std::endl;
239       return 1;
240    }
241
242    std::string strPixelTypeOut(pixelTypeOut);
243    int pixelSignOut;
244    int pixelSizeOut;
245    int pixelTypeOutCode; // for the switch case
246     
247    if (strPixelTypeOut == "8S")
248    {
249       pixelSizeOut = 1;
250       pixelSignOut = 1;
251       pixelTypeOutCode = -8;
252    }
253    else if (strPixelTypeOut == "8U")
254    {
255       pixelSizeOut = 1;
256       pixelSignOut = 0;
257       pixelTypeOutCode = 8;
258    }
259    else if (strPixelTypeOut == "16S")
260    {
261       pixelSizeOut = 2;
262       pixelSignOut = 1; 
263       pixelTypeOutCode = -16;
264    }   
265    else if (strPixelTypeOut == "16U")
266    {
267       pixelSizeOut = 2;
268       pixelSignOut = 0;
269       pixelTypeOutCode = 16;
270    }      
271    else if (strPixelTypeOut == "32S")
272    {
273       pixelSizeOut = 4;
274       pixelSignOut = 1;
275       pixelTypeOutCode = -32;
276    }   
277    else if (strPixelTypeOut == "32U")
278    {
279       pixelSizeOut = 4;
280       pixelSignOut = 0;
281       pixelTypeOutCode = 32;
282    }
283    else
284    {
285       std::cout << "Wrong 'pixeltypeout' (" << strPixelTypeOut << ")" << std::endl;
286       return 1;
287    }
288
289    std::string strStudyUID;
290    std::string strSerieUID;
291
292    if (userDefinedStudy)
293       strSerieUID = studyUID;
294    else
295       strStudyUID = GDCM_NAME_SPACE::Util::CreateUniqueUID();
296    
297    if (userDefinedSerie)
298      strSerieUID = serieUID;
299    else
300       strSerieUID = GDCM_NAME_SPACE::Util::CreateUniqueUID();
301       
302    // Read the pixels
303
304    int singlePlaneDataSize =  nX*nY*samplesPerPixel*pixelSizeOut;
305    int dataSizeIn          =  nX*nY*samplesPerPixel*pixelSize*nZ;
306
307    uint8_t *pixels         = new uint8_t[dataSizeIn];
308    uint8_t *planePixelsOut = new uint8_t[singlePlaneDataSize];
309
310    Fp->read((char*)pixels, (size_t)dataSizeIn);
311
312    if ( pixelSize !=1 && ( (l && bigEndian) || (b && ! bigEndian) ) )
313    {  
314       ConvertSwapZone(pixelSize, pixels, dataSizeIn);
315    }
316
317 // Copy (and convert) pixels of a single plane
318
319      switch ( pixelTypeCode )
320      {
321        case 8    : CFR(PU8);  break;
322        case -8   : CFR(PS8);  break;
323        case -16  : CFR(PU16); break;
324        case 16   : CFR(PS16); break;
325        case -32  : CFR(PS32); break;
326        case 32   : CFR(PU32); break;
327        case 33   : CFR(PF32); break;
328        case 64   : CFR(PD64); break;
329      }
330
331 // Create an empty FileHelper
332
333    GDCM_NAME_SPACE::FileHelper *fileH = GDCM_NAME_SPACE::FileHelper::New();
334  
335  // Get the (empty) image header.
336    GDCM_NAME_SPACE::File *fileToBuild = fileH->GetFile();
337
338    // 'Study Instance UID'
339    // The user is allowed to create his own Study, 
340    //          keeping the same 'Study Instance UID' for various images
341    // The user may add images to a 'Manufacturer Study',
342    //          adding new Series to an already existing Study
343
344    fileToBuild->InsertEntryString(strStudyUID,0x0020,0x000d,"UI");  //  Study UID   
345
346    // 'Serie Instance UID'
347    // The user is allowed to create his own Series, 
348    // keeping the same 'Serie Instance UID' for various images
349    // The user shouldn't add any image to a 'Manufacturer Serie'
350    // but there is no way no to prevent him for doing that
351    
352    fileToBuild->InsertEntryString(strSerieUID,0x0020,0x000e,"UI");  //  Serie UID
353    
354    std::ostringstream str;
355
356    // Set the image size
357    str.str("");
358    str << nX;
359    fileToBuild->InsertEntryString(str.str(),0x0028,0x0011, "US"); // Columns
360    str.str("");
361    str << nY;
362    fileToBuild->InsertEntryString(str.str(),0x0028,0x0010, "US"); // Rows
363    
364    str.str("");
365    str << nZ;
366    fileToBuild->InsertEntryString(str.str(),0x0028,0x0008, "IS"); // Number of Frames
367
368    // Set the pixel type
369    
370    str.str("");
371    str << pixelSizeOut*8;
372    fileToBuild->InsertEntryString(str.str(),0x0028,0x0100, "US"); // Bits Allocated
373
374    str.str("");
375    str << pixelSizeOut*8;
376    fileToBuild->InsertEntryString(str.str(),0x0028,0x0101, "US"); // Bits Stored
377
378    str.str("");
379    str << ( pixelSizeOut*8 - 1 );
380    fileToBuild->InsertEntryString(str.str(),0x0028,0x0102, "US"); // High Bit
381
382    str.str("");
383    str << pixelSign;
384    fileToBuild->InsertEntryString(str.str(),0x0028,0x0103, "US"); // Pixel Representation
385    
386 // If you deal with a Serie of images, as slices of a volume,
387 // it up to you to tell gdcm, for each image, what are the values of :
388 // 
389 // 0020 0032 DS 3 Image Position (Patient)
390 // 0020 0037 DS 6 Image Orientation (Patient)
391
392    str.str("");
393    str << "0.0 \\ 0.0 \\ 0.0";
394    fileToBuild->InsertEntryString(str.str(),0x0020,0x0032, "DS");
395
396    str.str("");
397    str << samplesPerPixel;
398    fileToBuild->InsertEntryString(str.str(),0x0028,0x0002, "US"); // Samples per Pixel
399
400    if (strlen(patientName) != 0)
401       fileToBuild->InsertEntryString(patientName,0x0010,0x0010, "PN"); // Patient's Name
402     
403    //  0=white  
404    if(monochrome1)
405       fileH->SetPhotometricInterpretationToMonochrome1();
406      
407 // Set the image Pixel Data
408    fileH->SetImageData(planePixelsOut,singlePlaneDataSize);
409
410 // Set the writting mode and write the image
411    fileH->SetWriteModeToRaw();
412
413  // Write a DICOM Explicit VR file
414    fileH->SetWriteTypeToDcmExplVR();
415
416    if( !fileH->Write(outputFileName ) )
417    {
418       std::cout << "Failed for [" << outputFileName << "]\n"
419                 << "           File is unwrittable\n";
420    }
421
422    fileH->Delete();
423
424    delete[] pixels;
425    delete[] planePixelsOut;
426    return 1;
427 }