1 /*=========================================================================
2 Program: vv http://www.creatis.insa-lyon.fr/rio/vv
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
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.
13 It is distributed under dual licence
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
21 /* =================================================
22 * @file clitkWriteDicomSeriesGenericFilter.txx
28 ===================================================*/
31 #include "clitkResampleImageWithOptionsFilter.h"
38 //-----------------------------------------------------------
40 //-----------------------------------------------------------
41 template<class args_info_type>
42 WriteDicomSeriesGenericFilter<args_info_type>::WriteDicomSeriesGenericFilter()
49 //-----------------------------------------------------------
51 //-----------------------------------------------------------
52 template<class args_info_type>
53 void WriteDicomSeriesGenericFilter<args_info_type>::Update()
55 // Read the Dimension and PixelType
57 std::string PixelType;
58 ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType);
62 if(Dimension==2) UpdateWithDim<2>(PixelType);
63 else if(Dimension==3) UpdateWithDim<3>(PixelType);
64 // else if (Dimension==4)UpdateWithDim<4>(PixelType);
66 std::cout<<"Error, Only for 2 or 3 Dimensions!!!"<<std::endl ;
71 //-------------------------------------------------------------------
72 // Update with the number of dimensions
73 //-------------------------------------------------------------------
74 template<class args_info_type>
75 template<unsigned int Dimension>
77 WriteDicomSeriesGenericFilter<args_info_type>::UpdateWithDim(std::string PixelType)
79 if (m_Verbose) std::cout << "Image was detected to be "<<Dimension<<"D and "<< PixelType<<"..."<<std::endl;
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>();
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>();
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>();
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>();
100 if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and float..." << std::endl;
101 UpdateWithDimAndPixelType<Dimension, float>();
105 //-------------------------------------------------------------------
106 // Update with the number of dimensions and the pixeltype read from
107 // the dicom files. The MHD files may be resampled to match the
108 // dicom spacing (and number of slices). Rounding errors in resampling
109 // are handled by removing files when generating the output dicom
111 //-------------------------------------------------------------------
112 template<class args_info_type>
113 template <unsigned int Dimension, class PixelType>
115 WriteDicomSeriesGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
119 typedef itk::Image<PixelType, Dimension> InputImageType;
120 typedef itk::Image<PixelType, 2> OutputImageType;
122 // Read the dicom directory
123 typedef itk::ImageSeriesReader< InputImageType > ReaderType;
124 typedef itk::GDCMImageIO ImageIOType;
125 typedef itk::GDCMSeriesFileNames NamesGeneratorType;
127 ImageIOType::Pointer gdcmIO = ImageIOType::New();
128 NamesGeneratorType::Pointer namesGenerator = NamesGeneratorType::New();
129 namesGenerator->SetInputDirectory( m_ArgsInfo.inputDir_arg );
130 namesGenerator->SetOutputDirectory( m_ArgsInfo.outputDir_arg );
131 typename ReaderType::FileNamesContainer filenames_in = namesGenerator->GetInputFileNames();
132 typename ReaderType::FileNamesContainer filenames_out = namesGenerator->GetOutputFileNames();
134 // Output the dicom files
135 unsigned int numberOfFilenames = filenames_in.size();
137 std::cout << numberOfFilenames <<" were read in the directory "<<m_ArgsInfo.inputDir_arg<<"..."<<std::endl<<std::endl;
138 for(unsigned int fni = 0; fni<numberOfFilenames; fni++) {
139 std::cout << "filename # " << fni << " = ";
140 std::cout << filenames_in[fni] << std::endl;
145 typename ReaderType::Pointer reader = ReaderType::New();
146 reader->SetImageIO( gdcmIO );
147 reader->SetFileNames( filenames_in );
150 } catch (itk::ExceptionObject &excp) {
151 std::cerr << "Error: Exception thrown while reading the DICOM series!!" << std::endl;
152 std::cerr << excp << std::endl;
155 // Read the input (MHD file)
156 typedef typename InputImageType::RegionType RegionType;
157 typedef typename RegionType::SizeType SizeType;
158 typedef itk::ImageFileReader<InputImageType> InputReaderType;
159 typename InputReaderType::Pointer volumeReader = InputReaderType::New();
160 volumeReader->SetFileName( m_InputFileName);
161 volumeReader->Update();
163 typename InputImageType::Pointer input = volumeReader->GetOutput();
164 if ((!m_ArgsInfo.useSizeAsReference_flag && (input->GetSpacing() != reader->GetOutput()->GetSpacing())) ||
165 (m_ArgsInfo.useSizeAsReference_flag && (input->GetLargestPossibleRegion().GetSize() != reader->GetOutput()->GetLargestPossibleRegion().GetSize()))) {
167 // resampling is carried out on the fly if resolution or size between
168 // the input mhd and input dicom series is different
171 typedef clitk::ResampleImageWithOptionsFilter<InputImageType, InputImageType> ResampleImageFilterType;
172 typename ResampleImageFilterType::Pointer filter = ResampleImageFilterType::New();
173 filter->SetInput(input);
174 filter->SetVerboseOptions(m_Verbose);
175 filter->SetGaussianFilteringEnabled(false);
176 filter->SetDefaultPixelValue(0);
178 if (!m_ArgsInfo.useSizeAsReference_flag) {
179 filter->SetOutputSpacing(reader->GetOutput()->GetSpacing());
181 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;
182 std::cout << "MHD -> " << input->GetSpacing() << std::endl;
183 std::cout << "dicom -> " << reader->GetOutput()->GetSpacing() << std::endl;
187 const SizeType& dicom_size = reader->GetOutput()->GetLargestPossibleRegion().GetSize();
188 SizeType output_size;
189 for (unsigned int i = 0; i < Dimension; i++)
190 output_size[i] = dicom_size[i];
191 filter->SetOutputSize(output_size);
193 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;
194 std::cout << "MHD -> " << input->GetLargestPossibleRegion().GetSize() << std::endl;
195 std::cout << "dicom -> " << reader->GetOutput()->GetLargestPossibleRegion().GetSize() << std::endl;
200 input = filter->GetOutput();
203 // In some cases, due to resampling approximation issues,
204 // the number of slices in the MHD file may be different (smaller)
205 // from the number of files in the template dicom directory.
206 // To avoid ITK generating an exception, we reduce the number
207 // of DCM files to be considered, and a warning is printed
209 const RegionType volumeRegion = input->GetLargestPossibleRegion();
210 const SizeType& volumeSize = volumeRegion.GetSize();
211 if (Dimension == 3 && volumeSize[2] < numberOfFilenames) {
213 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;
215 filenames_in.resize(volumeSize[2]);
216 filenames_out.resize(filenames_in.size());
217 numberOfFilenames = filenames_in.size();
220 // Modify the meta dictionary
221 typedef itk::MetaDataDictionary DictionaryType;
222 const std::vector<DictionaryType*>* dictionary = reader->GetMetaDataDictionaryArray();
225 unsigned int numberOfKeysGiven=0;
226 if(m_ArgsInfo.midP_flag && m_ArgsInfo.key_given)
227 std::cerr<<"Error: both keys and midP option are given"<<std::endl;
228 else if (m_ArgsInfo.midP_flag)
231 numberOfKeysGiven=m_ArgsInfo.key_given;
233 for (unsigned int i = 0; i < numberOfKeysGiven; i++) {
234 std::string entryId(m_ArgsInfo.key_arg[i] );
235 std::string value( m_ArgsInfo.tag_arg[i] );
236 for(unsigned int fni = 0; fni<numberOfFilenames; fni++)
237 itk::EncapsulateMetaData<std::string>( *((*dictionary)[fni]), entryId, value );
240 // Output directory and filenames
241 itksys::SystemTools::MakeDirectory( m_ArgsInfo.outputDir_arg ); // create if it doesn't exist
242 typedef itk::ImageSeriesWriter<InputImageType, OutputImageType > SeriesWriterType;
243 typename SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New();
245 seriesWriter->SetInput( input );
246 seriesWriter->SetImageIO( gdcmIO );
248 seriesWriter->SetFileNames( filenames_out );
249 seriesWriter->SetMetaDataDictionaryArray( dictionary );
253 seriesWriter->Update();
254 } catch( itk::ExceptionObject & excp ) {
255 std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
256 std::cerr << excp << std::endl;
262 //-------------------------------------------------------------------
263 // Update with the number of dimensions and the pixeltype
264 //-------------------------------------------------------------------
265 template<class args_info_type>
266 template <unsigned int Dimension, class PixelType>
268 WriteDicomSeriesGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
272 typedef itk::Image<PixelType, Dimension> InputImageType;
273 typedef itk::Image<PixelType, 2> OutputImageType;
275 // Read the input (volumetric)
276 typedef itk::ImageFileReader<InputImageType> InputReaderType;
277 typename InputReaderType::Pointer volumeReader = InputReaderType::New();
278 volumeReader->SetFileName( m_InputFileName);
279 volumeReader->Update();
280 typename InputImageType::Pointer input= volumeReader->GetOutput();
282 // Read the dicom directory
283 typedef itk::ImageSeriesReader< InputImageType > ReaderType;
284 typedef itk::GDCMImageIO ImageIOType;
285 typedef itk::GDCMSeriesFileNames NamesGeneratorType;
287 ImageIOType::Pointer gdcmIO = ImageIOType::New();
288 NamesGeneratorType::Pointer namesGenerator = NamesGeneratorType::New();
289 namesGenerator->SetInputDirectory( m_ArgsInfo.inputDir_arg );
290 namesGenerator->SetOutputDirectory( m_ArgsInfo.outputDir_arg );
291 typename ReaderType::FileNamesContainer filenames_in = namesGenerator->GetInputFileNames();
292 typename ReaderType::FileNamesContainer filenames_out = namesGenerator->GetOutputFileNames();
294 // Output the dicom files
295 unsigned int numberOfFilenames = filenames_in.size();
297 std::cout << numberOfFilenames <<" were read in the directory "<<m_ArgsInfo.inputDir_arg<<"..."<<std::endl<<std::endl;
298 for(unsigned int fni = 0; fni<numberOfFilenames; fni++) {
299 std::cout << "filename # " << fni << " = ";
300 std::cout << filenames_in[fni] << std::endl;
305 // In some cases, due to resampling approximation issues,
306 // the number of slices in the MHD file may be different
307 // from the number of slices in the template DCM directory.
308 // To avoid ITK generating an exception, we reduce the number
309 // of DCM files to be considered, provided the --force
311 typedef typename InputImageType::RegionType RegionType;
312 typedef typename RegionType::SizeType SizeType;
313 const RegionType volumeRegion = input->GetLargestPossibleRegion();
314 const SizeType& volumeSize = volumeRegion.GetSize();
315 if (m_ArgsInfo.force_given && Dimension == 3 && volumeSize[2] < numberOfFilenames)
317 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;
318 filenames_in.resize(volumeSize[2]);
319 filenames_out.resize(filenames_in.size());
320 numberOfFilenames = filenames_in.size();
324 typename ReaderType::Pointer reader = ReaderType::New();
325 reader->SetImageIO( gdcmIO );
326 reader->SetFileNames( filenames_in );
329 } catch (itk::ExceptionObject &excp) {
330 std::cerr << "Error: Exception thrown while reading the DICOM series!!" << std::endl;
331 std::cerr << excp << std::endl;
335 // Modify the meta dictionary
336 typedef itk::MetaDataDictionary DictionaryType;
337 const std::vector<DictionaryType*>* dictionary = reader->GetMetaDataDictionaryArray();
340 unsigned int numberOfKeysGiven=0;
341 if(m_ArgsInfo.midP_flag && m_ArgsInfo.key_given)
342 std::cerr<<"Error: both keys and midP option are given"<<std::endl;
343 else if (m_ArgsInfo.midP_flag)
346 numberOfKeysGiven=m_ArgsInfo.key_given;
348 for (unsigned int i = 0; i < numberOfKeysGiven; i++) {
349 std::string entryId(m_ArgsInfo.key_arg[i] );
350 std::string value( m_ArgsInfo.tag_arg[i] );
351 for(unsigned int fni = 0; fni<numberOfFilenames; fni++)
352 itk::EncapsulateMetaData<std::string>( *((*dictionary)[fni]), entryId, value );
355 // Output directory and filenames
356 itksys::SystemTools::MakeDirectory( m_ArgsInfo.outputDir_arg ); // create if it doesn't exist
357 typedef itk::ImageSeriesWriter<InputImageType, OutputImageType > SeriesWriterType;
358 typename SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New();
360 seriesWriter->SetInput( volumeReader->GetOutput() );
361 seriesWriter->SetImageIO( gdcmIO );
363 seriesWriter->SetFileNames( filenames_out );
364 seriesWriter->SetMetaDataDictionaryArray( dictionary );
368 seriesWriter->Update();
369 } catch( itk::ExceptionObject & excp ) {
370 std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
371 std::cerr << excp << std::endl;
379 #endif //#define clitkWriteDicomSeriesGenericFilter_txx