# Reading in vector volume from NRRD with variable scalar component count

**URL:** https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485
**Category:** Algorithms
**Tags:** itk
**Created:** [March 27, 2025, 4:03pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485 "2025-03-27T16:03:11Z")
**Posts on this page:** 13
**Page:** 1

<div class="post-metadata">

### Author: ![cpinter](https://discourse.itk.org/user_avatar/discourse.itk.org/cpinter/32/3683_2.png) [@cpinter](https://discourse.itk.org/u/cpinter)
#### Post date: [March 27, 2025, 4:03pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/1 "2025-03-27T16:03:11Z")

</div>

Hi all,

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

```auto
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:

```auto
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](https://github.com/cpinter/Slicer/blob/volume-sequence-io-5D/Libs/vtkITK/vtkITKImageSequenceReader.cxx#L172).

I also improved the ITK NRRD reader to support more complex cases (both component axis and list axis), in [this branch](https://github.com/cpinter/ITK/blob/nrrd-sequence-write/Modules/IO/NRRD/src/itkNrrdImageIO.cxx#L376). (However, this is not strictly needed for the actual question, but will be part of the whole improvement)

Thanks a lot!

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [March 27, 2025, 6:48pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/2 "2025-03-27T18:48:13Z")

</div>

What you need is [VectorImage](https://docs.itk.org/projects/doxygen/en/stable/classitk_1_1VectorImage.html) class. I remember using it in [Ultrasound module](https://github.com/search?q=repo%3AKitwareMedical%2FITKUltrasound%20VectorImage&type=code). Concrete example: [itkSpectra1DNormalizeImageFilterTest.cxx](https://github.com/KitwareMedical/ITKUltrasound/blob/master/test/itkSpectra1DNormalizeImageFilterTest.cxx).

---

<div class="post-metadata">

### Author: ![cpinter](https://discourse.itk.org/user_avatar/discourse.itk.org/cpinter/32/3683_2.png) [@cpinter](https://discourse.itk.org/u/cpinter)
#### Post date: [March 27, 2025, 8:00pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/3 "2025-03-27T20:00:19Z")

</div>

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

```auto
  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

```auto
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.

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [March 28, 2025, 2:32pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/4 "2025-03-28T14:32:49Z")

</div>

Does that code work on `UnfusedRF-a0-spectra-cropped.mhd` from [here](https://data.kitware.com/#folder/6218eff24acac99f425cf6a3)? 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:

```ini
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

```

---

<div class="post-metadata">

### Author: ![cpinter](https://discourse.itk.org/user_avatar/discourse.itk.org/cpinter/32/3683_2.png) [@cpinter](https://discourse.itk.org/u/cpinter)
#### Post date: [March 28, 2025, 3:01pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/5 "2025-03-28T15:01:57Z")

</div>

> [@dzenanz](#):
>
> Does that code work on `UnfusedRF-a0-spectra-cropped.mhd` from [here](https://data.kitware.com/#folder/6218eff24acac99f425cf6a3)?

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

 ![image](https://discourse.itk.org/uploads/default/original/2X/4/4d7aed7b799b3a40396344f08204ac837d6def48.png)

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).

> [@dzenanz](#):
>
> What you need is [VectorImage](https://docs.itk.org/projects/doxygen/en/stable/classitk_1_1VectorImage.html) class

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

> <https://github.com/cpinter/Slicer/blob/volume-sequence-io-5D/Libs/vtkITK/vtkITKImageSequenceReader.cxx#L166-L282>

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

@dzenanz Can you please take a look at this commit

> <https://github.com/cpinter/ITK/commit/6ee92b0a88dd8612d21b1230384c6de11d5aa529>

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

Thank you very much!

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [March 28, 2025, 3:58pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/6 "2025-03-28T15:58:01Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![cpinter](https://discourse.itk.org/user_avatar/discourse.itk.org/cpinter/32/3683_2.png) [@cpinter](https://discourse.itk.org/u/cpinter)
#### Post date: [March 28, 2025, 4:19pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/7 "2025-03-28T16:19:14Z")

</div>

> [@dzenanz](#):
>
> The commit message mentions writing which does not seem to be touched

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

```auto
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

```auto
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!

---

<div class="post-metadata">

### Author: ![dzenanz](https://discourse.itk.org/user_avatar/discourse.itk.org/dzenanz/32/1093_2.png) [@dzenanz](https://discourse.itk.org/u/dzenanz)
#### Post date: [March 28, 2025, 4:36pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/8 "2025-03-28T16:36:21Z")

</div>

The above post might be a reasonable commit message then.

---

<div class="post-metadata">

### Author: ![cpinter](https://discourse.itk.org/user_avatar/discourse.itk.org/cpinter/32/3683_2.png) [@cpinter](https://discourse.itk.org/u/cpinter)
#### Post date: [April 23, 2025, 9:37am UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/9 "2025-04-23T09:37:33Z")

</div>

I finished the reading and writing of the proposed image type, and created a PR, first in the Slicer ITK fork, hoping that I will be pointed to the best way to proceed, with the ultimate goal to have these changes available in Slicer’s ITK:

> <https://github.com/Slicer/ITK/pull/10>
>
> I'm not sure yet how best to integrate these changes to ITK, but as I was workin…g on top of the Slicer ITK fork, I'm starting the discussion here.
> 
> This PR aims to add support for reading/writing 5D NRRD files (sequence of vector volumes). See details in the commit message.
> 
> The major changes were approved, see this forum topic: https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485

Thank you for the help and guidance!

---

<div class="post-metadata">

### Author: ![blowekamp](https://discourse.itk.org/user_avatar/discourse.itk.org/blowekamp/32/79_2.png) [@blowekamp](https://discourse.itk.org/u/blowekamp)
#### Post date: [April 23, 2025, 12:57pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/10 "2025-04-23T12:57:37Z")

</div>

> [@cpinter](#):
>
> ading 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 options of how 5D images are handled and supported is of interest to me. In particular given that the OME ZARR/TIFF file specification are natively 5D of order XYZCT. Support for a similar descriptive NRRD header would be use ful.

---

<div class="post-metadata">

### Author: ![cpinter](https://discourse.itk.org/user_avatar/discourse.itk.org/cpinter/32/3683_2.png) [@cpinter](https://discourse.itk.org/u/cpinter)
#### Post date: [April 24, 2025, 8:53am UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/11 "2025-04-24T08:53:16Z")

</div>

One thing that will need to be added to support XYZCT I think is permutation of axes in case of multiple “range” axes. In my PR, permutation is only applied in case of only one range axis (e.g. XYZC → CXYZ - this is how the code worked before as well), and when there are two or more, they are left alone, and multi-component voxels are made only if the first axis is range (e.g. “kinds: vector domain domain domain list”, in which case the `rangeAxisIdx` vector contains [0, 4]). In the XYZCT case this vector is [3, 4], and it needs to be permuted first to CXYZT ([0, 4]). At least this is how I understand things after having worked on this for a bit.

---

<div class="post-metadata">

### Author: ![blowekamp](https://discourse.itk.org/user_avatar/discourse.itk.org/blowekamp/32/79_2.png) [@blowekamp](https://discourse.itk.org/u/blowekamp)
#### Post date: [April 24, 2025, 11:55am UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/12 "2025-04-24T11:55:58Z")

</div>

I’ll have to give your PR a closer look, but in general the ImageIO should load that data as it is in the file and not perform axis order permutations implicitly. It is up to the application, or a higher level filter to interpret the meta data, and provide it the application in the order required.

---

<div class="post-metadata">

### Author: ![cpinter](https://discourse.itk.org/user_avatar/discourse.itk.org/cpinter/32/3683_2.png) [@cpinter](https://discourse.itk.org/u/cpinter)
#### Post date: [April 24, 2025, 12:45pm UTC](https://discourse.itk.org/t/reading-in-vector-volume-from-nrrd-with-variable-scalar-component-count/7485/13 "2025-04-24T12:45:35Z")

</div>

Yes, but if the components are expected to be stored in the pixels, then it needs to be the first axis. This is why the current code permutes the axes, so that the resulting image of a XYZC NRRD file is not a 4D single-component image, but a 3D vector image.

If you don’t want this to happen, then no change is needed on top of my PR I think.
