VTK是用C ++实现的,但已设计为允许绑定到其他几种语言。这意味着可以使用多种语言来使用VTK。这些包括Java,Tcl,当然还有Python。您将获得C ++的性能,因为它仍然由C ++驱动可视化,但是易于使用您喜欢的语言-在这种情况下为Python。VTK提供了一套工具,可以读取几乎任何种类的科学数据,包括任何格式/结构,过滤数据,然后在3D图形环境中进行渲染。 VTK可以通过单个 pip 命令安装:
pip install vtk
并且只需要一个import语句即可从Python内部使用该库:
import vtk
VTK管道 使用VTK的关键是构造管道。大多数应用程序在创建管道时都会经历相似的阶段。在大约4个常规步骤中,这些步骤是: 上面显示了此VTK管道的基本轮廓。流程图中的每个步骤将对应于从VTK库实例化的至少一个对象,可能还对应于几个对象。这样,与本节上方的列表相比, 图1 可以更好地映射到您需要构造的实际管道和需要创建的对象。 VTK文档区分了可视化管道和渲染管道。VTK文档中的这种区别有点用词不当,因为渲染是创建可视化的重要步骤。创建演员后,重点便放在渲染上。当使用术语 可视化管道 渲染管道时 ,它们指的是 图1 所示的 那些部分。术语 VTK管道 将指整个管道。否则, 可视化 将在一般意义上指的是可视化我们的数据的过程的每个部分。 将可视化管道中的各个阶段链接在一起 为了进一步解释 图1, 我们从数据源开始。下一节将介绍更多数据,但是现在我们注意到,每个可视化都必须从可视化读取和处理的某些数据源开始。VTK中已经实现了许多格式。读取数据后,将其连接到管道的下一个阶段。将过滤器连接到管道中的过程是理解VTK及其工作方式的核心。 过滤器是一种以某种方式转换数据的算法。它们的使用是可选的,但是在构建可视化管道时,您可能会使用其中的几个。有了数据后,您可以使用以下VTK惯用法将其连接到管道中的下一步: filter2.SetInputConnection(filter1.GetOutputPort()) 。此处的输出 filter1 连接到的输入 filter2 。这些过滤器通过这种方式链接在一起以创建管道。 如上图所示,将过滤器链接在一起时,它们实际上不会在该步骤中处理任何数据。取而代之的是,仅连接了这些过滤器。它们对数据的计算仅在管道的更深层发出请求时才进行,通常是在呈现可视化的那一刻。评估是懒惰的,因此仅在需要时才进行评估。调用更新时,更新将向上移动到管道,然后将处理后的数据向下移动到管道的呈现步骤。 建议将这种将过滤器与延迟评估链接在一起的方法是创建管道的推荐方法。某些对象具有一种方法 .SetInputData(data) ,该方法只读取一次输入数据的源,并且如果对数据源进行了更改,则不会自动更新。如果期望更改该数据,则不应使用此方法。 示例:渲染一个简单的字形 让我们从一个简单的例子开始。以下是“ hello world”的VTK版本。我们将以3D渲染单个圆锥。尽管很简单,但它将逐步创建渲染管道。一旦理解,创建非常复杂的场景就变得非常简单。 科学可视化通常采用从现实世界中收集的数据。在这种情况下,我们将使用VTK中可用的内置几何之一。这些是简单的几何形状:箭头,圆柱体,立方体等,它们在表示数据时很有用。它们的名称遵循一个约定:形状的名称 vtkXXXXSource 在哪里 XXXX ,即圆锥体,箭头,圆柱体等。在这种情况下,我们的数据源为圆锥体。
cone = vtk.vtkConeSource()
圆锥被隐式描述。我们可以通过调用其关联方法为锥体设置一些参数。这可以包括 .SetRadius(radius) .SetResolution(facets) 设置圆锥的半径和用来表示圆锥的小平面的数量。 要使用多边形网格之类的几何数据,我们需要使用此数据创建的实例 vtkActor 。要到达那里至少需要一个中间步骤。我们从几何图形中创建一个映射器,然后将该映射器连接到actor。 此处,圆锥体的输出连接到映射器以继续管线。
mapper = vtk.vtkPolyDataMapper()
mapper.SetInputConnection(cone.GetOutputPort())
上面的习语在前面关于 VTK管道的 章节中讨论 。的输出 cone 连接到的输入 mapper vtkPolyDataMapper 该类的实例需要来自输出非结构化多边形数据的对象的连接。这样的对象可以是 vtkPolyData 存储每个点和像元的实例,或者在这种情况下 vtkConeSource ,是根据少量参数从程序生成几何的。或者,可以将锥的输出连接到滤波器,而将滤波器的输出连接到映射器。在此示例中不使用任何过滤器。
actor = vtk.vtkActor()
actor.SetMapper(mapper)
上面的代码与之前的代码列表有些不同。一个 vtkActor 具有使用不同的语法映射集。在VTK中,参与者被视为可视化管道的末端(请参阅上文有关 VTK管道的内容 )。从这里我们正在构建渲染管道。 映射者和演员 创建映射器是可视化管道中的中间步骤。它把数据和信息带入演员。它的作用是将数据映射到图形基元中。对于多边形网格,这比其他类型的数据更直接。它还包括进行有关如何将数据转换为颜色的设置(请参见下面的 查找表 ),或者是否使用单元格数据或节点数据为模型着色。演员关心模型如何适合场景。结果,可以从角色内部设置的参数通常涉及场景中对象的纹理,光照,位置等。 下面是完成可视化的最后步骤。
window = vtk.vtkRenderWindow()
# Sets the pixel width, length of the window.
window.SetSize(500, 500)
interactor = vtk.vtkRenderWindowInteractor()
interactor.SetRenderWindow(window)
renderer = vtk.vtkRenderer()
window.AddRenderer(renderer)
renderer.AddActor(actor)
# Setting the background to blue.
renderer.SetBackground(0.1, 0.1, 0.4)
window.Render()
interactor.Start()
VTK渲染的默认设置是默认启用某种程度的交互性。这是我们 免费 获得的 而进一步的交互性需要在可视化中进行编程。当用户与可视化交互时,会执行诸如单击/移动鼠标,按下键等操作,这些事件由交互器处理。可以使用不同的交互器设置来更改可视化如何响应特定用户事件。在这里我们使用了common vtkRenderWindowInteractor 。其他交互器实例具有不同的行为,可以随时对其进行修改。需要使用该 .SetRenderWindow() 方法将交互器连接到窗口。 每个VTK可视化需要一个窗口,并且每个窗口至少需要一个渲染器(尽管每个窗口可以有多个渲染器)。该窗口由 vtkRenderWindow 实例表示。渲染器可以视为一个单独的场景。每个对象可以具有不同的角色,相机位置,照明等。渲染器通过该 .AddRenderer(renderer) 方法连接到窗口。默认情况下,整个窗口区域将具有此渲染器的渲染功能。但是,可以设置多个渲染器以在窗口的不同区域中进行绘制。如果使用多个渲染器,则必须指定一个矩形,该矩形描述渲染器将在其上绘制的窗口部分。这是使用 .SetViewport( (x0, y0, x1, y1) ) 查看更多 )进行控制的。 渲染器的背景色是使用 .SetBackground() 方法设置的。最后的步骤是初始化可视化。 VTK中的数据 VTK管道始于一个或多个数据源。通常,这些数据是来自现实世界的实验,计算或其他测量结果。VTK提供了许多不同的数据结构,您可以使用它们来存储数据。它们具有需要提供多少信息来完全描述您的数据的优势。 图3.以4种不同方式构造的数据说明。从左上角开始,顺时针移动:结构化图像格式,直线网格,结构化网格和完全非结构化的多边形数据。 VTK可以读取和显示几乎任何类型的结构的数值数据。 图3 显示了具有4种不同结构的数据。从最大到最小的顺序排列,这些是图像,直线网格,结构化网格和多边形数据。数据越结构化,需要提供给VTK的信息越少,以完全表示该数据。例如,要完全描述图像数据,您只需要提供: 直线网格需要更多信息才能完全定义它。需要与图像相同的信息,以及每行节点之间的间距。如果查看 图3 中的直线网格,您会注意到单元格的边缘是对齐的,但是每行的任何单元格的宽度都是不同的。必须 为每一行 指定该距离。这是每个维度中每行的一个附加标量值。然后,对于其他数据结构,信息量进一步增加。 要使用的数据结构的类型取决于它将表示的数据。理想情况下,它是最结构化的数据类型,可以完全代表现实生活中的数据/度量。来自摄像机的图像数据可以由VTK图像对象表示,而从CAD程序导入的多边形网格则需要由非结构化多边形数据表示。VTK图像对象过于僵化,无法对多边形网格进行建模。 使用和创建多数据 在前面的示例中,从圆锥源对象创建了多边形网格。这是通过管道进行的,无需进一步修改即可呈现。在下面的示例中,通过手动指定每个点的位置,然后通过点之间的连接以创建单元来创建简单的多边形网格。每个网格仅包含一个三角形单元。数据值分配给一个网格中的点。将值分配给另一个网格中的像元。 科学可视化通常会涉及某种3D几何形状。在机械工程中,这可能是金属结构的几何形状,这是 有限元分析 的主题,或者是使用 MRI 可视化的骨骼或器官的几何形状。与几何一起的是分层存储在该几何上的数据。热图可以显示组件周围的温度,结构上的应力场可以显示该组件上机械应力的分布。VTK可以可视化标量或更高阶张量数据。使用VTK,您可以创建一个值数组,这些值映射到网格中的点或单元。
points = vtk.vtkPoints()
cell = vtk.vtkCellArray()
mesh_1 = vtk.vtkPolyData()
mesh_2 = vtk.vtkPolyData()
tri = vtk.vtkTriangle()
在上方,我们看到了用于生成多边形网格几何体的对象。将它们组合在一起以创建最终的网格。结果将是 vtkPolyData 单个三角形的两个网格,其中两个网格均引用该三角形的几何形状。
# First point with default ID of 0.
points.InsertNextPoint( (0.0, 0.0, 0.0) )
points.InsertNextPoint( (0.0, 1.0, 0.0) )
points.InsertNextPoint( (1.0, 0.0, 0.0) )
VTK中的内存管理 前面已经提到,VTK是用C ++实现的,但是我们通过绑定使用Python。语言绑定允许访问用另一种语言编写的代码。在Python中,内存是自动管理的,因此在以前的Python代码中可能不需要担心它。尽管使用C ++编写,但在使用Python中的库时,VTK内存管理通常对您来说是隐藏的。 在上方,我们看到了 vtkPoints 通过 .InsertNextPoint() 方法附加了数据的实例。还按插入顺序从0开始为所有点分配ID号。一种替代方法是使用 .SetNumberOfPoints(numPoints) 其中 numPoints 将要存储的点数的方法。然后使用设置单个点 .SetPoint(id, (x, y, z) ) 。重要的 id 是,不能超过 numPoints 上一命令中使用的值;换句话说,最大允许值是 id numPoints-1。超过最大允许ID数或 .SetPoint() 在第一次调用第一次分配内存之前设置一个点将 .SetNumberOfPoints() 导致错误。 .InsertNextPoint() 被调用时,在RAM中新的内存分配给新的点。同样,在调用 numPoints 时分配了存储所需的空间 .SetNumberOfPoints() 。其他类的相似之处在于,必须首先指定结构的尺寸。然后可以将各个项目添加到该结构中,直到指定的数量为止。如果附加值,则会自动设置其ID。 三角形有3个点。必须将全局点ID匹配到三角形的三个点。使用 .GetPointIds().SetId(i, i) 的第一个实例 i 为0、1或2的点进行匹配,并指定三角形的局部点ID。第二个 i 可以是任何全局点ID。这些对应于 .InsertNextPoint() 在调用 .SetPoint() 的实例时使用或显式分配给节点的ID vtkPoints
for i in range(3):
    tri.GetPointIds().SetId(i, i)
