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 clitkImage2DicomGenericFilter_txx
19 #define clitkImage2DicomGenericFilter_txx
21 /* =================================================
22 * @file clitkImage2DicomGenericFilter.txx
28 ===================================================*/
30 #include "itkVersion.h"
32 #include "itkGDCMImageIO.h"
33 #include "itkGDCMSeriesFileNames.h"
34 #include "itkNumericSeriesFileNames.h"
35 #include "itkImageSeriesReader.h"
36 #include "itkImageSeriesWriter.h"
38 #include "itkThresholdImageFilter.h"
40 #if ( ( ITK_VERSION_MAJOR == 4 ) && ( ITK_VERSION_MINOR < 6 ) )
41 #include "itkShiftScaleImageFilter.h"
46 #if GDCM_MAJOR_VERSION >= 2
47 #include <gdcmUIDGenerator.h>
48 #include <gdcmImageHelper.h>
49 #include <gdcmAttribute.h>
50 #include <gdcmReader.h>
51 #include <gdcmWriter.h>
52 #include <gdcmDataElement.h>
63 //-----------------------------------------------------------
65 //-----------------------------------------------------------
66 template<class args_info_type>
67 Image2DicomGenericFilter<args_info_type>::Image2DicomGenericFilter()
74 //-----------------------------------------------------------
76 //-----------------------------------------------------------
77 template<class args_info_type>
78 void Image2DicomGenericFilter<args_info_type>::Update()
80 // Read the Dimension and PixelType
82 std::string PixelType;
83 ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType);
88 if (m_ArgsInfo.volume_flag)
89 UpdateWithDim<2,2>(PixelType);
91 UpdateWithDim<2,1>(PixelType);
92 } else if(Dimension==3) {
93 if (m_ArgsInfo.volume_flag)
94 UpdateWithDim<3,3>(PixelType);
96 UpdateWithDim<3,2>(PixelType);
98 // else if (Dimension==4)UpdateWithDim<4>(PixelType);
100 std::cout<<"Error, Only for 2 or 3 Dimensions!!!"<<std::endl ;
105 //-------------------------------------------------------------------
106 // Update with the number of dimensions
107 //-------------------------------------------------------------------
108 template<class args_info_type>
109 template<unsigned int inputDimension, unsigned int outputDimension>
111 Image2DicomGenericFilter<args_info_type>::UpdateWithDim(std::string PixelType)
113 if (m_Verbose) std::cout << "Image was detected to be "<<inputDimension<<"D and "<< PixelType<<"..."<<std::endl;
115 if(PixelType == "short") {
116 if (m_Verbose) std::cout << "Launching filter in "<< inputDimension <<"D and signed short..." << std::endl;
117 UpdateWithDimAndPixelType<inputDimension, outputDimension, signed short>();
119 else if(PixelType == "unsigned_short"){
120 if (m_Verbose) std::cout << "Launching filter in "<< inputDimension <<"D and unsigned_short..." << std::endl;
121 UpdateWithDimAndPixelType<inputDimension, outputDimension, unsigned short>();
124 else if (PixelType == "unsigned_char") {
125 if (m_Verbose) std::cout << "Launching filter in "<< inputDimension <<"D and unsigned_char..." << std::endl;
126 UpdateWithDimAndPixelType<inputDimension, outputDimension, unsigned char>();
129 // else if (PixelType == "char"){
130 // if (m_Verbose) std::cout << "Launching filter in "<< inputDimension <<"D and signed_char..." << std::endl;
131 // UpdateWithDimAndPixelType<inputDimension, outputDimension, signed char>();
133 else if (PixelType == "double") {
134 if (m_Verbose) std::cout << "Launching filter in "<< inputDimension <<"D and double..." << std::endl;
135 UpdateWithDimAndPixelType<inputDimension, outputDimension, double>();
138 if (m_Verbose) std::cout << "Launching filter in "<< inputDimension <<"D and float..." << std::endl;
139 UpdateWithDimAndPixelType<inputDimension, outputDimension, float>();
143 //-------------------------------------------------------------------
144 // Update with the number of dimensions and the pixeltype read from
145 // the dicom files. The MHD files may be resampled to match the
146 // dicom spacing (and number of slices). Rounding errors in resampling
147 // are handled by removing files when generating the output dicom
149 //-------------------------------------------------------------------
150 template<class args_info_type>
151 template <unsigned int inputDimension, unsigned int outputDimension, class PixelType>
153 Image2DicomGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
155 // Validate input parameters
157 const unsigned int InputDimension = inputDimension;
158 const unsigned int OutputDimension = outputDimension;
160 typedef itk::Image< PixelType, InputDimension > InputImageType;
161 typedef itk::Image< PixelType, OutputDimension > OutputImageType;
162 typedef itk::ImageSeriesReader< InputImageType > ReaderType;
163 typedef itk::GDCMImageIO ImageIOType;
164 #if ( ( ITK_VERSION_MAJOR == 4 ) && ( ITK_VERSION_MINOR < 6 ) )
165 typedef itk::ShiftScaleImageFilter< InputImageType, InputImageType > ShiftScaleType;
167 typedef itk::ImageSeriesWriter< InputImageType, OutputImageType > SeriesWriterType;
169 ////////////////////////////////////////////////
170 // 1) Read the input series
172 // Read the input (MHD file)
173 typedef typename InputImageType::RegionType RegionType;
174 typedef typename RegionType::SizeType SizeType;
175 typedef itk::ImageFileReader<InputImageType> InputReaderType;
176 typename InputReaderType::Pointer volumeReader = InputReaderType::New();
177 volumeReader->SetFileName(m_ArgsInfo.input_arg);
178 volumeReader->Update();
179 typename InputImageType::Pointer input = volumeReader->GetOutput();
180 typedef itk::NumericSeriesFileNames OutputNamesGeneratorType;
182 ImageIOType::Pointer gdcmIO = ImageIOType::New();
183 gdcmIO->LoadPrivateTagsOn();
184 typename ReaderType::FileNamesContainer filenames;
185 filenames.push_back(m_ArgsInfo.inputDcm_arg);
186 typename ReaderType::Pointer reader = ReaderType::New();
187 reader->SetImageIO(gdcmIO);
188 reader->SetFileNames(filenames);
191 } catch (itk::ExceptionObject &excp) {
192 std::cerr << "Exception thrown while reading the series" << std::endl;
193 std::cerr << excp << std::endl;
197 typename InputImageType::SpacingType outputSpacing;
198 typename InputImageType::SizeType outputSize;
199 for (unsigned int i = 0; i < 3; i++) {
200 outputSpacing[i] = input->GetSpacing()[i];
201 outputSize[i] = input->GetLargestPossibleRegion().GetSize()[i];
204 ////////////////////////////////////////////////
205 // 2) Ensure to have value >= -1024
207 typedef itk::ThresholdImageFilter <InputImageType> ThresholdImageFilterType;
208 typename ThresholdImageFilterType::Pointer thresholdFilter = ThresholdImageFilterType::New();
209 thresholdFilter->SetInput(input);
210 thresholdFilter->ThresholdBelow(-1024);
211 thresholdFilter->SetOutsideValue(-1024);
212 thresholdFilter->Update();
214 input=thresholdFilter->GetOutput();
216 ////////////////////////////////////////////////
217 // 3) Create a MetaDataDictionary for each slice.
219 // Copy the dictionary from the first image and override slice
221 typename ReaderType::DictionaryRawPointer inputDict = (*(reader->GetMetaDataDictionaryArray()))[0];
222 typename ReaderType::DictionaryArrayType outputArray;
224 // To keep the new series in the same study as the original we need
225 // to keep the same study UID. But we need new series and frame of
227 #if ITK_VERSION_MAJOR >= 4
228 gdcm::UIDGenerator fuid;
229 std::string frameOfReferenceUID = fuid.Generate();
231 std::string frameOfReferenceUID = gdcm::Util::CreateUniqueUID( gdcmIO->GetUIDPrefix());
233 std::string seriesUID;
234 if (m_ArgsInfo.newUID_flag) {
235 #if ITK_VERSION_MAJOR >= 4
236 gdcm::UIDGenerator suid;
237 seriesUID = suid.Generate();
239 seriesUID = gdcm::Util::CreateUniqueUID( gdcmIO->GetUIDPrefix());
242 itk::ExposeMetaData<std::string>(*inputDict, "0020|000e", seriesUID);
244 std::string studyUID;
245 std::string sopClassUID;
246 itk::ExposeMetaData<std::string>(*inputDict, "0020|000d", studyUID);
247 itk::ExposeMetaData<std::string>(*inputDict, "0008|0016", sopClassUID);
248 gdcmIO->KeepOriginalUIDOn();
249 unsigned int numberOfKeysGiven=m_ArgsInfo.key_given;
250 unsigned int fLimit = outputSize[2];
251 if (m_ArgsInfo.volume_flag)
254 for (unsigned int f = 0; f < fLimit; f++)
256 // Create a new dictionary for this slice
257 typename ReaderType::DictionaryRawPointer dict = new typename ReaderType::DictionaryType;
259 typedef itk::MetaDataDictionary DictionaryType;
261 DictionaryType::ConstIterator itrDic = (*inputDict).Begin();
262 DictionaryType::ConstIterator endDic = (*inputDict).End();
263 typedef itk::MetaDataObject< std::string > MetaDataStringType;
265 while( itrDic != endDic )
267 itk::MetaDataObjectBase::Pointer entry = itrDic->second;
269 MetaDataStringType::Pointer entryvalue =
270 dynamic_cast<MetaDataStringType *>( entry.GetPointer() ) ;
273 std::string tagkey = itrDic->first;
274 std::string tagvalue = entryvalue->GetMetaDataObjectValue();
275 itk::EncapsulateMetaData<std::string>(*dict, tagkey, tagvalue);
280 // Set the UID's for the study, series, SOP and frame of reference
281 itk::EncapsulateMetaData<std::string>(*dict,"0020|000d", studyUID);
282 itk::EncapsulateMetaData<std::string>(*dict,"0020|000e", seriesUID);
283 itk::EncapsulateMetaData<std::string>(*dict,"0020|0052", frameOfReferenceUID);
285 #if ITK_VERSION_MAJOR >= 4
286 gdcm::UIDGenerator sopuid;
287 std::string sopInstanceUID = sopuid.Generate();
289 std::string sopInstanceUID = gdcm::Util::CreateUniqueUID( gdcmIO->GetUIDPrefix());
291 itk::EncapsulateMetaData<std::string>(*dict,"0008|0018", sopInstanceUID);
292 itk::EncapsulateMetaData<std::string>(*dict,"0002|0003", sopInstanceUID);
294 // Change fields that are slice specific
295 std::ostringstream value;
299 itk::EncapsulateMetaData<std::string>(*dict,"0020|0013", value.str());
301 // Series Description - Append new description to current series
303 std::string oldSeriesDesc;
304 itk::ExposeMetaData<std::string>(*inputDict, "0008|103e", oldSeriesDesc);
307 value << oldSeriesDesc
308 << ": Resampled with pixel spacing "
309 << outputSpacing[0] << ", "
310 << outputSpacing[1] << ", "
312 // This is an long string and there is a 64 character limit in the
314 unsigned lengthDesc = value.str().length();
316 std::string seriesDesc( value.str(), 0,
319 itk::EncapsulateMetaData<std::string>(*dict,"0008|103e", seriesDesc);
324 itk::EncapsulateMetaData<std::string>(*dict,"0020|0011", value.str());
326 // Derivation Description - How this image was derived
333 value << ": " << ITK_SOURCE_VERSION;
335 lengthDesc = value.str().length();
336 std::string derivationDesc( value.str(), 0,
337 lengthDesc > 1024 ? 1024
339 itk::EncapsulateMetaData<std::string>(*dict,"0008|2111", derivationDesc);
341 // Image Position Patient: This is calculated by computing the
342 // physical coordinate of the first pixel in each slice.
343 typename InputImageType::PointType position;
344 typename InputImageType::IndexType index;
348 input->TransformIndexToPhysicalPoint(index, position);
351 value << position[0] << "\\" << position[1] << "\\" << position[2];
352 itk::EncapsulateMetaData<std::string>(*dict,"0020|0032", value.str());
353 // Slice Location: For now, we store the z component of the Image
356 value << position[2];
357 itk::EncapsulateMetaData<std::string>(*dict,"0020|1041", value.str());
359 // Slice Thickness: For now, we store the z spacing
361 value << outputSpacing[2];
362 itk::EncapsulateMetaData<std::string>(*dict,"0018|0050", value.str());
363 // Spacing Between Slices
364 itk::EncapsulateMetaData<std::string>(*dict,"0018|0088", value.str());
367 for (unsigned int i = 0; i < numberOfKeysGiven; i++) {
368 std::string entryId(m_ArgsInfo.key_arg[i] );
369 std::string value( m_ArgsInfo.tag_arg[i] );
371 itk::EncapsulateMetaData<std::string>(*dict, entryId, value);
374 // Save the dictionary
375 outputArray.push_back(dict);
378 #if ( ( ITK_VERSION_MAJOR == 4 ) && ( ITK_VERSION_MINOR < 6 ) )
379 ////////////////////////////////////////////////
380 // 4) Shift data to undo the effect of a rescale intercept by the
382 std::string interceptTag("0028|1052");
383 typedef itk::MetaDataObject<std::string> MetaDataStringType;
384 itk::MetaDataObjectBase::Pointer entry = (*inputDict)[interceptTag];
386 MetaDataStringType::ConstPointer interceptValue =
387 dynamic_cast<const MetaDataStringType *>(entry.GetPointer());
389 int interceptShift = 0;
390 if(interceptValue ) {
391 std::string tagValue = interceptValue->GetMetaDataObjectValue();
392 interceptShift = -atoi (tagValue.c_str());
395 ShiftScaleType::Pointer shiftScale = ShiftScaleType::New();
396 shiftScale->SetInput(resampler->GetOutput());
397 shiftScale->SetShift(interceptShift );
400 ////////////////////////////////////////////////
401 // 5) Write the new DICOM series
402 // Generate the file names
403 typename ReaderType::FileNamesContainer fileNamesOutput;
405 // Generate the file names
406 OutputNamesGeneratorType::Pointer outputNames = OutputNamesGeneratorType::New();
407 std::string seriesFormat(m_ArgsInfo.outputDcm_arg);
408 seriesFormat = seriesFormat + "/";
409 if (m_ArgsInfo.nameDicom_given)
410 seriesFormat = seriesFormat + m_ArgsInfo.nameDicom_arg + "_";
411 seriesFormat = seriesFormat + "IM%d.dcm";
412 outputNames->SetSeriesFormat(seriesFormat.c_str());
413 outputNames->SetStartIndex(1);
414 if (m_ArgsInfo.volume_flag) {
415 outputNames->SetEndIndex(1);
417 outputNames->SetEndIndex(outputSize[2]);
420 typename SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New();
421 #if ( ( ITK_VERSION_MAJOR == 4 ) && ( ITK_VERSION_MINOR < 6 ) )
422 seriesWriter->SetInput(input);
424 seriesWriter->SetInput(input);
426 seriesWriter->SetImageIO(gdcmIO);
427 seriesWriter->SetFileNames(outputNames->GetFileNames());
428 seriesWriter->SetMetaDataDictionaryArray(&outputArray);
430 seriesWriter->Update();
431 } catch(itk::ExceptionObject & excp) {
432 std::cerr << "Exception thrown while writing the series " << std::endl;
433 std::cerr << excp << std::endl;
437 ////////////////////////////////////////////////
438 // 5) Read the new dicom data tag and copy it in the model data tag to have all dicom tags
439 gdcm::Reader readerModel, readerOutput;
440 readerModel.SetFileName(filenames[0].c_str());
441 readerOutput.SetFileName(fileNamesOutput[0].c_str());
444 gdcm::File &fileModel = readerModel.GetFile();
445 gdcm::File &fileOutput = readerOutput.GetFile();
446 gdcm::DataSet &dsModel = fileModel.GetDataSet();
447 gdcm::DataSet &dsOutput = fileOutput.GetDataSet();
448 const unsigned int ptr_len = 42;
449 char *ptr = new char[ptr_len];
450 memset(ptr,0,ptr_len);
452 const gdcm::DataElement &dataOutput = dsOutput.GetDataElement(gdcm::Tag(0x7fe0, 0x10));
453 dsModel.Replace(dataOutput);
455 w.SetFile(fileModel);
456 w.SetFileName(fileNamesOutput[0].c_str());
463 #endif //#define clitkImage2DicomGenericFilter_txx