NrrdImageIO and NRRD version 5

Hi!

Briefly: is there a way to force ITK to write NRRD version 5 files?


The longer story goes like this. I’m writing nrrd/nhrd files with

using DWIImageType = itk::VectorImage<short, 3>;
DWIImageType::Pointer dwi = DWIImageType::New();

// ... dwi is processed here ..

using WriterType = itk::ImageFileWriter<DWIImageType>;
WriterType::Pointer writer = WriterType::New();
writer->SetInput(dwi);
writer->SetFileName("dwi.nhdr");
writer->Update();

This works fine and the output will be NRRD version 4 (“NRRD0004”). Now, I would need version 5 because v4 does not support NRRD field “measurement frame”. If I add that field into the image e.g. like this

itk::EncapsulateMetaData<std::string>(imgMetaDictionary, 
         "measurement frame", "(1,0,0) (0,1,0) (0,0,1)");
dwi->SetMetaDataDictionary(imgMetaDictionary);

the output header will look like this

NRRD0004
measurement frame:= (1,0,0) (0,1,0) (0,0,1)

However, in v5 it would look like this

NRRD0005
measurement frame: (1,0,0) (0,1,0) (0,0,1)

The difference is minimal, but for some readers this actually causes problems and the measurement frame field in v4 is not read.

So far my smallest effort solution here has been to simply write v4 and then edit the ascii header (.nhdr) with few lines of code, but it would be nice to be able to actually write v5 in the first place.

We have ITK 5.0.1 (C++) in use and can’t change the version. We also want to avoid installing additonal external libraries just for this simple task.

Looking at the code for NRRD IO, I don’t see any mentions of version number. I don’t know whether ITK’s bundled NRRD library has support for specification version 5, and therefore whether it would be easy to use/expose it in ITK. Perhaps @glk might comment? But without code changes you can’t get what you want.

When ITK writes a nrrd image that includes measurement frame field then it should set the file version to 5, because this field was introduced in version 5. If ITK writer does not do this then it is a bug that should be fixed.

That said, it is not reasonable to make a nrrd file reader ignore the measurement frame if file version is set to 4. Maybe a warning could be justified, but even that would be unnecessary. Maybe you can request maintainers of that nrrd reader to remove this odd limitation.

I agree, this is how it should go. I ran a quick test also with ITK 5.3.0 and with the following simple code

    using ImageTyp = itk::Image<short, 3>;
    ImageTyp::Pointer dwi = ImageTyp::New();
    ImageTyp::SizeType  siz({ 100, 100, 100 });
    ImageTyp::RegionType reg(siz);
    dwi->SetRegions(reg);
    dwi->Allocate();
    itk::MetaDataDictionary imgMetaDictionary;
    itk::EncapsulateMetaData<std::string>(imgMetaDictionary,
        "measurement frame", "(1,0,0) (0,1,0) (0,0,1)");
    dwi->SetMetaDataDictionary(imgMetaDictionary);
    using WriterType = itk::ImageFileWriter<ImageTyp>;
    WriterType::Pointer writer = WriterType::New();
    writer->SetInput(dwi);
    writer->SetFileName("dwi.nhdr");
    writer->Update();        

I get the following nhdr file

NRRD0004
# Complete NRRD file format specification at:
# http://teem.sourceforge.net/nrrd/format.html
type: short
dimension: 3
space: left-posterior-superior
sizes: 100 100 100
space directions: (1,0,0) (0,1,0) (0,0,1)
kinds: domain domain domain
endian: little
encoding: raw
space origin: (0,0,0)
data file: dwi.raw
measurement frame:=(1,0,0) (0,1,0) (0,0,1)

So, it looks like a bug indeed. But thanks for letting me know that there is no specific functionality to determine the nrrd file version. :+1: I will continue to use my clumsy workaround for this issue as we are stuck to ITK v. 5.0.1 anyways.

OK, this explains everything. You set a custom field name, instead of setting the NRRD standard field using NRRD_measurement frame. Therefore, NrrdImageIO::Write wrote measurement frame as a custom field, as you can see from the := in the header (standard fields use : ) and version is kept at 4:

NRRD0004
# Complete NRRD file format specification at:
# http://teem.sourceforge.net/nrrd/format.html
type: short
dimension: 3
space: left-posterior-superior
sizes: 100 100 100
space directions: (1,0,0) (0,1,0) (0,0,1)
kinds: domain domain domain
endian: little
encoding: raw
space origin: (0,0,0)
data file: dwi.raw
measurement frame:=(-1,0,0) (0,-1,0) (0,0,1)

See complete example of how to set the measurement frame here:

2 Likes

Thanks a lot, @lassoan! I wasn’t aware that the writer has such standard fields. This solves my problem completely.

By the way, I noticed that simply modifying my above code like this

itk::EncapsulateMetaData<DoubleVectorType>(imgMetaDictionary,
    "NRRD_measurement frame", tagvalue);

is actually enough (in addition to creating tagvalue). It seems the writer tries to parse fields starting with “NRRD_” as standard fields and, in this case, notices that version 5 is needed. Now the field is written correctly as

measurement frame: (-1,0,0) (0,-1,0) (0,0,1)

as v5 and without “=” after the field name. :slight_smile:

1 Like