]> Creatis software - clitk.git/blob - tools/clitkPartitionEnergyWindowDicomGenericFilter.txx
Add clitkPartitionEnergyWindowDicom tool
[clitk.git] / tools / clitkPartitionEnergyWindowDicomGenericFilter.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 clitkPartitionEnergyWindowDicomGenericFilter_txx
19 #define clitkPartitionEnergyWindowDicomGenericFilter_txx
20
21 /* =================================================
22  * @file   clitkPartitionEnergyWindowDicomGenericFilter.txx
23  * @author
24  * @date
25  *
26  * @brief
27  *
28  ===================================================*/
29
30 #include <sstream>
31 // clitk
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 #else
40 #include "gdcmFile.h"
41 #include "gdcmUtil.h"
42 #endif
43
44 #include "itkImageRegionIterator.h"
45 #include "itkMetaImageIO.h"
46 #include "itkMetaDataDictionary.h"
47
48
49 namespace clitk
50 {
51
52
53 //-----------------------------------------------------------
54 // Constructor
55 //-----------------------------------------------------------
56 template<class args_info_type>
57 PartitionEnergyWindowDicomGenericFilter<args_info_type>::PartitionEnergyWindowDicomGenericFilter()
58 {
59   m_Verbose=false;
60   m_InputFileName="";
61 }
62
63
64 //-----------------------------------------------------------
65 // Update
66 //-----------------------------------------------------------
67 template<class args_info_type>
68 void PartitionEnergyWindowDicomGenericFilter<args_info_type>::Update()
69 {
70   // Read the Dimension and PixelType
71   int Dimension;
72   std::string PixelType;
73   ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType);
74
75
76   // Call UpdateWithDim
77   if(Dimension==2) UpdateWithDim<2>(PixelType);
78   else if(Dimension==3) UpdateWithDim<3>(PixelType);
79   // else if (Dimension==4)UpdateWithDim<4>(PixelType);
80   else {
81     std::cout<<"Error, Only for 2 or 3  Dimensions!!!"<<std::endl ;
82     return;
83   }
84 }
85
86 //-------------------------------------------------------------------
87 // Update with the number of dimensions
88 //-------------------------------------------------------------------
89 template<class args_info_type>
90 template<unsigned int Dimension>
91 void
92 PartitionEnergyWindowDicomGenericFilter<args_info_type>::UpdateWithDim(std::string PixelType)
93 {
94   if (m_Verbose) std::cout << "Image was detected to be "<<Dimension<<"D and "<< PixelType<<"..."<<std::endl;
95
96   if(PixelType == "short") {
97     if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and signed short..." << std::endl;
98     UpdateWithDimAndPixelType<Dimension, signed short>();
99   }
100   else if(PixelType == "unsigned_short"){
101     if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_short..." << std::endl;
102     UpdateWithDimAndPixelType<Dimension, unsigned short>();
103   }
104
105   else if (PixelType == "unsigned_char") {
106     if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_char..." << std::endl;
107     UpdateWithDimAndPixelType<Dimension, unsigned char>();
108   }
109
110   //     else if (PixelType == "char"){
111   //       if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and signed_char..." << std::endl;
112   //       UpdateWithDimAndPixelType<Dimension, signed char>();
113   //     }
114   else if (PixelType == "double") {
115     if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and double..." << std::endl;
116     UpdateWithDimAndPixelType<Dimension, double>();
117   }
118   else {
119     if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and float..." << std::endl;
120     UpdateWithDimAndPixelType<Dimension, float>();
121   }
122 }
123
124 //-------------------------------------------------------------------
125 // Update with the number of dimensions and the pixeltype read from
126 // the dicom files. The MHD files may be resampled to match the
127 // dicom spacing (and number of slices). Rounding errors in resampling
128 // are handled by removing files when generating the output dicom
129 // series.
130 //-------------------------------------------------------------------
131 template<class args_info_type>
132 template <unsigned int Dimension, class  PixelType>
133 void
134 PartitionEnergyWindowDicomGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
135 {
136
137 #if GDCM_MAJOR_VERSION == 2
138   // ImageTypes
139   typedef itk::Image<PixelType, Dimension> InputImageType;
140   typedef itk::Image<PixelType, Dimension> OutputImageType;
141   typedef itk::ImageSeriesReader< InputImageType >     ReaderType;
142   typedef itk::ImageSeriesWriter< InputImageType, InputImageType >     WriterType;
143   typedef itk::GDCMImageIO ImageIOType;
144   typedef typename InputImageType::RegionType RegionType;
145   typedef typename RegionType::SizeType SizeType;
146
147
148   // Read Dicom model file
149   typename ReaderType::Pointer reader = ReaderType::New();
150   ImageIOType::Pointer gdcmIO = ImageIOType::New();
151   std::string filename_out = m_ArgsInfo.output_arg;
152   gdcmIO->LoadPrivateTagsOn();
153   gdcmIO->KeepOriginalUIDOn();
154   reader->SetImageIO( gdcmIO );
155   typename ReaderType::FileNamesContainer fileNamesInput;
156   fileNamesInput.push_back(m_ArgsInfo.input_arg);
157   reader->SetFileNames( fileNamesInput );
158   try {
159     reader->Update();
160   } catch (itk::ExceptionObject &excp) {
161     std::cerr << "Error: Exception thrown while reading the DICOM model file !!" << std::endl;
162     std::cerr << excp << std::endl;
163   }
164   typename InputImageType::Pointer input = reader->GetOutput();
165
166   typename ReaderType::DictionaryRawPointer inputDict = (*(reader->GetMetaDataDictionaryArray()))[0];
167   typename ReaderType::DictionaryArrayType outputArray;
168   typename ReaderType::DictionaryRawPointer outputDict = new typename ReaderType::DictionaryType;
169   CopyDictionary (*inputDict, *outputDict);
170   //itk::EncapsulateMetaData<std::string>(*outputDict, "0054|0011", NumberToString(energyNumber));
171   //itk::EncapsulateMetaData<std::string>(*outputDict, "0054|0021", NumberToString(headNumber));
172   outputArray.push_back(outputDict);
173
174
175   //Read the number of slices and energy window
176   int nbSlice = input->GetLargestPossibleRegion().GetSize()[2];
177   gdcm::Reader hreader;
178   hreader.SetFileName(m_ArgsInfo.input_arg);
179   hreader.Read();
180   gdcm::DataSet& ds = hreader.GetFile().GetDataSet();
181   gdcm::Attribute<0x54,0x11> series_number_att;
182   series_number_att.SetFromDataSet(ds);
183   int nbEnergyWindow = series_number_att.GetValue();
184
185   std::cout << nbSlice << " " << nbEnergyWindow << std::endl;
186
187   int nbSlicePerEnergy = nbSlice / nbEnergyWindow;
188   if (nbSlicePerEnergy*nbEnergyWindow != nbSlice)
189     std::cerr << "Error: The number of slices is not correct !!" << std::endl;
190
191   for (unsigned int energy = 0; energy < nbEnergyWindow; ++energy) {
192     
193     //Create the output
194     typename InputImageType::Pointer energyImage = InputImageType::New();
195     energyImage->SetRegions(input->GetLargestPossibleRegion());
196     typename InputImageType::IndexType startIndex;
197     startIndex[0] = 0;
198     startIndex[1] = 0;
199     startIndex[2] = 0;//energy*nbSlicePerEnergy;
200     typename InputImageType::SizeType regionSize;
201     regionSize[0] = input->GetLargestPossibleRegion().GetSize()[0];
202     regionSize[1] = input->GetLargestPossibleRegion().GetSize()[1];
203     regionSize[2] = nbSlicePerEnergy;
204     typename InputImageType::RegionType region;
205     region.SetSize(regionSize);
206     region.SetIndex(startIndex);
207     energyImage->SetRegions(region);
208     energyImage->Allocate();
209
210     //Create the iterator on the output
211     itk::ImageRegionIterator<InputImageType> imageOutputIterator(energyImage,energyImage->GetLargestPossibleRegion());
212
213     //Create the iterator on the region of the input
214     typename InputImageType::IndexType startIndexIterator;
215     startIndexIterator[0] = 0;
216     startIndexIterator[1] = 0;
217     startIndexIterator[2] = energy*nbSlicePerEnergy;
218     typename InputImageType::RegionType regionIterator;
219     regionIterator.SetSize(regionSize);
220     regionIterator.SetIndex(startIndexIterator);
221     itk::ImageRegionIterator<InputImageType> imageInputIterator(input,regionIterator);
222
223     //Copy the requested region
224     while(!imageInputIterator.IsAtEnd())
225     {
226       imageOutputIterator.Set(imageInputIterator.Get());
227
228       ++imageInputIterator;
229       ++imageOutputIterator;
230     }
231
232     // Output directory and filenames
233     typename WriterType::Pointer writer = WriterType::New();
234     writer->SetInput( energyImage );
235     writer->SetImageIO( gdcmIO );
236     typename ReaderType::FileNamesContainer fileNamesOutput;
237     fileNamesOutput.push_back(filename_out);
238     writer->SetFileNames( fileNamesOutput );
239     writer->SetMetaDataDictionaryArray(&outputArray);
240
241     // Write
242     try {
243       if (m_ArgsInfo.verbose_flag)
244         std::cout << writer << std::endl;
245       writer->Update();
246     } catch( itk::ExceptionObject & excp ) {
247       std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
248       std::cerr << excp << std::endl;
249     }
250   }
251
252 /*
253
254   //Read the metadata informations in the mhd
255   MetaObject tObj(3);
256   tObj.AddUserField("NumberOfEnergyWindows", MET_STRING);
257   tObj.AddUserField("NbOfHeads", MET_STRING);
258   tObj.AddUserField("NbOfProjections", MET_STRING);
259   tObj.Read(m_InputFileName.c_str());
260   char *p_energyNumber, *p_headNumber, *p_rotationNumber;
261   int energyNumber, headNumber, rotationNumber;
262   p_energyNumber = static_cast<char*>(tObj.GetUserField("NumberOfEnergyWindows"));
263   p_headNumber = static_cast<char*>(tObj.GetUserField("NbOfHeads"));
264   p_rotationNumber = static_cast<char*>(tObj.GetUserField("NbOfProjections"));
265   energyNumber = atoi(p_energyNumber);
266   headNumber = atoi(p_headNumber);
267   rotationNumber = atoi(p_rotationNumber);
268   for (unsigned int i=0; i<energyNumber; ++i) {
269     std::string tagEnergyName("EnergyWindow_");
270     tagEnergyName += NumberToString(i).c_str();
271     std::string tagEnergyThreshold(tagEnergyName), tagEnergyUphold(tagEnergyName);
272     tagEnergyThreshold += "_threshold";
273     tagEnergyUphold += "_uphold";
274     tObj.AddUserField(tagEnergyName.c_str(), MET_STRING);
275     tObj.AddUserField(tagEnergyThreshold.c_str(), MET_STRING);
276     tObj.AddUserField(tagEnergyUphold.c_str(), MET_STRING);
277   }
278   tObj.Read(m_InputFileName.c_str());
279   std::vector<char*> p_EnergyWindowName(energyNumber), p_EnergyWindowThreshold(energyNumber), p_EnergyWindowUphold(energyNumber);
280   std::vector<int> energyWindowThreshold(energyNumber), energyWindowUphold(energyNumber);
281   for (unsigned int i=0; i<energyNumber; ++i) {
282     std::string tagEnergyName("EnergyWindow_");
283     tagEnergyName += NumberToString(i).c_str();
284     std::string tagEnergyThreshold(tagEnergyName), tagEnergyUphold(tagEnergyName);
285     tagEnergyThreshold += "_threshold";
286     tagEnergyUphold += "_uphold";
287     p_EnergyWindowName[i] = static_cast<char*>(tObj.GetUserField(tagEnergyName.c_str()));
288     p_EnergyWindowThreshold[i] = static_cast<char*>(tObj.GetUserField(tagEnergyThreshold.c_str()));
289     p_EnergyWindowUphold[i] = static_cast<char*>(tObj.GetUserField(tagEnergyUphold.c_str()));
290     energyWindowThreshold[i] = atoi(p_EnergyWindowThreshold[i]);
291     energyWindowUphold[i] = atoi(p_EnergyWindowUphold[i]);
292   }
293
294 //TODO if the input size/spacing and dicom model ones are different
295
296   // Create a new mhd image with the correct dicom order slices
297   typename InputImageType::Pointer mhdCorrectOrder = InputImageType::New();
298   mhdCorrectOrder->SetRegions(input->GetLargestPossibleRegion());
299   mhdCorrectOrder->Allocate();
300   unsigned int zAxis(0); //z value for the input mhd image
301   for (unsigned int energy = 0; energy < energyNumber; ++energy) {
302     for (unsigned int head = 0; head < headNumber; ++head) {
303       for (unsigned int rotation = 0; rotation < rotationNumber; ++rotation) {
304         std::cout << "Energy " << energy << " Head " << head << " Rotation " << rotation << std::endl;
305
306         typename InputImageType::IndexType startIteratorIndexCorrectOrder; //pixel index of mhdCorrectOrder
307         startIteratorIndexCorrectOrder[0] = 0;
308         startIteratorIndexCorrectOrder[1] = 0;
309         startIteratorIndexCorrectOrder[2] = rotation + head*rotationNumber + energy*headNumber;
310
311         typename InputImageType::IndexType startIteratorIndexOriginalOrder; //pixel index of input mhd
312         startIteratorIndexOriginalOrder[0] = 0;
313         startIteratorIndexOriginalOrder[1] = 0;
314         startIteratorIndexOriginalOrder[2] = head + energy*headNumber + rotation*energyNumber;
315
316         typename InputImageType::SizeType regionSizeIterator;
317         regionSizeIterator[0] = input->GetLargestPossibleRegion().GetSize()[0];
318         regionSizeIterator[1] = input->GetLargestPossibleRegion().GetSize()[1];
319         regionSizeIterator[2] = 1;
320
321         typename InputImageType::RegionType regionIteratorCorrectOrder;
322         regionIteratorCorrectOrder.SetSize(regionSizeIterator);
323         regionIteratorCorrectOrder.SetIndex(startIteratorIndexCorrectOrder);
324
325         typename InputImageType::RegionType regionIteratorOriginalOrder;
326         regionIteratorOriginalOrder.SetSize(regionSizeIterator);
327         regionIteratorOriginalOrder.SetIndex(startIteratorIndexOriginalOrder);
328
329         itk::ImageRegionIterator<InputImageType> CorrectOrderIterator(mhdCorrectOrder,regionIteratorCorrectOrder);
330         itk::ImageRegionIterator<InputImageType> OriginalOrderIterator(input,regionIteratorOriginalOrder);
331         while(!CorrectOrderIterator.IsAtEnd()) {
332           CorrectOrderIterator.Set(OriginalOrderIterator.Get());
333           ++CorrectOrderIterator;
334           ++OriginalOrderIterator;
335         }
336
337         ++zAxis;
338       }
339     }
340   }
341
342
343   // update output dicom keys/tags
344   // string for distinguishing items inside sequence:
345   const std::string ITEM_ENCAPSULATE_STRING("DICOM_ITEM_ENCAPSULATE");
346   std::string tempString = ITEM_ENCAPSULATE_STRING + "01";
347   typename ReaderSeriesType::DictionaryRawPointer inputDict = (*(readerSeries->GetMetaDataDictionaryArray()))[0];
348   typename ReaderSeriesType::DictionaryArrayType outputArray;
349   typename ReaderSeriesType::DictionaryRawPointer outputDict = new typename ReaderSeriesType::DictionaryType;
350   CopyDictionary (*inputDict, *outputDict);
351   itk::EncapsulateMetaData<std::string>(*outputDict, "0054|0011", NumberToString(energyNumber));
352   itk::EncapsulateMetaData<std::string>(*outputDict, "0054|0021", NumberToString(headNumber));
353   outputArray.push_back(outputDict);
354
355
356   // Output directory and filenames
357   typedef itk::ImageSeriesWriter<OutputImageType, OutputImageType>  WriterSerieType;
358   typename WriterSerieType::Pointer writerSerie = WriterSerieType::New();
359   writerSerie->SetInput( mhdCorrectOrder );
360   writerSerie->SetImageIO( gdcmIO );
361   typename ReaderSeriesType::FileNamesContainer fileNamesOutput;
362   fileNamesOutput.push_back(filename_out);
363   writerSerie->SetFileNames( fileNamesOutput );
364   writerSerie->SetMetaDataDictionaryArray(&outputArray);
365
366
367   // Write
368   try {
369     if (m_ArgsInfo.verbose_flag)
370       std::cout << writerSerie << std::endl;
371     writerSerie->Update();
372   } catch( itk::ExceptionObject & excp ) {
373     std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
374     std::cerr << excp << std::endl;
375   }
376
377
378   //Write sequence dicom tag with gdcm
379   gdcm::Reader reader;
380   reader.SetFileName( fileNamesOutput[0].c_str() );
381   reader.Read();
382   gdcm::File &file = reader.GetFile();
383   gdcm::DataSet &ds2 = file.GetDataSet();
384   const unsigned int ptr_len = 42;
385   char *ptr = new char[ptr_len];
386   memset(ptr,0,ptr_len);
387
388   //Write rotation tag
389   // Create a Sequence
390   gdcm::SmartPointer<gdcm::SequenceOfItems> rotationSq = new gdcm::SequenceOfItems();
391   rotationSq->SetLengthToUndefined();
392   // Create a dataelement
393   gdcm::DataElement rotationDE( gdcm::Tag(0x54, 0x53) );
394   rotationDE.SetVR( gdcm::VR::US );
395   char essai = (char)rotationNumber;
396   char *p_essai = &essai;
397   rotationDE.SetByteValue(p_essai, 1);
398   // Create an item
399   gdcm::Item rotationIt;
400   rotationIt.SetVLToUndefined();
401   gdcm::DataSet &rotationDS = rotationIt.GetNestedDataSet();
402   rotationDS.Insert(rotationDE);
403   rotationSq->AddItem(rotationIt);
404   // Insert sequence into data set
405   gdcm::DataElement rotationDEParent( gdcm::Tag(0x54, 0x52) );
406   rotationDEParent.SetVR(gdcm::VR::SQ);
407   rotationDEParent.SetValue(*rotationSq);
408   rotationDEParent.SetVLToUndefined();
409
410   //Write energy
411   gdcm::DataElement energyDEParent( gdcm::Tag(0x54, 0x12) );
412   energyDEParent.SetVR(gdcm::VR::SQ);
413   // Create a Sequence
414   gdcm::SmartPointer<gdcm::SequenceOfItems> energySq = new gdcm::SequenceOfItems();
415   energySq->SetLengthToUndefined();
416   for (unsigned int i=0; i<energyNumber; ++i) {
417     gdcm::SmartPointer<gdcm::SequenceOfItems> energyThresholdSq = new gdcm::SequenceOfItems();
418     energyThresholdSq->SetLengthToUndefined();
419     // Create a dataelement
420     gdcm::DataElement energyThresholdDE( gdcm::Tag(0x54, 0x14) );
421     gdcm::DataElement energyUpholdDE( gdcm::Tag(0x54, 0x15) );
422     energyThresholdDE.SetVR( gdcm::VR::DS );
423     energyUpholdDE.SetVR( gdcm::VR::DS );
424     energyThresholdDE.SetByteValue(p_EnergyWindowThreshold[i], (uint32_t)strlen(p_EnergyWindowThreshold[i]));
425     energyUpholdDE.SetByteValue(p_EnergyWindowUphold[i], (uint32_t)strlen(p_EnergyWindowUphold[i]));
426     // Create an item
427     gdcm::Item energyThresholdIt;
428     energyThresholdIt.SetVLToUndefined();
429     gdcm::DataSet &energyThresholdDS = energyThresholdIt.GetNestedDataSet();
430     energyThresholdDS.Insert(energyThresholdDE);
431     energyThresholdDS.Insert(energyUpholdDE);
432     energyThresholdSq->AddItem(energyThresholdIt);
433     // Insert sequence into data set
434     gdcm::DataElement energyThresholdDEParent( gdcm::Tag(0x54, 0x13) );
435     energyThresholdDEParent.SetVR(gdcm::VR::SQ);
436     energyThresholdDEParent.SetValue(*energyThresholdSq);
437     energyThresholdDEParent.SetVLToUndefined();
438     // Create a dataelement
439     gdcm::DataElement energyNameDE( gdcm::Tag(0x54, 0x18) );
440     energyNameDE.SetVR( gdcm::VR::SH );
441     energyNameDE.SetByteValue(p_EnergyWindowName[i], (uint32_t)strlen(p_EnergyWindowName[i]));
442     // Create an item
443     gdcm::Item energyIt;
444     energyIt.SetVLToUndefined();
445     gdcm::DataSet &energyDS = energyIt.GetNestedDataSet();
446     energyDS.Insert(energyNameDE);
447     energyDS.Insert(energyThresholdDEParent);
448     energySq->AddItem(energyIt);
449   }
450   // Insert sequence into data set
451   energyDEParent.SetValue(*energySq);
452   energyDEParent.SetVLToUndefined();
453   ds2.Insert(energyDEParent);
454   ds2.Insert(rotationDEParent);
455
456   gdcm::Writer w;
457   w.SetFile( file );
458   w.SetFileName( fileNamesOutput[0].c_str() );
459   w.Write();
460 */
461 #else
462   std::cout << "Use GDCM2" << std::endl;
463 #endif
464 }
465 //---------------------------------------------------------------------------
466
467
468 //---------------------------------------------------------------------------
469 void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict)
470 {
471   typedef itk::MetaDataDictionary DictionaryType;
472
473   DictionaryType::ConstIterator itr = fromDict.Begin();
474   DictionaryType::ConstIterator end = fromDict.End();
475   typedef itk::MetaDataObject< std::string > MetaDataStringType;
476
477   while( itr != end )
478     {
479     itk::MetaDataObjectBase::Pointer  entry = itr->second;
480
481     MetaDataStringType::Pointer entryvalue =
482       dynamic_cast<MetaDataStringType *>( entry.GetPointer() ) ;
483     if( entryvalue )
484       {
485       std::string tagkey   = itr->first;
486       std::string tagvalue = entryvalue->GetMetaDataObjectValue();
487       itk::EncapsulateMetaData<std::string>(toDict, tagkey, tagvalue);
488       }
489     ++itr;
490     }
491 }
492 //---------------------------------------------------------------------------
493
494
495 //---------------------------------------------------------------------------
496 template <typename T> std::string NumberToString ( T Number )
497 {
498    std::ostringstream ss;
499    ss << Number;
500    return ss.str();
501 }
502 //---------------------------------------------------------------------------
503
504 }//end clitk
505
506 #endif //#define clitkPartitionEnergyWindowDicomGenericFilter_txx