In this post I will show how to ‘convert’ NumPy arrays to VTK arrays and files by means of the vtk.util.numpy_support
module and the little-known PyEVTK package respectively.
Intro: The Conundrum
I wrote, or rather ranted, in my previous post about the value of VTK. Now lets say you were convinced (ha!) and decided to start including VTK in your scripts for visualization and processing.
Well, as if there weren’t enough deterrents in employing VTK, you will quickly realize that using your precious data – which let’s face it – will be stored in NumPy ndarray
objects, with VTK ain’t all that straightforward. And why would it be? VTK was made in C++ and C++ isn’t about ease-of-use and concise programing. C++ is about putting hair on your chest :).
The traditional/ugly way, is creating new VTK objects, setting a bunch of properties like dimensions etc, and looping over your NumPy data to copy and populate your new objects. Since, looping in Python must be avoided like the black plague I will be focusing on the two ways I prefer.
The first way is using the vtk.util.numpy_support
module that comes with VTK and allows you to ‘easily’ convert your data. The second way is by means of exporting your data into VTK-readable files using the PyEVTK package, a way which as you’ll see is great if you want to process and/or visualize that data in VTK-based applications.
Using the numpy_support
module
So, given the popularity of Python and the fact that VTK is exposed in its near entirety to Python, the VTK folk decided to create the numpy_support
module which resides under vtk.util
. Of course, given the near-absence of documentation and/or examples, using it is as convoluted as doing anything in VTK. However, I’m here to try and elucidate their usage.
Usage
The functions of interest to us are numpy_to_vtk
and vtk_to_numpy
. Let us first inspect the docstring of the first function which can be accessed as follows, assuming you have VTK installed in your Python distro:
from vtk.util import numpy_support
help(numpy_support.numpy_to_vtk)
with the result being
numpy_to_vtk(num_array, deep=0, array_type=None)
Converts a contiguous real numpy Array to a VTK array object.
This function only works for real arrays that are contiguous.
Complex arrays are NOT handled. It also works for multi-component
arrays. However, only 1, and 2 dimensional arrays are supported.
This function is very efficient, so large arrays should not be a
problem.
If the second argument is set to 1, the array is deep-copied from
from numpy. This is not as efficient as the default behavior
(shallow copy) and uses more memory but detaches the two arrays
such that the numpy array can be released.
WARNING: You must maintain a reference to the passed numpy array, if
the numpy data is gc'd and VTK will point to garbage which will in
the best case give you a segfault.
Parameters
----------
- num_array : a contiguous 1D or 2D, real numpy array.
Upon first inspection, one might think that 3D NumPy arrays weren’t possible to convert. At least that’s what I thought (yeah, yeah, I suck). However, all that one needs to do is create a 1D representation of the array using ‘numpy.ndarray’ methods such as flatten
or ravel
. So here’s how to use this function assuming you mean to export an numpy.ndarray
object named NumPy_data
of type float32
:
NumPy_data_shape = NumPy_data.shape
VTK_data = numpy_support.numpy_to_vtk(num_array=NumPy_data.ravel(), deep=True, array_type=vtk.VTK_FLOAT)
As you can see we use ravel
to flatten NumPy_data
using the default C, or row-major, ordering (especially important on 2D and 3D arrays). In addition, we specify that we do want the data to be deep-copied by setting deep=True
, while we also define the data type by array_type=vtk.VTK_FLOAT
. Note, that we keep a copy of the shape
of NumPy_data
which we will use later to reshape
the result of vtk_to_numpy
. Converting back is much easier and can be done as such:
NumPy_data = numpy_support.vtk_to_numpy(VTK_data)
NumPy_data = NumPy_data.reshape(NumPy_data_shape)
CAUTION: You may choose to allow for shallow-copies by setting
deep=False
but be warned: If for any reason, the array you pass is garbage-collected then the link will break and your nice VTK array will be useless. Should that occur, if you end up usingvtk_to_numpy
to back-convert, you will simply get memory trash. That is especially the case if you useflatten
instead ofravel
as the former always returns a ‘flattened’ copy of the array (check docs here) which is bound to get gc’ed if you use it directly in the call.ravel
is slightly safer as it typically returns a new ‘view’ to the array and only copies when it has to (check docs here) but you don’t want to depend on that. Bottom line: disable deep-copying at your own risk and only if you know what you’re doing.
Using the PyEVTK package
Some time ago, I was struggling with the numpy_support
module discussed above, mostly cause I sucked and didn’t think to ravel
the array, so I started googling and came across PyEVTK a great little package by a Paulo Herrera. While back then, the code relied on C routines which refused to work on Windows platforms, since v0.9 – v1.0.0 being the current version – the package is built on pure-Python code and works like a charm.
What PyEVTK
does, is allow you to save NumPy arrays straight to different types of VTK XML-based file formats (not the old legacy ones), without even needing to have VTK installed in your system, making NumPy the only dependency. This way, you can easily save your NumPy arrays into files that can be visualized and processed with any of the flagship VTK applications such as ParaView, VisIt, Mayavi, while obviously you can easily load said files with VTK and use all the goodies the toolkit offers.
However, while I’m supremely grateful to Mr. Herrera for creating PyEVTK, the package wasn’t hosted on PyPI and could only be used by checking out the code from the original repository in BitBucket, and using distutils
to build/install it via the good ol’ fashioned python setup.py install
.
Thus, possessed by the noble spirit of plagiarism, I took it upon myself to rip off, i.e., fork, the repository, re-package it, and upload it on PyPI. You can now find my fork on BitBucket here, while the package is hosted on PyPI under PyEVTK
here. Therefore, you can install with pip
with the following command:
pip install pyevtk
As you’ll see on PyPI, I claim no ownership, authorship, or entitlement of PyEVTK. Paulo Herrera wrote the thing and the only reason I ‘stole’ it was cause I use it a lot and wanted to make it widely available.
Usage
I’m gonna give a quick example here to show you how to save a NumPy array as a rectilinear-grid file with a .vtr
extension. In the interest of consistently misappropriating other people’s code, I modified the code for creating a Julia set from Ted Burke’s post here. I just thought I’d use a pretty dataset for my demo 🙂
Here, however, I’m only going to show the PyEVTK part of the code while you can see the full thing under this IPython Notebook. The result of the Julia code is the following pretty 2D array which was visualized with Plotly’s Heatmap
method (see this previous post on how to use Plotly):
So, here’s the PyEVTK part of the notebook:
from pyevtk.hl import gridToVTK
noSlices = 5
juliaStacked = numpy.dstack([julia]*noSlices)
x = numpy.arange(0, w+1)
y = numpy.arange(0, h+1)
z = numpy.arange(0, noSlices+1)
gridToVTK("./julia", x, y, z, cellData = {'julia': juliaStacked})
The first important part of this code is from pyevtk.hl import gridToVTK
which uses the high-level functions of the pyevtk
package and which reside under the pyevtk.hl
module. For the most-part, this module has all you need but the package also provides a pyevtk.vtk
module for low-level calls. As we want to export data residing on a rectilinear grid we use the gridToVTK
method.
The next 5 lines aren’t important but what we do is use the numpy.dstack
function to stack 5 copies of the 2D julia
array and create a 3D array to export. Note the numpy.arange
calls through which we calculate ‘fake’ axes for the array to be exported. An important point to make is that since we’re defining a grid we need axes with N+1 coordinates which define the grid edges (as the data resides in the centers of that grid). If you want to export point-data, use the pyevtk.hl.pointsToVTK
function.
As you guessed the magic happens with this one line:
gridToVTK("./julia", x, y, z, cellData = {'julia': juliaStacked})
As you can see, we pass the path to the file without an extension which will be automatically defined by PyEVTK depending on which function you used in order to create the appropriate VTK file. Subsequently, we pass the three coordinate axes, and provide a Python dictionary with a name for the array and the array itself. Doesn’t get easier than that :). The result of the above is .vtr
file which you can find here while the IPython Notebook of the entire code can be found here.
Resources
A quick note here: while Paulo’s repo was named PyEVTK, he had originally named the package evtk
but in the interest of consistency I renamed it to pyevtk
so his examples won’t work directly if you installed PyEVTK through pip
(see above section). If you want to see more examples of its usage, I refactored them a tad and can be seen in my repo here.
Well that’s it for today. Thanks for reading once more and I hope you gained something from this. Happy Python-ing 😀
Pingback: Surface Extraction: Creating a mesh from pixel-data using Python and VTK | PyScience
Pingback: Image Segmentation with Python and SimpleITK | PyScience
Pingback: Multi-Modal Image Segmentation with Python & SimpleITK | PyScience
Hi,
Many thanks for that. I needed exactly what you want to do (tridimensional NumPy array -> VTK rectilinear grid, with the goal of doing volume rendering using ParaView, or other tools), and I managed to create the file and load it into ParaView, but the moment I select a real rendering method (other than Outline) paraview hangs with the following error printing many times :
ERROR: In /build/paraview-B2WpY_/paraview-4.1.0+dfsg+1/VTK/Rendering/OpenGL/vtkOpenGLPainterDeviceAdapter.cxx, line 340
vtkOpenGLPainterDeviceAdapter (0x1d98360): Unsupported type for vertices: 8
The same problem happens with the julia.vtr file you provide. But not with other files coming from other sources. I don’t know if it is a bug in pyevtk, ParaView or a bit of both, but it looks like a problem
LikeLike
Hey Alex, that is indeed weird. What type of data are we talking about? Some of the volume-rendering algorithms in VTK only work with given data types. If you check my volume-rendering post (https://pyscience.wordpress.com/2014/11/16/volume-rendering-with-python-and-vtk/) you’ll see that classes like vtkVolumeRayCastMapper only work with unsigned char and unsigned short. You wouldn’t happen to have floats/signed ints or something would you 🙂
LikeLiked by 1 person
I am having this same issue using the same script you provided.
LikeLike
I changed the following lines for the x y and z to specify the datatype and it works.
x = numpy.arange(0, w+1, dtype=numpy.int32)
y = numpy.arange(0, h+1, dtype=numpy.int32)
z = numpy.arange(0, noSlices+1, dtype=numpy.int32)
LikeLike
OK — except you have not defined w or h.
LikeLike
Hi!
Thank you so much for this post. I only have one question: the ‘numpy_support.numpy_to_vtk’ function returns a vtkDataArray, but I need to convert it to vtkImageData. How can I do this?
Best regards,
Darya
LikeLike
Hehe I was wondering when would someone call me on this one. Great question Darya and its indeed not an intuitive process. I’ve been meaning to write a full post of SimpleITKNumPyVTK conversions but I haven’t the time to touch my blog in months. I’m at work right now so I can’t give you the code but I’ll do it when I get off
LikeLike
That would be an amazing post, since I couldn’t find much information on this topic 🙂 I guess I’ll play around with vtkImageImport and vtkImageData. Thank you again and good luck at your work!
LikeLike
You won’t find squat I’m afraid. The secret is in creating a new vtkImageData object, setting the size and spacing based on the NumPy array, and then using the setScalars method of the vtkImageData object to feed it the vtkDataArray you got from the initial conversion.
You can prob figure it out but I’ll write a snippet when I get the chance
LikeLike
Btw be extra careful with the setScalarType or whatever it’s called. You need to use the proper data type or VTK will crash wo explanation due to memory overflows
LikeLike
Hey Darya,
At the link below you can find an IPython Notebook with the process to convert a dataset from SimpleITK to NumPy and then VTK (the process we described). Its the one I started preparing for that post I mentioned but its rather half-assed as I never got the time to complete it but it has the process you need.
https://dl.dropboxusercontent.com/u/3091507/ConvertSimpleITKtoVTK.ipynb
Take a looksie and lemme know if you run into trouble 🙂
Cheers,
Adam
LikeLike
Hi Adam!
For some reason, I can’t access the link you sent… For now I tried to write my code avoiding image conversions and I have my VTK pipeline. It isn’t working, because it runs out of memory (Windows 10 – 8 GB). I thought I was linking the filters properly by means of SetInputConnection(…GetOutputPort())…
So, I tried it on Linux and it is working (adding some corrections to ScalarTypes), but still nothing on Windows. Does it happen often?
Thank you!
Best regards,
Darya
LikeLike
Ok that’s weird. Maybe its cause Dropbox gets mixed up with the format. I’ve zipped and uploaded the file on both my DropBox and GoogleDrive. One of the following links should work:
https://dl.dropboxusercontent.com/u/3091507/ConvertSimpleITKtoVTK.ipynb.zip
https://drive.google.com/file/d/0B_IXVGLixuWMVkw4c1JsOGhqWnc/view?usp=sharing
I found that the Windows/OSX VTK is a little more temperamental than the Linux version where development and testing happens. If you’re running out of memory there’s not much you can do. You can verify that, however, by testing it on a smaller portion of your data.
Anyway, I hope that one of the above links will do the trick.
Lemme know how you go
Cheers,
Adam
LikeLike
Thank you so much, your script was very helpful!
I successfully converted my VTK image data no numpy array and back to VTK image data!
I only have one issue: the final image data is not of the same ScalarType as the original one (instead of short I get float). I guess the problem is with the functions SetScalarType() and SetNumberOfScalarComponents(). Both of them require 2 arguments: an ‘int’ and a ‘vtkInformation’ object. Probably I messed up with that, but it works 🙂
LikeLike
I’m glad it worked :). The ‘SetScalarType’ is the obvious suspect (the ‘SetNumberOfScalarComponents’ should only affect the ‘depth’ and not the type). If you can’t figure out what’s off just cast it down to an integer/short with the ‘vtkImageCast’ class (e.g., http://www.vtk.org/Wiki/VTK/Examples/Cxx/Images/Cast).
LikeLike
Hi!
Thank you so much for all of your blogs, it’s made learning VTK so much easier.
I am also looking to use a numpy array in the VTK pipeline – it is a DICOM file that I read using pydicom but I want to volume render it using VTK (I don’t use the DICOM reader from VTK as it complains my file does not contain any DICOM images for some reason).
Could you please share the code that you shared with Darya once again? The Dropbox files seem to be deleted.
A
LikeLike
Hey Anna you can find the code under https://bitbucket.org/somada141/pyscience/src/master/20150215_ConvertSimpleITKtoVTK/_Material/ though I haven’t used it in ages. As you can tell this blog is quite dead but hopefully the existing material can help you 🥰
LikeLike
Hello!
First of all amazing post ! I am also interested in what Darya wants to achieve . Specifically I want to feed a 3d numpy array (which is a grid of of isovalues basicly ) to vtkMarchingCubes , and I noticed that the input to the latter has to be of type vtkImageData . Since you already solved this I would be interested in looking at that script ! Have a good one!
LikeLike
Hey there, I’m not sure if you’re referring to my script or Darya’s. Mine is linked in the above replies and can be easily grabbed 🙂
LikeLiked by 1 person
Hello again , I was referring to Darya’s code . I already read the notebook you provided , and had a small issue but solved my problem already ! Thanks again!
Keep up the good work file mou!
Darya if you are reading this I would still be interested to read your code !
LikeLike
Hi Michael!
Sorry for not answering in time to solve your problem! I’m glad that everything worked out. In my code I use something like this (just as Adamos explained):
…
CT_Data = reader.GetOutput()
… convert to numpy array and do some stuff …
NP_data = numpy_support.numpy_to_vtk(np_image_data.ravel(), deep=True, array_type=vtk.VTK_SHORT)
imageVTK = vtk.vtkImageData()
imageVTK.SetSpacing(CT_Data.GetSpacing())
imageVTK.SetOrigin(CT_Data.GetOrigin())
imageVTK.SetDimensions(CT_Data.GetDimensions())
imageVTK.GetPointData().SetScalars(NP_data)
And then I just feed the imageVTK to the rest of the pipeline (by the way, it is also vtkDiscreteMarchingCubes). I hope this helps!
Thank you all!
LikeLiked by 1 person
Hi:
Thanks for the great post and follow up comments. I am having an issue with the mesh produced by vtk – it appears ‘skewed’.
I have uploaded a zip that contains an ipython notebook, input file, and output files:
https://www.dropbox.com/s/0jy3e5idk61tr49/vtk_mesh.zip?dl=0
For comparison, I generated a mesh using the scikit-image marching cubes, and the marching cubes in PyMCubes. BTW, these two produce very similar results, which are worse than the mesh output from ITK-Snap, which I assume uses the VTK marching cubes algorithm.
My input volume is a pre-segmented and thresholded array, stored in a .nii (nifti) file format. It contains an artery, segmented from a contrast enhanced CT scan of a head. There is a large aneurysm visible in this vascular tree.
I load this volume, which has voxel values of either 0 or 1, then apply the various marching cubes algorithms to it. The results are saved as a .obj file (PyMCubes) or as a .stl file (VTK).
Both output files were loaded into MeshLab for visualization (and also visualized using mlab in the notebook). There is a screen capture showing the MeshLab renderings of these meshes – both the correct PyMCubes output (right panel) and the skewed VTK output (left panel).
I assume I am doing something wrong in in this block of code (from my notebook):
—-
# voxel_array is a numpy array obtained from the .nii file. The array has type np.int16
# and dimensions of (256, 256, 255)
#
vtk_data = numpy_support.numpy_to_vtk(num_array=voxel_array.ravel(), deep=True, array_type=vtk.VTK_SHORT)
# nii_spacing = (0.33868799, 0.33868799, 0.33868408)
# nii_shape = (256, 256, 255)
imageVTK = vtk.vtkImageData()
imageVTK.SetSpacing(nii_spacing)
imageVTK.SetOrigin([0.0, 0.0, 0.0])
imageVTK.SetDimensions(nii_shape)
imageVTK.GetPointData().SetScalars(vtk_data)
dmc = vtk.vtkDiscreteMarchingCubes()
dmc.SetInputData(imageVTK)
dmc.GenerateValues(1, 1, 1)
dmc.Update()
—-
Any insight appreciated!
Sincerely,
Ross Mitchell
LikeLike
Hey Ross! I’m currently at work but I promise I’ll take a look as soon as I can 🙂
LikeLike
Solved, you didn’t set the Origin correctly mate. The original origin of the image is (0.0, 0.0, -93.79782104492188) and you set it to all 0s. Hence the squashing. I’ve updated your notebook and re-run the process to get an updated STL. You can find the thing under https://db.tt/WnBhtjQg. The new STL is potatoes.stl (don’t ask why). Lemme know if you’re having any more trouble 🙂
LikeLike
Awesome! Thanks!
– Ross
LikeLiked by 1 person
Really cool tutorial. Didn’t manage to get it working for .vtp (vector_scatter, or quiver – just plots one component as the arrow length…), but very useful otherwise!
Thanks for your time.
LikeLike
Hey, nice tutorial 🙂 But I have some problems. For my project i need the coordinate combinations for the ranges x:10-310, y:10-310, z: 0-65. I thought to use numpy.mgrid for this: coords = np.mgrid[xmin:xmax, ymin:ymax, zmin:zmax] and convert it to vtk_data_array like : vtk_data_array = numpy_support.numpy_to_vtk(num_array=coords.ravel(),deep=True,array_type=vtk.VTK_FLOAT). And after this to points: points = vtk.vtkPoints()
points.SetData(vtk_data_array). But this just crashes my python. Do you have an idea how to manage this?
LikeLike
Hi Varlor. Off the top of my head that sounds like you haven’t initialised the object properly. I can’t recall exactly but you might need to set the dimension, data-type, and allocate the point arrays prior to plonking an array in the `vtkPoints` object. Try looping over and inserting the points through the `InsertNextPoint` method. Also perhaps `vtkPoints` isn’t the right class for it and you might want to consider using the `vtkStructuredGrid` class instead.
LikeLike
Hello! I’m really glad you created such a tutorial! I’d be extremely grateful if you could help me with one issue that I cannot overcome. To cut a long story short, I’ve got a 3D numpy array filled after some segmentation procedures (e.g. value 255 is for the artery, whereas 0 is just the empty space) and I’d like to obtain a model of that vessel. Basing on your tutorial I managed to create a nice STL file, but I’d like to have a volumetric object (I guess it’s the .vtp or .vtk file) that can be used in VMTK program. I haven’t got the foggiest idea how to do that… Let’s assume STL creation firstly:
stack=numpy.zeros((200,200,400),dtype=numpy.uint8)
stack[75:150,75:150,150:300]=255
dataImporter = vtk.vtkImageImport()
data_string = stack.tostring()
dataImporter.CopyImportVoidPointer(data_string, len(data_string))
dataImporter.SetDataScalarTypeToUnsignedChar()
dataImporter.SetNumberOfScalarComponents(1)
w, d, h = stack.shape
dataImporter.SetDataExtent(0, h-1,0, d-1, 0, w-1)
dataImporter.SetWholeExtent(0, h-1,0, d-1, 0, w-1)
dataImporter.SetDataSpacing(1,1,1)
threshold = vtk.vtkImageThreshold ()
threshold.SetInputConnection(dataImporter.GetOutputPort())
threshold.ThresholdByLower(254)
threshold.ReplaceInOn()
threshold.SetInValue(0)
threshold.ReplaceOutOn()
threshold.SetOutValue(1)
threshold.Update()
STL_Frame.dmc = vtk.vtkDiscreteMarchingCubes()
STL_Frame.dmc.SetInputConnection(threshold.GetOutputPort())
STL_Frame.dmc.GenerateValues(1, 1, 1)
STL_Frame.dmc.Update()
So that works perfectly and I can observe the surface of the artery. However, when I try to leave all ‘voxels’ as some objects and obtain a volumetric model composed of voxel-blocks (and save it as .vtk or .vtp file), I don’t have a clue what to do next…
stack=numpy.zeros((200,200,400),dtype=numpy.uint8)
stack[75:150,75:150,150:300]=255
from vtk.util import numpy_support
NumPy_data_shape = stack.shape
VTK_data = numpy_support.numpy_to_vtk(num_array=stack.ravel(), deep=True, array_type=vtk.VTK_INT)
imageVTK = vtk.vtkImageData()
imageVTK.SetSpacing((1,1,1))
imageVTK.SetOrigin([0.0, 0.0, 0.0])
imageVTK.SetDimensions((200,200,400))
imageVTK.GetPointData().SetScalars(VTK_data)
Considering the pyEVTK module, I managed to export a .vtr file, but I didn’t manage to export .vtk file (don’t know how to do it – gridToVTK saves as .vtr, while pointsToVTK doesn’t work). Additionally, I guess this module saves all 0-elements also as some ‘objects’.
I would be extremely grateful if you could help me in creating .VTP or .VTK from 3D numpy array – it’s a huge must-be for my work. Thank you in advance!
LikeLike
Oh man I’m so sorry I was on vaca when it came through and must’ve missed it at the time. Have you managed to solve it?
Btw STLs are surface-deep but what you’re talking for is a volumetric mesh that can either consist of voxels or tetrahedra. I was under the impression that VMTK works with surface models (I mean why not, its just a vessel) but I’ll have to do some more looking into it.
lemme know if you still need help
LikeLike
Hello!
Unfortunately I didn’t manage to solve that problem. Indeed, VMTK works with surface models (even after loading the vtp file – so PolyData, e.g. voxels – it applies marching cubes algorithm producing a surface). Hence, I guess I can operate on the STL files that I’m able to generate thanks to your tutorial. However, after applying all the code lines provided in VMTK tutorial (to tetrahedralize the mesh), I obtain only the surface mesh again, contrary to the tutorial guys. I haven’t got the foggiest idea whether VMTK somehow ‘remembers’ that it operated on the PolyData, and automatically seeks for the creation of the 3D volume mesh through Tetgen – initial file of every single tutorial is .vtp (volumetric) not .stl (surface). I’ve asked how to obtain a volume mesh from the pure STL file in the vmtk-users group, however, my question remains unanswered and unsolved. That’s why I wanted to test everything using .vtp files.
P.S. I think I’ll remind of myself in that group 🙂 Hopefully someone will provide me just a one line of code I was lacking.
Thanks for your time!
LikeLike
Unfortunately my experience with VMTK is extremely old and limited but out of curiosity, is the vessel surface model closed or do you actually have open surfaces at the vessel interfaces? I’m not sure how VMTK meshes surfaces but I’d expect the code to operate on closed surfaces
LikeLike
In terms of loading the STL it has to be a waterproof surface (although I’m not 100% sure if it really has to be closed). Then you have to perform clipping to open inlet/outlets surfaces – there is a user interface and you perform clipping manually (as perdpendicularly to the channel as possible). The next algorithm that is triggered automatically closes all the previously cut surfaces and as a result there is a smooth plane for the inlet/outlets with displayed IDs. You can also approximate inlet/outlets with a circular shape and protrude it, so as to obtain pure circle cross-section for inlet/outlets. Afterwards you call a meshgenerator function that should create a volumetric mesh and then you can tetrahedralize it.
LikeLike
Oh yeah I remember the interface bit. In any case you seem to be entirely on top of the process so I’m not sure I can help much 🙂
LikeLike
Hello!
I’ve got a problem with displaying the VTR files. I convert my DICOM (numpy arrays) into the VTR file and then I try to display it, I can see a rectangular cuboid of constant colour, instead of the most outer layers of the image set (e.g. part of the aorta, some bones, soft tissue, etc). Here are my scripts (maybe I’m doing something wrong):
from pyevtk.hl import gridToVTK
import SimpleITK
import numpy
#Loading dataset
path=’F:\\Users\\Zibi\\Desktop\\aorta\\’
reader = SimpleITK.ImageSeriesReader()
filenamesDICOM = reader.GetGDCMSeriesFileNames(path)
reader.SetFileNames(filenamesDICOM)
ArrayDicom = reader.Execute()
n_cols,n_rows,height=ArrayDicom.GetSize()
#Creating 3D numpy array
imgOriginal=ArrayDicom[:,:,0]
stack_array = SimpleITK.GetArrayFromImage(imgOriginal)
for i in range(1,height):
nda2 = SimpleITK.GetArrayFromImage(ArrayDicom[:,:,i])
stack_array = numpy.dstack((stack_array,nda2))
new_Dicom_Array=stack_array
#Doing VTR file
x = numpy.arange(0, n_rows+1,dtype=numpy.int32)
y = numpy.arange(0, n_cols+1,dtype=numpy.int32)
z = numpy.arange(0, height+1,dtype=numpy.int32)
gridToVTK(“./aorta_image”,x,y,z, cellData = {“aorta” : new_Dicom_Array})
#Load VTR file
filename = “aorta_image.vtr”
reader = vtk.vtkXMLRectilinearGridReader()
reader.SetFileName(filename)
reader.Update()
mapper = vtk.vtkDataSetMapper()
mapper.SetInputConnection(reader.GetOutputPort())
actor = vtk.vtkActor()
actor.SetMapper(mapper)
actor.GetProperty().SetPointSize(3)
# Setup rendering
renderer = vtk.vtkRenderer()
renderer.AddActor(actor)
renderer.SetBackground(1,1,1)
renderer.ResetCamera()
renderWindow = vtk.vtkRenderWindow()
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtk.vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.Initialize()
renderWindowInteractor.Start()
To be honest, it was just a test of conversion function (from numpy to VTR) and I guess it failed. What’s the most probable reason behind that? My target objective is to create a VTK rendering window where I could see three 2D projections that cross one another (something like 3 separate matplotlib imshows, each one in each anatomical plane). I’d be extremely grateful if you could give me a little tip on how to do that since even after looking in the internet, I haven’t got the foggiest idea how to do that… I was thinking about generating VTI file instead of VTR, but it failed as well – I could have observed a red rectangular cuboid instead of normal Hounsfield-voxels…
LikeLike
Hey, a quick skim over the code didn’t show anything wrong with the conversion but I may have missed something. Did you try opening the VTR file in Paraview? If it doesn’t work there then at least you know the issue is indeed in the conversion
LikeLike
Hello,
Thank you for your fast response. After reinstalling the ParaView, both VTR and VTI seem to be correct. When I select ‘aorta’ instead of the Solid Color, the displayed image was correct (otherwise I could have observed only a grey rectangular cuboid). However, when I load those files and try to display them in VTK rendering window (embedded in my application), I can observe only the grey surface of the rectangular cuboid. Do you know by any chance how to make vtk rendering window display true voxel intensities?
LikeLike
At least you now know that the conversion worked. However, I see you’re shoving the entire 3D image into the renderer with a whatever actor and I don’t think that would work.
Have a look at https://www.vtk.org/Wiki/VTK/Examples/Cxx/IO/ReadDICOMSeries . I think you need to use a `vtkImageViewer2` as used there.
LikeLike
Pingback: vtkファイルの出力(python) | IT技術情報局
Thank you very much for the much appreciated knowledge you are showing!
To be honest I am in a bit of a pickle and my problem is as follows
I have a set of multiples 4 channel slices(images) that I want to stack into a 3d numpy array and convert it to a vtk object and finally render it to vrml file or any suitable format in order to show the data in 3d and manipulate it. To do that I wrote this small script and didn’t know how to go further:
import glob
import numpy as np
from scipy.misc import imread
import imageio
import vtk
from vtk.util import numpy_support
pngfiles = glob.glob(‘*.png’)
pngfiles.sort()
print(“Found %i PNG images” % len(pngfiles))
stack = []
for file in pngfiles:
stack.append(imageio.imread(file, pilmode=’RGBA’))
stack = np.stack(stack, axis=2)
print(“Generated 3D array with shape %s”%str(stack.shape))
data_3D= numpy_support.numpy_to_vtk(num_array=np.array(stack).ravel(), deep=True, array_type=vtk.VTK_FLOAT)
Thank you very much in advance!
LikeLike
Hi Ali, did you check my posts after this? At a glance your code seems correct so once you have the VTK object you can manipulate or render it. Check my post on volume-rendering for the latter.
LikeLike
Hi Somada, thank you very much for your response!
I didn’t check the post you mentioned yet. I will check it and see if it works
LikeLike
After consulting your post on volume rendering with vtk I was able to generate a volume based on my data, which is a very good step. As I mentioned before the matrix I get from stacking all pics is a 4D one, the 4th dimension is RGBA. The volume rendering you posted uses colorfiles, which I don’t have, so I have been wondering, how add RGBA data to all the points in my domain using the matrix I created earlier. Otherwise I want to change the distance between the images in Z direction(height). It should work using volumeMapper.SetMaximumImageSampleDistance(0.01) but nothing is happening. Code is as follows :
import os,sys
import glob
import numpy as np
from scipy.misc import imread
import scipy.misc as sm
import imageio
import vtk
from numpy import *
pngfiles = glob.glob(‘./Images/*.png’)
pngfiles.sort()
print(“Found %i PNG images” % len(pngfiles))
stack = []
for file in pngfiles:
stack.append(imageio.imread(file, pilmode=’RGBA’))
stack1 = np.stack(stack, axis=2)
print(“Generated 3D array with shape %s”%str(stack1.shape))
dataImporter = vtk.vtkImageImport()
data_string = stack1[:,:,:,0].tostring()
dataImporter.CopyImportVoidPointer(data_string, len(data_string))
dataImporter.SetDataScalarTypeToUnsignedChar()
dataImporter.SetDataExtent(0, 849, 0, 1299, 0, 159)
dataImporter.SetWholeExtent(0, 849, 0, 1299, 0, 159)
alphaChannelFunc = vtk.vtkPiecewiseFunction()
alphaChannelFunc.AddPoint(50, 0.1)
colorFunc = vtk.vtkColorTransferFunction()
colorFunc.AddRGBPoint(50, 1.0, 0.0, 0.0)
volumeProperty = vtk.vtkVolumeProperty()
volumeProperty.SetColor(colorFunc)
volumeProperty.SetScalarOpacity(alphaChannelFunc)
volumeProperty.ShadeOn()
compositeFunction = vtk.vtkVolumeRayCastCompositeFunction()
volumeMapper = vtk.vtkVolumeRayCastMapper()
volumeMapper.SetMaximumImageSampleDistance(0.01)
volumeMapper.SetVolumeRayCastFunction(compositeFunction)
volumeMapper.SetInputConnection(dataImporter.GetOutputPort())
volume = vtk.vtkVolume()
volume.SetMapper(volumeMapper)
volume.SetProperty(volumeProperty)
renderer = vtk.vtkRenderer()
renderWin = vtk.vtkRenderWindow()
renderWin.AddRenderer(renderer)
renderInteractor = vtk.vtkRenderWindowInteractor()
renderInteractor.SetRenderWindow(renderWin)
renderer.AddVolume(volume)
renderer.SetBackground(1, 1, 1)
renderWin.SetSize(1000, 1000)
def exitCheck(obj, event):
if obj.GetEventPending() != 0:
obj.SetAbortRender(1)
renderWin.AddObserver(“AbortCheckEvent”, exitCheck)
renderInteractor.Initialize()
renderWin.Render()
renderInteractor.Start()
Thank you very much!
LikeLike
Hi Ali, I see you’re using transfer functions above to set the color but I don’t think volume rendering can take a given color per point. I had a quick look online and it looks like other people had the same issue as you but I could see no solution to that http://vtk.1045678.n5.nabble.com/Volume-rendering-using-predefined-RGBA-data-td1237722.html
LikeLike
An arguably easier way to VTK I/O in is meshio [1] (a project of mine). You can use it to read and write many different mesh formats, e.g., VTK and VTU. It’s also pretty light on the dependencies so you don’t have to worry about libvtk.
[1] https://github.com/nschloe/meshio
LikeLike
Thanks a lot for the info Nico! It seems fantastic and I’d be more than happy to help promote your effort here.
LikeLike
Hi;
Thank you a lot for this great work.
I have a question please.
I have a data file for magnetic spins from OOMMF as numpy.ndarray. And I want to represent the spins in 3D view by using Paraview. The problem that the shape of the array is ( 50, 70, 6, 3).
I tryed to use your code, but I get the followin message :
AssertionError: Bad array shape: (50, 14, 30, 3).
any suggestion?
Thank you 🙂
LikeLike
Hi there, its hard to know what’s going wrong without your code. Got a short snippet you can share?
LikeLike
While converting 3D array using numpy_to_vtk and keeping deep = True, why do you say it works for float32? Can’t a double precision array be converted as well?
LikeLike
It absolutely should work with double precision as well. I’m sorry if it came across confusing.
LikeLike
Hello:
Thanks for writing an excellent article. I appreciate your effort.
I have 2D simulations data in a rectilinear grid. I was trying to convert the data into VTR format. But got some error. I dug into the errors and found that the gridToVTK is only able to save 1D and 3D data points. I later defined z-axis coordinate as 0, still no luck. I was wondering if you were able to convert 2D data into VTR format. If so, could you please tell me the solutions?
LikeLike
Hi Sabber, thanks for the kind words! I haven’t tried exporting 2D data to VTR but I’d expect that you can have a singleton Z axis with a single coordinate (https://github.com/pyscience-projects/pyevtk/blob/master/examples/rectilinear.py). Have you tried that?
LikeLike
Hi,
Thanks for your efforts on this…it looks really useful.
I’m trying to install this pyevtk. Both Pycharm and pip seem to think the have done the job, however, trying to import produces the following from a bare bash terminal running python3:
No module named ‘evtk’
In pycharm, it hangs.
It does that with python3.6 and python3.7
Any tips or useful suggestions appreciated.
LikeLike
Hey Matt! Can you open an issue under the Github repo (https://github.com/pyscience-projects/pyevtk)? I haven’t been dealing with this project in ages but the maintainers should be able to help you 😌.
LikeLike
Thank you for your awesome report !!
Now I try to make real-time volume rendering for 4D data
I developed my interactor using numpy_to_vtk
It is work but the rendering change depends on time is not smooth
Thus, who is thinking about if I change numpy_to_vtk process to pyevtk ?
Is it going to be a solution to solve the time-consuming problem?
Any tips or useful suggestions appreciated.
LikeLike
Hmmm both `numpy_to_vtk` and `pyevtk` use C++ under the hood so I’m not sure you’ll see much of a speed increase by porting. 4D MRI is nasty business given how much data you’ll have. I’d suggest trying Python3 and the latest VTK cause the ones I show in these posts are old and I believe the latest versions have improved these conversions.
LikeLike