cell.InsertNextCell(tri)
在这种情况下,整个网格中只有1个像元和3个点,因此这很简单。VTK中的对象通常由许多较小的对象组成。通常,通过获取设置了参数的相关对象来设置参数/属性,然后设置该参数。使用VTK时,上面的代码很常见。这里 .GetPointIds() 返回一个 vtkIdList 对象,然后调用其 .SetId() 方法。注意,多边形是定向的。方向由 右手规则 以及元素的3个局部点ID与全局点ID之间的匹配关系决定。 然后将点和像元设置为网格。
mesh_1.SetPoints(points)
mesh_1.SetPolys(cell)
如上所述,可以在网格的点或单元处设置多边形网格的数据。下面,我们为的单元格数据设置了一个标量值 mesh_1 。这些标量值存储为双精度浮点值,适用于大多数情况。
cell_data = vtk.vtkDoubleArray()
cell_data.SetNumberOfComponents(1)
cell_data.InsertNextTuple([0.5])
mesh_1.GetCellData().SetScalars(cell_data)
在上面的数组中,首先创建了数组,然后设置了组件数。常规标量数据将具有1个分量,向量将使用3个分量,高阶张量将使用更多分量。数据,甚至是标量数据,都使用列表输入。要输入具有三个成分的向量数量,需要将成分数设置为3, .SetNumberOfComponents(1) 并将一个成分列表替换为三个数量的列表。 最后一行将数据分配给网格本身。数据插入的顺序 vtkDoubleArray 对应于该数据如何与网格中的每个像元对齐。在此,添加到数组中的第一个(也是唯一的)元组值与添加到的第一个(也是唯一的)单元格相对应 mesh_1 。以下所示的过程类似,但适用于三角形中的三个点并设置点数据。
point_data.InsertNextTuple([0.0])
point_data.InsertNextTuple([1.0])
point_data.InsertNextTuple([0.5])
mesh_2.GetPointData().SetScalars(point_data)
该代码示例的其余部分是样板,除了查找表的构造之外,该模板与上一个示例基本保持不变。查找表将在本章后面讨论。查找表用于确定用于表示给定数值的颜色。 图5.两个三角形多边形,其中单元数据(左)和点数据(右)连接到网格。 图5中, 我们可以看到左侧的单个彩色单元格。在这里,单元格的标量值通过查找表映射为单一颜色。在右边的单元格中填充了线性的颜色渐变。使用节点上的标量值选择颜色,然后这些颜色之间的渐变填充单元格的内容。 在上一节中,使用了一个查询表,根据单元和节点数据为多边形着色。精确的工作查找表在此处进行了更详细的描述。本节假定您对指定颜色的格式有深入的了解。您可能需要阅读本章末尾 的小节 ,并获得有关此内容的更多信息。 颜色在可视化中可能非常有用。它可以以非常直观的方式在大范围内传达信息。要为演员设置单一颜色,可以使用 actor.GetProperty().SetColor(r, g, b) 命令。这会将整个模型设置为由 (r, g, b) 提供的值定义的单一颜色。但是,在可视化中,您将需要绘制各种颜色的对象,这些对象用于传达有关与模型关联的数据的信息。在VTK中,查找表是将标量值映射为颜色的对象。分析人员修改查找表以调整颜色方案,或者控制标量值如何映射到颜色以表示数据。 使用创建一个多边形数据查询表 lut = vtk.vtkPolyDataMapper() 。查找表通过映射器对象与模型关联。该方法 .SetLookupTable(lut) 将查找表连接 lut 到映射器。此外, .SetUseLookupTableScalarRange(True) 需要在映射器上调用该方法。这将导致映射器使用与查找表相同的范围。不使用此方法将导致映射器覆盖查找表中设置的范围,这可能不是您想要的。 无论查找表中使用的参数如何,都必须调用该 .Build() 方法。这将更新查找表以反映更新的设置。 下面,我将讨论设置查询表以实现不同结果的不同方法。在本节的最后,有一个包含4个三角形元素的简单网格的示例。每种技术都以4种不同的方式用于可视化相同的网格。 默认查询表 默认情况下,查找表将使用科学可视化中经常出现的 彩虹 范围的颜色。这些颜色大约是红色,黄色,绿色,浅绿色,蓝色,红色映射到表格范围的下端,蓝色映射到上端。 查找表的范围是通过方法设置的 .SetTableRange(low, high) 。对于高于或低于此范围的值,默认设置是显示的颜色与该范围内最接近的值的颜色相同,例如,范围为[500,600]的表的400.0值将具有与值为500.0。它可以设置自定义颜色使用的方法的范围以外的值 .SetAboveRangeColor(colour) .SetBelowRangeColor(colour) 。这 colour 是4个值的列表,通常是3个RGB值加上一个alpha通道。无论一种或两种ofthese两种方法是否被调用,有可能这是否特征-使用自定义的颜色值外的范围-用这4种方法激活控制: .UseBelowRangeColorOn() .UseBelowRangeColorOff() .UseAboveRangeColorOn() .UseAboveRangeColorOff() 。该功能默认情况下处于关闭状态,因此必须激活。 创建渐变时指定自定义颜色范围 尽管默认的彩虹配色方案在许多应用中很有用,但可以控制颜色的映射方式。可以限制色调,饱和度或值的范围,以控制如何将数据映射到颜色。造成这种情况的方法是 .SetHueRange(low, high) .SetSaturationRange(low, high) .SetValueRange(low, high) 。如果 low high 是相同的值,则对应的参数是固定的,并且不会随输入数据而变化。 通过固定HSV模型的3个参数中的一些参数,同时允许其他参数随输入数据变化,可以创建不同的效果。默认值是饱和度和值固定为最大值1.0,而色相随输入数据而变化。结果是上述的 彩虹 色。这可以更改。下面的示例创建一个单色颜色图。它将色调固定为任意值,将饱和度固定为0.0,同时允许该值变化。这将创建一个根据输入数据从更亮到更暗变化的映射。
lut.SetHueRange(0.5, 0.5)
lut.SetSaturationRange(0.0, 0.0)
lut.SetValueRange(0.25, 1.0)
创建任意的颜色映射 前面的示例主要依靠VTK提供的默认查找表,同时调整其某些设置。第一个是默认的 Rainbow 查找表。第二个修改了3个HSV参数的范围,以提供不同的效果。VTK允许对查找表进行完全控制,以允许值到您要指定的颜色的任何特定映射。这意味着从值→颜色分别指定每个映射。 每个查询表都有固定数量的颜色。这是通过方法设置 .SetNumberOfColors(number) 为整数值的 number 。默认值为256。这会将256种颜色映射到所指定的值的范围内 .SetTableRange(low, high) 。值之间的距离相等,并且选择这些值可提供默认的 彩虹 可视化效果。颜色不一定需要以创建平滑渐变效果的方式进行设置。 一旦使用 .SetNumberOfColors(number) 每种方法设置了查找表中的颜色数量,就可以使用该方法进行设置 .SetTableValue(id, R, G, B, A) 。参数 R G B 指定要映射到的颜色。alpha通道由指定 A 。该参数 A 是可选的,如果未指定,则默认为1.0,即 .SetTableValue() 仅用4个参数调用。的 id 是,用于指定在范围内的本色彩坐的整数。如果ññ在表格中使用颜色,然后 id 从0到(N− 1 )(ñ--1个)。范围底部的值映射到ID为0的颜色;范围中心的值将映射到ID大约为ñ2个ñ2个; 并且,该范围顶端的值将映射到ID为的颜色ñ− 1ñ--1个。 显示分类数据 在值和颜色之间设置任意映射的功能可用于表示分类信息。一个示例可能是可视化模型的热模拟。温度高于或低于熔点的元素可以用蓝色/红色显示,以显示可能发生熔化的位置。可以使用以下方法对单元进行分类,并可视化每个单元在其分配的类别中的成员资格。 为了 ññ我们设置的数据类别 .SetNumberOfColors(N) 。然后,为每个类别分配一个浮点值,该浮点值从0.0开始并逐个增加1个ñ− 11个ñ--1个,例如,对于4个类别,这些值为0.0、0.3333、0.6666和1.0。如果我们为这些类别分配一个整数类别编号, cat_no 则第一个类别具有, cat_no = 0 并且该类别的所有单元格的标量单元格值为0.0,第二个类别的 cat_no = 1 单元格值为0.3333,依此类推。然后使用设置每个类别的颜色 .SetTableValue(cat_no, R, G, B) 创建自定义颜色渐变 前面的示例显示了如何在映射数据时通过操纵跨HSV颜色模型使用的范围来控制颜色梯度。颜色及其顺序是固定的。这些大约是红色,黄色,绿色,水绿色,蓝色。我们可以像以前一样通过指定色调范围来在任一端切断此颜色范围,但是我们无法选择这些颜色随输入值而变化的任意顺序。VTK没有提供方便的工具来简单地指定几种颜色,然后让VTK在这些颜色之间创建平滑的渐变。例如,我们不能指定绿色,然后是黄色,然后是浅绿色,作为查找表范围内的颜色,并且VTK无法自动在这些颜色之间创建平滑的颜色渐变。 VTK提供了一个对象a vtkColorTransferFunction ,以实现我们所需要的。我们使用 .AddRGBPoint(value, R, G, B) 带有4个参数的方法。这些是使用RGB值指定的值和颜色。一旦设置了这些值-颜色对, .GetColor(value) 就可以查询该方法。对于任意值,它返回一种颜色,该颜色是从具有最接近值的颜色中插入的。下面的示例是一个 vtkColorTransferFunction 将黄色(RGB:1.0,1.0,0.0)设置为值0.0和将红色(RGB:1.0,0.0,0.0)设置为1.0的示例。橙色(RGB:1.0、0.5、0.0)是介于两者之间的颜色。 .GetColor(value) 调用值为0.5时,将正确返回其RGB值。
ctransfer = vtk.vtkColorTransferFunction()
ctransfer.AddRGBPoint(0.0, 1.0, 1.0, 0.0) # Yellow
ctransfer.AddRGBPoint(1.0, 1.0, 0.0, 0.0) # Red
# Correctly outputs the colour orange.
ctransfer.GetColor(0.5) # (1.0, 0.5, 0.0)
创建完之后, vtkColorTransferFunction 我们需要在查找表中设置每种单独的颜色。为了创建平滑的渐变,我们在查找表中需要足够的颜色。默认的颜色数量是256,通常足够,但是如果需要,您可以使用更大的数量。下面,将颜色数量设置为 N ,并且查找表的范围为[a,b]。等式我×b − añ一世×b--一种ñ给出甚至踩值 a b N
lut = vtk.vtkLookupTable()
lut.SetTableRange(a, b)
for i in range(N):
    new_colour = ctransfer.GetColor( (i * ((b-a)/N) ) )
    lut.SetTableValue(i, *new_colour)
