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 clitkPartitionEnergyWindowDicomGenericFilter_txx
19 #define clitkPartitionEnergyWindowDicomGenericFilter_txx
21 /* =================================================
22 * @file clitkPartitionEnergyWindowDicomGenericFilter.txx
28 ===================================================*/
32 #include "clitkResampleImageWithOptionsFilter.h"
33 #if GDCM_MAJOR_VERSION >= 2
34 #include "gdcmUIDGenerator.h"
35 #include <gdcmImageHelper.h>
36 #include <gdcmAttribute.h>
37 #include <gdcmReader.h>
38 #include <gdcmWriter.h>
39 #include <gdcmDataElement.h>
46 #include "itkImageRegionIterator.h"
47 #include "itkMetaImageIO.h"
48 #include "itkMetaDataDictionary.h"
55 //-----------------------------------------------------------
57 //-----------------------------------------------------------
58 template<class args_info_type>
59 PartitionEnergyWindowDicomGenericFilter<args_info_type>::PartitionEnergyWindowDicomGenericFilter()
66 //-----------------------------------------------------------
68 //-----------------------------------------------------------
69 template<class args_info_type>
70 void PartitionEnergyWindowDicomGenericFilter<args_info_type>::Update()
72 // Read the Dimension and PixelType
74 std::string PixelType;
75 ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType);
79 if(Dimension==2) UpdateWithDim<2>(PixelType);
80 else if(Dimension==3) UpdateWithDim<3>(PixelType);
81 // else if (Dimension==4)UpdateWithDim<4>(PixelType);
83 std::cout<<"Error, Only for 2 or 3 Dimensions!!!"<<std::endl ;
88 //-------------------------------------------------------------------
89 // Update with the number of dimensions
90 //-------------------------------------------------------------------
91 template<class args_info_type>
92 template<unsigned int Dimension>
94 PartitionEnergyWindowDicomGenericFilter<args_info_type>::UpdateWithDim(std::string PixelType)
96 if (m_Verbose) std::cout << "Image was detected to be "<<Dimension<<"D and "<< PixelType<<"..."<<std::endl;
98 if(PixelType == "short") {
99 if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and signed short..." << std::endl;
100 UpdateWithDimAndPixelType<Dimension, signed short>();
102 else if(PixelType == "unsigned_short"){
103 if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and unsigned_short..." << std::endl;
104 UpdateWithDimAndPixelType<Dimension, unsigned short>();
107 else if (PixelType == "unsigned_char") {
108 if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and unsigned_char..." << std::endl;
109 UpdateWithDimAndPixelType<Dimension, unsigned char>();
112 // else if (PixelType == "char"){
113 // if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and signed_char..." << std::endl;
114 // UpdateWithDimAndPixelType<Dimension, signed char>();
116 else if (PixelType == "double") {
117 if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and double..." << std::endl;
118 UpdateWithDimAndPixelType<Dimension, double>();
121 if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and float..." << std::endl;
122 UpdateWithDimAndPixelType<Dimension, float>();
126 //-------------------------------------------------------------------
127 // Update with the number of dimensions and the pixeltype read from
128 // the dicom files. The MHD files may be resampled to match the
129 // dicom spacing (and number of slices). Rounding errors in resampling
130 // are handled by removing files when generating the output dicom
132 //-------------------------------------------------------------------
133 template<class args_info_type>
134 template <unsigned int Dimension, class PixelType>
136 PartitionEnergyWindowDicomGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
139 #if GDCM_MAJOR_VERSION == 2
141 typedef itk::Image<PixelType, Dimension> InputImageType;
142 typedef itk::Image<PixelType, Dimension> OutputImageType;
143 typedef itk::ImageSeriesReader< InputImageType > ReaderType;
144 typedef itk::ImageSeriesWriter< InputImageType, InputImageType > WriterType;
145 typedef itk::GDCMImageIO ImageIOType;
146 typedef typename InputImageType::RegionType RegionType;
147 typedef typename RegionType::SizeType SizeType;
150 // Read Dicom model file
151 typename ReaderType::Pointer reader = ReaderType::New();
152 ImageIOType::Pointer gdcmIO = ImageIOType::New();
153 gdcmIO->LoadPrivateTagsOn();
154 gdcmIO->KeepOriginalUIDOn();
155 reader->SetImageIO( gdcmIO );
156 typename ReaderType::FileNamesContainer fileNamesInput;
157 fileNamesInput.push_back(m_ArgsInfo.input_arg);
158 reader->SetFileNames( fileNamesInput );
161 } catch (itk::ExceptionObject &excp) {
162 std::cerr << "Error: Exception thrown while reading the DICOM model file !!" << std::endl;
163 std::cerr << excp << std::endl;
165 typename InputImageType::Pointer input = reader->GetOutput();
167 typename ReaderType::DictionaryRawPointer inputDict = (*(reader->GetMetaDataDictionaryArray()))[0];
168 typename ReaderType::DictionaryArrayType outputArray;
169 typename ReaderType::DictionaryRawPointer outputDict = new typename ReaderType::DictionaryType;
170 CopyDictionary (*inputDict, *outputDict);
171 itk::EncapsulateMetaData<std::string>(*outputDict, "0054|0011", NumberToString(1));
172 outputArray.push_back(outputDict);
175 //Read the number of slices and energy window, rotation
176 int nbSlice = input->GetLargestPossibleRegion().GetSize()[2];
177 gdcm::Reader hreader;
178 hreader.SetFileName(m_ArgsInfo.input_arg);
180 gdcm::DataSet& dsInput = hreader.GetFile().GetDataSet();
181 gdcm::Attribute<0x54,0x11> series_number_att;
182 series_number_att.SetFromDataSet(dsInput);
183 int nbEnergyWindow = series_number_att.GetValue();
185 const gdcm::DataElement &seqRotation = dsInput.GetDataElement(gdcm::Tag(0x54, 0x52));
186 gdcm::SmartPointer<gdcm::SequenceOfItems> sqiRotation = seqRotation.GetValueAsSQ();
187 gdcm::Item &itemRotation = sqiRotation->GetItem(1);
188 const gdcm::DataElement &deRotation = itemRotation.GetDataElement(gdcm::Tag(0x54, 0x53));
189 unsigned short SamplesPerPixelRotation = *((unsigned short *) deRotation.GetByteValue()->GetPointer());
190 std::stringstream s_SamplesPerPixelRotation;
191 s_SamplesPerPixelRotation << SamplesPerPixelRotation;
192 std::string p_StrRotation = s_SamplesPerPixelRotation.str();
193 int nbRotation = (int)atof(p_StrRotation.c_str());
195 int nbSlicePerEnergy = nbSlice / nbEnergyWindow;
196 if (nbSlicePerEnergy*nbEnergyWindow != nbSlice)
197 std::cerr << "Error: The number of slices is not correct !!" << std::endl;
199 for (unsigned int energy = 0; energy < nbEnergyWindow; ++energy) {
202 typename InputImageType::Pointer energyImage = InputImageType::New();
203 energyImage->SetRegions(input->GetLargestPossibleRegion());
204 typename InputImageType::IndexType startIndex;
207 startIndex[2] = 0;//energy*nbSlicePerEnergy;
208 typename InputImageType::SizeType regionSize;
209 regionSize[0] = input->GetLargestPossibleRegion().GetSize()[0];
210 regionSize[1] = input->GetLargestPossibleRegion().GetSize()[1];
211 regionSize[2] = nbSlicePerEnergy;
212 typename InputImageType::RegionType region;
213 region.SetSize(regionSize);
214 region.SetIndex(startIndex);
215 energyImage->SetRegions(region);
216 energyImage->Allocate();
218 //Create the iterator on the output
219 itk::ImageRegionIterator<InputImageType> imageOutputIterator(energyImage,energyImage->GetLargestPossibleRegion());
221 //Create the iterator on the region of the input
222 typename InputImageType::IndexType startIndexIterator;
223 startIndexIterator[0] = 0;
224 startIndexIterator[1] = 0;
225 startIndexIterator[2] = energy*nbSlicePerEnergy;
226 typename InputImageType::RegionType regionIterator;
227 regionIterator.SetSize(regionSize);
228 regionIterator.SetIndex(startIndexIterator);
229 itk::ImageRegionIterator<InputImageType> imageInputIterator(input,regionIterator);
231 //Copy the requested region
232 while(!imageInputIterator.IsAtEnd())
234 imageOutputIterator.Set(imageInputIterator.Get());
236 ++imageInputIterator;
237 ++imageOutputIterator;
240 // Output directory and filenames
241 typename WriterType::Pointer writer = WriterType::New();
242 writer->SetInput( energyImage );
243 writer->SetImageIO( gdcmIO );
244 typename ReaderType::FileNamesContainer fileNamesOutput;
245 std::string extension = NumberToString(energy) + ".dcm";
246 std::string filename_out = m_ArgsInfo.output_arg + extension;
247 fileNamesOutput.push_back(filename_out);
248 writer->SetFileNames( fileNamesOutput );
249 writer->SetMetaDataDictionaryArray(&outputArray);
253 if (m_ArgsInfo.verbose_flag)
254 std::cout << writer << std::endl;
256 } catch( itk::ExceptionObject & excp ) {
257 std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
258 std::cerr << excp << std::endl;
261 //Write sequence dicom tag with gdcm
263 reader.SetFileName( filename_out.c_str() );
265 gdcm::File &fileOutput = reader.GetFile();
266 gdcm::DataSet &dsOutput = fileOutput.GetDataSet();
267 const unsigned int ptr_len = 42;
268 char *ptr = new char[ptr_len];
269 memset(ptr,0,ptr_len);
272 gdcm::SmartPointer<gdcm::SequenceOfItems> rotationSq = new gdcm::SequenceOfItems();
273 rotationSq->SetLengthToUndefined();
274 // Create a dataelement
275 gdcm::DataElement rotationDE( gdcm::Tag(0x54, 0x53) );
276 rotationDE.SetVR( gdcm::VR::US );
277 char essai = (char)nbRotation;
278 char *p_essai = &essai;
279 rotationDE.SetByteValue(p_essai, 1);
281 gdcm::Item rotationIt;
282 rotationIt.SetVLToUndefined();
283 gdcm::DataSet &rotationDS = rotationIt.GetNestedDataSet();
284 rotationDS.Insert(rotationDE);
285 rotationSq->AddItem(rotationIt);
286 // Insert sequence into data set
287 gdcm::DataElement rotationDEParent( gdcm::Tag(0x54, 0x52) );
288 rotationDEParent.SetVR(gdcm::VR::SQ);
289 rotationDEParent.SetValue(*rotationSq);
290 rotationDEParent.SetVLToUndefined();
292 gdcm::DataElement energyDEParent( gdcm::Tag(0x54, 0x12) );
293 energyDEParent.SetVR(gdcm::VR::SQ);
295 gdcm::SmartPointer<gdcm::SequenceOfItems> energySq = new gdcm::SequenceOfItems();
296 energySq->SetLengthToUndefined();
298 //Read the caracteristics of this energy window : the name and the thresholds up and down
299 const gdcm::DataElement &seqWindow = dsInput.GetDataElement(gdcm::Tag(0x54, 0x12));
300 gdcm::SmartPointer<gdcm::SequenceOfItems> sqiWindow = seqWindow.GetValueAsSQ();
301 gdcm::Item &itemWindow = sqiWindow->GetItem(energy+1);
302 const gdcm::DataElement &deNameWindow = itemWindow.GetDataElement(gdcm::Tag(0x54, 0x18));
303 std::string nameWindow( deNameWindow.GetByteValue()->GetPointer(), deNameWindow.GetByteValue()->GetLength() );
305 const gdcm::DataElement &deThresholds = itemWindow.GetDataElement(gdcm::Tag(0x54, 0x13));
306 gdcm::SmartPointer<gdcm::SequenceOfItems> sqiThresholds = deThresholds.GetValueAsSQ();
307 for (unsigned int nbWindow=1; nbWindow <= sqiThresholds->GetNumberOfItems() ; ++nbWindow) {
308 gdcm::Item &itemThresholds = sqiThresholds->GetItem(nbWindow);
309 const gdcm::DataElement &deThresholdDown = itemThresholds.GetDataElement(gdcm::Tag(0x54, 0x14));
310 std::string p_StrThresholdDown( deThresholdDown.GetByteValue()->GetPointer(), deThresholdDown.GetByteValue()->GetLength() );
311 double thresholdDown = (double)atof(p_StrThresholdDown.c_str());
312 const gdcm::DataElement &deThresholdUp = itemThresholds.GetDataElement(gdcm::Tag(0x54, 0x15));
313 std::string p_StrThresholdUp( deThresholdUp.GetByteValue()->GetPointer(), deThresholdUp.GetByteValue()->GetLength() );
314 double thresholdUp = (double)atof(p_StrThresholdUp.c_str());
317 gdcm::SmartPointer<gdcm::SequenceOfItems> energyThresholdSq = new gdcm::SequenceOfItems();
318 energyThresholdSq->SetLengthToUndefined();
319 // Create a dataelement
320 gdcm::DataElement energyThresholdDE( gdcm::Tag(0x54, 0x14) );
321 gdcm::DataElement energyUpholdDE( gdcm::Tag(0x54, 0x15) );
322 energyThresholdDE.SetVR( gdcm::VR::DS );
323 energyUpholdDE.SetVR( gdcm::VR::DS );
324 energyThresholdDE.SetByteValue(NumberToString(thresholdDown).c_str(), (uint32_t)strlen(NumberToString(thresholdDown).c_str()));
325 energyUpholdDE.SetByteValue(NumberToString(thresholdUp).c_str(), (uint32_t)strlen(NumberToString(thresholdUp).c_str()));
327 gdcm::Item energyThresholdIt;
328 energyThresholdIt.SetVLToUndefined();
329 gdcm::DataSet &energyThresholdDS = energyThresholdIt.GetNestedDataSet();
330 energyThresholdDS.Insert(energyThresholdDE);
331 energyThresholdDS.Insert(energyUpholdDE);
332 energyThresholdSq->AddItem(energyThresholdIt);
333 // Insert sequence into data set
334 gdcm::DataElement energyThresholdDEParent( gdcm::Tag(0x54, 0x13) );
335 energyThresholdDEParent.SetVR(gdcm::VR::SQ);
336 energyThresholdDEParent.SetValue(*energyThresholdSq);
337 energyThresholdDEParent.SetVLToUndefined();
338 // Create a dataelement
339 gdcm::DataElement energyNameDE( gdcm::Tag(0x54, 0x18) );
340 energyNameDE.SetVR( gdcm::VR::SH );
341 energyNameDE.SetByteValue(nameWindow.c_str(), (uint32_t)strlen(nameWindow.c_str()));
344 energyIt.SetVLToUndefined();
345 gdcm::DataSet &energyDS = energyIt.GetNestedDataSet();
346 energyDS.Insert(energyNameDE);
347 energyDS.Insert(energyThresholdDEParent);
348 energySq->AddItem(energyIt);
350 // Insert sequence into data set
351 energyDEParent.SetValue(*energySq);
352 energyDEParent.SetVLToUndefined();
353 dsOutput.Insert(energyDEParent);
354 dsOutput.Insert(rotationDEParent);
356 w.SetFile( fileOutput );
357 w.SetFileName( filename_out.c_str() );
361 std::cout << "Use GDCM2" << std::endl;
364 //---------------------------------------------------------------------------
367 //---------------------------------------------------------------------------
368 void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict)
370 typedef itk::MetaDataDictionary DictionaryType;
372 DictionaryType::ConstIterator itr = fromDict.Begin();
373 DictionaryType::ConstIterator end = fromDict.End();
374 typedef itk::MetaDataObject< std::string > MetaDataStringType;
378 itk::MetaDataObjectBase::Pointer entry = itr->second;
380 MetaDataStringType::Pointer entryvalue =
381 dynamic_cast<MetaDataStringType *>( entry.GetPointer() ) ;
384 std::string tagkey = itr->first;
385 std::string tagvalue = entryvalue->GetMetaDataObjectValue();
386 itk::EncapsulateMetaData<std::string>(toDict, tagkey, tagvalue);
391 //---------------------------------------------------------------------------
394 //---------------------------------------------------------------------------
395 template <typename T> std::string NumberToString ( T Number )
397 std::ostringstream ss;
401 //---------------------------------------------------------------------------
405 #endif //#define clitkPartitionEnergyWindowDicomGenericFilter_txx