[ITK-users] [ITK] SimpleITK Serieswriter and DicomTags

Matias matimontg at gmail.com
Thu Mar 30 12:14:33 EDT 2017


Yes, this is what I would need to migrate to C#:
It basically reads a dicom directory and performs rotation on the volume,
then writes the resulting images back to a directory, copying the tags from
the original images.

#include "itkImage.h"
#include "itkGDCMImageIO.h"
#include "itkGDCMSeriesFileNames.h"
#include "itkImageSeriesReader.h"
#include "itkResampleImageFilter.h"
#include "itkEuler3DTransform.hxx"
#include "gdcmUIDGenerator.h"

#include "itkImageFileWriter.h"
#include "itkImageSeriesWriter.h"
#include "itkNumericSeriesFileNames.h"
#include "itkTranslationTransform.h"
#include "string.h";
#include <itkSliceIterator.h>

#include <iostream>
#include <string>
#include <fstream>



static void CopyDictionary(itk::MetaDataDictionary &fromDict,
itk::MetaDataDictionary &toDict);

int main(int argc, char* argv[])
{
if (argc < 8)
{
std::cerr << "Uso: " << std::endl;
std::cerr << argv[0] << " Directorio_A_Rotar  DirectorioResultante Gamma
Beta Alfa CentroRotacionX CentroRotacionY CentroRotacionZ"
<< std::endl;
return EXIT_FAILURE;
}

typedef signed short    PixelType;
const unsigned int      Dimension = 3;
const unsigned int Dimension_Serie = 2;
typedef itk::Image< PixelType, Dimension >         ImageType;
typedef itk::Image<PixelType, Dimension_Serie> ImageType_Serie;

typedef itk::ImageSeriesReader< ImageType >        ReaderType;
ReaderType::Pointer reader = ReaderType::New();

typedef itk::GDCMImageIO       ImageIOType;
ImageIOType::Pointer gdcmIO = ImageIOType::New();
reader->SetImageIO(gdcmIO);

typedef itk::GDCMSeriesFileNames NamesGeneratorType;
NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New();
nameGenerator->SetUseSeriesDetails(true);
nameGenerator->AddSeriesRestriction("0008|0021");
nameGenerator->SetDirectory(argv[1]);

try
{
std::cout << std::endl << "The directory: " << std::endl;
std::cout << std::endl << argv[1] << std::endl << std::endl;
std::cout << "Contains the following DICOM Series: ";
std::cout << std::endl << std::endl;

typedef std::vector< std::string >    SeriesIdContainer;
const SeriesIdContainer & seriesUID = nameGenerator->GetSeriesUIDs();
SeriesIdContainer::const_iterator seriesItr = seriesUID.begin();
SeriesIdContainer::const_iterator seriesEnd = seriesUID.end();
while (seriesItr != seriesEnd)
{
std::cout << seriesItr->c_str() << std::endl;
++seriesItr;
}

std::string seriesIdentifier;

seriesIdentifier = seriesUID.begin()->c_str();


std::cout << std::endl << std::endl;
std::cout << "Now reading series: " << std::endl << std::endl;
std::cout << seriesIdentifier << std::endl;
std::cout << std::endl << std::endl;

typedef std::vector< std::string >   FileNamesContainer;
FileNamesContainer fileNames;
fileNames = nameGenerator->GetFileNames(seriesIdentifier);

reader->SetFileNames(fileNames);

try
{
reader->Update();
}
catch (itk::ExceptionObject &ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}

const ImageType * inputImage = reader->GetOutput();
/*int numerodedicoms = inputImage->GetLargestPossibleRegion().GetSize()[2];
int dicomcentral = numerodedicoms / 2;
std::cout << "Dimenion " <<  dicomcentral << std::endl;*/
//itk::EncapsulateMetaData<std::string>(dictionary,
"0020|0032","-208\\-236\\66");

typedef itk::ResampleImageFilter<ImageType, ImageType> FilterType;
FilterType::Pointer FiltroResample = FilterType::New();
FiltroResample->SetInput(reader->GetOutput());

typedef itk::LinearInterpolateImageFunction<ImageType, double >
InterpolatorType;
InterpolatorType::Pointer interpolator = InterpolatorType::New();
FiltroResample->SetInterpolator(interpolator);
FiltroResample->SetOutputDirection(inputImage->GetDirection());
FiltroResample->SetOutputOrigin(inputImage->GetOrigin());

ImageType::SizeType inputSize =
inputImage->GetLargestPossibleRegion().GetSize();
FiltroResample->SetSize(inputSize);

const ImageType::SpacingType& inputSpacing = inputImage->GetSpacing();
FiltroResample->SetOutputSpacing(inputSpacing);

FiltroResample->SetDefaultPixelValue(-1000); //Cambiar por un parametro

typedef itk::Euler3DTransform< double > TransformType; //Transform
TransformType::Pointer transform = TransformType::New();
double alfa, beta, gamma, centro_rotacion_X, centro_rotacion_Y,
centro_rotacion_Z;
gamma = atof(argv[3]);
beta= atof(argv[4]);
alfa = atof(argv[5]);
centro_rotacion_X = atof(argv[6]);
centro_rotacion_Y = atof(argv[7]);
centro_rotacion_Z = atof(argv[8]);
transform->SetRotation(gamma, beta, alfa); //Radianes en el siguiente orden
en ITK: Gamma, Beta, Alfa | Ibarra
//double centro[3] = { -14.8371, -54.9443, 175.75 }; //XmmPromedio,
YmmPromedio, Z Central (mm): Leer directorio y tomar la del medio
double centro[3] = { centro_rotacion_X, centro_rotacion_Y,
centro_rotacion_Z }; //XmmPromedio, YmmPromedio, Z Central (mm): Leer
directorio y tomar la del medio
transform->SetCenter(centro);
std::cout << "Centro: " << std::endl << std::endl;
std::cout << transform->GetCenter() << std::endl;

FiltroResample->SetTransform(transform);
//FiltroResample->SetMetaDataDictionary(dictionary);
try
{
FiltroResample->Update();
}
catch (itk::ExceptionObject &ex)
{
return EXIT_FAILURE;
}



ReaderType::DictionaryRawPointer inputDict =
(*(reader->GetMetaDataDictionaryArray()))[0];
ReaderType::DictionaryArrayType outputArray;
//std::cout << "array: " << std::endl << outputArray[0] << std::endl;
// To keep the new series in the same study as the original we need
// to keep the same study UID. But we need new series and frame of
// reference UID's.
gdcm::UIDGenerator suid;
//std::string seriesUID = suid.Generate();
gdcm::UIDGenerator fuid;
std::string frameOfReferenceUID = fuid.Generate();

std::string studyUID;
std::string sopClassUID;
itk::ExposeMetaData<std::string>(*inputDict, "0020|000d", studyUID);
itk::ExposeMetaData<std::string>(*inputDict, "0008|0016", sopClassUID);
gdcmIO->KeepOriginalUIDOn();

using namespace std;
double myArray_Z[70]; //Cambiar esto por un argumento que especifica la
cantidad de imagenes.
double myArray_X[70]; //Cambiar esto por un argumento que especifica la
cantidad de imagenes.
double myArray_Y[70]; //Cambiar esto por un argumento que especifica la
cantidad de imagenes.

ifstream file("file.txt");
if (file.is_open())
{
for (int i = 0; i < 70; ++i) //Recordar cambiar por el argumento que
especifica cantidad de imagenes
{
file >> myArray_Z[i];
}
}
std::cout << "valor primer Z array: " << std::endl << std::endl;
std::cout << myArray_Z[0] << std::endl;

for (unsigned int f = 0; f < inputSize[2]; f++)
{
// Create a new dictionary for this slice
ReaderType::DictionaryRawPointer dict = new ReaderType::DictionaryType;

// Copy the dictionary from the first slice
CopyDictionary(*inputDict, *dict);

// Set the UID's for the study, series, SOP  and frame of reference
itk::EncapsulateMetaData<std::string>(*dict, "0020|000d", studyUID);
//itk::EncapsulateMetaData<std::string>(*dict, "0020|000e", seriesUID);
itk::EncapsulateMetaData<std::string>(*dict, "0020|0052",
frameOfReferenceUID);

gdcm::UIDGenerator sopuid;
std::string sopInstanceUID = sopuid.Generate();

itk::EncapsulateMetaData<std::string>(*dict, "0008|0018", sopInstanceUID);
itk::EncapsulateMetaData<std::string>(*dict, "0002|0003", sopInstanceUID);

// Change fields that are slice specific
std::ostringstream value;
value.str("");
value << f + 1;

// Image Number
itk::EncapsulateMetaData<std::string>(*dict, "0020|0013", value.str());

// Series Description - Append new description to current series
// description
std::string oldSeriesDesc;
itk::ExposeMetaData<std::string>(*inputDict, "0008|103e", oldSeriesDesc);

value.str("");
value << oldSeriesDesc
<< ": Resampled with pixel spacing "
<< inputSpacing[0] << ", "
<< inputSpacing[1] << ", "
<< inputSpacing[2];
// This is an long string and there is a 64 character limit in the
// standard
unsigned lengthDesc = value.str().length();

std::string seriesDesc(value.str(), 0,
lengthDesc > 64 ? 64
: lengthDesc);
itk::EncapsulateMetaData<std::string>(*dict, "0008|103e", seriesDesc);

// Series Number
value.str("");
value << 1001;
itk::EncapsulateMetaData<std::string>(*dict, "0020|0011", value.str());

// Derivation Description - How this image was derived
value.str("");
for (int i = 0; i < argc; i++)
{
value << argv[i] << " ";
}

lengthDesc = value.str().length();
std::string derivationDesc(value.str(), 0,
lengthDesc > 1024 ? 1024
: lengthDesc);
itk::EncapsulateMetaData<std::string>(*dict, "0008|2111", derivationDesc);

// Image Position Patient: This is calculated by computing the
// physical coordinate of the first pixel in each slice.
ImageType::PointType position;
ImageType::IndexType index;
index[0] = 0;
index[1] = 0;
index[2] = myArray_Z[f];
FiltroResample->GetOutput()->TransformIndexToPhysicalPoint(index, position);

//El origen que calculamos en el proyecto no se toca. (Origen = origen -
average)

//Cambiamos el ImageOrientationPatient SOLAMENTE si el valor original en la
imagen es: 1\0\0\0\1\0. En el caso que se cambia el signo, se debe cambiar
el signo del origen
//value.str("");
//value << -1 << "\\" << 0 << "\\" << 0 << "\\" << 0 << "\\" << -1 << "\\"
<< 0; //PASAR ESTO POR ARGUMENTO!!!
//itk::EncapsulateMetaData<std::string>(*dict, "0020|0037", value.str());

value.str("");
value << -235.1629 << "\\" << -195.0557 << "\\" << myArray_Z[f]; //PASAR
ESTO POR ARGUMENTO!!! El origen - Centro
itk::EncapsulateMetaData<std::string>(*dict, "0020|0032", value.str());


// Slice Location: For now, we store the z component of the Image
// Position Patient.
value.str("");
value << position[2];
itk::EncapsulateMetaData<std::string>(*dict, "0020|1041", value.str());

// Slice Thickness: For now, we store the z spacing
value.str("");
value << inputSpacing[2];
itk::EncapsulateMetaData<std::string>(*dict, "0018|0050",
value.str());
// Spacing Between Slices
itk::EncapsulateMetaData<std::string>(*dict, "0018|0088",
value.str());

// Save the dictionary
outputArray.push_back(dict);
}