lut.Build()
示例:创建自定义查找表 一个由4个元素和5个节点组成的示例网格用于显示具有不同查找表的同一数据的不同可视化效果。网格具有标量单元和节点数据。其值 如图6 所示。如果模型中同时存在两种类型的数据,则VTK中的默认值是可视化节点数据。 图6.具有4个元素和5个节点的网格。方括号[]中显示的标量节点数据以及方括号外的节点号。标量单元格数据显示在大括号{}中。节点标签从左下角开始,然后增加,逆时针移动。 创建了四个不同的查找表以可视化 图6 所示 的数据。最终结果 如图7 所示。下面以与在 图7 的标题中描述的顺序相同的顺序讨论它们。代码清单中的查找表被命名为 lut_n ,其中 n 是查找表的编号。类似地,相应的映射器和其他对象也相应地编号。仅 vtkPolyData 创建一个网格(一个实例)。由于每个映射器都链接到其相应的查找表,因此出现了可视化方面的差异。范围使用以下设置:
lut_1.SetTableRange(5.0, 7.5)
lut_2.SetTableRange(5.0, 7.5)
lut_3.SetTableRange(5.0, 7.5)
lut_4.SetTableRange(0.0, 1.0)
前三个查询表使用平滑渐变显示点数据,而第四个用于可视化为单元格存储的类别数据。因此,它具有不同的范围。第一个查询表保留默认设置。 .Build() 在分配给相应的映射器之前,只有方法被调用。
lut_1.Build()
mesh_mapper_1.SetLookupTable(lut_1)
第二个查找表修改HSV颜色值以创建单个彩色渐变,其中HSV颜色描述中的V大小会发生变化,而色相和饱和度则是固定的。结果是创建了与较低和较高标量数据值相对应的较暗和较亮的区域。对于3D模型,应谨慎使用此方法。在3D阴影中,当以与网格数据无关的方式渲染模型时,也会使用阴影,因此应谨慎使用这种特殊的着色技术。
lut_2.SetHueRange(0.5, 0.5)
lut_2.SetSaturationRange(1.0, 1.0)
lut_2.SetValueRange(0.25, 1.0)
lut_2.Build()
mesh_mapper_2.SetLookupTable(lut_2)
第三个查找表创建自定义的颜色组合,在构建查找表时从中创建渐变。这是使用的方法创建 如上所述 。选择了三种颜色,并将它们设置为反映值范围的低端,中端和高端。A vtkColorTransferFunction 用于对颜色值进行插值,以在这三个点之间创建渐变。
no_of_colours = 256
lut_3.SetNumberOfColors(no_of_colours)
ctransfer = vtk.vtkColorTransferFunction()
ctransfer.AddRGBPoint(0.0, 1.0, 0.25, 0.0) # Orange
ctransfer.AddRGBPoint(0.5, 1.0, 0.00, 0.0) # Red
ctransfer.AddRGBPoint(1.0, 1.0, 0.95, 0.95) # White
for i in range(no_of_colours):
    new_colour = ctransfer.GetColor(i / float(no_of_colours))
    lut_3.SetTableValue(i, *new_colour)
