Dicom tags lost while saving scene

When I save the scene as a mrb the dicom tags of my files are lost.

  1. Load a study from dicom node (modality and acquisition time are available, also other dicom tags)
  2. Do some processing
  3. save scene as mrb
  4. Load scene back (drag and drop)
  5. dicom tags no longer available

The problem is that the scene saves the files as nrrd, changing the tags functionality, but then they are not saved, or at least I can’t find them anymore.

This is especially critical when moving mrb files to another computer, the functions that depend on this tags no longer work

I have seen that the scalar nodes contain the function

/// Set node attributes
  void ReadXMLAttributes( const char** atts) override; 

but this is not visible from within python

File "/Users/alexvergaragil/Documents/GIT/dosimetry4d/Dosimetry4D/Logic/dicomutils.py", line 22, in getDICOMattributes
    self.imagenode.ReadXMLAttributes(att)
AttributeError: 'MRMLCorePython.vtkMRMLScalarVolumeNode' object has no attribute 'ReadXMLAttributes'
' object has no attribute 'GetMetaDataDictionary'

In the above exception I also discovered that there is also a ‘GetMetaDataDictionary’ in the vtkMRMLVolumeNode from which the nodes inherits. This function is also hidden for python

dicomutils.py", line 21, in getDICOMattributes
    print(self.imagenode.GetMetaDataDictionary())
AttributeError: 'MRMLCorePython.vtkMRMLScalarVolumeNode' object has no attribute 'GetMetaDataDictionary'

There is a way to get the Node data with GetImageData() but this is the output

vtkImageData (0x7f8fec3b0b80)
  Debug: Off
  Modified Time: 1956077
  Reference Count: 5
  Registered Events: 
    Registered Observers:
      vtkObserver (0x7f8fec3ac590)
        Event: 36
        EventName: ModifiedEvent
        Command: 0x7f8fec3ac530
        Priority: 0
        Tag: 1
  Information: 0x7f8fec3b0d20
  Data Released: False
  Global Release Data: Off
  UpdateTime: 1956397
  Field Data:
    Debug: Off
    Modified Time: 1956049
    Reference Count: 1
    Registered Events: (none)
    Number Of Arrays: 0
    Number Of Components: 0
    Number Of Tuples: 0
  Number Of Points: 1474560
  Number Of Cells: 1435481
  Cell Data:
    Debug: Off
    Modified Time: 1956073
    Reference Count: 1
    Registered Events: 
      Registered Observers:
        vtkObserver (0x7f8fec3ab840)
          Event: 36
          EventName: ModifiedEvent
          Command: 0x7f8fec3ab570
          Priority: 0
          Tag: 1
    Number Of Arrays: 0
    Number Of Components: 0
    Number Of Tuples: 0
    Copy Tuple Flags: ( 1 1 1 1 1 0 1 1 )
    Interpolate Flags: ( 1 1 1 1 1 0 0 1 )
    Pass Through Flags: ( 1 1 1 1 1 1 1 1 )
    Scalars: (none)
    Vectors: (none)
    Normals: (none)
    TCoords: (none)
    Tensors: (none)
    GlobalIds: (none)
    PedigreeIds: (none)
    EdgeFlag: (none)
  Point Data:
    Debug: Off
    Modified Time: 1956077
    Reference Count: 1
    Registered Events: 
      Registered Observers:
        vtkObserver (0x7f8fec3ab6e0)
          Event: 36
          EventName: ModifiedEvent
          Command: 0x7f8fec3ab570
          Priority: 0
          Tag: 1
    Number Of Arrays: 1
    Array 0 name = ImageScalars
    Number Of Components: 1
    Number Of Tuples: 1474560
    Copy Tuple Flags: ( 1 1 1 1 1 0 1 1 )
    Interpolate Flags: ( 1 1 1 1 1 0 0 1 )
    Pass Through Flags: ( 1 1 1 1 1 1 1 1 )
    Scalars: 
      Debug: Off
      Modified Time: 1955813
      Reference Count: 2
      Registered Events: 
        Registered Observers:
          vtkObserver (0x7f8fd32380c0)
            Event: 36
            EventName: ModifiedEvent
            Command: 0x7f8fd3237ec0
            Priority: 0
            Tag: 2
          vtkObserver (0x7f8fd3238090)
            Event: 2
            EventName: DeleteEvent
            Command: 0x7f8fd3237ec0
            Priority: 0
            Tag: 1
      Name: ImageScalars
      Data type: double
      Size: 1474560
      MaxId: 1474559
      NumberOfComponents: 1
      Information: 0x7f8fec3a0c30
        Debug: Off
        Modified Time: 1956426
        Reference Count: 1
        Registered Events: (none)
        PER_COMPONENT: vtkInformationVector(0x7f8fec3a1250)
      Name: ImageScalars
      Number Of Components: 1
      Number Of Tuples: 1474560
      Size: 1474560
      MaxId: 1474559
      LookupTable: (none)
    Vectors: (none)
    Normals: (none)
    TCoords: (none)
    Tensors: (none)
    GlobalIds: (none)
    PedigreeIds: (none)
    EdgeFlag: (none)
  Bounds: 
    Xmin,Xmax: (0, 127)
    Ymin,Ymax: (0, 127)
    Zmin,Zmax: (0, 89)
  Compute Time: 2391599
  Spacing: (1, 1, 1)
  Origin: (0, 0, 0)
  Dimensions: (128, 128, 90)
  Increments: (0, 0, 0)
  Extent: (0, 127, 0, 127, 0, 89)

