Search code examples
pythonnumpyvtk

Numpy uint8_t arrays to vtkImageData


I am attempting to take 2D images of either one or three channels and display them in VTK using vtkImageActor. As I understand it, the current frame to be displayed can be updated by invoking SetImageData on vtkImageActor and providing an instance of vtkImageData.

I have set up my visualiser as shown below. However, I am unsure how to build the vtkImageData object from the numpy arrays(this would go in the updateFrames method). The type of my numpy arrays is np.uint8_t.

I am using VTK8.0, Python 3.6 and Numpy 1.13.1

class VTKStreamVisualiser:
    def __init__(self, displayRGB):
        self.__displayRGB = displayRGB
        self.__started = False

        #Setup window.
        self.__renderWindow = vtk.vtkRenderWindow()
        self.__renderWindowInteractor = vtk.vtkRenderWindowInteractor()
        self.__renderWindowInteractor.SetRenderWindow(self.__renderWindow)

        #To store renderers and actors.
        self.__renderers = []
        self.__actors = []

        #Initialise to None to check if ready when invoking start()
        self.__depthImageData = None
        self.__rgbImageData = None

        #Determine viewport ranges for depth and setup renderer.
        xMinDepth = 0.0
        xMaxDepth = 0.5 if displayRGB else 1.0
        yMin = 0.0
        yMax = 1.0
        self.__setupRenderer(xMinDepth, yMin, xMaxDepth, yMax)

        #Determine viewport ranges for rgb and setup renderer.
        if self.__displayRGB:
            xMinRGB = xMaxDepth
            xMaxRGB = 2.0 * xMinRGB
            self.__setupRenderer(xMinRGB, yMin, xMaxRGB, yMax)

    def __setupRenderer(self, xMin, yMin, xMax, yMax):
        #Setup renderer.
        self.__renderers.append(vtk.vtkRenderer())
        idx = len(self.__renderers) - 1
        self.__renderWindow.AddRenderer(self.__renderers[idx])
        self.__renderers[idx].SetViewport(xMin, yMin, xMax, yMax)
        self.__actors.append(vtk.vtkImageActor())
        self.__renderers[idx].AddActor(self.__actors[idx])
        self.__renderers[idx].ResetCamera()

    def start(self):
        self.__depthImageData is None or (self.__rgbImageData is None and self.__displayRGB):
            return None

        if self.__started:
            return

        self.__renderWindowInteractor.Initialize()
        self.__renderWindow.Render()
        self.__renderWindowInteractor.Start()
        self.__started = True

    def stop(self):
        if not self.__started:
            return

        self.__renderWindowInteractor.Stop()
        self.__renderWindow.Finalize()
        self.__renderWindowInteractor.TerminateApp()
        self.__started = False

    def updateFrames(self, depthFrame, rgbFrame=None):
        #Build vtkImageData here from the given numpy uint8_t arrays.
        pass

EDIT: I realise that I can manually copy the data over as demonstrated here, which wouldn't be too bad with Cython(assuming I am able to work with vtkImageData in Cython), however it would be preferable to use the numpy arrays directly.


Solution

  • Using the numpy_support library one can convert numpy arrays into a vtk data arrays

    from vtk.util import numpy_support
    
    def updateFrames(self, depthFrame, rgbFrame=None):
       #Build vtkImageData here from the given numpy uint8_t arrays.
       self.__depthImageData = vtk.vtkImageData()
       depthArray = numpy_support.numpy_to_vtk(depthFrame.ravel(), deep=True, array_type=vtk.VTK_UNSIGNED_CHAR) 
       # .transpose(2, 0, 1) may be required depending on numpy array order see - https://github.com/quentan/Test_ImageData/blob/master/TestImageData.py
    
       __depthImageData.SetDimensions(depthFrame.shape)
      #assume 0,0 origin and 1,1 spacing.
       __depthImageData.SetSpacing([1,1])
       __depthImageData.SetOrigin([0,0])
       __depthImageData.GetPointData().SetScalars(depthArray)
    

    Should provide a working example of how to generate the depthFrame as a starting point