lut_3.Build()
mesh_mapper_3.SetLookupTable(lut_3)
最终查找表使用3种自定义颜色来显示4个类别之一中的每个单元格的成员资格。使用设置颜色数为3, lut_4.SetNumberOfColors(3) 并分别设置要使用的3种颜色中的每一种。表范围[0.0,1.0]设置得较早。这意味着要将单元格的颜色设置为红色,琥珀色或绿色,需要将其值设置为0.0、0.5或1.0。请注意,从 图6可以 看出,每个单元格都具有这些值之一作为其单元格数据。将查找表设置为相应的映射器后, .SetScalarModeToUseCellData() 将使用该方法。由于网格同时包含像元数据和点数据,因此VTK将默认使用点数据进行可视化。需要调用此方法。 .SetScalarModeToUsePointData() 但是,点数据的相应方法是默认值,因为点数据是默认方法,而以前的查找表从未使用过该方法。
lut_4.SetNumberOfColors(3)
lut_4.SetTableValue(0, 1.0, 0.0, 0.0) # Red
lut_4.SetTableValue(1, 1.0, 0.75, 0.0) # Amber
lut_4.SetTableValue(2, 0.0, 1.0, 0.0) # Green
lut_4.Build()
mesh_mapper_4.SetLookupTable(lut_4)
mesh_mapper_4.SetScalarModeToUseCellData()
的实例 vtkScalarBarActor 已添加到每个渲染器。它们通过显示颜色及其对应的值来帮助注释可视化。对于分类信息,我们的代码使用单元格值将成员资格分配给某个类别,但是实际值本身对查看可视化效果的用户没有用。默认标签被从标量栏中,通过使用该方法其数量设置为0除去 .SetNumberOfLabels(0) vtkScalarBarActor bar_4 。使用查找表方法在查找表中设置每个类别的注释 .SetAnnotation(value, annotation)
lut_4.SetAnnotation(0.0, "Red")
lut_4.SetAnnotation(0.5, "Amber")
lut_4.SetAnnotation(1.0, "Green")
条形图使用对应的查找表进行关联, bar.SetLookupTable(lut) 并作为角色添加到渲染器中。作为2D演员,使用方法将它们添加 .AddActor2D(actor) 。关于2D画布和2D角色的更多讨论将在下一章中介绍。 所有4个结果 如图7 所示。 图7.从 图6中 的网格创建的可视化。左上角按顺时针方向显示的结果是:默认查找表;固定HSV模型中的色相和饱和度,并允许其值变化;通过在任意选择的颜色之间插值颜色而创建的自定义颜色渐变;以及用于将元素分为三类的单元格数据。 VTK可以通过多种方式转换场景中的模型。多边形网格将具有其自己的局部原点。分析人员正在构建的整个场景也有其起源。使用角色引用的网格的 .AddActor(actor) 方法 vtkRenderer 将角色添加到场景时,会将角色添加到场景中。放置网格时,应使网格的本地原点与装配场景的全局原点重合,其中局部轴和全局轴均已对齐。 如果要移动模型,则可以修改面数据本身。如果这样做,您将修改每个引用该数据的对象所使用的数据。先前的示例表明,可以创建一个几何实例,然后让多个映射器引用相同的数据。如果随后修改了数据,则对该数据的所有引用都将被修改。相反,每个角色都允许您定义一个转换,该转换描述模型如何在场景中移动。有几种定义这些转换的方法。下面我们讨论如何通过调用方法来完成此操作 vtkActor 。在后面的章节中将介绍另外两种方法,其中涉及定义该 vtkTransform 参与者所引用的对象。此处描述的方法更简单,适用于大多数情况。 演员有几种定义转换的方法。我们对平移,缩放和旋转actor的3个操作感兴趣。 .SetPosition(x, y, z) 演员的方法,将移动的演员,使得演员的本地原点移动到指定的位置 x y z 。演员的当前位置可通过该 .GetPosition() 方法获得。如果你想演员相对的移动到其目前的现在的位置,而不是你可以使用一个绝对位置 .GetPosition() ,添加到这些返回的 x y z 坐标,然后调用 .SetPosition(x, y, z) 但是VTK还提供了 .AddPosition(x, y, z) 执行所有这一切都从一个单一的方法方法。 演员的比例是通过该 .SetScale() 方法设置的。它可以使用1或3个参数。单个标量值将在所有方向上均等地缩放角色,例如,用于在所有方向上将 .SetScale(3.0) 模型的大小均等地增加三倍。要在模型的宽度和深度而不是高度上缩放模型(请记住VTK使用Y轴作为其垂直方向),可以将其 .SetScale(x, 1, x) 用作缩放因子 x 。在actor下方,首先在每个方向上将因子缩放为5,然后在x,y和z方向上将因子缩放为4、2和3。
factor = 5
actor.SetScale(factor)
factor = [4.0, 2.0, 3.0]
actor.SetScale(factor)
演员可以用三种方式进行旋转: .RotateX(theta) .RotateY(theta) ,和 .RotateZ(theta) 。这 theta 是旋转模型的角度(以度为单位)。这些角度是相对于模型的局部坐标系,而不是全局坐标系。 关于订购的注意事项 重要的是要考虑旋转的顺序。以不同顺序执行的相同旋转可能会提供不同的方向。平移发生在全局坐标系中,比例因子和旋转相对于模型的局部坐标系。您不仅需要考虑旋转是一般旋转还是非交换(这意味着如果以不同的顺序执行旋转,它们可能会产生不同的结果),而且定义旋转的坐标系也会随旋转而变化。因此,调用 .RotateX(theta) 实际上取决于绕其进行的旋转来指定绕不同轴的旋转。 与上一点在一起的是,有多种方法可以旋转对象以达到特定方向。Actor具有一种 .GetOrientation() 返回模型当前方向的方法。指定这些值,以便如果按照z,x,然后y的顺序执行旋转,则actor将到达其当前方向。类似地,该方法 .AddOrientation(x, y, z) 是相当于执行 .RotateZ(z) .RotateX(x) .RotateY(y) 以该顺序。 示例:3D绘图脚本 在本章的3D绘图应用程序示例中,将本章前面开发的许多较小的技能结合在一起。VTK库通常提供几种方法来获得相同的结果。有很多方法可以开发此示例。选择此处使用的方法是因为它们建立在上面开发的技能之上。最终结果是一个小型应用程序,可以绘制3D中任意曲面的方程式。尽管这显示的是由方程式定义的更抽象的表面,而不是现实生活中的对象或几何图形-VTK非常适合该对象或几何图形-这是仅使用上面开发的技能创建的有用且完整的应用程序的示例。 第一步是提供用于绘制表面的必要输入参数。当然,这些参数中的第一个是将要绘制的函数本身。函数是 func_to_calculate(x, y) 。给定X和Y坐标时,它可以是返回Z值的任何有效函数。
x0 = -7 # X domain low end
y0 = -7 # Y domain low end
x_max = 7 # X domain high end
y_max = 7 # Y domain high end
X = 100 # Resolution in x direction. Integer
Y = 100 # Resolution in y direction. Integer
上面列出了下一个输入参数。它们描述了要绘制的函数的域以及进行该绘制的数值分辨率。这些参数也显示在[ mesh_grid_with_origin_figure ] 。其中指定域中的4个变量是 x0 x_max y_0 ,和 y_max 。它们描述了XX 和 ÿÿ 指示。 在此示例的代码清单中,没有错误检查,但是更精致的示例将检查这些参数以确保它们正确。这可以包括确保 x_max x0 其他检查更大。实际上,比起在Python文件顶部输入参数,更精美的示例将找到一种更优雅的方式来输入这些参数!参数 X Y 指定要在其各自方向上使用的像元数。通过这些详细信息,我们可以计算出XX 和 ÿÿ 每个单元的尺寸。
deltax = (x_max - x0) / float(X)
deltay = (y_max - y0) / float(Y)
并非必须使用float(),但很有用,因为如果 int 仅对(整数)个值执行除法,则Python版本2会丢弃余数。此功能可强制进行浮点除法并避免此问题。 就像前面的示例一样,我们创建的实例, vtkPoints vtkCellArray 在其中存储单元和点以定义曲面。
points = vtk.vtkPoints()
cells = vtk.vtkCellArray()
我们需要计算网格中的所有点。该表面由在x和y方向上具有均匀间距的四边形单元定义。对于x和y方向,点的行比单元格多。作为 X Y 包含每个维度上的像元数,我们遍历 X+1 Y+1 指向每个维度。
zvals = []
for j in range(Y+1):
    for i in range(X+1):
        x = x0 + deltax*i
        y = y0 + deltay*j
        z_val = func_to_calculate(x, y)
        zvals.append(z_val)
        coord = x, y, z_val
        points.InsertNextPoint(coord)
