VTK学习笔记(一)

VTK是一套可视化工具箱,可以用来完成复杂的3D可视化,其中提供了大量的高级可视化功能,有一些现成的库可以调用VTK完成可视化,不过如果需要更底层的实现对于库的了解也是必不可少的。

目录


背景

背景不做介绍了,VTK是非常有名的可视化工具箱,具体可到VTK的官网查询。下面是官网对自己的简要介绍:

VTK is an open-source software system for image processing, 3D graphics, volume rendering and visualization. VTK includes many advanced algorithms (e.g., surface reconstruction, implicit modelling, decimation) and rendering techniques (e.g., hardware-accelerated volume rendering, LOD control).

VTK可以运行在诸多环境下,从Windows, Linux 到 iOS, Android都可以运行,这点应该还是非常吸引人的。

VTK的基本流程

VTK的渲染流程如下:

source -> mapper -> actor -> renderer -> renderwindow

所以一个基本的VTK程序会比其他可视化库来得复杂。

VTK通过Pipeline实现不同的可视化,这需要用到大量的filter和mapper。

自6.0开始VTK的data object和pipeline彻底分离了,这个将导致6.0以前的代码出现兼容性问题。

VTK自带一些简单的教程,讲解了关于VTK的一些基本操作。我们这里简单的过一遍,到六个案例都过完之后大家会发现其实这个教程的质量并不很好。这是因为这六个案例过完之后你了解的仅仅是VTK的一些皮毛,当然你大概知道了VTK的流程,但是VTK之所以被叫做VTK的可视化功能的核心在这六个案例里完全没有提到。不过古话说得好:

千里之行 始于足下

我们花两个网页的篇幅简单的过一下这六个案例,在案例代码中我添加了一些评注,希望有点帮助。

VTK教程一:显示一个圆锥

下面引用VTK教程里面的第一个案例(略微添加了一些注释):

#!/usr/bin/env python
#
# This example creates a polygonal model of a cone, and then renders it to
# the screen. It will rotate the cone 360 degrees and then exit. The basic
# setup of source -> mapper -> actor -> renderer -> renderwindow is
# typical of most VTK programs.
# 这个案例建立一个圆锥,然后在屏幕上渲染它,并旋转360度。
#

#
# First we include the VTK Python packages that will make available
# all of the VTK commands to Python.
#
import vtk
import time

#
# Next we create an instance of vtkConeSource and set some of its
# properties. The instance of vtkConeSource "cone" is part of a visualization
# pipeline (it is a source process object); it produces data (output type is
# vtkPolyData) which other filters may process.
# 案例使用内嵌的ConSource生成圆锥
#
cone = vtk.vtkConeSource()
cone.SetHeight( 3.0 )
cone.SetRadius( 1.0 )
cone.SetResolution( 10 )

#
# In this example we terminate the pipeline with a mapper process object.
# (Intermediate filters such as vtkShrinkPolyData could be inserted in
# between the source and the mapper.)  We create an instance of
# vtkPolyDataMapper to map the polygonal data into graphics primitives. We
# connect the output of the cone souece to the input of this mapper.
# 这里讲这个算例没有添加filter
#
coneMapper = vtk.vtkPolyDataMapper()
coneMapper.SetInputConnection( cone.GetOutputPort() )

#
# Create an actor to represent the cone. The actor orchestrates rendering of
# the mapper's graphics primitives. An actor also refers to properties via a
# vtkProperty instance, and includes an internal transformation matrix. We
# set this actor's mapper to be coneMapper which we created above.
# 这里讲Actor可以用来修改属性和包含一个变换矩阵
#
coneActor = vtk.vtkActor()
coneActor.SetMapper( coneMapper )

#
# Create the Renderer and assign actors to it. A renderer is like a
# viewport. It is part or all of a window on the screen and it is
# responsible for drawing the actors it has.  We also set the background
# color here
# renderer类似于其他软件里的viewport,可以设置一些背景之类的属性。
#
ren1= vtk.vtkRenderer()
ren1.AddActor( coneActor )
ren1.SetBackground( 0.1, 0.2, 0.4 )

#
# Finally we create the render window which will show up on the screen
# We put our renderer into the render window using AddRenderer. We also
# set the size to be 300 pixels by 300
# 这个是建立渲染窗口,大小为300x300
#
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer( ren1 )
renWin.SetSize( 300, 300 )

#
# now we loop over 360 degreeees and render the cone each time
# 这个是旋转360度
#
for i in range(0,360):
    time.sleep(0.03)

    renWin.Render()
    ren1.GetActiveCamera().Azimuth( 1 )

VTK教程二:添加Observer

教程二介绍了observer,相当于其他软件里面的Event Handler。下面是教程二的代码,新内容很少:

