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>
44 #include "itkImageRegionIterator.h"
45 #include "itkMetaImageIO.h"
46 #include "itkMetaDataDictionary.h"
53 //-----------------------------------------------------------
55 //-----------------------------------------------------------
56 template<class args_info_type>
57 PartitionEnergyWindowDicomGenericFilter<args_info_type>::PartitionEnergyWindowDicomGenericFilter()
64 //-----------------------------------------------------------
66 //-----------------------------------------------------------
67 template<class args_info_type>
68 void PartitionEnergyWindowDicomGenericFilter<args_info_type>::Update()
70 // Read the Dimension and PixelType
72 std::string PixelType;
73 ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType);
77 if(Dimension==2) UpdateWithDim<2>(PixelType);
78 else if(Dimension==3) UpdateWithDim<3>(PixelType);
79 // else if (Dimension==4)UpdateWithDim<4>(PixelType);
81 std::cout<<"Error, Only for 2 or 3 Dimensions!!!"<<std::endl ;
86 //-------------------------------------------------------------------
87 // Update with the number of dimensions
88 //-------------------------------------------------------------------
89 template<class args_info_type>
90 template<unsigned int Dimension>
92 PartitionEnergyWindowDicomGenericFilter<args_info_type>::UpdateWithDim(std::string PixelType)
94 if (m_Verbose) std::cout << "Image was detected to be "<<Dimension<<"D and "<< PixelType<<"..."<<std::endl;
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>();
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>();
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>();
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>();
114 else if (PixelType == "double") {
115 if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and double..." << std::endl;
116 UpdateWithDimAndPixelType<Dimension, double>();
119 if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and float..." << std::endl;
120 UpdateWithDimAndPixelType<Dimension, float>();
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
130 //-------------------------------------------------------------------
131 template<class args_info_type>
132 template <unsigned int Dimension, class PixelType>
134 PartitionEnergyWindowDicomGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
137 #if GDCM_MAJOR_VERSION == 2
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;
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 );
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;
164 typename InputImageType::Pointer input = reader->GetOutput();
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);
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);
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();
185 std::cout << nbSlice << " " << nbEnergyWindow << std::endl;
187 int nbSlicePerEnergy = nbSlice / nbEnergyWindow;
188 if (nbSlicePerEnergy*nbEnergyWindow != nbSlice)
189 std::cerr << "Error: The number of slices is not correct !!" << std::endl;
191 for (unsigned int energy = 0; energy < nbEnergyWindow; ++energy) {
194 typename InputImageType::Pointer energyImage = InputImageType::New();
195 energyImage->SetRegions(input->GetLargestPossibleRegion());
196 typename InputImageType::IndexType startIndex;
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();
210 //Create the iterator on the output
211 itk::ImageRegionIterator<InputImageType> imageOutputIterator(energyImage,energyImage->GetLargestPossibleRegion());
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);
223 //Copy the requested region
224 while(!imageInputIterator.IsAtEnd())
226 imageOutputIterator.Set(imageInputIterator.Get());
228 ++imageInputIterator;
229 ++imageOutputIterator;
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);
243 if (m_ArgsInfo.verbose_flag)
244 std::cout << writer << std::endl;
246 } catch( itk::ExceptionObject & excp ) {
247 std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
248 std::cerr << excp << std::endl;
254 //Read the metadata informations in the mhd
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);
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]);
294 //TODO if the input size/spacing and dicom model ones are different
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;
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;
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;
316 typename InputImageType::SizeType regionSizeIterator;
317 regionSizeIterator[0] = input->GetLargestPossibleRegion().GetSize()[0];
318 regionSizeIterator[1] = input->GetLargestPossibleRegion().GetSize()[1];
319 regionSizeIterator[2] = 1;
321 typename InputImageType::RegionType regionIteratorCorrectOrder;
322 regionIteratorCorrectOrder.SetSize(regionSizeIterator);
323 regionIteratorCorrectOrder.SetIndex(startIteratorIndexCorrectOrder);
325 typename InputImageType::RegionType regionIteratorOriginalOrder;
326 regionIteratorOriginalOrder.SetSize(regionSizeIterator);
327 regionIteratorOriginalOrder.SetIndex(startIteratorIndexOriginalOrder);
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;
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);
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);
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;
378 //Write sequence dicom tag with gdcm
380 reader.SetFileName( fileNamesOutput[0].c_str() );
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);
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);
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();
411 gdcm::DataElement energyDEParent( gdcm::Tag(0x54, 0x12) );
412 energyDEParent.SetVR(gdcm::VR::SQ);
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]));
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]));
444 energyIt.SetVLToUndefined();
445 gdcm::DataSet &energyDS = energyIt.GetNestedDataSet();
446 energyDS.Insert(energyNameDE);
447 energyDS.Insert(energyThresholdDEParent);
448 energySq->AddItem(energyIt);
450 // Insert sequence into data set
451 energyDEParent.SetValue(*energySq);
452 energyDEParent.SetVLToUndefined();
453 ds2.Insert(energyDEParent);
454 ds2.Insert(rotationDEParent);
458 w.SetFileName( fileNamesOutput[0].c_str() );
462 std::cout << "Use GDCM2" << std::endl;
465 //---------------------------------------------------------------------------
468 //---------------------------------------------------------------------------
469 void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict)
471 typedef itk::MetaDataDictionary DictionaryType;
473 DictionaryType::ConstIterator itr = fromDict.Begin();
474 DictionaryType::ConstIterator end = fromDict.End();
475 typedef itk::MetaDataObject< std::string > MetaDataStringType;
479 itk::MetaDataObjectBase::Pointer entry = itr->second;
481 MetaDataStringType::Pointer entryvalue =
482 dynamic_cast<MetaDataStringType *>( entry.GetPointer() ) ;
485 std::string tagkey = itr->first;
486 std::string tagvalue = entryvalue->GetMetaDataObjectValue();
487 itk::EncapsulateMetaData<std::string>(toDict, tagkey, tagvalue);
492 //---------------------------------------------------------------------------
495 //---------------------------------------------------------------------------
496 template <typename T> std::string NumberToString ( T Number )
498 std::ostringstream ss;
502 //---------------------------------------------------------------------------
506 #endif //#define clitkPartitionEnergyWindowDicomGenericFilter_txx