在上面,Z值存储在列表中, zvals 以供以后在代码中以及在使用该 .InsertNextPoint(coord) 方法的坐标点中使用。将它们存储在本地Python数据结构中也更加方便。请注意, .InsertNextPoint() .append() 方法在同一循环中调用。该点的相同全局ID(在 .InsertNextPoint() 调用时隐式设置)也可以用于索引 zvals 以获取相同的Z值。稍后在创建添加到网格的标量点数据时使用。
for j in range(Y):
    for i in range(X):
        quad = vtk.vtkQuad()
        corner_id = get_id(i, j)
        quad.GetPointIds().SetId(0, corner_id)
        quad.GetPointIds().SetId(1, corner_id + 1)
        quad.GetPointIds().SetId(2, corner_id + (X+2))
        quad.GetPointIds().SetId(3, corner_id + (X+1))
        cells.InsertNextCell(quad)
下一步是 vtkQuad 在多边形网格中创建元素。这些元素有四个ID为0、1、2和3的本地点,这些本地点需要与4个全局点ID关联。使用插入点的顺序分配了点ID points.InsertNextPoint(coord) 。如果使用了上面代码清单所示的二维循环,则ID,ññ,具有最低节点的 XX 和 ÿÿ坐标(请参见[ arbitrary_cell ] )由i + j (X+ 1 )一世+Ĵ(X+1个),其中 X 提供了输入参数。和ññ其他3个点的ID可以直接确定。创建点和像元后,它们将被添加到网格中。
mesh = vtk.vtkPolyData()
mesh.SetPoints(points)
mesh.SetPolys(cells)
点数据放置在中 vtkDoubleArray (见下文)。然后将其设置为网格中的点数据。
point_data = vtk.vtkDoubleArray()
point_data.SetNumberOfComponents(1)
for zval in zvals:
    point_data.InsertNextTuple([zval])
