[ITK] [ITK-users] SimpleITK Serieswriter and DicomTags
Yaniv, Ziv Rafael (NIH/NLM/LHC) [C]
zivrafael.yaniv at nih.gov
Fri Mar 31 18:17:37 EDT 2017
Hello Matias,
Please take a look at the following github pull request (https://github.com/SimpleITK/SimpleITK/pull/134), this branch should provide the functionality you are looking for. See the Python example script included in the commit for the usage of the DICOM series writing.
hope this helps
Ziv
From: Matias <matimontg at gmail.com>
Date: Thursday, March 30, 2017 at 12:14 PM
To: "insight-users at itk.org" <insight-users at itk.org>
Subject: Re: [ITK-users] [ITK] SimpleITK Serieswriter and DicomTags
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] <[hidden email]<file:////user/SendEmail.jtp%3ftype=node&node=38056&i=0>> 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<http://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<http://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.
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: Re: [ITK-users] [ITK] SimpleITK Serieswriter and DicomTags<http://itk-users.7.n7.nabble.com/SimpleITK-Serieswriter-and-DicomTags-tp38050p38056.html>
Sent from the ITK - Users mailing list archive<http://itk-users.7.n7.nabble.com/> at Nabble.com.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://public.kitware.com/pipermail/community/attachments/20170331/216b228f/attachment-0001.html>
-------------- next part --------------
_____________________________________
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
More information about the Community
mailing list