Add storageNode to volumeNode created from a numpy array

Hello,

I am creating a volumeNode and populating it with data from a 3D numpy array.

 volumeNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLScalarVolumeNode')
 slicer.util.updateVolumeFromArray(volumeNode, data)

Later in my code, I expect all volumes to have a storageNode() and a fileName.

I tried both:

   storageNode = volumeNode.createDefaultStorageNode();
   storageNode.setFileName('path/to/the/file/that/contains/the/data')

and

    storageNode = slicer.vtkMRMLVolumeArchetypeStorageNode()
    storageNode.SetFileName( 'path/to/the/file/that/contains/the/data' )   
    volumeNode.AddAndObserveStorageNodeID( storageNode.GetID() )

but in both cases when I try to get the storage node from the volumes I get nothing.

    volumeNode.getStorageNode() #returns a NoneType

Any idea on how to add a storageNode successfully?

Thank you in advance :slight_smile:
Marc

Hi -

Looks like you are close. My suggestion is to make reproducible snippets that anyone can paste into a freshly started Slicer instance and reproduce your question. Disconnected lines of code are really impossible to debug, but I can tell they aren’t part of a real program because of syntax errors like setFileName instead of SetFileName. Formulating such concise and reproducible examples can often help you find the issues yourself. We often point to this page for guidance: http://sscce.org/

Hi, my bad. Here is reproducible code that you should be able to copy into Slicer’s Python Interactor.

# Method 1
import numpy as np
data = np.random.rand( 50, 50, 50 ); # dummy data. In reality I am loading czi files, ex "C:/users/marc/data.czi"
volumeNode1 = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLScalarVolumeNode');
slicer.util.updateVolumeFromArray(volumeNode1, data );
storageNode1 = slicer.vtkMRMLVolumeArchetypeStorageNode() # creating storageNode separeate from volumeNode
storageNode1.SetFileName( "C:/users/marc/data.czi" ) 
volumeNode1.AddAndObserveStorageNodeID( storageNode1.GetID() ) # AddAndObserve to link storageNode to volumeNode
print( "Method 1", volumeNode1.GetStorageNode() )

And the other version:

# Method 2
import numpy as np
data = np.random.rand( 50, 50, 50 );  # dummy data. In reality I am loading czi files, ex "C:/users/marc/data.czi"
volumeNode2 = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLScalarVolumeNode');
slicer.util.updateVolumeFromArray(volumeNode2, data );
storageNode2 = volumeNode2.CreateDefaultStorageNode()  # Creating storageNode from volumeNode. I don't think AddAndObserve is required.
storageNode2.SetFileName( "C:/users/marc/data.czi" )
print( "Method 2", volumeNode2.GetStorageNode() )

I found the problem: In method 1, I needed to add the storageNode to the scene… otherwise the storageNode.GetID() is not available to the rest of nodes, and it cannot be “AddedAndObserved”.

This now works:

import numpy as np
data = np.random.rand( 50, 50, 50 ); 
volumeNode1 = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLScalarVolumeNode');
slicer.util.updateVolumeFromArray(volumeNode1, data );
storageNode1 = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLVolumeArchetypeStorageNode') # this creates storageNode and adds it to the scene
storageNode1.SetFileName( "C:/users/marc/data.czi" ) 
volumeNode1.AddAndObserveStorageNodeID( storageNode1.GetID() )
print( "Method 1", volumeNode1.GetStorageNode() )
1 Like

You can simplify this even further (based on examples in the script repository):

import numpy as np
voxelArray = np.random.rand(50, 50, 50)

volumeNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLScalarVolumeNode')
slicer.util.updateVolumeFromArray(volumeNode, voxelArray)
volumeNode.AddDefaultStorageNode()
volumeNode.GetStorageNode().SetFileName( "C:/users/marc/volume.nrrd" ) 

Or, if you want to immediately save to file:

import numpy as np
voxelArray = np.random.rand(50, 50, 50)

volumeNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLScalarVolumeNode')
slicer.util.updateVolumeFromArray(volumeNode, voxelArray)
slicer.util.saveNode(volumeNode, "C:/users/marc/volume.nrrd")