mhe / pynrrd

Simple pure-python module for reading and writing nrrd files.
https://pynrrd.readthedocs.io/
MIT License
116 stars 51 forks source link

I am not able to read a nrrd file saved from Slicer (volume sequence) #71

Closed jcnils closed 6 years ago

jcnils commented 6 years ago

To read the nrrd volume works perfectly if I save it as single volume using Slicer, but if I save it as volume sequence I find the following errors:

#both return the same errors

filedata, fileheader = nrrd.read(VOL0)
fileheader = nrrd.read_header(VOL0)

File "...\nrrd\reader.py", line 209, in read_header header = read_header(fh, custom_field_map) File "...\nrrd\reader.py", line 261, in read_header raise NRRDError(dup_message) nrrd.errors.NRRDError: Duplicate header field: 'space'

filedata = nrrd.read_data(VOL0)

filedata = nrrd.read_data(VOL0)

File "...\nrrd\reader.py", line 313, in read_data raise NRRDError('Header is missing required field: "%s".' % field) nrrd.errors.NRRDError: Header is missing required field: "dimension".

When I realized that the problem is with sequences, I thought sequences are not the focus of the library, but I am opening this just in case. Thanks

addisonElliott commented 6 years ago

Will need to look at this later. Can you provide a copy of the NRRD file that is causing problems?

tashrifbillah commented 6 years ago

I think it's already telling us that there is a duplicate header field. @jcnils Can you modify the header to omit any duplicity and write back with nrrd.write() and then check?

addisonElliott commented 6 years ago

