the speed of vtk.vtkMarchingCubes is slow, how to speed it up?

Currently, I want to extract the isosurface from a volume image, and I use vtk.vtkMarchingCubes to implement it. Moreover, I want to change the iso-value by mouse. I have implement the code as following:

import vtk, sys

from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

val = [1000, None]
def setVal(v):
    val[0] = v
    val[1].SetValue(v)

class MyInteractor(vtk.vtkInteractorStyleTrackballCamera):

    def __init__(self):
        self.AddObserver("LeftButtonPressEvent", self.leftButtonPressEvent)
        self.AddObserver("LeftButtonReleaseEvent", self.leftButtonReleaseEvent)
        self.AddObserver("MouseMoveEvent", self.leftButtonMoveEvent)
        self.btnPress = False
        self.lastPoint = [0, 0]

    def leftButtonPressEvent(self, obj, event):
        self.btnPress = True
        pos = self.GetInteractor().GetEventPosition()
        self.lastPoint = [pos[0], pos[1]]

    def leftButtonReleaseEvent(self, obj, event):
        self.btnPress = False

    def leftButtonMoveEvent(self, obj, event):
        if self.btnPress == True:
            curPos = self.GetInteractor().GetEventPosition()
            dx = curPos[0] - self.lastPoint[0]
            dy = curPos[1] - self.lastPoint[1]

            isoValue = val[0]
            if abs(dx) > abs(dy):
                isoValue += dx
            else:
                isoValue += dy
            setVal(isoValue)

            self.lastPoint = [curPos[0], curPos[1]]
        else:
            super().OnMouseMove()

class MainWindow(QMainWindow):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent=parent)

        self.frame = QFrame()

        self.vl = QVBoxLayout()
        self.vtkWidget = QVTKRenderWindowInteractor(self.frame)
        self.vl.addWidget(self.vtkWidget)

        self.ren = vtk.vtkRenderer()
        self.vtkWidget.GetRenderWindow().AddRenderer(self.ren)
        self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()

        style = MyInteractor()
        self.iren.SetInteractorStyle(style)
        reader = vtk.vtkDICOMImageReader()
        reader.SetDirectoryName("C:\\Users\\MLoong\\Desktop\\Angiograph Data\\Chang Cheng\\TOF")
        reader.Update()

        surface = vtk.vtkMarchingCubes()
        surface.SetInputConnection(reader.GetOutputPort())
        surface.ComputeNormalsOn()
        surface.SetValue(0, 300)
        surface.Update()
        self.surface = surface

        # Create a mapper
        mapper = vtk.vtkPolyDataMapper()
        mapper.SetInputConnection(surface.GetOutputPort())
        mapper.ScalarVisibilityOff()

        # Create an actor
        actor = vtk.vtkActor()
        actor.SetMapper(mapper)
        actor.GetProperty().SetColor(1, 1, 1)
        actor.GetProperty().ShadingOff()

        self.ren.AddActor(actor)

        self.ren.ResetCamera()

        self.frame.setLayout(self.vl)
        self.setCentralWidget(self.frame)

        self.show()
        self.iren.Initialize()

    def SetValue(self, val):
        print(val)
        self.surface.SetValue(0, val)
        self.surface.Update()
        self.vtkWidget.GetRenderWindow().Render()

if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    val[1] = window

    sys.exit(app.exec_())

This code has no bug after provide a correct image in reader.SetDirectoryName(“path”). I can change the iso-value by left mouse press and move. But the problem is that the speed is slow. Does anybody has any suggestion to speed it up?

You can try vtkFlyingEdges3D filter instead. vtkFlyingEdges3D is about a magnitude faster than marching cubes if you build VTK with TBB SMP backend.

Thanks. vtkFlyingEdges3D is much faster than vtkMarchingCube. In addition, can GPU be used to speed up isosurface extraction?

If you just want to render the isosurface then you can use GPU raycasting directly on the image data. You will probably get much higher quality results at very high frame rates (you should be able to get 100+ FPS on a 512^3 volume with recent GPU).

If rendering is not enough but you also need to process the isosurface then extracting it on the GPU will not likely to get you much farther, since you need to transfer data to/from GPU, perform other time consuming operations, etc., which will overall diminish any computation time advantage.

How to set the iso-value for marching cube in c++? I mean there is already a vtkactor in the scene, how to reset the threshold for the actor?

You set it in the GPU volume mapper’s scalar opacity transfer function. For isosurface-like rendering you need to set a ramp or step function. The isovalue is the location of the step in the function.

Thanks for the reply. The following code crashed while setting the iso-value, any suggestions?
vtkFlyingEdges3D::SafeDownCast(pActor->GetMapper())->SetValue(0, dIso);

You need to set a step function as opacity transfer function. See VTK volume rendering examples.

I think the opacity transfer function is a property of vtkVolumeProperty, but the property of ISO is vtkProperty and cannot be set like volume rendering.

This is how isosurface-like rendering looks if you use a step (or steep ramp) function as scalar opacity transfer function:

If the step is at a higher intensity value:

You get classic “volume rendering” look if the step is not so steep and you set a more more interesting color transfer function:

Now I realize that we are using different algorithms. The algorithm you are using is raycasting with ISO, while the algorithm I am using is marching cube or flying edges. Therefore, the objects in the scene are also different, one is vtkVolume and the other is vtkActor. So the method of setting ISO is also different.

The question was how to make isosurface rendering faster. Answer in 2020 was use FlyingEdges (~10x faster) or use raycasting (~1000x faster). Since then Surface Nets are added to VTK (~100x faster).