]> Creatis software - clitk.git/blob - tools/clitkDicom2Image.cxx
From Benoit P, use clitkDicomRTStruct2Image with image with direction cosine
[clitk.git] / tools / clitkDicom2Image.cxx
1 /*=========================================================================
2   Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
3
4   Authors belong to:
5   - University of LYON              http://www.universite-lyon.fr/
6   - Léon Bérard cancer center       http://www.centreleonberard.fr
7   - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
8
9   This software is distributed WITHOUT ANY WARRANTY; without even
10   the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11   PURPOSE.  See the copyright notices for more information.
12
13   It is distributed under dual licence
14
15   - BSD        See included LICENSE.txt file
16   - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17 ===========================================================================**/
18
19 // clitk includes
20 #include "clitkIO.h"
21 #include "clitkDicom2Image_ggo.h"
22 #include "clitkCommon.h"
23 #include "clitkImageCommon.h"
24 #include "vvImageReader.h"
25 #include "vvImageWriter.h"
26 #include <itkGDCMImageIO.h>
27 #include <itkGDCMSeriesFileNames.h>
28 #include <gdcmFile.h>
29 #include <vtkVersion.h>
30 #include <vtkImageChangeInformation.h>
31 #if GDCM_MAJOR_VERSION >= 2
32   #include <gdcmImageHelper.h>
33   #include <gdcmAttribute.h>
34   #include <gdcmReader.h>
35 #endif
36
37 #include <set>
38
39 //====================================================================
40 int main(int argc, char * argv[])
41 {
42   // init command line
43   GGO(clitkDicom2Image, args_info);
44   CLITK_INIT;
45
46   std::vector<std::string> input_files;
47   ///if std_input is given, read the input files from stdin
48   if (args_info.std_input_given) {
49     while (true) {
50       std::string tmp;
51       std::cin >> tmp;
52       if(std::cin.good())
53         input_files.push_back(tmp);
54       else break;
55     }
56     args_info.inputs_num=input_files.size();
57   } else for (unsigned int i=0; i<args_info.inputs_num; i++)
58       input_files.push_back(args_info.inputs[i]);
59
60   //Get GDCMSeriesFileNames order to sort filenames
61   typedef itk::GDCMSeriesFileNames NamesGeneratorType;
62   NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New();
63   nameGenerator->SetUseSeriesDetails(false);
64   std::string folderName=".";
65 #ifdef _WIN32
66   const size_t last_slash_idx = input_files[0].rfind('\\');
67 #else
68   const size_t last_slash_idx = input_files[0].rfind('/');
69 #endif
70   if (std::string::npos != last_slash_idx)
71     folderName = input_files[0].substr(0, last_slash_idx);
72   nameGenerator->SetInputDirectory(folderName);
73
74   //===========================================
75   /// Get slices locations ...
76   std::string series_UID = "";
77   std::set<std::string> series_UIDs;
78   std::map< std::string, std::vector<double> > theorigin;
79   std::map< std::string, std::vector<double> > theorientation;
80   std::map< std::string, std::vector<double> > sliceLocations;
81   std::map< std::string, std::vector<double> > instanceNumber;
82   std::map< std::string, std::vector<std::string> > seriesFiles;
83 #if GDCM_MAJOR_VERSION >= 2
84   if (args_info.verbose_flag)
85     std::cout << "Using GDCM-2.x" << std::endl;
86 #else
87   if (args_info.verbose_flag) {
88     std::cout << "Not using GDCM-2.x" << std::endl;
89     std::cout<< "The image orientation is not supported with this version of GDCM" <<std::endl;
90   }
91 #endif
92   for(unsigned int i=0; i<args_info.inputs_num; i++) {
93     if (args_info.verbose_flag)
94         std::cout << "Reading < " << input_files[i] << std::endl;
95 #if GDCM_MAJOR_VERSION >= 2
96     gdcm::Reader hreader;
97     hreader.SetFileName(input_files[i].c_str());
98     hreader.Read();
99     gdcm::DataSet& ds = hreader.GetFile().GetDataSet();
100
101     gdcm::Attribute<0x20,0x000e> series_UID_att;
102     series_UID_att.SetFromDataSet(ds);
103     series_UID = series_UID_att.GetValue().c_str();
104
105     series_UIDs.insert(series_UID);
106     theorigin[series_UID] = gdcm::ImageHelper::GetOriginValue(hreader.GetFile());
107     theorientation[series_UID] = gdcm::ImageHelper::GetDirectionCosinesValue(hreader.GetFile());
108     if (args_info.patientSystem_flag) {
109       double n1 = theorientation[series_UID][1]*theorientation[series_UID][5]-
110                   theorientation[series_UID][2]*theorientation[series_UID][4];
111       double n2 = theorientation[series_UID][3]*theorientation[series_UID][2]-
112                   theorientation[series_UID][5]*theorientation[series_UID][0];
113       double n3 = theorientation[series_UID][0]*theorientation[series_UID][4]-
114                   theorientation[series_UID][1]*theorientation[series_UID][3];
115       double sloc = theorigin[series_UID][0]*n1+
116                     theorigin[series_UID][1]*n2+
117                     theorigin[series_UID][2]*n3;
118       sliceLocations[series_UID].push_back(sloc);
119     } else
120       sliceLocations[series_UID].push_back(theorigin[series_UID][2]);
121     seriesFiles[series_UID].push_back(input_files[i]);
122
123     gdcm::Attribute<0x20,0x0013> instanceNumber_att;
124     instanceNumber_att.SetFromDataSet(ds);
125     instanceNumber[series_UID].push_back(instanceNumber_att.GetValue());
126
127     gdcm::Attribute<0x28, 0x100> pixel_size;
128     pixel_size.SetFromDataSet(ds);
129     /* if (pixel_size.GetValue() != 16)
130        {
131        std::cerr << "Pixel type not 2 bytes ! " << std::endl;
132        std::cerr << "In file " << input_files[i] << std::endl;
133        exit(0);
134        }
135     */
136 #else
137   gdcm::File *header = new gdcm::File();
138   header->SetFileName(input_files[i]);
139   header->SetMaxSizeLoadEntry(16384); // required ?
140   header->Load();
141
142   series_UID = header->GetEntryValue(0x20,0x000e).c_str();
143
144   series_UIDs.insert(series_UID);
145   theorigin[series_UID].resize(3);
146   theorigin[series_UID][0] = header->GetXOrigin();
147   theorigin[series_UID][1] = header->GetYOrigin();
148   theorigin[series_UID][2] = header->GetZOrigin();
149   sliceLocations[series_UID].push_back(theorigin[series_UID][2]);
150   seriesFiles[series_UID].push_back(input_files[i]);
151   /*if (header->GetPixelSize() != 2) {
152     std::cerr << "Pixel type 2 bytes ! " << std::endl;
153     std::cerr << "In file " << input_files[i] << std::endl;
154     exit(0);
155   }
156   */
157 #endif
158   }
159
160   //===========================================
161   // Sort slices locations ...
162   std::set<std::string>::iterator sn = series_UIDs.begin();
163   while ( sn != series_UIDs.end() ) {
164     std::vector<double> locs = sliceLocations[*sn];
165     std::vector<double> origin = theorigin[*sn];
166     std::vector<double> instanceNumberSerie = instanceNumber[*sn];
167     std::vector<std::string> files = seriesFiles[*sn];
168     //Let's process the filenames -- it is mandatory for the line "if (tempFilename == files[i])"
169     for(unsigned int i=0; i<files.size(); i++) {
170 #ifdef _WIN32
171         const size_t first_slash_idx_fn = files[i].find('\\');
172 #else
173         const size_t first_slash_idx_fn = files[i].find('/');
174 #endif
175         if (std::string::npos != first_slash_idx_fn && first_slash_idx_fn == 1 && files[i][0] == '.')
176           files[i] = files[i].substr(first_slash_idx_fn+1);
177     }
178     std::vector<int> sliceIndex(files.size());
179     //clitk::GetSortedIndex(locs, sliceIndex);
180     //Look for files into GDCMSeriesFileNames, because it sorts files correctly and take the order
181     const std::vector<std::string> & temp = nameGenerator->GetFileNames(*sn);
182     for(unsigned int i=0; i<files.size(); i++) {
183       int j(0);
184       bool found(false);
185       while (!found && j<temp.size()) {
186         std::string tempFilename(temp[j]);
187 #ifdef _WIN32
188         // There is a bug on Windows, the last \ is a /...
189         // Let's substitute it
190         const size_t last_slash_idx_win = tempFilename.rfind('/');
191         tempFilename[last_slash_idx_win] = '\\';
192         const size_t first_slash_idx = tempFilename.find('\\');
193 #else
194         const size_t first_slash_idx = tempFilename.find('/');
195 #endif
196         if (std::string::npos != first_slash_idx && first_slash_idx == 1 && tempFilename[0] == '.')
197           tempFilename = tempFilename.substr(first_slash_idx+1);
198         if (tempFilename == files[i]) {
199           sliceIndex[j] = i;
200           found = true;
201         }
202         ++j;
203       }
204     }
205     if (sliceIndex.size() == 0) { //ie. sn is not a serie present in files
206       sn++;
207       continue;
208     }
209
210     if (args_info.verbose_flag) {
211       std::cout << locs[sliceIndex[0]] << " -> "
212                 << sliceIndex[0] << " / " << 0 << " => "
213                 << "0 mm "
214                 << files[sliceIndex[0]]
215                 << std::endl;
216       for(unsigned int i=1; i<sliceIndex.size(); i++) {
217         std::cout << locs[sliceIndex[i]] << " -> "
218                   << sliceIndex[i] << " / " << i << " => "
219                   << locs[sliceIndex[i]] - locs[sliceIndex[i-1]]
220                   << "mm "
221                   << files[sliceIndex[i]]
222                   << std::endl;
223       }
224     }
225
226     //===========================================
227     // Analyze slices locations ...
228     double currentDist;
229     double dist=0;
230     double tolerance = args_info.tolerance_arg;
231     double previous = locs[sliceIndex[0]];
232     for(unsigned int i=1; i<sliceIndex.size(); i++) {
233       currentDist = locs[sliceIndex[i]]-previous;
234       if (i!=1) {
235         if (fabs(dist-currentDist) > tolerance) {
236           std::cout << "ERROR : " << std::endl
237                     << "Current slice pos is  = " << locs[sliceIndex[i]] << std::endl
238                     << "Previous slice pos is = " << previous << std::endl
239                     << "Current file is       = " << files[sliceIndex[i]] << std::endl
240                     << "Current index is      = " << i << std::endl
241                     << "Current sortindex is  = " << sliceIndex[i] << std::endl
242                     << "Current slice diff    = " << dist << std::endl
243                     << "Current error         = " << fabs(dist-currentDist) << std::endl;
244           exit(1);
245         }
246       } else dist = currentDist;
247       previous = locs[sliceIndex[i]];
248     }
249
250     //===========================================
251     // Create ordered vector of filenames
252     std::vector<std::string> sorted_files;
253     sorted_files.resize(sliceIndex.size());
254     if (!args_info.instanceNumber_flag) {
255       for(unsigned int i=0; i<sliceIndex.size(); i++)
256         sorted_files[i] = files[ sliceIndex[i] ];
257     } else {
258       std::vector<double>::iterator maxInstanceNumber = std::max_element(instanceNumberSerie.begin(), instanceNumberSerie.end());
259       std::vector<std::string> instanceNumberTemp(*maxInstanceNumber, "");
260       for(unsigned int i=0; i<instanceNumberSerie.size(); i++)
261         instanceNumberTemp[instanceNumberSerie[i]-1] = files[i];
262       unsigned int fillFiles(0);
263       for(unsigned int i=0; i<instanceNumberTemp.size(); i++) {
264           if (instanceNumberTemp[i] != "") {
265             sorted_files[fillFiles] = instanceNumberTemp[i];
266             ++fillFiles;
267           }
268        }
269     }
270
271     //===========================================
272     // Reverse the slice order (for MR from HEH)
273     if (args_info.reverse_flag)
274       std::reverse(sorted_files.begin(), sorted_files.end());
275
276     //===========================================
277     // Read write serie
278     vvImageReader::Pointer reader = vvImageReader::New();
279     reader->SetInputFilenames(sorted_files);
280     reader->SetPatientCoordinateSystem(args_info.patientSystem_flag);
281     reader->Update(vvImageReader::DICOM);
282     if (reader->GetLastError().size() != 0) {
283       std::cerr << reader->GetLastError() << std::endl;
284       return 1;
285     }
286
287     vvImage::Pointer image = reader->GetOutput();
288     vtkImageData* vtk_image = image->GetFirstVTKImageData();
289     vtkImageChangeInformation* modifier = vtkImageChangeInformation::New();
290     if  (args_info.focal_origin_given) {
291       std::vector<double> spacing = image->GetSpacing();
292       std::vector<int> size = image->GetSize();
293       origin[0] = -spacing[0]*size[0]/2.0;
294       origin[1] = -spacing[1]*size[1]/2.0;
295 #if VTK_MAJOR_VERSION <= 5
296       modifier->SetInput(vtk_image);
297 #else
298       modifier->SetInputData(vtk_image);
299 #endif
300       modifier->SetOutputOrigin(origin[0], origin[1], locs[sliceIndex[0]]);
301       modifier->Update();
302       vvImage::Pointer focal_image = vvImage::New();
303       focal_image->AddVtkImage(modifier->GetOutput(), image->GetTransform()[0]);
304       image = focal_image;
305     }
306
307     std::string outfile;
308     if (series_UIDs.size() == 1)
309       outfile = args_info.output_arg;
310     else {
311       std::ostringstream name;
312       std::vector<std::string> directory = clitk::SplitFilename(args_info.output_arg);
313       if (directory.size() == 2)
314 #ifdef _WIN32
315         name << directory[0] << "\\" << *sn << "_" << directory[1];
316 #else
317         name << directory[0] << "/" << *sn << "_" << directory[1];
318 #endif
319       else
320         name << *sn << "_" << args_info.output_arg;
321       outfile = name.str();
322     }
323     vvImageWriter::Pointer writer = vvImageWriter::New();
324     writer->SetInput(image);
325     if (args_info.patientSystem_flag && !image->GetTransform().empty())
326       writer->SetSaveTransform(true);
327     writer->SetOutputFileName(outfile);
328     writer->Update();
329
330     modifier->Delete();
331
332     sn++;
333   }
334
335   return 0;
336 }