Also, @ihnorton made a PR (#65) that allows overriding of duplicate field headers, but it is not well documented.

nrrd.ALLOW_DUPLICATE_FIELD = True

# Will ignore duplicate fields now
nrrd.read(...)
jcnils commented 6 years ago

Thank you for the reply @addisonElliott , I asked the lab for the anonymized data or one volume sequence with the phantom. But I got the same problem with the dataset from 3D Slicer.

@tashrifbillah I could not find a way to edit the header on the 3D Slicer natively, and I still need a way to load the binaries in an array before writing it back. I will try to do it using the simpleitk.io

CTP-cardio.zip

#CTP is just one image from the sequence, and works properly

CTP = r'CTP-cardio.nrrd'
CTPSEQ = r'CTP-cardio.seq.nrrd'

def main():

    filedata, fileheader = nrrd.read(CTP)

    print (fileheader)

    nrrd.ALLOW_DUPLICATE_FIELD = True

    print(nrrd.ALLOW_DUPLICATE_FIELD)

    filedata, fileheader = nrrd.read(CTPSEQ)

    print(fileheader)

if __name__ == "__main__":
    main()
jcnils commented 6 years ago

@tashrifbillah I managed to read it using simpleitk, then write it as you told. I was able to open the new file with pynrrd.

I noticed that simpleitk and 3d slicer deal with the sequence as being components instead of an x+1 array, in this case a 3D data with 26 components.

When I export it to a numpy array it becomes a 4D array instead, and can be written/read by pynrrd. But I cannot open it as a sequence on the 3D slicer again, instead I get a really weird image.

import SimpleITK as sitk
import numpy as np
import nrrd

CTPSEQ = r'CTP-cardio.seq.nrrd'

reader = sitk.ImageFileReader()
reader.SetFileName(CTPSEQ)
image = reader.Execute()
nda = sitk.GetArrayFromImage(image)
print (nda)
filename = 'testdata.nrrd'

nrrd.write(filename, nda)
addisonElliott commented 6 years ago

Alright, finally got some time to take a look at this. I was able to get your NRRD file loaded without using SimpleITK. There are two ways to do this.

Method 1

The first way is to edit the NRRD file manually to remove the duplicate fields. Not sure how familiar you are with the NRRD file format, but the headers is just text at the beginning of the file. Thus, you can open the NRRD file with your text editor and remove the additional space line.

Here is what the header of the CTP-cardio-seg.nrrd looks like:

NRRD0005
# Complete NRRD file format specification at:
# http://teem.sourceforge.net/nrrd/format.html
type: int
dimension: 4
space: right-anterior-superior
sizes: 26 102 102 61
space directions: none (1.9531249999999991,0,0) (0,1.9531249999999991,0) (0,0,1.9531249999999991)
kinds: list domain domain domain
labels: "frame" "" "" ""
endian: little
encoding: gzip
space origin: (-137.16099548339844,-36.806499481201172,-309.71899414062506)
measurement frame: (1,0,0) (0,1,0) (0,0,1)
axis 0 index type:=numeric
axis 0 index values:=0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
space:=right-anterior-superior

The duplicate line is the space field at the very bottom. If you remove that line and try again, it works fine.

Method 2

If you want to ignore any duplicate field errors like I mentioned, then there is some code to do this. The snippet of code I gave you earlier was wrong and doesn't work. Here is what does work:

import nrrd

CTPSEQ = 'CTP-cardio.seq.nrrd'

nrrd.reader.ALLOW_DUPLICATE_FIELD = True
data, header = nrrd.read(CTPSEQ)
nrrd.reader.ALLOW_DUPLICATE_FIELD = False

Note: In your snippet, you use r to prefix the string. This is used for regex strings. Not necessary for a normal string.

With that in mind, you're welcome to keep using SimpleITK but it takes a few more lines that what is required for pynrrd. In addition, I'm not sure if saving the NRRD file will cause issues with loading it in 3D Slicer again. I have loaded .seg.nrrd files and saved them before using pynrrd and I was able to load it back into 3D Slicer.

addisonElliott commented 6 years ago

@ihnorton @tashrifbillah I don't understand why but Slicer sometimes adds duplicate fields to NRRD files. For new users who just want to load in their NRRD, I think it can be difficult to know to use the ALLOW_DUPLICATE_FIELD switch.

What do you think about the following?

ihnorton commented 6 years ago

@jcnils what version of Slicer are you using? I think I fixed the duplicate field issue in the last few months (but I didn't try with sequences).

ihnorton commented 6 years ago

@addisonElliott documentation and a suggestion in the duplicate field error would be helpful, for people using software versions with this issue.

jcnils commented 6 years ago

@ihnorton We are using the 4.8.1 in the lab, accordingly to our supervisor there are some modules and libraries not working with the 4.11, in my case I am also using the dtcwt library but the 4.11 didn't install it properly using the pip.

I will post the difficulties we are facing about the last version on slicer forum. It will be ideal to use the newer versions.

this example do not work on slicer 4.11:

pip._internal import main as pipmain
pipmain(['install', 'dtc'])

import dtcwt
trans = dtcwt.Transform3d()

I tried to save the sequence with the 4.11 and it works, no duplicated header. Thank you very much.

jcnils commented 6 years ago

@addisonElliott Thank you ! Both methods are working perfectly.

I managed to open it back on slicer by also writing the header.

I am using this documentation https://media.readthedocs.org/pdf/pynrrd/latest/pynrrd.pdf, it is very useful.

def main():

    nrrd.reader.ALLOW_DUPLICATE_FIELD = True

    print(nrrd.reader.ALLOW_DUPLICATE_FIELD)

    filedata, fileheader = nrrd.read(CTPSEQ)

    print(filedata)
    print(fileheader)

    nrrd.write('output.nrrd', filedata, fileheader)#slicer open this one
    nrrd.write('output_noheader.nrrd', filedata) #slicer cannot open this one

Thank you very much, devs on the lab will be very pleased, since we decided to change file formats to .nrrd people are looking for some reliable way to get the arrays, and this library is perfect for it.

addisonElliott commented 6 years ago

Going to leave this open as a reminder to document and add code example for ALLOW_DUPLICATE_FIELD.

Will try and get the PR going this weekend, unless @jcnils is up for the task.

jcnils commented 6 years ago

Of course. I was fixing my example and typing the email to send it to my coworkers on how to use it.