typedef itk::ImageFileWriter< ImageType > WriterType;
WriterType::Pointer writer = WriterType::New();

typedef itk::ImageSeriesWriter< ImageType, ImageType_Serie >
SeriesWriterType;
SeriesWriterType::Pointer seriesWriter = SeriesWriterType::New();
seriesWriter->SetInput(FiltroResample->GetOutput());

writer->SetFileName(argv[2]);
writer->SetInput(FiltroResample->GetOutput());

itksys::SystemTools::MakeDirectory("Test"); //PASAR ESTO POR ARGUMENTO!!
typedef itk::NumericSeriesFileNames OutputNamesGeneratorType;
OutputNamesGeneratorType::Pointer outputNames =
OutputNamesGeneratorType::New();
std::string seriesFormat("Test"); //PASAR ESTO POR ARGUMENTO!!
seriesFormat = seriesFormat + "/" + "IM%d.dcm";
outputNames->SetSeriesFormat(seriesFormat.c_str());
outputNames->SetStartIndex(1);
outputNames->SetEndIndex(inputSize[2]);

seriesWriter->SetImageIO(gdcmIO);
seriesWriter->SetFileNames(outputNames->GetFileNames());
seriesWriter->SetMetaDataDictionaryArray(&outputArray);

std::cout << "Escribiendo la imagen como..." << std::endl << std::endl;
std::cout << argv[2] << std::endl << std::endl;

