How to Enable Maximum Intensity Projection (MIP) Automatically for MRML Presets?

Hello,

I have created custom volume rendering presets in an MRML file. Below is an example structure:

<MRML version="Slicer4.4.0">
  <VolumeProperty id="vtkMRMLVolumeProperty1" name="MyPreset1" references="IconVolume:vtkMRMLVectorVolumeNode1;" interpolation="1" shade="1" diffuse="0.66" ambient="0.1" specular="0.62" specularPower="14" scalarOpacity="10 -3.52844023704529 0 56.7852325439453 0 79.2550277709961 0.428571432828903 415.119384765625 1 641 1" gradientOpacity="4 0 1 160.25 1" colorTransfer="16 0 0 0 0 98.7223 0.196078431372549 0.945098039215686 0.956862745098039 412.406 0 0.592157 0.807843 641 1 1 1" />
  <VectorVolume id="vtkMRMLVectorVolumeNode1" references="storage:vtkMRMLVolumeArchetypeStorageNode1;" />
  <VolumeArchetypeStorage id="vtkMRMLVolumeArchetypeStorageNode1" fileName="MyPreset1.png" fileListMember0="MyPreset1.png" />

  <VolumeProperty id="vtkMRMLVolumeProperty2" name="MyPreset2" references="IconVolume:vtkMRMLVectorVolumeNode2;" interpolation="1" shade="1" diffuse="0.66" ambient="0.1" specular="0.62" specularPower="14" scalarOpacity="10 -3.52844023704529 0 56.7852325439453 0 79.2550277709961 0.428571432828903 415.119384765625 1 641 1" gradientOpacity="4 0 1 160.25 1" colorTransfer="16 0 0 0 0 98.7223 0.196078431372549 0.945098039215686 0.956862745098039 412.406 0 0.592157 0.807843 641 1 1 1" />
  <VectorVolume id="vtkMRMLVectorVolumeNode2" references="storage:vtkMRMLVolumeArchetypeStorageNode2;" />
  <VolumeArchetypeStorage id="vtkMRMLVolumeArchetypeStorageNode2" fileName="MyPreset2.png" fileListMember0="MyPreset2.png" />
</MRML>

I would like to automatically enable Maximum Intensity Projection (MIP) when switching to the MyPreset2 preset.

Do I need to add a ViewNode, a VolumeRenderingDisplayNode, or some other configuration in the MRML file? If not, what is the best way to associate MIP with specific presets?

Thank you for your help!

1 Like

MIP mode has to be enabled on the view node - see full example in the script repository.

It would make more sense to include raycast technique in the volume rendering preset. I’ve added an issue to track this task. But to reduce API churn, we probably will not change this until we need to make other significant changes/improvements to rendering presets. A grant proposal was submitted for improving volume rendering presets, so if it is successful (or if other funding sources come in) then this will all be implemented.

3 Likes

Thank you @lassoan for the clarification and for adding this to the issue tracker.

I plan to implement a workaround using a Python function onPresetChanged in my module within my extension. The function would trigger when the preset changes, allowing me to manually set the raycast technique for specific presets. For example:

def onPresetChanged(presetName):
    if presetName == "MyPreset2":
        # Switch views to MIP mode
        for viewNode in slicer.util.getNodesByClass("vtkMRMLViewNode"):
            viewNode.SetRaycastTechnique(slicer.vtkMRMLViewNode.MaximumIntensityProjection)

Is there a way to integrate such a function so that it is automatically triggered when a preset is changed?

May be there’s a cleaner way to do this, but the following should work however:

presetComboBox = slicer.util.mainWindow().findChild(slicer.qSlicerVolumeRenderingPresetComboBox, "PresetComboBox")
presetComboBox.connect("currentNodeIDChanged(QString)", yourFunction)

yourFunction’ will receive a preset ID, which you can follow to get the name.

BTW, I just noticed that the preset combobox does not select the chosen preset actually; that’s on preview in Linux. It is indeed selected in 5.6.

Thank you for your suggestion! Unfortunately, this line of code

presetComboBox = slicer.util.mainWindow().findChild(slicer.qSlicerVolumeRenderingPresetComboBox, "PresetComboBox")

is not working in my case. The presetComboBox is returning None, so it seems the PresetComboBox is either not found or not available in my setup.

I’ve tried listing all child objects under slicer.util.mainWindow() but couldn’t locate it there. Could it be named differently or located somewhere else, like within the Volume Rendering module widget? Any additional pointers would be appreciated!

w = getModuleWidget("VolumeRendering")
presetComboBox = w.findChild(slicer.qSlicerVolumeRenderingPresetComboBox, "PresetComboBox")

should work. It’s quick and dirty, but does help.

It’s likely that the previous code needs the VolumeRendering module to have been shown once.

Thank you for the help! I managed to get it working with this approach:

def OnPresetChanged(ID: str):
    """Automatically switch to MIP when a preset is changed."""
    vrLogic = slicer.modules.volumerendering.logic()
    presetsScene = vrLogic.GetPresetsScene()
    vrNodes = presetsScene.GetNodesByClass("vtkMRMLVolumePropertyNode")
    for i in range(vrNodes.GetNumberOfItems()):
        node = vrNodes.GetItemAsObject(i)
        if node.GetID() == ID:
            raycastTechnique = "Max" if node.GetName().lower().endswith("max") else "Composite"
            SetRaycastTechnique(raycastTechnique)
            break

w = slicer.util.getModuleWidget("VolumeRendering")
presetComboBox = w.findChild(slicer.qSlicerVolumeRenderingPresetComboBox, "PresetComboBox")
presetComboBox.connect("currentNodeIDChanged(QString)", OnPresetChanged)

I made sure that the presets I want to activate MIP for have “Max” at the end of their names. This works well, but I’m sure there’s a cleaner or more efficient way to achieve this. Let me know if you have any suggestions!