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 clitkGateSimulation2DicomGenericFilter_txx
19 #define clitkGateSimulation2DicomGenericFilter_txx
21 /* =================================================
22 * @file clitkGateSimulation2DicomGenericFilter.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 GateSimulation2DicomGenericFilter<args_info_type>::GateSimulation2DicomGenericFilter()
64 //-----------------------------------------------------------
66 //-----------------------------------------------------------
67 template<class args_info_type>
68 void GateSimulation2DicomGenericFilter<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 GateSimulation2DicomGenericFilter<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 GateSimulation2DicomGenericFilter<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::ImageFileReader< InputImageType > ReaderType;
142 typedef itk::ImageSeriesReader< InputImageType > ReaderSeriesType;
143 typedef itk::GDCMImageIO ImageIOType;
146 // Read Dicom model file
147 typename ReaderType::Pointer reader = ReaderType::New();
148 typename ReaderSeriesType::Pointer readerSeries = ReaderSeriesType::New();
149 ImageIOType::Pointer gdcmIO = ImageIOType::New();
150 std::string filename_out = m_ArgsInfo.outputFilename_arg;
151 gdcmIO->LoadPrivateTagsOn();
152 gdcmIO->KeepOriginalUIDOn();
153 reader->SetImageIO( gdcmIO );
154 reader->SetFileName( m_ArgsInfo.inputModelFilename_arg );
155 typename ReaderSeriesType::FileNamesContainer fileNames;
156 fileNames.push_back(m_ArgsInfo.inputModelFilename_arg);
157 readerSeries->SetImageIO( gdcmIO );
158 readerSeries->SetFileNames( fileNames );
161 readerSeries->Update();
162 } catch (itk::ExceptionObject &excp) {
163 std::cerr << "Error: Exception thrown while reading the DICOM model file !!" << std::endl;
164 std::cerr << excp << std::endl;
168 // Read the input (MHD file)
169 typedef typename InputImageType::RegionType RegionType;
170 typedef typename RegionType::SizeType SizeType;
171 typedef itk::ImageFileReader<InputImageType> InputReaderType;
172 typename InputReaderType::Pointer volumeReader = InputReaderType::New();
173 volumeReader->SetFileName( m_InputFileName);
174 volumeReader->Update();
175 typename InputImageType::Pointer input = volumeReader->GetOutput();
178 //Read the metadata informations in the mhd
180 tObj.AddUserField("NumberOfEnergyWindows", MET_STRING);
181 tObj.AddUserField("NbOfHeads", MET_STRING);
182 tObj.AddUserField("NbOfProjections", MET_STRING);
183 tObj.Read(m_InputFileName.c_str());
184 char *p_energyNumber, *p_headNumber, *p_rotationNumber;
185 int energyNumber, headNumber, rotationNumber;
186 p_energyNumber = static_cast<char*>(tObj.GetUserField("NumberOfEnergyWindows"));
187 p_headNumber = static_cast<char*>(tObj.GetUserField("NbOfHeads"));
188 p_rotationNumber = static_cast<char*>(tObj.GetUserField("NbOfProjections"));
189 energyNumber = atoi(p_energyNumber);
190 headNumber = atoi(p_headNumber);
191 rotationNumber = atoi(p_rotationNumber);
192 for (unsigned int i=0; i<energyNumber; ++i) {
193 std::string tagEnergyName("EnergyWindow_");
194 tagEnergyName += NumberToString(i).c_str();
195 std::string tagEnergyThreshold(tagEnergyName), tagEnergyUphold(tagEnergyName);
196 tagEnergyThreshold += "_threshold";
197 tagEnergyUphold += "_uphold";
198 tObj.AddUserField(tagEnergyName.c_str(), MET_STRING);
199 tObj.AddUserField(tagEnergyThreshold.c_str(), MET_STRING);
200 tObj.AddUserField(tagEnergyUphold.c_str(), MET_STRING);
202 tObj.Read(m_InputFileName.c_str());
203 std::vector<char*> p_EnergyWindowName(energyNumber), p_EnergyWindowThreshold(energyNumber), p_EnergyWindowUphold(energyNumber);
204 std::vector<int> energyWindowThreshold(energyNumber), energyWindowUphold(energyNumber);
205 for (unsigned int i=0; i<energyNumber; ++i) {
206 std::string tagEnergyName("EnergyWindow_");
207 tagEnergyName += NumberToString(i).c_str();
208 std::string tagEnergyThreshold(tagEnergyName), tagEnergyUphold(tagEnergyName);
209 tagEnergyThreshold += "_threshold";
210 tagEnergyUphold += "_uphold";
211 p_EnergyWindowName[i] = static_cast<char*>(tObj.GetUserField(tagEnergyName.c_str()));
212 p_EnergyWindowThreshold[i] = static_cast<char*>(tObj.GetUserField(tagEnergyThreshold.c_str()));
213 p_EnergyWindowUphold[i] = static_cast<char*>(tObj.GetUserField(tagEnergyUphold.c_str()));
214 energyWindowThreshold[i] = atoi(p_EnergyWindowThreshold[i]);
215 energyWindowUphold[i] = atoi(p_EnergyWindowUphold[i]);
218 //TODO if the input size/spacing and dicom model ones are different
220 // Create a new mhd image with the correct dicom order slices
221 typename InputImageType::Pointer mhdCorrectOrder = InputImageType::New();
222 mhdCorrectOrder->SetRegions(input->GetLargestPossibleRegion());
223 mhdCorrectOrder->Allocate();
224 unsigned int zAxis(0); //z value for the input mhd image
225 for (unsigned int energy = 0; energy < energyNumber; ++energy) {
226 for (unsigned int head = 0; head < headNumber; ++head) {
227 for (unsigned int rotation = 0; rotation < rotationNumber; ++rotation) {
228 std::cout << "Energy " << energy << " Head " << head << " Rotation " << rotation << std::endl;
230 typename InputImageType::IndexType startIteratorIndexCorrectOrder; //pixel index of mhdCorrectOrder
231 startIteratorIndexCorrectOrder[0] = 0;
232 startIteratorIndexCorrectOrder[1] = 0;
233 startIteratorIndexCorrectOrder[2] = rotation + head*rotationNumber + energy*headNumber;
235 typename InputImageType::IndexType startIteratorIndexOriginalOrder; //pixel index of input mhd
236 startIteratorIndexOriginalOrder[0] = 0;
237 startIteratorIndexOriginalOrder[1] = 0;
238 startIteratorIndexOriginalOrder[2] = head + energy*headNumber + rotation*energyNumber;
240 typename InputImageType::SizeType regionSizeIterator;
241 regionSizeIterator[0] = input->GetLargestPossibleRegion().GetSize()[0];
242 regionSizeIterator[1] = input->GetLargestPossibleRegion().GetSize()[1];
243 regionSizeIterator[2] = 1;
245 typename InputImageType::RegionType regionIteratorCorrectOrder;
246 regionIteratorCorrectOrder.SetSize(regionSizeIterator);
247 regionIteratorCorrectOrder.SetIndex(startIteratorIndexCorrectOrder);
249 typename InputImageType::RegionType regionIteratorOriginalOrder;
250 regionIteratorOriginalOrder.SetSize(regionSizeIterator);
251 regionIteratorOriginalOrder.SetIndex(startIteratorIndexOriginalOrder);
253 itk::ImageRegionIterator<InputImageType> CorrectOrderIterator(mhdCorrectOrder,regionIteratorCorrectOrder);
254 itk::ImageRegionIterator<InputImageType> OriginalOrderIterator(input,regionIteratorOriginalOrder);
255 while(!CorrectOrderIterator.IsAtEnd()) {
256 CorrectOrderIterator.Set(OriginalOrderIterator.Get());
257 ++CorrectOrderIterator;
258 ++OriginalOrderIterator;
267 // update output dicom keys/tags
268 // string for distinguishing items inside sequence:
269 const std::string ITEM_ENCAPSULATE_STRING("DICOM_ITEM_ENCAPSULATE");
270 std::string tempString = ITEM_ENCAPSULATE_STRING + "01";
271 typename ReaderSeriesType::DictionaryRawPointer inputDict = (*(readerSeries->GetMetaDataDictionaryArray()))[0];
272 typename ReaderSeriesType::DictionaryArrayType outputArray;
273 typename ReaderSeriesType::DictionaryRawPointer outputDict = new typename ReaderSeriesType::DictionaryType;
274 CopyDictionary (*inputDict, *outputDict);
275 itk::EncapsulateMetaData<std::string>(*outputDict, "0054|0011", NumberToString(energyNumber));
276 itk::EncapsulateMetaData<std::string>(*outputDict, "0054|0021", NumberToString(headNumber));
277 outputArray.push_back(outputDict);
280 // Output directory and filenames
281 typedef itk::ImageFileWriter<OutputImageType> WriterType;
282 typedef itk::ImageSeriesWriter<OutputImageType, OutputImageType> WriterSerieType;
283 typename WriterType::Pointer writer = WriterType::New();
284 typename WriterSerieType::Pointer writerSerie = WriterSerieType::New();
285 writer->SetInput( mhdCorrectOrder );
286 writer->SetImageIO( gdcmIO );
287 writer->SetFileName( filename_out );
288 writerSerie->SetInput( mhdCorrectOrder );
289 writerSerie->SetImageIO( gdcmIO );
290 typename ReaderSeriesType::FileNamesContainer fileNamesOutput;
291 fileNamesOutput.push_back(filename_out);
292 writerSerie->SetFileNames( fileNamesOutput );
293 writerSerie->SetMetaDataDictionaryArray(&outputArray);
298 if (m_ArgsInfo.verbose_flag)
299 std::cout << writer << std::endl;
301 writerSerie->Update();
302 } catch( itk::ExceptionObject & excp ) {
303 std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
304 std::cerr << excp << std::endl;
308 //Write sequence dicom tag with gdcm
309 gdcm::Reader reader2;
310 reader2.SetFileName( fileNamesOutput[0].c_str() );
312 gdcm::File &file = reader2.GetFile();
313 gdcm::DataSet &ds2 = file.GetDataSet();
314 const unsigned int ptr_len = 42;
315 char *ptr = new char[ptr_len];
316 memset(ptr,0,ptr_len);
320 gdcm::SmartPointer<gdcm::SequenceOfItems> rotationSq = new gdcm::SequenceOfItems();
321 rotationSq->SetLengthToUndefined();
322 // Create a dataelement
323 gdcm::DataElement rotationDE( gdcm::Tag(0x54, 0x53) );
324 rotationDE.SetVR( gdcm::VR::US );
325 char essai = (char)rotationNumber;
326 char *p_essai = &essai;
327 rotationDE.SetByteValue(p_essai, 1);
329 gdcm::Item rotationIt;
330 rotationIt.SetVLToUndefined();
331 gdcm::DataSet &rotationDS = rotationIt.GetNestedDataSet();
332 rotationDS.Insert(rotationDE);
333 rotationSq->AddItem(rotationIt);
334 // Insert sequence into data set
335 gdcm::DataElement rotationDEParent( gdcm::Tag(0x54, 0x52) );
336 rotationDEParent.SetVR(gdcm::VR::SQ);
337 rotationDEParent.SetValue(*rotationSq);
338 rotationDEParent.SetVLToUndefined();
341 gdcm::DataElement energyDEParent( gdcm::Tag(0x54, 0x12) );
342 energyDEParent.SetVR(gdcm::VR::SQ);
344 gdcm::SmartPointer<gdcm::SequenceOfItems> energySq = new gdcm::SequenceOfItems();
345 energySq->SetLengthToUndefined();
346 for (unsigned int i=0; i<energyNumber; ++i) {
347 gdcm::SmartPointer<gdcm::SequenceOfItems> energyThresholdSq = new gdcm::SequenceOfItems();
348 energyThresholdSq->SetLengthToUndefined();
349 // Create a dataelement
350 gdcm::DataElement energyThresholdDE( gdcm::Tag(0x54, 0x14) );
351 gdcm::DataElement energyUpholdDE( gdcm::Tag(0x54, 0x15) );
352 energyThresholdDE.SetVR( gdcm::VR::DS );
353 energyUpholdDE.SetVR( gdcm::VR::DS );
354 energyThresholdDE.SetByteValue(p_EnergyWindowThreshold[i], (uint32_t)strlen(p_EnergyWindowThreshold[i]));
355 energyUpholdDE.SetByteValue(p_EnergyWindowUphold[i], (uint32_t)strlen(p_EnergyWindowUphold[i]));
357 gdcm::Item energyThresholdIt;
358 energyThresholdIt.SetVLToUndefined();
359 gdcm::DataSet &energyThresholdDS = energyThresholdIt.GetNestedDataSet();
360 energyThresholdDS.Insert(energyThresholdDE);
361 energyThresholdDS.Insert(energyUpholdDE);
362 energyThresholdSq->AddItem(energyThresholdIt);
363 // Insert sequence into data set
364 gdcm::DataElement energyThresholdDEParent( gdcm::Tag(0x54, 0x13) );
365 energyThresholdDEParent.SetVR(gdcm::VR::SQ);
366 energyThresholdDEParent.SetValue(*energyThresholdSq);
367 energyThresholdDEParent.SetVLToUndefined();
368 // Create a dataelement
369 gdcm::DataElement energyNameDE( gdcm::Tag(0x54, 0x18) );
370 energyNameDE.SetVR( gdcm::VR::SH );
371 energyNameDE.SetByteValue(p_EnergyWindowName[i], (uint32_t)strlen(p_EnergyWindowName[i]));
374 energyIt.SetVLToUndefined();
375 gdcm::DataSet &energyDS = energyIt.GetNestedDataSet();
376 energyDS.Insert(energyNameDE);
377 energyDS.Insert(energyThresholdDEParent);
378 energySq->AddItem(energyIt);
380 // Insert sequence into data set
381 energyDEParent.SetValue(*energySq);
382 energyDEParent.SetVLToUndefined();
383 ds2.Insert(energyDEParent);
384 ds2.Insert(rotationDEParent);
388 w.SetFileName( fileNamesOutput[0].c_str() );
392 std::cout << "Use GDCM2" << std::endl;
395 //---------------------------------------------------------------------------
398 //---------------------------------------------------------------------------
399 void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict)
401 typedef itk::MetaDataDictionary DictionaryType;
403 DictionaryType::ConstIterator itr = fromDict.Begin();
404 DictionaryType::ConstIterator end = fromDict.End();
405 typedef itk::MetaDataObject< std::string > MetaDataStringType;
409 itk::MetaDataObjectBase::Pointer entry = itr->second;
411 MetaDataStringType::Pointer entryvalue =
412 dynamic_cast<MetaDataStringType *>( entry.GetPointer() ) ;
415 std::string tagkey = itr->first;
416 std::string tagvalue = entryvalue->GetMetaDataObjectValue();
417 itk::EncapsulateMetaData<std::string>(toDict, tagkey, tagvalue);
422 //---------------------------------------------------------------------------
425 //---------------------------------------------------------------------------
426 template <typename T> std::string NumberToString ( T Number )
428 std::ostringstream ss;
432 //---------------------------------------------------------------------------
436 #endif //#define clitkGateSimulation2DicomGenericFilter_txx