mesh.GetPointData().SetScalars(point_data)
本可视化中有两个新功能尚未在本章中介绍。如果您已经阅读了本章的上一内容,尽管它们是新的,但它们的用法很简单。当我们渲染表面时,它被框的轮廓和轴所围绕。这两个附加功能可以帮助可视化对象:第一个功能是在模型周围勾勒出一个框,第二个功能是显示坐标轴以帮助确定尺寸。这两个都要求 vtkPolyDataNormals 从多边形网格派生实例。使用以下内容可以轻松生成此信息:
norms_generator = vtk.vtkPolyDataNormals()
norms_generator.SetInputData(mesh)
然后使用该数据生成坐标轴。
axes = vtk.vtkCubeAxesActor2D()
axes.SetInputConnection(norms_generator.GetOutputPort())
axes.SetCamera(renderer.GetActiveCamera())
axes.SetLabelFormat("%1.1g")
类似地,轮廓过滤器使用以下内容创建。
outline_filter = vtk.vtkOutlineFilter()
outline_filter.SetInputConnection(norms_generator.GetOutputPort())
轴是Actor2D,可以像其他任何actor一样添加到渲染器中。轮廓过滤器可以像常规实例一样对待, vtkPolyData 这意味着将其连接到a vtkPolyDataMapper 和a vtkActor ,然后使用添加到渲染器 renderer.AddActor(outline_actor) 最终结果如下所示。 在科学的可视化中,颜色的使用非常重要。在VTK中,数据通常被映射到一个调色板,当呈现时,该调色板允许用户非常快速地吸收有关一组数据的大量信息。了解VTK中颜色的定义非常重要,尤其是对于定义查找表而言。 通过指定3个单独的浮点值可以描述任何颜色。VTK使用范围[0.0,1.0]。VTK使用两种模型来描述颜色。第一个颜色模型是红/绿/蓝(RGB)模型[ 2 ]。VTK中的方法采用或返回[0.0,1.0]范围内的3个浮点数。这些描述了构成该颜色的红色,绿色和蓝色的数量。RGB值(1.0,0.0,0.0)为亮红色;(1.0,1.0,1.0)的RGB值为白色,(0.0,0.0,0.0)的RGB值为黑色。同时增大或减小这三个值将使所得的颜色变亮或变暗。 图12.三行色样,显示了更改HSV参数时结果颜色的效果。在每一行中,一个参数都会变化,而其他参数则保持最大值。第一行:色调变化,显示红色,黄色,绿色和蓝色之间的变化。第二行:紫色的饱和度降低了,变为灰色。第三行:与第二行相同的颜色值降低了,变为黑色。 另一个模型是色相/饱和度/值(HSV)模型[ 3 ]。这也需要3个参数来描述颜色,但是这3个值描述了不同的信息。对于某些人来说,该模型可能更直观。第一个数字 hue 是一个人可能会认为 颜色。想象一下,穿过彩虹,色调将决定颜色是红色,橙色,黄色等。饱和度和值描述了这种颜色的强度,但是以两种不同的方式。饱和度描述了有多少种颜色。饱和度为0.0会更改所有颜色以灰度显示。该值描述颜色的亮或暗。不管其他值如何,值0.0始终会产生黑色,而最亮的值为1.0。 在某些地方,指定了第4个值:alpha值。这指定了表面的透明度。Alpha值0.0是一个完全透明的表面,而与其他3种颜色值无关。Alpha值为1.0时,表面完全是不透明/不透明,并显示为其他3个值指定的颜色。alpha值为1.0时将完全不透明。 下表总结了本章中描述的命令。 lut.UseBelowRangeColorOn() lut.UseBelowRangeColorOff() lut.UseAboveRangeColorOn() lut.UseAboveRangeColorOff() 对于查找表, lut 控制是否对范围之外的值使用自定义颜色。默认情况下关闭。 lut.SetHueRange(low, high) lut.SetSaturationRange(low, high) lut.SetValueRange(low, high) 对于该 vtkLookupTable lut 设置,色相,饱和度或值范围从 low high 。设置 low = high 固定为单个值。