[vtkusers] vtkImageImportFromArray.py problem on Debian Linux

Tony Willis Tony.Willis at nrc-cnrc.gc.ca
Thu Apr 14 12:32:47 EDT 2005


Hi All

A bit of background - I want to use VTK to visualize python 3D numarrays.
The contents of these arrays can change as a function of time, and
when they do, I want the display to be updated accordingly. However
I'm encountering a problem in the visualization because the
conversion of these numarrays into a VTK data type is failing under
Debian Linux.

To demonstrate this effect, at the end of this note
I append a modified version of the
ImagePlaneWidget.py script from the current
VTK/Examples/GUI/Python directory. Essentially I replace the readin
of the human head data by an internal initialization of a 3D numarray
with f(x,y,z)  = 0.1 * z. And I replace the CaptureImage callback
to output a Tif by an UpdateImage callback where the numarray
is updated to f(x,y,z)  = 0.1 * z * (iteration+1)
and then re-displayed. Otherwise the programs are basically the same.

The basic configuration of the computer system would appear to be:

compiler: gcc version 3.3.5 (Debian 1:3.3.5-8)
kernel: Linux  2.6.8-2-k7-smp #1 SMP Mon Jan 24 04:27:32 EST 2005 i686 GNU/Linux
vtk: vtk 4.4.2 package
numarray: numarray 1.1.1 package

The problem centers around the CopyImportVoidPointer method call at
line 116 of VTK/Wrapping/Python/vtk/util/vtkImageImportFromArray.py
There are a couple of issues:

1) If I just use this piece of python code as supplied in the release
I just get

Traceback (most recent call last):
  File "test_import.py", line 43, in ?
    image_array_test.SetArray(image_numarray_test)
  File "/home/twillis/VTK/Wrapping/Python/vtk/util/vtkImageImportFromArray.py",
line 116, in SetArray
    self.__import.CopyImportVoidPointer(imTmpArr, size)
TypeError: argument 1 must be string or read-only buffer, not NumArray

I got past this complaint by hard coding the _NEW_NUMERIC variable to a
value of 0, which makes sure the incoming array is converted to a string.


2) If I then load and display the numarray, it comes up on the
vtk render window fine. Then the first time I click on the Update button,
the numarray is updated and the screen display is also updated.
But my problem occurs when I then request a second update. Then
the system crashes with a seqmentation fault. Insertion of a
few print statements into vtkImageImportFromArray.py reveals that again
the CopyImportVoidPointer call is the culprit.

I tried checking out the latest version of the VTK code from CVS
and compiling that but both problems just reappear.

I suspect this is a system dependent issue since the same script + VTK4.4
work straight out ot the box on an older stock RedHat 9.0 system
with gcc 3.2.2.

However I have a bit of a show-stopper since the delivered
system must work on the newer Debian Linux platform; so
I'd appreciate any insight into this problem or any work arounds
that the group can suggest.

Thanks

Tony
___________
Tony Willis
National Research Council   Tony.Willis at nrc-cnrc.gc.ca
Box 248                     (250)493-2277
Penticton, BC  V2A 6J9      fax: 493-7767
Government of Canada        Gouvernement du Canada

=================================================================
Here is the python test script:

#!/usr/bin/env python

# An adaption of VTK/Examples/GUI/Python/ImagePlaneWidget.py to
# illustrate a problem with vtkImageImportFromArray.py

import vtk
import Tkinter
from vtk.tk.vtkTkRenderWindowInteractor import \
     vtkTkRenderWindowInteractor
from vtk.util.misc import vtkGetDataRoot
from vtk.util.vtkImageImportFromArray import *
from numarray import *

###############################
print 'this first loop will call the vtkImageImportFromArray '
print 'SetArray method 5x and it works'
image_array_test = vtkImageImportFromArray()
spacing = (3.2, 3.2, 1.5)
image_array_test.SetDataSpacing(spacing)
num_arrays = 93
array_dim = 64
image_numarray_test = ones((num_arrays,array_dim,array_dim),type=Float32)
for i in range(5):
  print i
  for k in range(num_arrays):
    for i in range(array_dim):
      for j in range(array_dim):
        image_numarray_test[k,i,j] = (i+1) * k * 0.01
  print 'calling SetArray'
  image_array_test.SetArray(image_numarray_test)
  print 'called SetArray'
