Hello,
I have been attempting to create a reader plugin for my hdf5 dataset. The problem I am facing is that animation frames are consistently showing as the default, 10 steps between 1 and 10 inclusive. Is this expected? I have had trouble finding insight in the documentation, but I had expected setting executive.TIME_STEPS() and executive.TIME_RANGE() to link my data to the animation. Is there a way to set these from the reader, or am I going about this the wrong way? I have included an example reader similar to my own using a randomly generated numpy array.
"""
ParaView Python Reader for a 4D NumPy array of shape (T, X, Y, Z).
"""
from __future__ import division, print_function
import os
import numpy as np
import vtk
from paraview.util.vtkAlgorithm import *
from vtkmodules.util import numpy_support as vtknp
def generate_seeded_4d_array(T, X, Y, Z, seed=0):
"""
Generate a 4D NumPy array of shape (T, X, Y, Z) with random values.
The 'seed' parameter ensures reproducible random numbers.
"""
np.random.seed(seed)
# Creates a 4D array with values in [0, 1)
data = np.random.rand(T, X, Y, Z)
return data
@smproxy.reader(name="Random4DNumPyReader",
label="Random 4D NumPy Reader",
extensions=["npy"],
file_description="4D NumPy array (T, X, Y, Z)")
class Random4DNumPyReader(VTKPythonAlgorithmBase):
def __init__(self):
# We have one output port (the 3D image) and no inputs.
VTKPythonAlgorithmBase.__init__(self,
nInputPorts=0,
nOutputPorts=1,
outputType="vtkImageData")
self.__FileName = None
self.__Data = None
self.__TimeSteps = None
#### FileName property ####
@smproperty.stringvector(name="FileName")
@smdomain.filelist()
def SetFileName(self, fname):
"""Called by ParaView when the user selects a file."""
if self.__FileName != fname:
self.__FileName = fname
self.Modified() # Mark the reader "modified" so it reloads
def RequestInformation(self, request, inInfo, outInfo):
"""
ParaView calls RequestInformation first to query the data extents,
time steps, etc.
"""
if not self.__FileName or not os.path.exists(self.__FileName):
return 1
executive = self.GetExecutive()
# Load the 4D array. Shape expected: (T, X, Y, Z)
data_array = np.load(self.__FileName)
self.__Data = data_array # Keep a reference for RequestData
# Suppose data_array.shape = (T, X, Y, Z)
shape = data_array.shape
if len(shape) != 4:
raise RuntimeError(
f"Expected a 4D array (T, X, Y, Z), got shape={shape}"
)
T, X, Y, Z = shape
# Define discrete time steps as integers 0..T-1 (or floats)
self.__TimeSteps = list(range(T))
# Tell ParaView about the available time steps
outInfo_0 = outInfo.GetInformationObject(0)
outInfo_0.Remove(executive.TIME_STEPS())
outInfo_0.Remove(executive.TIME_RANGE())
for t in self.__TimeSteps:
outInfo_0.Append(executive.TIME_STEPS(), t)
outInfo_0.Append(executive.TIME_RANGE(), self.__TimeSteps[0])
outInfo_0.Append(executive.TIME_RANGE(), self.__TimeSteps[-1])
extent = [0, X - 1, 0, Y - 1, 0, Z - 1]
outInfo_0.Set(
executive.WHOLE_EXTENT(),
extent,
6
)
return 1
def RequestData(self, request, inInfo, outInfo):
if self.__Data is None:
return 1
executive = self.GetExecutive()
time = request.Get(executive.UPDATE_TIME_STEP())
if self.__TimeSteps:
if time not in self.__TimeSteps:
idx = int(round(time))
else:
idx = int(time)
else:
idx = 0
vol_3d = self.__Data[idx, :, :, :]
output = vtk.vtkImageData.GetData(outInfo, 0)
X, Y, Z = vol_3d.shape
output.SetDimensions(X, Y, Z)
output.AllocateScalars(vtk.VTK_FLOAT, 1)
flat_data = vol_3d.astype(np.float32).ravel(order="C")
vtk_array = vtknp.numpy_to_vtk(flat_data, deep=True, array_type=vtk.VTK_FLOAT)
vtk_array.SetName("RandomData")
output.GetPointData().SetScalars(vtk_array)
output.GetInformation().Set(
vtk.vtkDataObject.DATA_TIME_STEP(), time
)
return 1