pyvista / pyvista-support

[moved] Please head over to the Discussions tab of the PyVista repository
https://github.com/pyvista/pyvista/discussions
60 stars 4 forks source link

export_vtkjs fails on a volume #240

Closed Clonkk closed 4 years ago

Clonkk commented 4 years ago

export_vtkjs fails on a volume and I don't understand why. If I show the volume, it display correctly.

Here is the code :

def display_3d(acq_3d_data, DX, DY, DT, C):
    DIMX = (DX)
    DIMY = (DY)
    DIMT = (DT * C)
    local_cmap = "viridis"
    local_cmap = "plasma"

    values = acq_3d_data
    values.shape

    # Create the spatial reference
    grid = pv.UniformGrid()

    # Set the grid dimensions: shape + 1 because we want to inject our values on
    #   the CELL data
    grid.dimensions = np.array(values.shape) + 1

    # Edit the spatial reference
    grid.origin = (0, 0, 0)  # The bottom left corner of the data set
    # These are the cell sizes along each axis
    grid.spacing = (DIMX, DIMY, DIMT)

    # Add the data values to the cell data
    grid.cell_arrays["values"] = values.flatten(
        order="F")  # Flatten the array!

    # Now plot the grid!
    # grid.plot(show_edges=True)

    p = pv.Plotter()

    vol = p.add_volume(
        grid,
        cmap=local_cmap,
        opacity="linear_r",
        blending="maximum",
        n_colors=256,
        show_scalar_bar=True,
    )

    p.set_background("lightgrey")
    p.show_axes()

    # Replace axis with miniature of the volume
    # p.add_orientation_widget(vol)
    DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
    print(DATA_DIR)
    vtkjs_file_path = os.path.join(DATA_DIR, "vtkjs_data_export")
    print(vtkjs_file_path)
    p.export_vtkjs(vtkjs_file_path)
    # p.show()

Here is the error (EDIT with full error message as requested) :

Traceback (most recent call last):
  File "./py_vista_render.py", line 129, in <module>
    p.export_vtkjs(vtkjs_file_path)
  File "/home/user/.local/lib/python3.8/site-packages/pyvista/plotting/plotting.py", line 3546, in export_vtkjs
    return export_plotter_vtkjs(self, filename, compress_arrays=compress_arrays)
  File "/home/user/.local/lib/python3.8/site-packages/pyvista/plotting/export_vtkjs.py", line 478, in export_plotter_vtkjs
    scalarVisibility = mapper.GetScalarVisibility()
AttributeError: 'MapperHelper' object has no attribute 'GetScalarVisibility'
akaszynski commented 4 years ago

Without seeing the full traceback, it's difficult to guess what the issue is, but I'm guessing that the mapper we're using isn't a genuine VTK mapper and doesn't have that property. Can you upload the mesh you're using along with the full code that generates this error?

Thanks!

Clonkk commented 4 years ago

Hi, thanks for your help. Here are the full error message :

Traceback (most recent call last):
  File "./py_vista_render.py", line 129, in <module>
    p.export_vtkjs(vtkjs_file_path)
  File "/home/user/.local/lib/python3.8/site-packages/pyvista/plotting/plotting.py", line 3546, in export_vtkjs
    return export_plotter_vtkjs(self, filename, compress_arrays=compress_arrays)
  File "/home/user/.local/lib/python3.8/site-packages/pyvista/plotting/export_vtkjs.py", line 478, in export_plotter_vtkjs
    scalarVisibility = mapper.GetScalarVisibility()
AttributeError: 'MapperHelper' object has no attribute 'GetScalarVisibility'

It happens no matter the data. I tried using np.random.rand(140, ,130, 480) and it happens as well.

For reference here is the full code that reproducde the error :


def display_3d(DX, DY, DT, C):
    DIMX = (DX)
    DIMY = (DY)
    DIMT = (DT * C)
    local_cmap = "plasma"
    local_cmap = "viridis"
    values = np.random.rand(140, 130, 480)
    # Create the spatial reference
    grid = pv.UniformGrid()
    # Set the grid dimensions: shape + 1 because we want to inject our values on
    #   the CELL data
    grid.dimensions = np.array(values.shape) + 1
    # Edit the spatial reference
    grid.origin = (0, 0, 0)  # The bottom left corner of the data set
    # These are the cell sizes along each axis
    grid.spacing = (DIMX, DIMY, DIMT)
    # Add the data values to the cell data
    grid.cell_arrays["values"] = values.flatten(
        order="F")  # Flatten the array!
    # Now plot the grid!
    # grid.plot(show_edges=True)
    p = pv.Plotter()
    vol = p.add_volume(
        grid,
        mapper='open_gl',
        cmap=local_cmap,
        opacity="linear_r",
        blending="maximum",
        n_colors=256,
        show_scalar_bar=True,
    )
    p.set_background("lightgrey")
    return p

DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
vtkjs_file_path = os.path.join(DATA_DIR, "vtkjs_data_export")

C = 1565.0
DX = 0.0001059999995050021
DY = 0.0001059999995050021
DT = 1.666666626931601e-08

p = display_3d(DX, DY, DT, C)
p.export_vtkjs(vtkjs_file_path)
p.show()

The error happens when I try to export a volume and not a mesh. If I do add_mesh the export works. What I want is to export a volume; From the documentation I've read it seemed possible.

I'm using Python 3.8 and PyVista 0.25.3 installed using pip.

akaszynski commented 4 years ago

Interesting. I'd try converting the uniform grid to an unstructured mesh (see cast_to_unstructured_grid), and then saving that.

Clonkk commented 4 years ago

I modified the code to use an unstructured grid as suggested :

ugrid = pv.Common.cast_to_unstructured_grid(grid)
# ...
p.add_volume(
    ugrid, 
    #... 
)

But it fails with an error saying volume are not supported with unstructured grid :

Traceback (most recent call last):
  File "./py_vista_render.py", line 124, in <module>
    p = display_3d(acq_3d_data, DX, DY, DT, C)
  File "./py_vista_render.py", line 51, in display_3d
    vol = p.add_volume(
  File "/home/user/.local/lib/python3.8/site-packages/pyvista/plotting/plotting.py", line 1915, in add_volume
    raise TypeError('Type {} not supported for volume rendering at this time. Use `pyvista.UniformGrid`.'.format(type(volume)))
TypeError: Type <class 'pyvista.core.pointset.UnstructuredGrid'> not supported for volume rendering at this time. Use `pyvista.UniformGrid`.

If that's not the intended behaviour maybe we should open an issue (or move this one to avoid duplication) on the PyVista repo ?

akaszynski commented 4 years ago

Looks like vtk.vtkImageData isn't supported within the legacy exporter. However, the latest VTK supports this with:

import vtk
vwrite = vtk.vtkJSONDataSetWriter()
vwrite.SetInputData(vol)
vwrite.SetFileName('stuff')  # directory
vwrite.Update()

I suggest you try updating to vtk>=9.0 and trying this out. You should be able to use a pyvista.UniformGrid as the input data.

Clonkk commented 4 years ago

Just to clairify, in SetInputData is it the result of p.add_volume(#...) or the uniform grid or the Plotter ?

I'll test this today or tomorrow and get back to you ! Thanks for the help

akaszynski commented 4 years ago

The input to SetInputData will be the grid you have it in your display_3d function.

Clonkk commented 4 years ago

Hello, Thanks for your help ! After looking through the VTK API, I ended using it directly for exporting datas from Numpy and I use PyVista only for the plotting part.

In case anyone has a similar need here is the code to generate a VTI File containing a Volume (with spacing) :

def toVtkImageData(a, DIMX, DIMY, DIMT):
    importer = vtk.vtkImageImport()
    #For some reason you need to reverse the shape
    importer.SetDataExtent(0,a.shape[2]-1,0,a.shape[1]-1,0,a.shape[0]-1)
    importer.SetWholeExtent(0,a.shape[2]-1,0,a.shape[1]-1,0,a.shape[0]-1)
    importer.SetDataSpacing(DIMT, DIMY, DIMX)

    #Set data pointer
    importer.SetImportVoidPointer(a)
    importer.Update()
    return importer.GetOutput()

def make_vti_from_numpy(data_matrix, DX, DY, DT, C):
    DIMX = (DX)
    DIMY = (DY)
    DIMT = (DT * C)
    # For VTK to be able to use the data, it must be stored as a VTK-image. This can be done by the vtkImageImport-class which
    # imports raw data and stores it.
    dataImporter = toVtkImageData(data_matrix, DIMX, DIMY, DIMT)

    filename = "data/vtkjs_data_export2.vti"
    writer = vtk.vtkXMLImageDataWriter()
    writer.SetFileName(filename)
    writer.SetDataModeToBinary()
    writer.SetInputData(dataImporter)
    writer.Write()