print 'this loop worked without visualization being involved'
###############################

###############################
print ' '
print 'Now we will get serious about visualization ...'
print 'To see the problem, after the display appears, '
print 'click the Update button twice. The program will '
print 'then crash.'
print ' '
print ' '
iteration = 0

# Start by creating a 3D numarray and put it into VTK
num_arrays = 93
array_dim = 64
image_numarray = ones((num_arrays,array_dim,array_dim),type=Float32)
for k in range(num_arrays):
  for i in range(array_dim):
    for j in range(array_dim):
      image_numarray[k,i,j] =  k * 0.01

v16  = vtkImageImportFromArray()
print 'main program calling first v16.SetArray'
v16.SetArray(image_numarray)
print 'main program called first v16.SetArray'
spacing = (3.2, 3.2, 1.5)
v16.SetDataSpacing(spacing)

xMin, xMax, yMin, yMax, zMin, zMax = v16.GetDataExtent()

spacing = v16.GetDataSpacing()
sx, sy, sz = spacing

origin = v16.GetDataOrigin()
ox, oy, oz = origin

# An outline is shown for context.
outline = vtk.vtkOutlineFilter()
outline.SetInput(v16.GetOutput())

outlineMapper = vtk.vtkPolyDataMapper()
outlineMapper.SetInput(outline.GetOutput())

outlineActor = vtk.vtkActor()
outlineActor.SetMapper(outlineMapper)

# The shared picker enables us to use 3 planes at one time
# and gets the picking order right
picker = vtk.vtkCellPicker()
picker.SetTolerance(0.005)

# create blue to red color table
lut = vtk.vtkLookupTable()
lut.SetHueRange(0.6667, 0.0)
lut.Build()

# The 3 image plane widgets are used to probe the dataset.
planeWidgetX = vtk.vtkImagePlaneWidget()
planeWidgetX.DisplayTextOn()
planeWidgetX.SetInput(v16.GetOutput())
planeWidgetX.SetPlaneOrientationToXAxes()
planeWidgetX.SetSliceIndex(32)
planeWidgetX.SetPicker(picker)
planeWidgetX.SetKeyPressActivationValue("x")
planeWidgetX.SetLookupTable(lut)
prop1 = planeWidgetX.GetPlaneProperty()
prop1.SetColor(1, 0, 0)

planeWidgetY = vtk.vtkImagePlaneWidget()
planeWidgetY.DisplayTextOn()
planeWidgetY.SetInput(v16.GetOutput())
planeWidgetY.SetPlaneOrientationToYAxes()
planeWidgetY.SetSliceIndex(32)
planeWidgetY.SetPicker(picker)
planeWidgetY.SetKeyPressActivationValue("y")
prop2 = planeWidgetY.GetPlaneProperty()
prop2.SetColor(1, 1, 0)
planeWidgetY.SetLookupTable(planeWidgetX.GetLookupTable())

# for the z-slice, turn off texture interpolation:
# interpolation is now nearest neighbour, to demonstrate
# cross-hair cursor snapping to pixel centers
planeWidgetZ = vtk.vtkImagePlaneWidget()
planeWidgetZ.DisplayTextOn()
planeWidgetZ.SetInput(v16.GetOutput())
planeWidgetZ.SetPlaneOrientationToZAxes()
planeWidgetZ.SetSliceIndex(46)
planeWidgetZ.SetPicker(picker)
planeWidgetZ.SetKeyPressActivationValue("z")
prop3 = planeWidgetZ.GetPlaneProperty()
prop3.SetColor(0, 0, 1)
planeWidgetZ.SetLookupTable(planeWidgetX.GetLookupTable())

# Create the RenderWindow and Renderer
ren = vtk.vtkRenderer()
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(ren)

# Add the outline actor to the renderer, set the background color and size
ren.AddActor(outlineActor)
renWin.SetSize(600, 600)
ren.SetBackground(0.1, 0.1, 0.2)

