Cannot Control Opacity of vtkImageActors

I tried the code, and it looks like vtkImageActor is rescaling the color values before displaying them. This only happens when vtkImageActor is used with vtkImageStack. This seems very strange, and I’ll have to investigate to see why this happens.

Luckily, there is an easy fix that you can apply in your code: use vtkImageSlice instead of vtkImageActor. The vtkImageSlice class is actually the superclass of vtkImageActor and it shares a lot of the same functionality but it is used a little differently, e.g. you have to explicitly tell it what mapper to use (just like vtkActor needs a vtkDataSetMapper).

bw_actor = vtk.vtkImageSlice()
bw_actor.SetMapper(vtk.vtkImageSliceMapper())
bw_actor.GetProperty().SetOpacity(1.0)
bw_actor.GetMapper().SetInputConnection(anatomy_mapper.GetOutputPort())

It works with vtkImageStack and the SetOpacity() method works, too (but SetOpacity() is only used for the overlay image, not the underlay).

Here is the whole code that I used to test:

import vtk
import math

M_PI = math.pi

# miscellaneous setings
curr_dynamic_num = 0
displaying = 5
render_window_width = 1024
thermometryDICOM = "TEMP"

anatomy = vtk.vtkImageGaussianSource()
anatomy.SetWholeExtent(0, 1023, 0, 1023, 0, 0)
anatomy.SetCenter(500, 500, 0)
anatomy.SetMaximum(1000)
anatomy.SetStandardDeviation(100)

current_dynamic = vtk.vtkImageGaussianSource()
current_dynamic.SetWholeExtent(0, 1023, 0, 1023, 0, 0)
current_dynamic.SetCenter(500, 500, 0)
current_dynamic.SetMaximum(1000)
current_dynamic.SetStandardDeviation(100)

# Create a grey scale lookup table for the anatomy
bw_table = vtk.vtkLookupTable()
bw_table.SetRange(0, 1000)  # image intensity range
bw_table.SetValueRange(0.0, 1.0)  # from black to white
bw_table.SetSaturationRange(0.0, 0.0)  # no color saturation
bw_table.SetRampToLinear()
bw_table.Build()

# Create a color lookup table for the thermometry series
jet_table = vtk.vtkLookupTable()
if displaying == 1 or displaying == 2 or displaying == 5:
    jet_table.SetRange(-M_PI, M_PI)
else:
    jet_table.SetRange(-5, 20)
jet_table.SetBelowRangeColor(0, 0, 1.0, 1.0)  # RGB A
jet_table.UseBelowRangeColorOn()
jet_table.SetAboveRangeColor(1.0, 0, 0, 1.0)  # RGB A
jet_table.UseAboveRangeColorOn()
jet_table.SetRampToLinear()
jet_table.SetAlphaRange(1.0, 1.0)
jet_table.SetValueRange(1.0, 1.0)
jet_table.SetSaturationRange(1.0, 1.0)
jet_table.SetHueRange(0.667, 0)  # 0.667 == blue, 0 == red
jet_table.Build()

# Map the images through the lookup tables
anatomy_mapper = vtk.vtkImageMapToColors()
anatomy_mapper.SetLookupTable(bw_table)
anatomy_mapper.SetInputConnection(anatomy.GetOutputPort())
delta_temp_mapper = vtk.vtkImageMapToColors()
delta_temp_mapper.SetLookupTable(jet_table)
if displaying == 1 or displaying == 5:  # displaying original phase data
    delta_temp_mapper.SetInputConnection(current_dynamic.GetOutputPort())
else:  # displaying diff or delta data
    delta_temp_mapper.SetInputConnection(current_delta_temp.GetOutputPort())

# Display window
window = vtk.vtkRenderWindow()
window.SetSize(render_window_width, 750)

# Set up the interaction
interactorStyle = vtk.vtkInteractorStyleImage()
interactor = vtk.vtkRenderWindowInteractor()
interactor.SetInteractorStyle(interactorStyle)
window.SetInteractor(interactor)

# Set up the actors
#   anatomy
bw_actor = vtk.vtkImageSlice()
bw_actor.SetMapper(vtk.vtkImageSliceMapper())
bw_actor.GetProperty().SetOpacity(1.0)  # doesn't seem to work
bw_actor.GetMapper().SetInputConnection(anatomy_mapper.GetOutputPort())
bw_actor.SetScale(1.0, 1.0, 1.0)
bw_actor.SetPosition(0.0, 0.0, 0.0)
bw_actor.SetPickable(0)
bw_actor.Update()
#   thermometry
jet_actor = vtk.vtkImageSlice()
jet_actor.SetMapper(vtk.vtkImageSliceMapper())
jet_actor.GetProperty().SetOpacity(1.0)
jet_actor.GetMapper().SetInputConnection(delta_temp_mapper.GetOutputPort())
jet_actor.SetPickable(1)
jet_actor.Update()
#   color scale bar
scale_bar_actor = vtk.vtkScalarBarActor()
scale_bar_actor.SetLookupTable(delta_temp_mapper.GetLookupTable())
scale_bar_actor.SetLookupTable(jet_table)
scale_bar_actor.SetWidth(0.1)  # viewport coordinates
scale_bar_actor.SetHeight(0.515)
scale_bar_actor.UnconstrainedFontSizeOn()
scale_bar_actor.GetLabelTextProperty().BoldOff()
scale_bar_actor.GetLabelTextProperty().SetItalic(0)
scale_bar_actor.GetLabelTextProperty().SetFontSize(14)
scale_bar_actor.SetNumberOfLabels(6)
scale_bar_actor.SetLabelFormat("%0.1f")
scale_bar_actor.SetPosition(0.9, 0.239)

# image stack? 
stack = vtk.vtkImageStack() 
stack.AddImage(bw_actor)
stack.AddImage(jet_actor)

# renderers
#   left viewport
temp_renderer = vtk.vtkRenderer()

#  ORIGINAL =============================
#if displaying == 4 or displaying == 5:
#    temp_renderer.AddActor(bw_actor)
#temp_renderer.AddActor(jet_actor)
# NEW ==================================
temp_renderer.AddViewProp(stack)

temp_renderer.AddActor2D(scale_bar_actor)

window.AddRenderer(temp_renderer)
#temp_renderer.SetViewport(0, 0, 0.5, 1.0)

# for displaying coordinates and voxel value - temperature
#   data picker
picker = vtk.vtkVolumePicker()
#   annotation
anno = vtk.vtkCornerAnnotation()
anno.SetLinearFontScaleFactor(2)
anno.SetNonlinearFontScaleFactor(1)
anno.SetMaximumFontSize(18)
anno.GetTextProperty().SetColor(1, 1, 1)
anno.SetPickable(0)
anno.SetText(3, "Dynamic: " + str(curr_dynamic_num))
temp_renderer.AddViewProp(anno)

window.Render()
window.SetWindowName(thermometryDICOM)  # Note - needs to be set AFTER window is rendered
interactor.Start()
1 Like