Read DICOM Series and Write 3D Image#
Synopsis#
This example reads all the DICOM series in a given folder argv[1] and writes them in the same folder with following file pattern: seriesIdentifier.nrrd
if output file name argv[2] and series name argv[3] are given, then it behaves like DicomSeriesReadImageWrite2.cxx (writing just the requested series with the specified name).
Based on DicomSeriesReadImageWrite2.cxx
Author: Dženan Zukić <dzenan.zukic@kitware.com>
Code#
Python#
#!/usr/bin/env python
import sys
import os
import itk
import argparse
parser = argparse.ArgumentParser(description="Read DICOM Series And Write 3D Image.")
parser.add_argument(
"dicom_directory",
nargs="?",
help="If DicomDirectory is not specified, current directory is used",
)
parser.add_argument("output_image", nargs="?")
parser.add_argument("series_name", nargs="?")
args = parser.parse_args()
# current directory by default
dirName = "."
if args.dicom_directory:
dirName = args.dicom_directory
PixelType = itk.ctype("signed short")
Dimension = 3
ImageType = itk.Image[PixelType, Dimension]
namesGenerator = itk.GDCMSeriesFileNames.New()
namesGenerator.SetUseSeriesDetails(True)
namesGenerator.AddSeriesRestriction("0008|0021")
namesGenerator.SetGlobalWarningDisplay(False)
namesGenerator.SetDirectory(dirName)
seriesUID = namesGenerator.GetSeriesUIDs()
if len(seriesUID) < 1:
print("No DICOMs in: " + dirName)
sys.exit(1)
print("The directory: " + dirName)
print("Contains the following DICOM Series: ")
for uid in seriesUID:
print(uid)
seriesFound = False
for uid in seriesUID:
seriesIdentifier = uid
if args.series_name:
seriesIdentifier = args.series_name
seriesFound = True
print("Reading: " + seriesIdentifier)
fileNames = namesGenerator.GetFileNames(seriesIdentifier)
reader = itk.ImageSeriesReader[ImageType].New()
dicomIO = itk.GDCMImageIO.New()
reader.SetImageIO(dicomIO)
reader.SetFileNames(fileNames)
reader.ForceOrthogonalDirectionOff()
writer = itk.ImageFileWriter[ImageType].New()
outFileName = os.path.join(dirName, seriesIdentifier + ".nrrd")
if args.output_image:
outFileName = args.output_image
writer.SetFileName(outFileName)
writer.UseCompressionOn()
writer.SetInput(reader.GetOutput())
print("Writing: " + outFileName)
writer.Update()
if seriesFound:
break
C++#
#include "itkImage.h"
#include "itkGDCMImageIO.h"
#include "itkGDCMSeriesFileNames.h"
#include "itkImageSeriesReader.h"
#include "itkImageFileWriter.h"
int
main(int argc, char * argv[])
{
if (argc < 2)
{
std::cerr << "Usage: " << std::endl;
std::cerr << argv[0] << " [DicomDirectory [outputFileName [seriesName]]]";
std::cerr << "\nIf DicomDirectory is not specified, current directory is used\n";
}
std::string dirName = "."; // current directory by default
if (argc > 1)
{
dirName = argv[1];
}
using PixelType = signed short;
constexpr unsigned int Dimension = 3;
using ImageType = itk::Image<PixelType, Dimension>;
using NamesGeneratorType = itk::GDCMSeriesFileNames;
auto nameGenerator = NamesGeneratorType::New();
nameGenerator->SetUseSeriesDetails(true);
nameGenerator->AddSeriesRestriction("0008|0021");
nameGenerator->SetGlobalWarningDisplay(false);
nameGenerator->SetDirectory(dirName);
try
{
using SeriesIdContainer = std::vector<std::string>;
const SeriesIdContainer & seriesUID = nameGenerator->GetSeriesUIDs();
auto seriesItr = seriesUID.begin();
auto seriesEnd = seriesUID.end();
if (seriesItr != seriesEnd)
{
std::cout << "The directory: ";
std::cout << dirName << std::endl;
std::cout << "Contains the following DICOM Series: ";
std::cout << std::endl;
}
else
{
std::cout << "No DICOMs in: " << dirName << std::endl;
return EXIT_SUCCESS;
}
while (seriesItr != seriesEnd)
{
std::cout << seriesItr->c_str() << std::endl;
++seriesItr;
}
seriesItr = seriesUID.begin();
while (seriesItr != seriesUID.end())
{
std::string seriesIdentifier;
if (argc > 3) // If seriesIdentifier given convert only that
{
seriesIdentifier = argv[3];
seriesItr = seriesUID.end();
}
else // otherwise convert everything
{
seriesIdentifier = seriesItr->c_str();
seriesItr++;
}
std::cout << "\nReading: ";
std::cout << seriesIdentifier << std::endl;
using FileNamesContainer = std::vector<std::string>;
FileNamesContainer fileNames = nameGenerator->GetFileNames(seriesIdentifier);
using ReaderType = itk::ImageSeriesReader<ImageType>;
auto reader = ReaderType::New();
using ImageIOType = itk::GDCMImageIO;
auto dicomIO = ImageIOType::New();
reader->SetImageIO(dicomIO);
reader->SetFileNames(fileNames);
reader->ForceOrthogonalDirectionOff(); // properly read CTs with gantry tilt
std::string outFileName;
if (argc > 2)
{
outFileName = argv[2];
}
else
{
outFileName = dirName + std::string("/") + seriesIdentifier + ".nrrd";
}
std::cout << "Writing: " << outFileName << std::endl;
try
{
itk::WriteImage(reader->GetOutput(), outFileName, true); // compression
}
catch (const itk::ExceptionObject & ex)
{
std::cout << ex << std::endl;
continue;
}
}
}
catch (const itk::ExceptionObject & ex)
{
std::cout << ex << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Classes demonstrated#
-
class GDCMSeriesFileNames : public itk::ProcessObject
Generate a sequence of filenames from a DICOM series.
This class generates a sequence of files whose filenames point to a DICOM file. The ordering is based on the following strategy: Read all images in the directory (assuming there is only one study/series)
Extract Image Orientation & Image Position from DICOM images, and then calculate the ordering based on the 3D coordinate of the slice.
If for some reason this information is not found or failed, another strategy is used: the ordering is based on ‘Instance Number’.
If this strategy also failed, then the filenames are ordered by lexicographical order.
If multiple volumes are being grouped as a single series for your DICOM objects, you may want to try calling SetUseSeriesDetails(true) prior to calling SetDirectory().
-
class GDCMImageIO : public itk::ImageIOBase
ImageIO class for reading and writing DICOM V3.0 and ACR/NEMA 1&2 uncompressed images. This class is only an adaptor to the GDCM library.
GDCM can be found at: http://sourceforge.net/projects/gdcm
To learn more about the revision shipped with ITK, call
git log Modules/ThirdParty/GDCM/src/
from an ITK Git checkout.
The compressors supported include “JPEG2000” (default), and “JPEG”. The compression level parameter is not supported.
- Warning
There are several restrictions to this current writer:
Even though during the writing process you pass in a DICOM file as input The output file may not contains ALL DICOM field from the input file. In particular:
The SeQuence DICOM field (SQ).
Fields from Private Dictionary.
Some very long (>0xfff) binary fields are not loaded (typically 0029|0010), you need to explicitly set the maximum length of elements to load to be bigger (see Get/SetMaxSizeLoadEntry).
In DICOM some fields are stored directly using their binary representation. When loaded into the MetaDataDictionary some fields are converted to ASCII (only VR: OB/OW/OF and UN are encoded as mime64).
- ITK Sphinx Examples:
-
template<typename TOutputImage>
class ImageSeriesReader : public itk::ImageSource<TOutputImage> Data source that reads image data from a series of disk files.
This class builds an n-dimension image from multiple n-1 dimension image files. The files stored in a vector of strings are read using the ImageFileReader. File format may vary between the files, but the image data must have the same Size for all dimensions.
- See
GDCMSeriesFileNames
- See
NumericSeriesFileNames