current_widget = planeWidgetZ
mode_widget = planeWidgetZ

# Update numarray and then VTK
def UpdateImage():
    global iteration, v16
    num_arrays = 93
    array_dim = 64
    image_numarray = ones((num_arrays,array_dim,array_dim),type=Float32)
    iteration = iteration + 1
    if iteration == 2:
      print ' '
      print 'second update will crash ... :-('
    for k in range(num_arrays):
      for i in range(array_dim):
        for j in range(array_dim):
          image_numarray[k,i,j] = (iteration+1) * k * 0.01
    print 'UpdateImage calling v16.SetArray'
    v16.SetArray(image_numarray)
    print 'UpdateImage called v16.SetArray'
# finally update display
    renWin.Render()


# Create the GUI
# We first create the supporting functions (callbacks) for the GUI
#
# Align the camera so that it faces the desired widget
def AlignCamera():
    #global ox, oy, oz, sx, sy, sz, xMax, xMin, yMax, yMin, zMax, \
    #      zMin, slice_number
    #global current_widget
    cx = ox+(0.5*(xMax-xMin))*sx
    cy = oy+(0.5*(yMax-yMin))*sy
    cz = oy+(0.5*(zMax-zMin))*sz
    vx, vy, vz = 0, 0, 0
    nx, ny, nz = 0, 0, 0
    iaxis = current_widget.GetPlaneOrientation()
    if iaxis == 0:
        vz = -1
        nx = ox + xMax*sx
        cx = ox + slice_number*sx
    elif iaxis == 1:
        vz = -1
        ny = oy+yMax*sy
        cy = oy+slice_number*sy
    else:
        vy = 1
        nz = oz+zMax*sz
        cz = oz+slice_number*sz

    px = cx+nx*2
    py = cy+ny*2
    pz = cz+nz*3

    camera = ren.GetActiveCamera()
    camera.SetViewUp(vx, vy, vz)
    camera.SetFocalPoint(cx, cy, cz)
    camera.SetPosition(px, py, pz)
    camera.OrthogonalizeViewUp()
    ren.ResetCameraClippingRange()
    renWin.Render()


# Align the widget back into orthonormal position,
# set the slider to reflect the widget's position,
# call AlignCamera to set the camera facing the widget
def AlignXaxis():
    global xMax, xMin, current_widget, slice_number
    po = planeWidgetX.GetPlaneOrientation()
    if po == 3:
        planeWidgetX.SetPlaneOrientationToXAxes()
        slice_number = (xMax-xMin)/2
        planeWidgetX.SetSliceIndex(slice_number)
    else:
        slice_number = planeWidgetX.GetSliceIndex()

    current_widget = planeWidgetX

    slice.config(from_=xMin, to=xMax)
    slice.set(slice_number)
    AlignCamera()


def AlignYaxis():
    global yMin, yMax, current_widget, slice_number
    po = planeWidgetY.GetPlaneOrientation()
    if po == 3:
        planeWidgetY.SetPlaneOrientationToYAxes()
        slice_number = (yMax-yMin)/2
        planeWidgetY.SetSliceIndex(slice_number)
    else:
        slice_number = planeWidgetY.GetSliceIndex()

    current_widget = planeWidgetY

    slice.config(from_=yMin, to=yMax)
    slice.set(slice_number)
    AlignCamera()

def AlignZaxis():
    global yMin, yMax, current_widget, slice_number
    po = planeWidgetZ.GetPlaneOrientation()
    if po == 3:
        planeWidgetZ.SetPlaneOrientationToZAxes()
        slice_number = (zMax-zMin)/2
        planeWidgetZ.SetSliceIndex(slice_number)
    else:
        slice_number = planeWidgetZ.GetSliceIndex()

    current_widget = planeWidgetZ

    slice.config(from_=zMin, to=zMax)
    slice.set(slice_number)
    AlignCamera()


# Set the widget's reslice interpolation mode
# to the corresponding popup menu choice
def SetInterpolation():
    global mode_widget, mode
    if mode.get() == 0:
        mode_widget.TextureInterpolateOff()
    else:
        mode_widget.TextureInterpolateOn()

    mode_widget.SetResliceInterpolate(mode.get())
    renWin.Render()

