Hi Jon,<br><br>Since this is not shown in the ITK software guide,<br>I thought this code that I wrote based on <a href="http://www.paraview.org/Bug/view.php?id=6258&nbn=1" target="_blank">http://www.paraview.org/Bug/view.php?id=6258&nbn=1</a> , might help someone:<br>
It shows how to change a MetaDataDictionaryArray when dealing with <span class="il">changing</span> <span class="il">header</span> info in Dicom series.<br><br>Thanks,<br>John<br><br>#if defined(_MSC_VER)<br>#pragma warning ( disable : 4786 )<br>
#endif<br><br>#ifdef __BORLANDC__<br>
#define ITK_LEAN_AND_MEAN<br>#endif<br><br>//<br>// This example illustrates how to change a MetaDataDictionaryArray.<br>// This example illustrates how to read two DICOM series, adds key <br>// tags that are in the second DICOM series to the first DICOM series<br>
// and write it back with some changed <span class="il">header</span> information as another <br>// DICOM series. This can be useful when sometimes conversion between<br>// different file formats as between NIFTI and DICOM and some dicom tags <br>
// are lost.<br>// The keys are defined in the file<br>//<br>// \code{Insight/Utilities/gdcm/<div id=":366">Dicts/dicomV3.dic}<br>// <br>// Please note that modifying the content of a DICOM <span class="il">header</span> is a very risky<br>
// operation. The <span class="il">Header</span> contains fundamental information about the patient<br>
// and therefore its consistency must be protected from any data corruption.<br>// Before attempting to modify the DICOM headers of your files, you must make<br>// sure that you have a very good reason for doing so, and that you can ensure<br>
// that this information change will not result in a lower quality of health<br>// care to be delivered to the patient.<br>//<br>// \index{DICOM!<span class="il">Changing</span> Headers}<br>//<br><br>// Software Guide : BeginLatex<br>
// <br>// We must start by including the relevant <span class="il">header</span> files. Here we include the<br>
// image reader, image writer, the image, the Meta data dictionary and its<br>// entries the Meta data objects and the GDCMImageIO. The Meta data dictionary<br>// is the data container that stores all the entries from the DICOM <span class="il">header</span> once<br>
// the DICOM image file is read into an ITK image.<br>//<br>// Software Guide : EndLatex<br><br>// Software Guide : BeginCodeSnippet<br>#include "itkImageFileReader.h"<br>#include "itkImageFileWriter.h"<br>
#include "itkImage.h"<br>#include "itkMetaDataDictionary.h"<br>#include "itkMetaDataObject.h"<br>#include "itkGDCMImageIO.h"<br>// Software Guide : EndCodeSnippet<br><br>#include <list><br>
#include <fstream><br><br><br>#include "itkGDCMImageIO.h"<br>#include "itkGDCMSeriesFileNames.h"<br>#include "itkImageSeriesReader.h"<br>#include "itkImageSeriesWriter.h"<br>
<br>
<br>int main(int argc, char* argv[])<br>{<br><br> if( argc < 4 )<br> {<br>
std::cerr << "Usage: " << argv[0] << "
OriginalDicomImageDIR ProcessedDicomImageDIR OutputDicomImageDIR\n";<br>
//DIR means Directory<br><br><br> return EXIT_FAILURE;<br> }<br> <br> typedef unsigned short InputPixelType;<br><br> <br> const unsigned int Dimension = 3;<br><br> typedef itk::Image< InputPixelType, Dimension > InputImageType;<br>
<br> typedef itk::GDCMSeriesFileNames NamesGeneratorTypeOrig;<br> NamesGeneratorTypeOrig::Pointer namesGeneratororig = NamesGeneratorTypeOrig::New();<br><br> namesGeneratororig->SetInputDirectory( argv[1] );<br>
<br> typedef itk::Image< InputPixelType, Dimension > ImageTypeOrig;<br> typedef itk::ImageSeriesReader< ImageTypeOrig > ReaderTypeOrig;<br><br> const ReaderTypeOrig::FileNamesContainer & filenamesorig = <br>
namesGeneratororig->GetInputFileNames();<br><br> unsigned int numberOfFilenames = filenamesorig.size();<br> std::cout << numberOfFilenames << std::endl; <br> for(unsigned int fni = 0; fni<numberOfFilenames; fni++)<br>
{<br> std::cout << "filename # " << fni << " = ";<br> std::cout << filenamesorig[fni] << std::endl;<br> }<br><br> typedef itk::GDCMImageIO ImageIOTypeOrig;<br>
ImageIOTypeOrig::Pointer gdcmImageIOorig = ImageIOTypeOrig::New();<br> //reader->SetImageIO( gdcmImageIOorig );<br><br> ReaderTypeOrig::Pointer readerorig = ReaderTypeOrig::New();<br><br> readerorig->SetImageIO( gdcmImageIOorig );<br>
readerorig->SetFileNames( filenamesorig );<br><br> //end of added code<br><br>// Software Guide : BeginLatex<br>// <br>// The reading of the image is triggered by invoking \code{Update()} in the<br>// reader.<br>//<br>
// Software Guide : EndLatex<br><br><br> try<br> {<br>// Software Guide : BeginCodeSnippet<br> readerorig->Update();<br>// Software Guide : EndCodeSnippet<br> }<br> catch (itk::ExceptionObject & e)<br> {<br>
std::cerr << "exception in file reader " << std::endl;<br> std::cerr << e.GetDescription() << std::endl;<br> std::cerr << e.GetLocation() << std::endl;<br> return EXIT_FAILURE;<br>
}<br><br> typedef itk::Image< InputPixelType, Dimension > InputImageType2;<br><br> typedef itk::GDCMSeriesFileNames NamesGeneratorTypeProc;<br> NamesGeneratorTypeProc::Pointer namesGeneratorproc = NamesGeneratorTypeProc::New();<br>
<br> namesGeneratorproc->SetInputDirectory( argv[2] );<br><br> typedef itk::Image< InputPixelType, Dimension > ImageTypeProc;<br> typedef itk::ImageSeriesReader< ImageTypeProc > ReaderTypeProc;<br>
<br> const ReaderTypeProc::FileNamesContainer & filenamesproc = <br> namesGeneratorproc->GetInputFileNames();<br> <br> numberOfFilenames = filenamesproc.size();<br> std::cout << numberOfFilenames << std::endl; <br>
for(unsigned int fni = 0; fni<numberOfFilenames; fni++)<br> {<br> std::cout << "filename # " << fni << " = ";<br> std::cout << filenamesproc[fni] << std::endl;<br>
}<br><br> typedef itk::GDCMImageIO ImageIOTypeProc;<br> ImageIOTypeProc::Pointer gdcmImageIOproc = ImageIOTypeProc::New();<br><br> ReaderTypeProc::Pointer readerproc = ReaderTypeProc::New();<br><br> readerproc->SetImageIO( gdcmImageIOproc );<br>
readerproc->SetFileNames( filenamesproc );<br><br> try<br> {<br> readerproc->Update();<br> }<br> catch (itk::ExceptionObject & e)<br> {<br> std::cerr << "exception in file reader " << std::endl;<br>
std::cerr << e.GetDescription() << std::endl;<br> std::cerr << e.GetLocation() << std::endl;<br> return EXIT_FAILURE;<br> }<br><br> typedef itk::MetaDataObject< std::string > MetaDataStringType;<br>
<br> typedef itk::MetaDataDictionary DictionaryType1b;<br> typedef itk::MetaDataDictionary DictionaryType2b;<br> <br> std::vector<DictionaryType2b*> pdictArray2b;<br><br> DictionaryType1b::ConstIterator itr1b;<br>
DictionaryType1b::ConstIterator end1b;<br><br> DictionaryType2b::ConstIterator itr2b;<br> DictionaryType2b::ConstIterator end2b;<br> <br> for ( unsigned int idx = 0;<br> idx < readerproc->GetMetaDataDictionaryArray()->size();<br>
idx++ )<br> {<br><br> itk::MetaDataDictionary* pdictionary1b = new itk::MetaDataDictionary;<br> itk::MetaDataDictionary* pSrc1b = (*(readerorig->GetMetaDataDictionaryArray()))[idx];<br>
<br> itk::MetaDataDictionary* pdictionary2b = new itk::MetaDataDictionary;<br> itk::MetaDataDictionary* pSrc2b = (*(readerproc->GetMetaDataDictionaryArray()))[idx];<br><br> if ( pSrc2b )<br>
{<br> *pdictionary1b = (*pSrc1b);<br> *pdictionary2b = (*pSrc2b);<br><br><br> itr1b = pdictionary1b->Begin();<br> end1b = pdictionary1b->End();<br><br> itr2b = pdictionary2b->Begin();<br>
end2b = pdictionary2b->End();<br> <br> while( itr1b != end1b )<br> {<br> itk::MetaDataObjectBase::Pointer entry1b = itr1b->second;<br><br> MetaDataStringType::Pointer entryvalue1b = <br>
dynamic_cast<MetaDataStringType *>( entry1b.GetPointer() );<br><br> std::string tagkey1b = itr1b->first;<br><br> <br> DictionaryType2b::ConstIterator tagItr2b = pdictionary2b->Find( tagkey1b );<br>
<br> if( tagItr2b == end2b )<br> {<br> std::string tagkey = itr1b->first;<br> std::string tagvalue = entryvalue1b->GetMetaDataObjectValue();<br> //std::cout << tagkey << " = " << tagvalue << std::endl;<br>
itk::EncapsulateMetaData<std::string>( *pdictionary2b, tagkey , tagvalue ); <br> }<br> <br> ++itr1b;<br> }<br> <br> <br> std::string entryIdMR = "0008|0060";<br>
std::string valueMR = "MR";<br> itk::EncapsulateMetaData<std::string>( *pdictionary2b, entryIdMR, valueMR );<br><br> <br> pdictArray2b.push_back( pdictionary2b );<br>
<br> }<br> }<br><br> typedef unsigned short OutputPixelType;<br> const unsigned int OutputDimension = 2;<br><br> typedef itk::Image< OutputPixelType, OutputDimension > Image2DType;<br>
<br> typedef itk::ImageSeriesWriter< <br> ImageTypeProc, Image2DType > SeriesWriterType;<br><br> SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New();<br><br> seriesWriter->SetMetaDataDictionaryArray( &pdictArray2b );<br>
<br> seriesWriter->SetInput( readerproc->GetOutput() );<br> seriesWriter->SetImageIO( gdcmImageIOproc );<br><br> namesGeneratorproc->SetOutputDirectory( argv[3] );<br><br> seriesWriter->SetFileNames( namesGeneratorproc->GetOutputFileNames() );<br>
<br> try<br> {<br> seriesWriter->Update();<br> }<br> catch (itk::ExceptionObject & e)<br> {<br> std::cerr << "exception in file writer " << std::endl;<br> std::cerr << e.GetDescription() << std::endl;<br>
std::cerr << e.GetLocation() << std::endl;<br> return EXIT_FAILURE;<br> }<br><br> // Software Guide : BeginLatex<br> // <br> // Remember again, that modifying the <span class="il">header</span> entries of a DICOM file involves<br>
// very serious risks for patients and therefore must be done with extreme<br> // caution.<br> //<br> // Software Guide : EndLatex<br><br><br> return EXIT_SUCCESS;<br><br>}<br><br><br>Thanks,<br><font color="#888888">John</font></div>
<br><br><div class="gmail_quote">2011/2/17 Jon Haitz Legarreta Gorroño <span dir="ltr"><<a href="mailto:jhlegarreta@vicomtech.org">jhlegarreta@vicomtech.org</a>></span><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Hi there,<br>
<br>
I'm using ITK and GDCM to convert to DICOM format a stereolitography model I built.<br>
I used a software to convert between .stl and DICOM. The thing is that the header I got is apparently lacking some fields that prevent the DICOM files from being correctly loaded by some DICOM viewers/workstations.<br>
<br>
I was thinking that editing the DICOM header would allow me to solve the problem. I know editing the DICOM header is not desirable, but in this case my source is not a DICOM node (nobody to blame on exepct the software used to convert between STL and DICOM!) and it's just an artificial model I built.<br>
<br>
What I basically do is this:<br>
<br>
1. Let the user edit the source DICOM header<br>
2. Try to save the edited header together with the original slices into a new dataset.<br>
<br>
I'm puzzled when I check that the members of the itk::GDCMImageIO object I handle for the itk::ImageSeriesWriter do not reflect the changes in the dictionary after I've called the methods to change the original tags by the new values, that is, in short:<br>
<br>
<br>
std::vector<itk::MetaDataDictionary *> dictionaryArray = new std::vector<itk::MetaDataDictionary*> ;<br>
itk::GDCMImageIO::Pointer gdcmIO = itk::GDCMImageIO::New();<br>
itk::ImageSeriesWriter< TInputImage, TOutputImage>::Pointer seriesWriter = itk::ImageSeriesWriter< TInputImage, TOutputImage>::New(); // TInputImage and TOutputImage are properly set somewhere above<br>
itk::NumericSeriesFileNames::Pointer outputNames = itk::NumericSeriesFileNames::New();<br>
itk::MetaDataDictionary *toDict= new itk::MetaDataDictionary ();<br>
<br>
// Do for each tag of the DICOM Dictionary<br>
itk::EncapsulateMetaData<std::string>( *toDict, tagKey, tagValue );<br>
//<br>
<br>
dictionaryArray->push_back( toDict );<br>
<br>
seriesWriter->SetInput( PointerToITKImage );<br>
<br>
// Generate output names<br>
...<br>
<br>
seriesWriter->SetImageIO( gdcmIO );<br>
seriesWriter->SetFileNames( outputNames->GetFileNames() );<br>
seriesWriter->SetMetaDataDictionaryArray( dictionaryArray );<br>
seriesWriter->Update();<br>
<br>
Even if at disk the written header tags are the user-edited ones, at this point of the program the gdcmIO object still contains the information related to the original dataset. Is that the expected behavior?<br>
<br>
I had a look at some older posts concerning the DICOM header issue but didn't find an answer to this.<br>
I also had a look at an (old) ITK example (DicomImageReadChangeHeaderWrite.cxx).<br>
<br>
<br>
For sure, I am missing some concept.<br>
<br>
Thank you,<br>
JON HAITZ<br>
<br>
<br>
<br>
<br>
_____________________________________<br>
Powered by <a href="http://www.kitware.com" target="_blank">www.kitware.com</a><br>
<br>
Visit other Kitware open-source projects at<br>
<a href="http://www.kitware.com/opensource/opensource.html" target="_blank">http://www.kitware.com/opensource/opensource.html</a><br>
<br>
Kitware offers ITK Training Courses, for more information visit:<br>
<a href="http://www.kitware.com/products/protraining.html" target="_blank">http://www.kitware.com/products/protraining.html</a><br>
<br>
Please keep messages on-topic and check the ITK FAQ at:<br>
<a href="http://www.itk.org/Wiki/ITK_FAQ" target="_blank">http://www.itk.org/Wiki/ITK_FAQ</a><br>
<br>
Follow this link to subscribe/unsubscribe:<br>
<a href="http://www.itk.org/mailman/listinfo/insight-users" target="_blank">http://www.itk.org/mailman/listinfo/insight-users</a><br>
</blockquote></div><br><br clear="all"><br>-- <br><div>John Drozd<br></div>
<div>Post-Doctoral Fellow, Robarts Research Institute</div>
<div>The University of Western Ontario</div>
<div>London, ON, Canada<br></div>
<br>