]> Creatis software - clitk.git/blob - tools/clitkWriteDicomSeriesGenericFilter.txx
Merge branch 'master' of git.creatis.insa-lyon.fr:clitk
[clitk.git] / tools / clitkWriteDicomSeriesGenericFilter.txx
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 #ifndef clitkWriteDicomSeriesGenericFilter_txx
19 #define clitkWriteDicomSeriesGenericFilter_txx
20
21 /* =================================================
22  * @file   clitkWriteDicomSeriesGenericFilter.txx
23  * @author
24  * @date
25  *
26  * @brief
27  *
28  ===================================================*/
29
30 // clitk
31 #include "clitkResampleImageWithOptionsFilter.h"
32
33
34 namespace clitk
35 {
36
37
38 //-----------------------------------------------------------
39 // Constructor
40 //-----------------------------------------------------------
41 template<class args_info_type>
42 WriteDicomSeriesGenericFilter<args_info_type>::WriteDicomSeriesGenericFilter()
43 {
44   m_Verbose=false;
45   m_InputFileName="";
46 }
47
48
49 //-----------------------------------------------------------
50 // Update
51 //-----------------------------------------------------------
52 template<class args_info_type>
53 void WriteDicomSeriesGenericFilter<args_info_type>::Update()
54 {
55   // Read the Dimension and PixelType
56   int Dimension;
57   std::string PixelType;
58   ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType);
59
60
61   // Call UpdateWithDim
62   if(Dimension==2) UpdateWithDim<2>(PixelType);
63   else if(Dimension==3) UpdateWithDim<3>(PixelType);
64   // else if (Dimension==4)UpdateWithDim<4>(PixelType);
65   else {
66     std::cout<<"Error, Only for 2 or 3  Dimensions!!!"<<std::endl ;
67     return;
68   }
69 }
70
71 //-------------------------------------------------------------------
72 // Update with the number of dimensions
73 //-------------------------------------------------------------------
74 template<class args_info_type>
75 template<unsigned int Dimension>
76 void
77 WriteDicomSeriesGenericFilter<args_info_type>::UpdateWithDim(std::string PixelType)
78 {
79   if (m_Verbose) std::cout << "Image was detected to be "<<Dimension<<"D and "<< PixelType<<"..."<<std::endl;
80
81   if(PixelType == "short") {
82     if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and signed short..." << std::endl;
83     UpdateWithDimAndPixelType<Dimension, signed short>();
84   }
85   else if(PixelType == "unsigned_short"){
86     if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_short..." << std::endl;
87     UpdateWithDimAndPixelType<Dimension, unsigned short>();
88   }
89
90   else if (PixelType == "unsigned_char") {
91     if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_char..." << std::endl;
92     UpdateWithDimAndPixelType<Dimension, unsigned char>();
93   }
94
95   //     else if (PixelType == "char"){
96   //       if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and signed_char..." << std::endl;
97   //       UpdateWithDimAndPixelType<Dimension, signed char>();
98   //     }
99   else if (PixelType == "double") {
100     if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and double..." << std::endl;
101     UpdateWithDimAndPixelType<Dimension, double>();
102   }
103   else {
104     if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and float..." << std::endl;
105     UpdateWithDimAndPixelType<Dimension, float>();
106   }
107 }
108
109 //-------------------------------------------------------------------
110 // Update with the number of dimensions and the pixeltype read from
111 // the dicom files. The MHD files may be resampled to match the
112 // dicom spacing (and number of slices). Rounding errors in resampling
113 // are handled by removing files when generating the output dicom
114 // series.
115 //-------------------------------------------------------------------
116 template<class args_info_type>
117 template <unsigned int Dimension, class  PixelType>
118 void
119 WriteDicomSeriesGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
120 {
121
122   // ImageTypes
123   typedef itk::Image<PixelType, Dimension> InputImageType;
124   typedef itk::Image<PixelType, 2> OutputImageType;
125
126   // Read the dicom directory
127   typedef itk::ImageSeriesReader< InputImageType >     ReaderType;
128   typedef itk::GDCMImageIO ImageIOType;
129   typedef itk::GDCMSeriesFileNames NamesGeneratorType;
130
131   ImageIOType::Pointer gdcmIO = ImageIOType::New();
132   NamesGeneratorType::Pointer namesGenerator = NamesGeneratorType::New();
133   namesGenerator->SetInputDirectory( m_ArgsInfo.inputDir_arg );
134   namesGenerator->SetOutputDirectory( m_ArgsInfo.outputDir_arg  );
135   typename   ReaderType::FileNamesContainer filenames_in = namesGenerator->GetInputFileNames();
136   typename   ReaderType::FileNamesContainer filenames_out = namesGenerator->GetOutputFileNames();
137
138   // Output the dicom files
139   unsigned int numberOfFilenames =  filenames_in.size();
140   if (m_Verbose) {
141     std::cout << numberOfFilenames <<" were read in the directory "<<m_ArgsInfo.inputDir_arg<<"..."<<std::endl<<std::endl;
142     for(unsigned int fni = 0; fni<numberOfFilenames; fni++) {
143       std::cout << "filename # " << fni << " = ";
144       std::cout << filenames_in[fni] << std::endl;
145     }
146   }
147
148   // Read the series
149   typename ReaderType::Pointer reader = ReaderType::New();
150   reader->SetImageIO( gdcmIO );
151   reader->SetFileNames( filenames_in );
152   try {
153     reader->Update();
154   } catch (itk::ExceptionObject &excp) {
155     std::cerr << "Error: Exception thrown while reading the DICOM series!!" << std::endl;
156     std::cerr << excp << std::endl;
157   }
158
159   // Read the input (MHD file)
160   typedef typename InputImageType::RegionType RegionType;
161   typedef typename RegionType::SizeType SizeType;
162   typedef itk::ImageFileReader<InputImageType> InputReaderType;
163   typename InputReaderType::Pointer volumeReader = InputReaderType::New();
164   volumeReader->SetFileName( m_InputFileName);
165   volumeReader->Update();
166   
167   typename InputImageType::Pointer input = volumeReader->GetOutput();
168   if ((!m_ArgsInfo.useSizeAsReference_flag && (input->GetSpacing() != reader->GetOutput()->GetSpacing())) || 
169       (m_ArgsInfo.useSizeAsReference_flag && (input->GetLargestPossibleRegion().GetSize() != reader->GetOutput()->GetLargestPossibleRegion().GetSize()))) {
170         
171     // resampling is carried out on the fly if resolution or size between 
172     // the input mhd and input dicom series is different
173     
174     // Filter
175     typedef clitk::ResampleImageWithOptionsFilter<InputImageType, InputImageType> ResampleImageFilterType;
176     typename ResampleImageFilterType::Pointer filter = ResampleImageFilterType::New();
177     filter->SetInput(input);
178     filter->SetVerboseOptions(m_Verbose);
179     filter->SetGaussianFilteringEnabled(false);
180     filter->SetDefaultPixelValue(0);
181     
182     if (!m_ArgsInfo.useSizeAsReference_flag) {
183       filter->SetOutputSpacing(reader->GetOutput()->GetSpacing());
184       if (m_Verbose) {
185         std::cout << "Warning: The image spacing differs between the MHD file and the input dicom series. Performing resampling with default options using spacing as reference (for advanced options, use clitkResampleImage)." << std::endl;
186         std::cout << "MHD -> " << input->GetSpacing() << std::endl;
187         std::cout << "dicom -> " << reader->GetOutput()->GetSpacing() << std::endl;
188       }
189     }
190     else {
191       const SizeType& dicom_size = reader->GetOutput()->GetLargestPossibleRegion().GetSize();
192       SizeType output_size;
193       for (unsigned int i = 0; i < Dimension; i++)
194         output_size[i] = dicom_size[i];
195       filter->SetOutputSize(output_size);
196       if (m_Verbose) {
197           std::cout << "Warning: The image size differs between the MHD file and the input dicom series. Performing resampling with default options using size as reference (for advanced options, use clitkResampleImage)." << std::endl;
198           std::cout << "MHD -> " << input->GetLargestPossibleRegion().GetSize() << std::endl;
199           std::cout << "dicom -> " << reader->GetOutput()->GetLargestPossibleRegion().GetSize() << std::endl;
200       }
201     }
202
203     try {
204       filter->Update();
205       input = filter->GetOutput();
206     } catch( itk::ExceptionObject & excp ) {
207     std::cerr << "Error: Exception thrown while resampling!!" << std::endl;
208     std::cerr << excp << std::endl;
209     }
210   }
211   
212   //    In some cases, due to resampling approximation issues, 
213   //    the number of slices in the MHD file may be different (smaller)
214   //    from the number of files in the template dicom directory. 
215   //    To avoid ITK generating an exception, we reduce the number 
216   //    of DCM files to be considered, and a warning is printed
217   //    in verbose mode
218   const RegionType volumeRegion = input->GetLargestPossibleRegion();
219   const SizeType& volumeSize = volumeRegion.GetSize();
220   if (m_Verbose) {
221     std::cout << volumeRegion << volumeSize << std::endl;
222   }
223   if (Dimension == 3 && volumeSize[2] < numberOfFilenames) {
224     if (m_Verbose)
225       std::cout << "Warning: The number of files in " << m_ArgsInfo.inputDir_arg << " (" << filenames_in.size() << " files) is greater than the number of slices in MHD (" << volumeSize[2] << " slices). Using only " << volumeSize[2] << " files." << std::endl;
226     
227     filenames_in.resize(volumeSize[2]);
228     filenames_out.resize(filenames_in.size());
229     numberOfFilenames =  filenames_in.size();
230   }
231
232   // Modify the meta dictionary
233   typedef itk::MetaDataDictionary   DictionaryType;
234   const std::vector<DictionaryType*>* dictionary = reader->GetMetaDataDictionaryArray();
235
236   // Get keys
237   unsigned int numberOfKeysGiven=m_ArgsInfo.key_given;
238
239   for (unsigned int i = 0; i < numberOfKeysGiven; i++) {
240     std::string entryId(m_ArgsInfo.key_arg[i]  );
241     std::string value( m_ArgsInfo.tag_arg[i] );
242     std::cout << entryId << " " << value << std::endl;
243     for(unsigned int fni = 0; fni<numberOfFilenames; fni++)
244       itk::EncapsulateMetaData<std::string>( *((*dictionary)[fni]), entryId, value );
245   }
246   
247
248   // Output directory and filenames
249   itksys::SystemTools::MakeDirectory( m_ArgsInfo.outputDir_arg ); // create if it doesn't exist
250   typedef itk::ImageSeriesWriter<InputImageType, OutputImageType >  SeriesWriterType;
251   typename SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New();
252
253   seriesWriter->SetInput( input );
254   seriesWriter->SetImageIO( gdcmIO );
255   
256   seriesWriter->SetFileNames( filenames_out );
257   seriesWriter->SetMetaDataDictionaryArray( dictionary );
258
259   // Write
260   try {
261     seriesWriter->Update();
262   } catch( itk::ExceptionObject & excp ) {
263     std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
264     std::cerr << excp << std::endl;
265   }
266
267 }
268
269 /*
270 //-------------------------------------------------------------------
271 // Update with the number of dimensions and the pixeltype
272 //-------------------------------------------------------------------
273 template<class args_info_type>
274 template <unsigned int Dimension, class  PixelType>
275 void
276 WriteDicomSeriesGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
277 {
278
279   // ImageTypes
280   typedef itk::Image<PixelType, Dimension> InputImageType;
281   typedef itk::Image<PixelType, 2> OutputImageType;
282
283   // Read the input (volumetric)
284   typedef itk::ImageFileReader<InputImageType> InputReaderType;
285   typename InputReaderType::Pointer volumeReader = InputReaderType::New();
286   volumeReader->SetFileName( m_InputFileName);
287   volumeReader->Update();
288   typename InputImageType::Pointer input= volumeReader->GetOutput();
289
290   // Read the dicom directory
291   typedef itk::ImageSeriesReader< InputImageType >     ReaderType;
292   typedef itk::GDCMImageIO ImageIOType;
293   typedef itk::GDCMSeriesFileNames NamesGeneratorType;
294
295   ImageIOType::Pointer gdcmIO = ImageIOType::New();
296   NamesGeneratorType::Pointer namesGenerator = NamesGeneratorType::New();
297   namesGenerator->SetInputDirectory( m_ArgsInfo.inputDir_arg );
298   namesGenerator->SetOutputDirectory( m_ArgsInfo.outputDir_arg  );
299   typename   ReaderType::FileNamesContainer filenames_in = namesGenerator->GetInputFileNames();
300   typename   ReaderType::FileNamesContainer filenames_out = namesGenerator->GetOutputFileNames();
301
302   // Output the dicom files
303   unsigned int numberOfFilenames =  filenames_in.size();
304   if (m_Verbose) {
305     std::cout << numberOfFilenames <<" were read in the directory "<<m_ArgsInfo.inputDir_arg<<"..."<<std::endl<<std::endl;
306     for(unsigned int fni = 0; fni<numberOfFilenames; fni++) {
307       std::cout << "filename # " << fni << " = ";
308       std::cout << filenames_in[fni] << std::endl;
309     }
310   }
311   
312   // RP: 16/03/2011
313   //    In some cases, due to resampling approximation issues, 
314   //    the number of slices in the MHD file may be different 
315   //    from the number of slices in the template DCM directory. 
316   //    To avoid ITK generating an exception, we reduce the number 
317   //    of DCM files to be considered, provided the --force
318   //    option is set.
319   typedef typename InputImageType::RegionType RegionType;
320   typedef typename RegionType::SizeType SizeType;
321   const RegionType volumeRegion = input->GetLargestPossibleRegion();
322   const SizeType& volumeSize = volumeRegion.GetSize();
323   if (m_ArgsInfo.force_given && Dimension == 3 && volumeSize[2] < numberOfFilenames)
324   {
325     std::cout << "Warning: Number of files in " << m_ArgsInfo.inputDir_arg << " is greater than the number of slices in MHD. Using only " << volumeSize[2] << " files." << std::endl;
326     filenames_in.resize(volumeSize[2]);
327     filenames_out.resize(filenames_in.size());
328     numberOfFilenames =  filenames_in.size();
329   }
330
331   // Read the series
332   typename ReaderType::Pointer reader = ReaderType::New();
333   reader->SetImageIO( gdcmIO );
334   reader->SetFileNames( filenames_in );
335   try {
336     reader->Update();
337   } catch (itk::ExceptionObject &excp) {
338     std::cerr << "Error: Exception thrown while reading the DICOM series!!" << std::endl;
339     std::cerr << excp << std::endl;
340   }
341
342
343   // Modify the meta dictionary
344   typedef itk::MetaDataDictionary   DictionaryType;
345   const std::vector<DictionaryType*>* dictionary = reader->GetMetaDataDictionaryArray();
346
347   // Get keys
348   unsigned int numberOfKeysGiven=0;
349   if(m_ArgsInfo.midP_flag && m_ArgsInfo.key_given)
350     std::cerr<<"Error: both keys and midP option are given"<<std::endl;
351   else if (m_ArgsInfo.midP_flag)
352     numberOfKeysGiven=1;
353   else
354     numberOfKeysGiven=m_ArgsInfo.key_given;
355
356   for (unsigned int i = 0; i < numberOfKeysGiven; i++) {
357     std::string entryId(m_ArgsInfo.key_arg[i]  );
358     std::string value( m_ArgsInfo.tag_arg[i] );
359     for(unsigned int fni = 0; fni<numberOfFilenames; fni++)
360       itk::EncapsulateMetaData<std::string>( *((*dictionary)[fni]), entryId, value );
361   }
362
363   // Output directory and filenames
364   itksys::SystemTools::MakeDirectory( m_ArgsInfo.outputDir_arg ); // create if it doesn't exist
365   typedef itk::ImageSeriesWriter<InputImageType, OutputImageType >  SeriesWriterType;
366   typename SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New();
367
368   seriesWriter->SetInput( volumeReader->GetOutput() );
369   seriesWriter->SetImageIO( gdcmIO );
370   
371   seriesWriter->SetFileNames( filenames_out );
372   seriesWriter->SetMetaDataDictionaryArray( dictionary );
373
374   // Write
375   try {
376     seriesWriter->Update();
377   } catch( itk::ExceptionObject & excp ) {
378     std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
379     std::cerr << excp << std::endl;
380   }
381
382 }
383 */
384
385 }//end clitk
386
387 #endif //#define clitkWriteDicomSeriesGenericFilter_txx