# Share the popup menu among buttons, keeping track of associated
# widget's interpolation mode
def buttonEvent(event, arg=None):
    global mode, mode_widget, popm
    if arg == 0:
        mode_widget = planeWidgetX
    elif arg == 1:
        mode_widget = planeWidgetY
    elif arg == 2:
        mode_widget = planeWidgetZ
    else:
        return
    mode.set(mode_widget.GetResliceInterpolate())
    popm.entryconfigure(arg, variable=mode)
    popm.post(event.x + event.x_root, event.y + event.y_root)

def SetSlice(sl):
    global current_widget
    current_widget.SetSliceIndex(int(sl))
    ren.ResetCameraClippingRange()
    renWin.Render()

###
# Now actually create the GUI
root = Tkinter.Tk()
root.withdraw()
top = Tkinter.Toplevel(root)

# Define a quit method that exits cleanly.
def quit(obj=root):
    obj.quit()

# Popup menu
popm = Tkinter.Menu(top, tearoff=0)
mode = Tkinter.IntVar()
mode.set(1)
popm.add_radiobutton(label="nearest", variable=mode, value=0,
                     command=SetInterpolation)
popm.add_radiobutton(label="linear", variable=mode, value=1,
                     command=SetInterpolation)
popm.add_radiobutton(label="cubic", variable=mode, value=2,
                     command=SetInterpolation)

display_frame = Tkinter.Frame(top)
display_frame.pack(side="top", anchor="n", fill="both", expand="false")

# Buttons
ctrl_buttons = Tkinter.Frame(top)
ctrl_buttons.pack(side="top", anchor="n", fill="both", expand="false")

quit_button = Tkinter.Button(ctrl_buttons, text="Quit", command=quit)
update_button = Tkinter.Button(ctrl_buttons, text="Update",
                                command=UpdateImage)

x_button = Tkinter.Button(ctrl_buttons, text="x", command=AlignXaxis)
y_button = Tkinter.Button(ctrl_buttons, text="y", command=AlignYaxis)
z_button = Tkinter.Button(ctrl_buttons, text="z", command=AlignZaxis)
x_button.bind("<Button-3>", lambda e: buttonEvent(e, 0))
y_button.bind("<Button-3>", lambda e: buttonEvent(e, 1))
z_button.bind("<Button-3>", lambda e: buttonEvent(e, 2))

for i in (quit_button, update_button, x_button, y_button, z_button):
    i.pack(side="left", expand="true", fill="both")


# Create the render widget
renderer_frame = Tkinter.Frame(display_frame)
renderer_frame.pack(padx=3, pady=3,side="left", anchor="n",
                    fill="both", expand="false")

render_widget = vtkTkRenderWindowInteractor(renderer_frame,
                                            rw=renWin, width=600,
                                            height=600)
for i in (render_widget, display_frame):
    i.pack(side="top", anchor="n",fill="both", expand="false")

# Add a slice scale to browse the current slice stack
slice_number = Tkinter.IntVar()
slice_number.set(current_widget.GetSliceIndex())
slice = Tkinter.Scale(top, from_=zMin, to=zMax, orient="horizontal",
                      command=SetSlice,variable=slice_number,
                      label="Slice")
slice.pack(fill="x", expand="false")

# Done with the GUI.
###

# Set the interactor for the widgets
iact = render_widget.GetRenderWindow().GetInteractor()
planeWidgetX.SetInteractor(iact)
planeWidgetX.On()
planeWidgetY.SetInteractor(iact)
planeWidgetY.On()
planeWidgetZ.SetInteractor(iact)
planeWidgetZ.On()

# Create an initial interesting view
cam1 = ren.GetActiveCamera()
cam1.Elevation(110)
cam1.SetViewUp(0, 0, -1)
cam1.Azimuth(45)
ren.ResetCameraClippingRange()

# Render it
render_widget.Render()

iact.Initialize()
renWin.Render()
iact.Start()

# Start Tkinter event loop
root.mainloop()





More information about the vtkusers mailing list