#!/usr/bin/env python
#
#
# This example shows how to add an observer to a Python program. It extends
# the Step1/Python/Cone.py Python example (see that example for information on
# the basic setup).
#
# VTK uses a command/observer design pattern. That is, observers watch for
# particular events that any vtkObject (or subclass) may invoke on
# itself. For example, the vtkRenderer invokes a "StartEvent" as it begins
# to render. Here we add an observer that invokes a command when this event
# is observed.
# 这一部分主要介绍observer,相当于eventhandler,会去监控event。
#

from __future__ import print_function
import vtk
import time

#
# define the callback
#
def myCallback(obj,string):
    print("Starting a render")


#
# create the basic pipeline as in Step1
#
cone = vtk.vtkConeSource()
cone.SetHeight( 3.0 )
cone.SetRadius( 1.0 )
cone.SetResolution( 10 )

coneMapper = vtk.vtkPolyDataMapper()
coneMapper.SetInputConnection( cone.GetOutputPort() )
coneActor = vtk.vtkActor()
coneActor.SetMapper( coneMapper )

ren1= vtk.vtkRenderer()
ren1.AddActor( coneActor )
ren1.SetBackground( 0.1, 0.2, 0.4 )

#
# Add the observer here
# observer是添加在render上面的,监控的也是render的event
#
ren1.AddObserver("StartEvent", myCallback)

renWin = vtk.vtkRenderWindow()
renWin.AddRenderer( ren1 )
renWin.SetSize( 300, 300 )

#
# now we loop over 360 degreeees and render the cone each time
#
for i in range(0,360):
    time.sleep(0.03)
    renWin.Render()
    ren1.GetActiveCamera().Azimuth( 1 )

VTK教程三:同时显然多个对象

教程三主要解决多个render的问题,这样可以在一个窗口里添加多个render。属于一个比较重要的功能。下面是教程三的代码:

#!/usr/bin/env python
#
# This example demonstrates how to use multiple renderers within a
# render window. It is a variation of the Cone.py example. Please
# refer to that example for additional documentation.
# 可视化内容还是基于教程一
#

import vtk
import time

#
# Next we create an instance of vtkConeSource and set some of its
# properties. The instance of vtkConeSource "cone" is part of a visualization
# pipeline (it is a source process object); it produces data (output type is
# vtkPolyData) which other filters may process.
# 产生一个圆锥对象,这个对象会在后面多次用到,这也说明VTK里一个对象是可以被多次按照
# 不同方式render的
#
cone = vtk.vtkConeSource()
cone.SetHeight( 3.0 )
cone.SetRadius( 1.0 )
cone.SetResolution( 10 )

#
# In this example we terminate the pipeline with a mapper process object.
# (Intermediate filters such as vtkShrinkPolyData could be inserted in
# between the source and the mapper.)  We create an instance of
# vtkPolyDataMapper to map the polygonal data into graphics primitives. We
# connect the output of the cone souece to the input of this mapper.
#
coneMapper = vtk.vtkPolyDataMapper()
coneMapper.SetInputConnection(cone.GetOutputPort())

#
# Create an actor to represent the cone. The actor orchestrates rendering of
# the mapper's graphics primitives. An actor also refers to properties via a
# vtkProperty instance, and includes an internal transformation matrix. We
# set this actor's mapper to be coneMapper which we created above.
# 到这里都只有一个对象
#
coneActor = vtk.vtkActor()
coneActor.SetMapper(coneMapper)

#
# Create two renderers and assign actors to them. A renderer renders into a
# viewport within the vtkRenderWindow. It is part or all of a window on the
# screen and it is responsible for drawing the actors it has.  We also set
# the background color here. In this example we are adding the same actor
# to two different renderers; it is okay to add different actors to
# different renderers as well.
# 将一个actor给了两个render,其中的SetViewport函数
#
# Specify the viewport for the Viewport to draw in the rendering window.#
# Coordinates are expressed as (xmin,ymin,xmax,ymax), where each coordinate is 0 <= coordinate <= 1.0.
#
# 注意到这里并没有旋转对象
#
ren1 = vtk.vtkRenderer()
ren1.AddActor(coneActor)
ren1.SetBackground(0.1, 0.2, 0.4)
ren1.SetViewport(0.0, 0.0, 0.5, 1.0)

ren2 = vtk.vtkRenderer()
ren2.AddActor(coneActor)
ren2.SetBackground(0.1, 0.2, 0.4)
ren2.SetViewport(0.5, 0.0, 1.0, 1.0)

#
# Finally we create the render window which will show up on the screen.
# We add our two renderers into the render window using AddRenderer. We also
# set the size to be 600 pixels by 300.
#
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer( ren1 )
renWin.AddRenderer( ren2 )
renWin.SetSize(600, 300)

#
# Make one camera view 90 degrees from other.
# 旋转对象是在这里完成的
#
ren1.ResetCamera()
ren1.GetActiveCamera().Azimuth(90)

