Advanced Tutorial Visualization 3D Vessel Segmentation

Example 8: Vessel Segmentation using SoVascularSystem

Introduction

In this tutorial, we are using an input mask to create a vessel centerline using the DtfSkeletonization module and visualize the vascular structures in 3D using the SoVascularSystem module. The second part uses the distance between centerline and surface of the vessel structures to color thin vessels red and thick vessels green.

Steps to do

Develop your network

Load the example tree mask by using the LocalImage module. Connect the output to a DtfSkeletonization module as seen below. The initial output of the DtfSkeletonization module is empty. Press the Update button to calculate the skeleton and the erosion distances.

Network

Network

Below you can see the output of the original image taken from the LocalImage module (left) compared to the output after calculating the skeleton via DtfSkeletonization module (right).

Output comparison

Output comparison

The output DtfSkeletonization.outBase1 shows nothing. Here you can find the 3-dimensional graph of the vascular structures. To generate it, open the panel of the DtfSkeletonization module, set Update Mode to Auto Update and select Update skeleton graph. Now the output additionally provides a 3D graph. Additionally, enable the Compile Graph Voxels to provide all object voxels at the output.

DtfSkeletonization

DtfSkeletonization

You can use the Output Inspector to see the 3D graph.

Graph output of DtfSkeletonization

Graph output of DtfSkeletonization

If you want to visualize your graph, you should connect a GraphToVolume module to the DtfSkeletonization module. The result is a 2D or 3D volume of your graph which you can connect to any 2D or 3D viewer. Add a View2D and a View3D module to the GraphToVolume module and update the volume.

GraphToVolume

GraphToVolume

For coloring the vessels depending on their distances to the centerline, we need a SoLUTEditor module. Change your network to use a SoExaminerViewer module, a SoLUTEditor module and a SoBackground module instead of a View3D module.

Use the SoLUTEditor for the View2D, too.

Network

Network

Open the output of the GraphToVolume module and inspect the images in Output Inspector. You will see that the HU value of the black background is defined as -1, the vessel tree is defined as 0.

Output Inspector

Output Inspector

Open the Panel of the SoLUTEditor and select tab Range. Define New Range Min as -1 and New Range Max as 0.

SoLUTEditor Range

SoLUTEditor Range

Change to Editor tab and define the following LUT:

SoLUTEditor Editor

SoLUTEditor Editor

The viewers now show your vessel graph.

View2D and SoExaminerViewer

View2D and SoExaminerViewer

Store Edge IDs in Skeletons with RunPythonScript

Each edge of the calculated skeleton gets a unique ID defined by the DtfSkeletonization module. We now want to use this ID to define a different color for each edge of the skeleton. You can use the Label property of each skeleton to store the ID of the edge.

Add a RunPythonScript module to your network, open the panel of the module and enter the following Python code:

ctx.field("DtfSkeletonization.update").touch()

graph = ctx.field("DtfSkeletonization.outBase1").object()
if graph is not None:
  for edge in graph.getEdges():
    print(edge.getId())
  
ctx.field("GraphToVolume.update").touch()

First, we always want a fresh skeleton. We touch the update trigger of the module DtfSkeletonization. Then we get the graph from the DtfSkeletonization.outBase1 output. If a valid graph is available, we walk through all edges of the graph and print the ID of each edge. In the end, we update the GraphToVolume module to get the calculated values of the Python script in the viewers. Click Execute.

The Debug Output of the MeVisLab IDE shows a numbered list of edge IDs from 1 to 153.

RunPythonScript

RunPythonScript

We now want the edge ID to be used for coloring each of the skeletons differently. Open the Panel of the SoLUTEditor and select tab Range. Define New Range Min as 0 and New Range Max as 153. Define different colors for your LUT.

SoLUTEditor

SoLUTEditor

The SoGVRVolumeRenderer module also needs a different setting. Open its panel in the Main tab, select Illuminated as the Render Mode. Adjust the Quality setting to 0.10. On tab Advanced, set Filter Volume Data to Nearest. Change to the Illumination tab and define below parameters:

SoGVRVolumeRendererMain

SoGVRVolumeRendererMain

SoGVRVolumeRendererIllumination

SoGVRVolumeRendererIllumination

Change your Python script as follows:

ctx.field("DtfSkeletonization.update").touch()

