I need to visualize 2000 images each 2560x1440 and 8 bit color using Python3. The images are stored in a properietary 3d-printing format (RLE encoded). I have code to extract each image as an numpy array. Using vtk.vtkImageImport().CopyImportVoidPointer I could read this into vtk. Problem is that reading all images in one numpy-array causes memory problems (6GB of data).
I see a few possible solutions:
- use numpy.memmap but this is slow on linux, I could investigate this further;
- save all images to disk and use a vtk file reader, but saving to disk is slow, so also not optimal;
- write my own vtk file reader which directly reads the 3d-printing file, however I have found a way to do this in python.
- read each slice into a vtk.vtkVolume() and add it to the renderer using vtk.vtkRenderer(). I encountered the problem that the Z-order is not adjusted when rotating the scene.
Am I missing another way to handle this much data? If not which solution is the best to investigate further?
Below the code which shows my Z-order problem in solution 4:
import numpy as np import vtk from tempfile import mkdtemp import os.path as path import psutil installpath='' def init(): import sys,os global installpath if getattr(sys, 'frozen', False):# frozen installpath = os.path.dirname(sys.executable) else: # unfrozen installpath = os.path.dirname(os.path.realpath(__file__)) print ("Installed at: ",installpath) def addCube(offset,colorIndex): dw,dh,dd=offset w,h,d=75,75,75 data_matrix=np.zeros((d,h,w),dtype='uint8') data_matrix[0+dd:35+dd,0+dh:35+dh,0+dw:35+dw] = colorIndex # 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 = vtk.vtkImageImport() # The previously created array is converted to a string of chars and imported. data_string = data_matrix.tostring() del (data_matrix) dataImporter.CopyImportVoidPointer(data_string, len(data_string)) del (data_string) # The type of the newly imported data is set to unsigned char (uint8) dataImporter.SetDataScalarTypeToUnsignedChar() # Because the data that is imported only contains an intensity value # (it isnt RGB-coded or someting similar), the importer must be told this is the case. dataImporter.SetNumberOfScalarComponents(1) # The following two functions describe how the data is stored and the dimensions of the array it is stored in. # For this simple case, all axes are of length 75 and begins with the first element. # For other data, this is probably not the case. # I have to admit however, that I honestly dont know the difference between SetDataExtent() # and SetWholeExtent() although VTK complains if not both are used. dataImporter.SetDataExtent(0, w-1, 0, h-1, 0, d-1) dataImporter.SetWholeExtent(0, w-1, 0, h-1, 0, d-1) # The following class is used to store transparency-values for later retrival. # In our case, we want the value 0 to be # completely opaque whereas the three different cubes are given different transparency-values to show how it works. alphaChannelFunc = vtk.vtkPiecewiseFunction() alphaChannelFunc.AddPoint(0, 0.0) alphaChannelFunc.AddPoint(colorIndex, 1.0)#0.05) # This class stores color data and can create color tables from a few color points. # For this demo, we want the three cubes to be of the colors red green and blue. colorFunc = vtk.vtkColorTransferFunction() #colorFunc.AddRGBPoint(0, 0.0, 0.0, 0.0) colorFunc.AddRGBPoint(1, 1.0, 0.0, 0.0) colorFunc.AddRGBPoint(2, 0.0, 1.0, 0.0) colorFunc.AddRGBPoint(3, 0.0, 0.0, 1.0) colorFunc.AddRGBPoint(4, 1.0, 1.0, 0.0) colorFunc.AddRGBPoint(5, 1.0, 0.0, 1.0) colorFunc.AddRGBPoint(6, 0.0, 1.0, 1.0) colorFunc.AddRGBPoint(7, 1.0, 1.0, 1.0) # The previous two classes stored properties. # Because we want to apply these properties to the volume we want to render, # we have to store them in a class that stores volume properties. volumeProperty = vtk.vtkVolumeProperty() volumeProperty.SetColor(colorFunc) volumeProperty.SetScalarOpacity(alphaChannelFunc) volumeProperty.ShadeOn() volumeMapper = vtk.vtkFixedPointVolumeRayCastMapper() volumeMapper.SetInputConnection(dataImporter.GetOutputPort()) # The class vtkVolume is used to pair the previously declared volume as well as the properties # to be used when rendering that volume. volume = vtk.vtkVolume() volume.SetMapper(volumeMapper) volume.SetProperty(volumeProperty) return volume def main(): global installpath colors = vtk.vtkNamedColors() # We begin by creating the data we want to render. volume1=addCube((0,0,0),4) volume2=addCube((25,25,25),2) # With almost everything else ready, its time to initialize the renderer and window, as well as # creating a method for exiting the application renderer = vtk.vtkRenderer() renderWin = vtk.vtkRenderWindow() renderWin.AddRenderer(renderer) renderInteractor = vtk.vtkRenderWindowInteractor() renderInteractor.SetRenderWindow(renderWin) # We add the volume to the renderer ... renderer.AddVolume(volume1) renderer.AddVolume(volume2) renderer.SetBackground(colors.GetColor3d("MistyRose")) # ... and set window size. renderWin.SetSize(400, 400) # A simple function to be called when the user decides to quit the application. def exitCheck(obj, event): if obj.GetEventPending() != 0: obj.SetAbortRender(1) # Tell the application to use the function as an exit check. renderWin.AddObserver("AbortCheckEvent", exitCheck) renderInteractor.Initialize() # Because nothing will be rendered without any input, we order the first render manually # before control is handed over to the main-loop. renderWin.Render() renderInteractor.Start() if __name__ == '__main__': init() main()