Reproducing Nifty metadata

Greetings!

I have a question regarding Nifti format circulation. I’m developing a plugin that interacts with a remote repository and can upload/download images and whole datasets (viewing and fixing labels on the way if necessary). We prefer the Nifti format for images, since it’s widely used and accepted by many software viewers.
The data is 3D scans of plants without strict reliance on orientation; only the resolution is important. Roughly speaking, I needed only numpy arrays, and I figured out how to bring them in and out of Slicer nodes, both source images and labels. So I used Nifti just as a container, and the required metadata is attached to datasets as a text/json file.
But for the prospect, I think about how to preserve more metadata from the sources (scanner, other editors, automatic segmentation frameworks…). This metadata is stripped from the Nifty header and distributed to other locations during transformation to the Slicer node.
Is there a way to gracefully collect it back when I want to re-export the node to Nifti?
At least I figured out how to recollect the resolution from the spacing property, but it doesn’t exist for label nodes. Where to obtain it? There is a workaround to get the source image for the label and get spacing from the source image, but having a source node for the label node is not guaranteed and/or requires manual actions for the user.
So there are two points in my question -

  • One is specifically for the resolution of label nodes. How to collect it?
  • and other more general, if there is a way to re-collect more parameters for source and label images to recreate image/label metadata (provided no transformation was applied in-between)?

The spacing and dimensions of the volumes are preserved when you load and save in nifti from slicer. Can you describe what other metadata you are referring to and how it is stored in nifti?

Loading the labels as either a Slicer segmentation or labelmap volume will also preserve the spacing and dimensions so it’s not clear to me what specific issue you are having trouble with.

Perhaps you need some custom python code to track the data you are loading.

Here is the Nifti header content. I guess it’s a Nifti1 format according to the reference


sizeof_hdr      : 348
data_type       : b''
db_name         : b''
extents         : 0
session_error   : 0
regular         : b'r'
dim_info        : 0
dim             : [  3 360 360 390   0   0   0   0]
intent_p1       : 0.0
intent_p2       : 0.0
intent_p3       : 0.0
intent_code     : none
datatype        : int16
bitpix          : 16
slice_start     : 0
pixdim          : [1.  0.1 0.1 0.1 0.  0.  0.  0. ]
vox_offset      : 0.0
scl_slope       : nan
scl_inter       : nan
slice_end       : 0
slice_code      : unknown
xyzt_units      : 0
cal_max         : 0.0
cal_min         : 0.0
slice_duration  : 0.0
toffset         : 0.0
glmax           : 0
glmin           : 0
descrip         : b''
aux_file        : b''
qform_code      : scanner
sform_code      : unknown
quatern_b       : 0.0
quatern_c       : 0.0
quatern_d       : 1.0
qoffset_x       : 17.95
qoffset_y       : 17.95
qoffset_z       : -19.45
srow_x          : [0. 0. 0. 0.]
srow_y          : [0. 0. 0. 0.]
srow_z          : [0. 0. 0. 0.]
intent_name     : b''
magic           : b'n+1'

So far I use only pixdim, potentially xyzt_units and datatype, when unsure, but I want to preserve the maximum number of parameters to avoid inconsistency - I mean, sometimes I see warnings in the Slicer console regarding sform/qform, when I import Nifti images with rigged headers, but didn’t have time to investigate, I’m gonna address it later somehow.

In the code, I don’t see an example to extract spacing from the label node. Volume node has the function GetSpacing(), label node has not. Also in the code snippets, it is assumed that you have a reference (source) volume when you export label into labelMap. (I don’t know what spacing is assigned when you export without it using ExportAllSegmentsToLabelmapNode - perhaps default spacing 1.1.1.).

Anyway, these examples imply access to the image path, I thought about it already - for example, reading the niffi header and storing it somewhere in memory, associated with the loaded node, to place it back during the export. But I’d like to use the drag-n-drop feature, when the user just drops Nifti images into Slicer and they become nodes on the scene. And I can’t intercept the metadata after that.

If you really want to preserve all those fields then you can do that with some custom scripts. You can give a custom file reader to override the default drag-and-drop behavior and track the data as you wish.

But you should probably check if any of your other code or tools really use any of that info. Nifti is a complex format and many of those fields are disused and/or used differently by different custom use cases (e.g. for fMRI) but there’s a long history of different software using them inconsistently. Even the qform/sform part, which it sounds like you don’t even need, has not being created consistently in these files, which is why you get the warnings.

Regarding your other questions, Slicer’s label maps do have a GetSpacing method and can be saved as nifti in a way that preserves the input spacing and dimensions. Segmentations are more sophisticated and you can learn more about them to see if you need them (check the geometry options to decide what sampling grid you want to use, it’s all nicely documented so an LLM can guide you through it).

1 Like