Getting Started with the NRRD Format
The NRRD format is a little bit different from other image formats. This format has spatial dimensions and other dimensions. The number of spatial dimensions in the image determines the number of dimensions in the image. Other dimensions are loaded into a Vector and used as the pixel type for the image.
Using NRRD to Load Diffusion Weighted Images
Diffusion weighted image are used to generate the diffusion tensor. This subsequently can be used to generate scalar measures as well as fiber tracts. The use of the NRRD format to describe DTI data is described in greater detail on the NA-MIC Wiki Pages. Here is an example of how to read and write the diffusion weighted images in NRRD format using ITK. Since it was not clear upon looking at only the Doxygen pages for the IO factory method. Here is a basic example of the image format:
- testNrrd.nhdr
NRRD0005 content: SomeIDNumber42 type: short dimension: 4 space: right-anterior-superior sizes: 2 2 2 2 thicknesses: NaN NaN 2.5 NaN space directions: (2,0,0) (0,2.5,0) (0,0,2) none centerings: cell cell cell none kinds: space space space list endian: little encoding: ascii space units: "mm" "mm" "mm" space origin: (0,0,0) data file: testNrrd.ascii measurement frame: (1,0,0) (0,0,1) (0,1,0) modality:=DWMRI DWMRI_b-value:=1000 DWMRI_gradient_0000:= 0 0 0 DWMRI_gradient_0001:= 1 0 1
- testNrrd.ascii
2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1
Here is an outline of a basic program to read and write the NRRD format:
#include <vector> #include <string> #include <algorithm> #include <iostream> #include <itkImage.h> #include <itkExceptionObject.h> #include <itkImageFileWriter.h> #include <itkImageFileReader.h> #include <itkMetaDataObject.h> #include <itkNrrdImageIO.h> int main(int argc, char* argv[]) { typedef itk::Vector<signed short,2> VectorType; typedef itk::Image<VectorType,3> DiffusionImageType; typedef DiffusionImageType::Pointer DiffusionImagePointer; typedef itk::ImageFileReader<DiffusionImageType> FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(argv[1]); reader->Update(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); // This statement is to be added to the ITK CVS Release io->SetNrrdVectorType( nrrdKindList ); io->SetFileType( itk::ImageIOBase::ASCII ); typedef itk::ImageFileWriter<DiffusionImageType> WriterType; WriterType::Pointer nrrdWriter = WriterType::New(); nrrdWriter->UseInputMetaDataDictionaryOn(); nrrdWriter->SetInput( reader->GetOutput() ); nrrdWriter->SetImageIO(io); nrrdWriter->SetFileName(argv[2]); try { nrrdWriter->Update(); } catch (itk::ExceptionObject e) { std::cout << e << std::endl; } return 0; }
An alternate approach to the fixed vector size is to use the VectorImage class. Here is the above example code modified to work with Vector Images
#include <vector> #include <string> #include <algorithm> #include <iostream> #include <itkImage.h> #include <itkImageSeriesReader.h> #include <itkGDCMImageIO.h> #include <itkGDCMSeriesFileNames.h> #include <itkExceptionObject.h> #include <itkImageRegionIteratorWithIndex.h> #include <itkImageFileWriter.h> #include <itkImageFileReader.h> #include <itkMetaDataObject.h> #include <itkNrrdImageIO.h> #include <itkVectorImage.h> #include <itkVariableLengthVector.h> int main(int argc, char* argv[]) { typedef signed short PixelType; typedef itk::VectorImage<PixelType,3> DiffusionImageType; typedef DiffusionImageType::Pointer DiffusionImagePointer; typedef itk::ImageFileReader<DiffusionImageType, itk::DefaultConvertPixelTraits< PixelType > > FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(argv[1]); reader->Update(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetNrrdVectorType( nrrdKindList ); io->SetFileType( itk::ImageIOBase::ASCII ); typedef itk::ImageFileWriter<DiffusionImageType > WriterType; WriterType::Pointer nrrdWriter = WriterType::New(); nrrdWriter->UseInputMetaDataDictionaryOn(); nrrdWriter->SetInput( reader->GetOutput() ); nrrdWriter->SetImageIO(io); nrrdWriter->SetFileName(argv[2]); try { nrrdWriter->Update(); } catch (itk::ExceptionObject e) { std::cout << e << std::endl; } return 0; }
It should be noted that the order of the data is different after writing with ITK. Since the image values are vectors the vector direction is written out first followed by the spatial dimensions. Here is the header and image that results from running the code listed above on the example image:
- testNRRD2.nhdr
NRRD0005 # Complete NRRD file format specification at: # http://teem.sourceforge.net/nrrd/format.html content: SomeIDNumber42 type: short dimension: 4 space: right-anterior-superior sizes: 2 2 2 2 thicknesses: NaN NaN NaN 2.5 space directions: none (-2,0,0) (0,-2.5,0) (0,0,2) centerings: ??? cell cell cell kinds: list space space space encoding: ASCII space origin: (0,0,0) measurement frame: (1,0,0) (0,0,1) (0,1,0) data file: testNRRD2.ascii DWMRI_b-value:=1000 DWMRI_gradient_0000:= 0 0 0 DWMRI_gradient_0001:= 1 0 1 ITK_InputFilterName:=NrrdImageIO modality:=DWMRI
- testNRRD2.ascii
2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1
Formatting Diffusion Tensor Weighted DICOM Images into NRRD Format