#
# Now we loop over 360 degreeees and render the cone each time.
#
for i in range(0,360):
    time.sleep(0.03)

    renWin.Render()
    ren1.GetActiveCamera().Azimuth( 1 )
    ren2.GetActiveCamera().Azimuth( 1 )

VTK教程四:添加多个Actors

上一个案例里同时显示了两个一样的圆锥,可以通过Actor修改圆锥的属性获得不同的渲染结果。这就是下面这个案例的目的。下面是第四个案例的代码:

#!/usr/bin/env python
#
# This example demonstrates the creation of multiple actors and the
# manipulation of their properties and transformations. It is a
# derivative of Cone.py, see that example for more information.
#

import vtk
import time

#
# Next we create an instance of vtkConeSource and set some of its
# properties. The instance of vtkConeSource "cone" is part of a visualization
# pipeline (it is a source process object); it produces data (output type is
# vtkPolyData) which other filters may process.
# 还是一个对象
#
cone = vtk.vtkConeSource ()
cone.SetHeight( 3.0 )
cone.SetRadius( 1.0 )
cone.SetResolution( 10 )

#
# In this example we terminate the pipeline with a mapper process object.
# (Intermediate filters such as vtkShrinkPolyData could be inserted in
# between the source and the mapper.)  We create an instance of
# vtkPolyDataMapper to map the polygonal data into graphics primitives. We
# connect the output of the cone source to the input of this mapper.
#
coneMapper = vtk.vtkPolyDataMapper()
coneMapper.SetInputConnection(cone.GetOutputPort())

#
# Create an actor to represent the first cone. The actor's properties are
# modified to give it different surface properties. By default, an actor
# is create with a property so the GetProperty() method can be used.
# 此处设计很逆天,需要注意:
#
# vtkProperty* vtkActor::GetProperty  ()
# Set/Get the property object that controls this actors surface properties.#
#
# This should be an instance of a vtkProperty object. Every actor must have a property associated with it. If one isn't specified, then one will be generated automatically. Multiple actors can share one property object.
#
# 设置属性的第一步是 GetProperty,也有一个函数叫SetProperty,不过是用来直接使用Property对象设置的,后面会用到:
#
# void vtkActor::SetProperty  (   vtkProperty *   lut )
# Set/Get the property object that controls this actors surface properties.#
#
# This should be an instance of a vtkProperty object. Every actor must have a property associated with it. If one isn't specified, then one will be generated automatically. Multiple actors can share one property object.
#
coneActor = vtk.vtkActor()
coneActor.SetMapper(coneMapper)
coneActor.GetProperty().SetColor(0.2, 0.63, 0.79)
coneActor.GetProperty().SetDiffuse(0.7)
coneActor.GetProperty().SetSpecular(0.4)
coneActor.GetProperty().SetSpecularPower(20)

#
# Create a property and directly manipulate it. Assign it to the
# second actor.
# 新建一个Property对象
#
property = vtk.vtkProperty()
property.SetColor(1.0, 0.3882, 0.2784)
property.SetDiffuse(0.7)
property.SetSpecular(0.4)
property.SetSpecularPower(20)

#
# Create a second actor and a property. The property is directly
# manipulated and then assigned to the actor. In this way, a single
# property can be shared among many actors. Note also that we use the
# same mapper as the first actor did. This way we avoid duplicating
# geometry, which may save lots of memory if the geoemtry is large.
# 这个地方强调了使用的是同一个mapper可以省内存
coneActor2 = vtk.vtkActor()
coneActor2.SetMapper(coneMapper)
coneActor2.GetProperty().SetColor(0.2, 0.63, 0.79)
coneActor2.SetProperty(property)
coneActor2.SetPosition(0, 2, 0)

#
# Create the Renderer and assign actors to it. A renderer is like a
# viewport. It is part or all of a window on the screen and it is responsible
# for drawing the actors it has.  We also set the background color here.
# 注意,这个算例没有使用两个actors,仅有一个viewport,而是直接使用SetPosition将两个对象分开,viewport是一个。
#
ren1 = vtk.vtkRenderer()
ren1.AddActor(coneActor)
ren1.AddActor(coneActor2)
ren1.SetBackground(0.1, 0.2, 0.4)

#
# Finally we create the render window which will show up on the screen
# We put our renderer into the render window using AddRenderer. We also
# set the size to be 300 pixels by 300.
#
renWin = vtk.vtkRenderWindow()
renWin.AddRenderer(ren1)
renWin.SetSize(300, 300)

#
# Now we loop over 360 degreeees and render the cone each time.
#
for i in range(0,360):
    time.sleep(0.03)

    renWin.Render()
    ren1.GetActiveCamera().Azimuth( 1 )

控制页面长度,此处分割。

评论