Reading in vector volume from NRRD with variable scalar component count

Hi all,

I’d like to read an NRRD volume that looks like this:

type: int
dimension: 4
space: right-anterior-superior
sizes: 26 102 102 61
kinds: list domain domain domain 

I have this code that is supposed to do the reading:

constexpr unsigned int ImageDimension = 3;
using ImageType = itk::Image<PixelType, ImageDimension>;

using ReaderType = itk::ImageFileReader<ImageType>;
ReaderType::Pointer reader = ReaderType::New();
reader->SetFileName(self->GetFileName());
reader->Update();
ImageType::ConstPointer image = reader->GetOutput();

PixelType is itk::Vector<int> for example. As you see from the NRRD we expect reading in 26 components in each voxel (contains the frames of a sequence), but the default vector type only reads 3 components.

Can you think of a way to allow reading in a variable size vector for each voxel? May it be 3, or 26, or more? One problem is that I cannot pass variables as template arguments due to the limitations, which really make developing this feature hard.

By the way this snippet comes from a new vtkITKImageSequenceReader class in Slicer, in this branch.

I also improved the ITK NRRD reader to support more complex cases (both component axis and list axis), in this branch. (However, this is not strictly needed for the actual question, but will be part of the whole improvement)

Thanks a lot!

What you need is VectorImage class. I remember using it in Ultrasound module. Concrete example: itkSpectra1DNormalizeImageFilterTest.cxx.

Thanks! I forgot to mention that I tried it. This code

  using ImageType = itk::VectorImage<PixelType, ImageDimension>;

  using ReaderType = itk::ImageFileReader<ImageType>;
  ReaderType::Pointer reader = ReaderType::New();
  reader->SetFileName(self->GetFileName());
  reader->Update();
  ImageType::ConstPointer image = reader->GetOutput();

results in

1>C:\d\S5D\ITK\Modules\Core\Common\include\itkPixelTraits.h(49,57): error C2039: 'Length': is not a member of 'slicer_itk::VariableLengthVector<TPixel>'
1>C:\d\S5D\ITK\Modules\Core\Common\include\itkPixelTraits.h(49,57): error C2039:         with
1>C:\d\S5D\ITK\Modules\Core\Common\include\itkPixelTraits.h(49,57): error C2039:         [
1>C:\d\S5D\ITK\Modules\Core\Common\include\itkPixelTraits.h(49,57): error C2039:             TPixel=vtkITKExecuteDataFromFile_FramesInComponent::PixelType
1>C:\d\S5D\ITK\Modules\Core\Common\include\itkPixelTraits.h(49,57): error C2039:         ]
1>C:\d\S5D\ITK\Modules\Core\Common\include\itkPixelTraits.h(49,57): error C2065: 'Length': undeclared identifier
1>C:\d\S5D\ITK\Modules\Core\Common\include\itkPixelTraits.h(49,57): error C2131: expression did not evaluate to a constant
1>C:\d\S5D\ITK\Modules\Bridge\VTK\include\itkVTKImageExport.hxx(339,53): error C2660: 'slicer_itk::NumericTraits<slicer_itk::VTKImageExport<slicer_itk::VectorImage<vtkITKExecuteDataFromFile_FramesInComponent::PixelType,3>>::NumberOfComponentsCallback::PixelType>::GetLength': function does not take 0 arguments

Maybe it’s further down the line with the VTK export, but I need that part.

In any case I’ll review the example you sent.

Does that code work on UnfusedRF-a0-spectra-cropped.mhd from here? How does the NRRD header look like if you then write it by itk::WriteImage(image, "test.nrrd");? UnfusedRF-a0-spectra-cropped has this header:

ObjectType = Image
NDims = 3
BinaryData = True
BinaryDataByteOrderMSB = False
CompressedData = False
TransformMatrix = 1 0 0 0 1 0 0 0 1
Offset = 0 0 17.5
CenterOfRotation = 0 0 0
AnatomicalOrientation = RAI
ElementSpacing = 0.61640159999999999 0.10000000000000001 0.10000000000000001
DimSize = 96 128 10
ElementNumberOfChannels = 31
ElementType = MET_FLOAT
ElementDataFile = UnfusedRF-a0-spectra-cropped.raw

Yes it seems to work. I loaded it into Slicer, here’s the volume information

UPDATE: This is probably loaded by the part of the code that has not been changed, since it is not a sequence. The core I am working on is only for sequences (of 3D or 4D data, so in total 4D or 5D).

I managed to fix the issue by doing the ITK-VTK conversion directly copying the buffer. Here’s the code:

I’ll finalize the branch and then we’ll try to integrate it all.

@dzenanz Can you please take a look at this commit

so that we have a preliminary idea how acceptable this change would be?

Thank you very much!

It is unclear to me what the commit is trying to accomplish. The commit message mentions writing which does not seem to be touched. But so far I don’t see a problem with the code.

It is though. We determine the list kind dimension before setting every non-component axis to domain, and we keep the list axis as list kind when writing. Also fix the image properties (dirs, origin) accordingly. For example this

dimension: 5
space dimension: 4
sizes: 4 320 320 1 5
space directions: none (1,0,0,0) (0,1,0,0) (0,0,1,0) (0,0,0,1)
kinds: RGBA-color domain domain domain domain
encoding: gzip
space origin: (0,0,0,1) 

which is the output before the commit, becomes this

dimension: 5
space: left-posterior-superior
sizes: 4 320 320 1 5
space directions: none (-1,0,0) (0,-1,0) (0,0,1) none
kinds: vector domain domain domain list
encoding: gzip
space origin: (0,0,0)

For the reading part, the big difference is that it now allows having more than one “range axes”, so for example a multi-component voxel axis, and a list axes as well.

The commit message should be improved, I agree with that. I’m glad you don’t see an issue with the code. We’ll do further testing, finalize the commit, and then start the merge request.

Thanks a lot! Have a nice weekend!

1 Like

The above post might be a reasonable commit message then.

1 Like