try
{
writer->Update();
seriesWriter->Update();
}
catch (itk::ExceptionObject &ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}


}
catch (itk::ExceptionObject &ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}

return EXIT_SUCCESS;

}

void CopyDictionary(itk::MetaDataDictionary &fromDict,
itk::MetaDataDictionary &toDict)
{
typedef itk::MetaDataDictionary DictionaryType;

DictionaryType::ConstIterator itr = fromDict.Begin();
DictionaryType::ConstIterator end = fromDict.End();
typedef itk::MetaDataObject< std::string > MetaDataStringType;

while (itr != end)
{
itk::MetaDataObjectBase::Pointer  entry = itr->second;

MetaDataStringType::Pointer entryvalue =
dynamic_cast<MetaDataStringType *>(entry.GetPointer());
if (entryvalue)
{
std::string tagkey = itr->first;
std::string tagvalue = entryvalue->GetMetaDataObjectValue();
itk::EncapsulateMetaData<std::string>(toDict, tagkey, tagvalue);
}
++itr;
}
}


El jue., 30 de mar. de 2017 a la(s) 11:25, Lowekamp, Bradley (NIH/NLM/LHC)
[C] [via ITK - Users] <ml-node+s7n38052h81 at n7.nabble.com> escribió:

> Hello,
>
> Writing correct DICOM continues to be a struggle with SimpleITK and ITK.
> It is generally recommended to directly use GDCM or DCMTK to write a proper
> DICOM series.
>
> SimpleITK tries to keep things, well, simple and straight forward. But ITK
> ties to do some smart things, which get in the way for certain uses with
> SimpleITK. We are trying to document and develop a nominal set of DICOM
> output operations that work in SimpleITK.
>
> Do you have working C++ code that works for your intended operation? Can
> you share a small section of code which does what you expect it C++?
>
> Thank,
> Brad
>
>
>
>
> > On Mar 30, 2017, at 8:49 AM, Matias <[hidden email]
> <http:///user/SendEmail.jtp?type=node&node=38052&i=0>> wrote:
> >
> > Hi,
> >
> > I've been dealing with ITK for years in C++ and now I would need to use
> > SimpleITK and C# as far as I can in a new proyect.
> >
> > Is the SimpleITK SeriesWriter working for Dicom Files? Last time I tried
> to
> > use it I had problems with the DicomTags, these would not copy or there
> was
> > no method to copy the tags to the resulting slices.
> >
> > Currently, I read a volume of slices, apply rotation and then I need to
> > write the resulting image as another set of slices AND keeping tag
> > information such as patient name, etc.
> >
> > Thank you,
> >
> > Matias.
> >
> >
> >
> > --
> > View this message in context:
> http://itk-users.7.n7.nabble.com/SimpleITK-Serieswriter-and-DicomTags-tp38050.html
> > Sent from the ITK - Users mailing list archive at Nabble.com.
> > _____________________________________
> > Powered by www.kitware.com
> >
> > Visit other Kitware open-source projects at
> > http://www.kitware.com/opensource/opensource.html
> >
> > Kitware offers ITK Training Courses, for more information visit:
> > http://www.kitware.com/products/protraining.php
> >
> > Please keep messages on-topic and check the ITK FAQ at:
> > http://www.itk.org/Wiki/ITK_FAQ
> >
> > Follow this link to subscribe/unsubscribe:
> > http://public.kitware.com/mailman/listinfo/insight-users
> > _______________________________________________
> > Community mailing list
> > [hidden email] <http:///user/SendEmail.jtp?type=node&node=38052&i=1>
> > http://public.kitware.com/mailman/listinfo/community
>
> _____________________________________
> Powered by www.kitware.com
>
> Visit other Kitware open-source projects at
> http://www.kitware.com/opensource/opensource.html
>
> Kitware offers ITK Training Courses, for more information visit:
> http://www.kitware.com/products/protraining.php
>
> Please keep messages on-topic and check the ITK FAQ at:
> http://www.itk.org/Wiki/ITK_FAQ
>
> Follow this link to subscribe/unsubscribe:
> http://public.kitware.com/mailman/listinfo/insight-users
>
>
> ------------------------------
> If you reply to this email, your message will be added to the discussion
> below:
>
> http://itk-users.7.n7.nabble.com/SimpleITK-Serieswriter-and-DicomTags-tp38050p38052.html
> To unsubscribe from SimpleITK Serieswriter and DicomTags, click here
> <http://itk-users.7.n7.nabble.com/template/NamlServlet.jtp?macro=unsubscribe_by_code&node=38050&code=bWF0aW1vbnRnQGdtYWlsLmNvbXwzODA1MHwtMTk0NzIxNTA4Mw==>
> .
> NAML
> <http://itk-users.7.n7.nabble.com/template/NamlServlet.jtp?macro=macro_viewer&id=instant_html%21nabble%3Aemail.naml&base=nabble.naml.namespaces.BasicNamespace-nabble.view.web.template.NabbleNamespace-nabble.view.web.template.NodeNamespace&breadcrumbs=notify_subscribers%21nabble%3Aemail.naml-instant_emails%21nabble%3Aemail.naml-send_instant_email%21nabble%3Aemail.naml>
>
-- 
Matias




--
View this message in context: http://itk-users.7.n7.nabble.com/SimpleITK-Serieswriter-and-DicomTags-tp38050p38056.html
Sent from the ITK - Users mailing list archive at Nabble.com.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://public.kitware.com/pipermail/insight-users/attachments/20170330/c42e704d/attachment.html>


More information about the Insight-users mailing list