From all these records I see no reference to the initial Dicom tags, What is the “Modified Time”? Where is the modality?

Right, the MetaDataDictionary is only used if you load dicom via itk, so it doesn’t get stored in the scene or in nrrd. I’d avoid relying on that.

If you want to access dicom information across scene save and restore, then you can import the volume via the DICOM module and the DICOM.instanceUIDs attribute will be stored and restored as a node attribute. Then you can use these UIDs to look up information in the database or reload the files with pydicom to get a the headers.

This is an internal part of VTK, nothing to do with dicom.

Note too that if you want to move the data to another machine you need to be sure they also have the dicom data in their database.

Depending on your use case it may be better to cache whatever data you need, for example in an attribute of a vtkMRMLScriptedModuleNode.

A number of essential DICOM tags are stored in Subject Hierarchy, associated with Patient, Study, and data items. You can save additional DICOM tags there.

2 Likes

Do you mean these:
image

Thats only useful if I have only one DICOM file, but I have several (several acquisitions in different times, forget for now about the segmentations, transfoms, tables and intermediate volumes). I would need at least the acquisition time and modality of original acquisition files, for sanity checks about wether a proposed file is SPECT or CT and if the suggested time is correct. It works If I load the original DICOM, but after processing the scene and reloading then it no longer works.

This is likely the solution, I can have the required tags inside a vtkMRMLScriptedModuleNode, but this module is hidden and not exportable to mrb. I tried setting tagnode.HideFromEditors to 0 and to False but nothing works.

image

So far so good BUT

image
image

You can save patient-level tags in patient subject hierarchy item, study-level tags in study item, and series-level tags in the subject hierarchy item or in custom node attributes of the image, transform, etc. node. These tags are already automatically populated during DICOM import and used during DICOM export. Since you can save any additional custom tags there, I don’t see any reason why you would use a different mechanism.

Saving DICOM tags in scripted module nodes is possible, too, but you would need to manage association of these tags with data nodes. So, it is simpler to store these tags directly in the data node or the node’s subject hierarchy item.

Storing tags in the subject hierarchy item is useful if you want to allow the user to edit the DICOM patient/study/series tags (the tags are stored at one place, so you don’t need to worry about keeping redundant copies consistent) or you want to allow the users to create/change patients/studies/series by drag-and-dropping items in the subject hierarchy tree. There is also GUI available to view/edit custom subject hierarchy item and node attributes, which is useful for advanced users and for debugging.

1 Like

I have tried what you seggested but I found this error

    node.SetParameter('DICOM.Acquisition', str(acquisition))
AttributeError: 'MRMLCorePython.vtkMRMLScalarVolumeNode' object has no attribute 'SetParameter'

However in the subject hierarchy item information I get
image

So, How to access and set those attributes?

OK found it! It is in the subject hierarchy structure itself, not in the node. Now exporting to mrb and drag and drop back maintain the required attributes!

        shNode.SetItemAttribute(itemID, 'DICOM.Acquisition', str(acquisition))
        shNode.SetItemAttribute(itemID, 'DICOM.Modality', str(modality))
        acquisition = datetime.strptime(shNode.GetItemAttribute(itemID,'DICOM.Acquisition'), '%Y-%m-%d %H:%M:%S')
        modality = shNode.GetItemAttribute(itemID,'DICOM.Modality')

That was very well hidden in the source code!

image

3 Likes

This is how the item attributes are populated in the DICOM plugin: https://github.com/Slicer/Slicer/blob/1b7888e90e233729e4beb4941e8b33e622373655/Modules/Scripted/DICOMLib/DICOMPlugin.py#L164

1 Like