[HELP] Python Plugin: How do I use vtkContourFilter / vtkGenericContourFilter?

Hey,

I’m relatively new to Paraview, vtk and plugin development. I’ve been looking up and down the documentation but I don’t seem to get things to work.

I need to write a plugin that has vtkImageData as input, alters the values, applies contours and then gives the contours as output. Altering the values worked and I managed to output it as vtkImageData again. However contouring doesn’t seem to work. I’ve got it to run without any compile errors but it also does nothing. The contour filter array is empty and packing it into vtkPolyData as output didn’t seem to work either.
I really hope someone here can help me or point me in the right direction because I don’t know what else to try.

Working randomization:

 __all__ = ['prtlRnd']
from pyprtl.util.vtkAlgorithm import *
import pyprtl.util.arrays as util
from vtkmodules.vtkCommonDataModel import vtkImageData
import numpy as np

from vtk.vtkCommonCore import vtkDoubleArray
import vtk

from short_inputarray import * # allows to write @smproperty_inputarray

import random

@smproxy.filter(label='Rnd')
@smhint_menu('prtl')
@smproperty.input(name='Input', port_index=0)
@smdomain.datatype(dataTypes=['vtkImageData'])

class prtlRnd(VTKPythonAlgorithmBase):
    def __init__(self):
        #self.A = 0
        self._array_field = 0
        self._array_name = None
        VTKPythonAlgorithmBase.__init__(self,
                                        nInputPorts=1, nOutputPorts=1,
                                        inputType='vtkImageData', outputType='vtkImageData')

    @smproperty_inputarray('Scalars')
    def SetInputArrayToProcess(self, idx, port, connection, field, name):
        self._array_field = field
        self._array_name = name
        self.Modified()
        return 1

    def RequestData(self, request, inInfo, outInfo):
        # get the first input
        input0 = vtkImageData.GetData(inInfo[0])
        # get the output
        output = vtkImageData.GetData(outInfo)
        
        # Create an instance of vtkImageData
        myVtkImageData = vtk.vtkImageData()
        # Copy Structure from input: 
        myVtkImageData.CopyStructure(input0)
        
        
        dimensions = myVtkImageData.GetDimensions()
        inputArray = input0.GetPointData().GetArray(self._array_name)
        #print("inputArray")
        #print(type(inputArray))

        # Create a data array to hold the sorted input
        myVtkDataArray = vtk.vtkDoubleArray()
        myVtkDataArray.SetNumberOfComponents(inputArray.GetNumberOfComponents()) # Do this before SetNumberOfTuples to request enough memory
        myVtkDataArray.SetNumberOfTuples(np.prod(dimensions))
        myVtkDataArray.SetName("myData")
        
        perturbedInput = [inputArray.GetTuple(i) for i in range(inputArray.GetNumberOfTuples())]
        
        # load list of seeds
        f = open("/media/mario/Volume/Google Drive/Dropbox/Studium/Bachelorarbeit/Paraview/seeds.txt", "r")
        seeds = list()
        for line in f:
            seeds.append(line)
        if not seeds:
            print("seeds is empty!")
        
        # add random values and add to the list
        # this is done ~1000 times and added to pOutList (list of list results)
        pOutList = list()
        while seeds:
            tmp = list()
            random.seed(seeds.pop())
            for k in range(len(perturbedInput)):
                x = perturbedInput[k][0] + random.uniform(-0.1,0.1)
                tmp.append((x,))
            pOutList.append(tmp)
              
        
        # Fill our data array
        for z in range(dimensions[2]):
          for y in range(dimensions[1]):
            for x in range(dimensions[0]):
              index = x + y * dimensions[0] + z * dimensions[0] * dimensions[1]
              myVtkDataArray.SetTuple(index, pOutList[5][index]) #isolines
              
        # Add the array to the image data
        myVtkImageData.GetPointData().AddArray(myVtkDataArray)
        
        
        # copy input to output
        output.ShallowCopy(myVtkImageData)

        return 1

@mwestphal @utkarsh.ayachit

I am not sure what example we are looking at. I don’t see anything about contouring or packing it into vtkPolyData. Can you please post/comment the code appropriately. thanks.

Sorry, my bad.

__all__ = ['prtlRnd']
from pyprtl.util.vtkAlgorithm import *
import pyprtl.util.arrays as util
from vtkmodules.vtkCommonDataModel import vtkImageData, vtkPolyData
import numpy as np

from vtk.vtkCommonCore import vtkDoubleArray
import vtk

from pyprtl.short_inputarray import * # allows to write @smproperty_inputarray

import random

@smproxy.filter(label='Rnd')
@smhint_menu('prtl')
@smproperty.input(name='Input', port_index=0)
@smdomain.datatype(dataTypes=['vtkImageData'])