graph = ctx.field("DtfSkeletonization.outBase1").object()
if graph is not None:
  label = "Label"
  for edge in graph.getEdges():
    for skeleton in edge.getSkeletons():
      if label not in skeleton.properties:
        skeleton.createPropertyDouble(label, edge.getId())
      skeleton.setProperty(label, edge.getId())
  
ctx.field("GraphToVolume.update").touch()

In case the graph is valid, we now define a static text for the label. Instead of printing the edge ID, we also walk through each skeleton of the edge and define the property for the label using the ID of the edge as value.

Your viewers now show a different color for each skeleton, based on our LUT.

View2D and SoExaminerViewer

View2D and SoExaminerViewer

Render Vascular System Using SoVascularSystem

The SoVascularSystem module is optimized for rendering vascular structures. In comparison to the SoGVRVolumeRenderer module, it allows to render the surface, the skeleton or points of the structure in an open inventor scene graph. Interactions with edges of the graph are also already implemented.

Add a SoVascularSystem module to your workspace. Connect it to your DtfSkeletonization module and to the SoLUTEditor as seen below. Add another SoExaminerViewer for comparing the two visualization. The same SoBackground can be added to your new scene.

Uncheck Use skeleton colors and Use integer LUT on Appearance tab of the SoVascularSystem module panel.

 EditedNetwork

EditedNetwork

Draw parameter connections from one SoExaminerViewer to the other. Use the fields seen below to synchronize your camera interaction.

 Camera positions

Camera positions

Connect the backwards direction of the two SoExaminerViewer by using multiple SyncFloat modules and two SyncVector modules for position and orientation fields.

 SyncFloat & SyncVector

SyncFloat & SyncVector

Camera interactions are now synchronized between both SoExaminerViewer modules.

Now you can notice the difference between the two modules. We use SoVascularSystem for a smoother visualization of the vascular structures by using the graph as reference. The SoGVRVolumeRenderer renders the volume from the GraphToVolume module, including the visible stairs from pixel representations in the volume.

 SoVascularSystem & SoGVRVolumeRenderer

SoVascularSystem & SoGVRVolumeRenderer

The SoVascularSystem module has additional visualization examples unlike SoGVRVolumeRenderer. Open the panel of the SoVascularSystem module and select Random Points for Display Mode in the Main tab to see the difference.

 Random Points

Random Points

Change it to Skeleton to only show the centerlines/skeletons of the vessels.

 Skeleton

Skeleton

Enhance Vessel Visualization Based on Distance Information

Now that you’ve successfully obtained the vessel skeleton graph using DtfSkeletonization, let’s take the next step to enhance the vessel visualization based on the radius information of the vessels. We will modify the existing code to use the minimum distance between centerline and surface of the vessels for defining the color.

The values for the provided vascular tree vary between 0 and 10mm. Therefore define the range of the SoLUTEditor to New Range Min as 1 and New Range Max as 10. On Editor tab, define the following LUT:

SoLUTEditor

SoLUTEditor

In the RunPythonScript module, change the existing code to the following:

ctx.field("DtfSkeletonization.update").touch()

graph = ctx.field("DtfSkeletonization.outBase1").object()
if graph is not None:
  label = "Label"
  print('Num edges', len(graph.getEdges()))
  for edge in graph.getEdges():
    end_node = edge.getEndNode()
    for skeleton in edge.getSkeletons():
      if label not in skeleton.properties:
        skeleton.createPropertyDouble(label, skeleton.getProperty("MinDistance"))
      skeleton.setProperty(label, skeleton.getProperty("MinDistance"))

ctx.field("GraphToVolume.update").touch()
ctx.field("SoVascularSystem.apply").touch()

Instead of using the ID of each edge for the label property, we are now using the MinDistance property of the skeleton. The result is a color coded 3D visualization depending on the radius of the vessels. Small vessels are red, large vessels are green.

Radius based Visualization

Radius based Visualization

Mouse Clicks on Vessel Graph

Open the Interaction tab of the SoVascularSystem module. In SoExaminerViewer module, change to Pick Mode and click into your vessel structure. The panel of the SoVascularSystem module shows all information about the hit of your click in the vessel tree.

Getting the click point in a vascular tree

Getting the click point in a vascular tree

Summary

  • Vessel centerlines can be created using a DtfSkeletonization module
  • Vascular structures can be visualized using a SoVascularSystem module, which provides several vessel specific display modes
  • The SoVascularSystem module provides information about mouse clicks into a vascular tree
  • The labels of a skeleton can be used to store additional information for visualization