class prtlRnd(VTKPythonAlgorithmBase):
    def __init__(self):
        #self.A = 0
        self._array_field = 0
        self._array_name = None
        VTKPythonAlgorithmBase.__init__(self,
                                        nInputPorts=1, nOutputPorts=1,
                                        inputType='vtkImageData', outputType='vtkPolyData')

    @smproperty_inputarray('Scalars')
    def SetInputArrayToProcess(self, idx, port, connection, field, name):
        self._array_field = field
        self._array_name = name
        self.Modified()
        return 1

    def RequestData(self, request, inInfo, outInfo):
        # get the first input
        input0 = vtkImageData.GetData(inInfo[0])
        # get the output
        #output = vtkImageData.GetData(outInfo)
        output = vtkPolyData.GetData(outInfo)
        
        # Create an instance of vtkImageData
        myVtkImageData = vtk.vtkImageData()
        # Copy Structure from input: 
        myVtkImageData.CopyStructure(input0)
        
        
        dimensions = myVtkImageData.GetDimensions()
        inputArray = input0.GetPointData().GetArray(self._array_name)
        #print("inputArray")
        #print(type(inputArray))

        # Create a data array to hold the sorted input
        myVtkDataArray = vtk.vtkDoubleArray()
        myVtkDataArray.SetNumberOfComponents(inputArray.GetNumberOfComponents()) # Do this before SetNumberOfTuples to request enough memory
        myVtkDataArray.SetNumberOfTuples(np.prod(dimensions))
        myVtkDataArray.SetName("myData")
        
        perturbedInput = [inputArray.GetTuple(i) for i in range(inputArray.GetNumberOfTuples())]
        
        # load list of seeds
        f = open("C:/Users/MarioLaptop/Google Drive/Dropbox/Studium/Bachelorarbeit/Paraview/seeds.txt", "r")
        #f = open("/media/mario/Volume/Google Drive/Dropbox/Studium/Bachelorarbeit/Paraview/seeds.txt", "r")
        seeds = list()
        for line in f:
            seeds.append(line)
        if not seeds:
            print("seeds is empty!")
        
        # add random values and add to the list
        # this is done ~1000 times and added to pOutList (list of list results)
        pOutList = list()
        while seeds:
            tmp = list()
            random.seed(seeds.pop())
            for k in range(len(perturbedInput)):
                x = perturbedInput[k][0] + random.uniform(-0.1,0.1)
                tmp.append((x,))
            pOutList.append(tmp)
              
        
        # Fill our data array
        for z in range(dimensions[2]):
          for y in range(dimensions[1]):
            for x in range(dimensions[0]):
              index = x + y * dimensions[0] + z * dimensions[0] * dimensions[1]
              myVtkDataArray.SetTuple(index, pOutList[5][index]) #isolines
              
        # Add the array to the image data
        myVtkImageData.GetPointData().AddArray(myVtkDataArray)

        contour = vtk.vtkContourFilter()
        contour.SetInputDataObject(0,myVtkImageData)
        contour.SetInputArrayToProcess(0,0,0,0,myVtkImageData.GetPointData().GetArray(self._array_name))
        contour.GenerateValues(10, 0, 2)
        contour.Update()

        #output.CopyStructure(contour.GetOutputDataObject(0))
        output.DeepCopy(contour.GetOutputDataObject(0))
        
        
        # copy input to output
        #output.ShallowCopy(myVtkImageData)

        return 1

I don’t know whether the countouring works, I get no errors, however contouring is empty. Maybe that’s how filter functions work but I also don’t get anything displayed. Are there any errors or did I just forget something?

My thought was that the access of the array of myVtkImageData is wrong:

    contour.SetInputArrayToProcess(0,0,0,0,myVtkImageData.GetPointData().GetArray(self-_array_name))

When printing out myVtkImageData, it says that the array is called “myData”, however if I try to use that name it say that array doesn’t exist.

This is incorrect. SetInputArrayToProcess does not take the array as the argument, but it’s name. Your intention, I suspect, would be contour.SetInputArrayToProcess(0, 0, 0, self. _array_field, self._array_name) but that too may not work in your case. You’re creating a myVtkImageData with only 1 array named myData. Note you’re using myVtkImageData.CopyStructure(input0) which does not copy any array from the input. If your intent was to pass input arrays, you should call ShallowCopy instead. Also output.DeepCopy(contour.GetOutputDataObject(0)) is an overkill. output.ShallowCopy(contour.GetOutputDataObject(0)) is adequate.

In other words, there are many issues with this code. I’d recommend simply using vtkpython (or pvpython) to build your pipeline using VTK python API and once you’re confirmed that works, continue to package it in a filter.