【译者:这个系列教程是以Kitware公司出版的《VTK User’s Guide -11th edition》一书作的中文翻译(出版时间2010年,ISBN: 978-1-930934-23-8),因为时间关系,我们不能保证每周都能更新本书内容。但尽量做到一周更新一篇到两篇内容。敬请期待^_^。欢迎转载。另请转载时注明本文出处,谢谢合作!同一时候,因为译者水平有限。出错之处在所难免,欢迎指出订正。】
【本小节内容相应原书的第89页至第95页】
前面的章节中我们已经介绍了一些数据渲染和交互的基本工具。本章中我们主要介绍一下可视化技术。这些技术(实现为Filter)依据其操作数据类型来组织。当中部分Filter是可以处理不论什么数据类型的一般性Filter,他们可以接收vtkDataSet(或者随意的子类)。而很多其它的Filter则是依赖于其处理的数据类型(如vtkPolyData)的专门性Filter。
当中处理vtkImageData(或者其子类vtkStructuredData)数据类型的一类Filter本章中暂不涉及。将在下一章中详细讨论(第103页“图像处理和可视化”)。
阅读本章时。有两点须要铭记在心。一是Filter会产生多种输出数据类型,而这些类型并不一定与输入数据类型一致。第二,Filter会通过组合来创建复杂的数据处理管线。
从本章中的样例中能够看到一些常见的Filter组合。
5.1 vtkDataSet(及其子类)的可视化
本节主要解说vtkDataSet数据对象的一些经常使用可视化操作。
注意vtkDataSet是VTK中全部数据类型的超类类型(见图3-2)。因此,这里讲到的操作都适用于全部的数据类型。换句话说。全部接收vtkDataSet类型的Filter都能够接收vtkPolyData,vtkImageData,vtkStructured,vtkRectilinearGrid和vtkUnstructuredGrid类型。
数据属性操作(WorkingWith Data Attributes)
数据属性是与数据结构相关的信息(如25页“可视化管线”所述)。VTK中,属性数据分为点属性数据和单元属性数据。属性数据与结构数据一起被很多的Filter处理产生新的结构和属性。关于属性数据的内容不是本章的重点。这里给出了一个简单的样例来讲述主要的思想。(很多其它信息请參阅362页“场和属性数据”和图16-1。
)
数据属性是简单的vtkDataArray类型,它能够是标量scalar,向量vector,张量tensor,法向normal,纹理坐标texture coordinate,全局id (global id,如标识大量元素),或者pedigreeids(用来追踪管线中的元素历史)。vtkDataSet中的一个点或者单元都有独立的属性数据。数据属性能够关联vtkDataSet中的点或者单元。与vtkDataSet关联的vtkDataArray都是vtkDataArray的一个详细子类,比如vtkFloatArray或者vtkIntArray。这些数据数组能够看做连续的、线性的内存块。
在内存块中,数据数组能够看作由子数组或者“元组”(Tuple)组成。创建属性数据即是依据类型实例化数组内存。制定元组大小,插入数据并与数据集关联,如以下Tcl脚本所看到的。
属性数据类型能够指定为标量,向量。张量,纹理坐标或者法向。比如:
vtkFloatArray scalars scalarsInsertTuple1 0 1.0 scalarsInsertTuple1 1 1.2 ...etc... vtkDoubleArray vectors vectorsSetNumberOfComponents 3 vectorsInsertTuple3 0 0.0 0.0 1.0 vectorsInsertTuple3 1 1.2 0.3 1.1 ...ect... vtkIntArray justAnArray justAnArray SetNumberOfComponents 3 justAnArray SetNumberOfTuples $numberOfPoints justAnArray SetName "Solution Attributes" justAnArray SetTuple2 0 1 2 justAnArray SetTuple2 1 3 4 ...etc... vtkPolyData polyData; #A concrete type of vtkDataSet [polyDataGetPointData] SetScalars scalars [polyDataGetCellData] SetVectors vectors [polyDataGetPointData] AddArray justAnArray
这里创建了三种类型的属性数据,float。double和int。第一个数组scalars实例化后。默认的元组大小为1。
InsertTuple1()方法用来向数组中插入数据(全部Insert___()方法负责分配足够的内存来保存数据)。下一个数组vectors的元组数为3,因此vectors定义为含有三个分量,InsertTuple3用来向数组中加入数据。最后创建的是元组数为2的数组,通过SetNumberOfTuples()分配内存。接着通过SetTuple2()加入数据;该方法使用的前提是内存已经分配,因此速度要明显快于Insert__()方法。当将属性数据关联到点数据或者单元数据时,注意差别设置类型的方法(SetScalars()和SetVectors())。注意点属性个数必须与数据结构中的点个数一致,单元属性与数据结构的单元个数一致。
类似的,採用例如以下方法訪问属性数据
set scalars [[polyData GetPointData] GetScalars] set vectors [[polyData GetCellData] GetVectors]
很多的Filter须要专门的属性数据进行工作。比如,vtkElevationFilter依赖于对应的高度数据产生标量值。其它的Filter仅仅依赖于结构数据,并忽略传来的属性数据。另一些Filter须要结构数据和属性数据来工作,如vtkMarchingCubes。它利用输入的标量属性数据和结构数据来产生轮廓结构。其它类型的属性数据。比如向量。在计算轮廓时进行差值计算并输出。
还有一个与属性数据相关的重要问题是,有些Filter仅仅输出一种类型的属性。忽略其它的类型。比如,当你想採用一个不能处理输入数据的属性数据类型的Filter来处理数据时,或者你想直接将其从一种类型转换至还有一个类型。有2个Filter能够帮你实现:vtkPointDataToCellData和vtkCellDataToPointData。
以下样例演示来如何使用他们(摘自VTK/Examples/DataManipulation/Tcl/pointToCellData.tcl)。
vtkUnstructuredGridReader reader readerSetFileName "$VTK_DATA_ROOT/Data/blow.vtk" readerSetScalarsName "thickness9" readerSetVectorsName "displacement9" vtkPointDataToCellData p2c p2cSetInputConnection [reader GetOutputPort] p2cPassPointDataOn vtkWarpVector warp warpSetInputConnection [p2c GetOutputPort] vtkThreshold thresh threshSetInputConnection [warp GetOutputPort] threshThresholdBetween 0.25 0.75 threshSetAttributeModeToUseCellData<span style="font-size: 14px; line-height: 1.846153846; font-family: Arial; background-color: rgb(255, 255, 255);"> </span>
该样例演示了如何转换属性数据类型,以及一个能够随意处理单元数据或者点数据的Filter(vtkThreshold)。
PassPointDataOn()方法设置vtkPointDataToCellData来创建单元数据,并将点数据输出。
SetAttributeModeToUseCellData()设置vtkThreshold使用单元数据来进行处理。
点数据和单元数据之间的转换採用的是一个平均算法。
将一个给定单元的全部点的数据求平均值就可以将点的属性数据转换为单元数据。而单元数据转换为点数据则是通过计算全部用到该点的单元的数据的平均值。
颜色映射(ColorMapping)
最经常使用的可视化技术可能是通过标量值或者颜色映射来对物体着色。着色技术的思想比較简单,将标量值映射到一个颜色查找表来获取颜色,然后在渲染时使用颜色来改变点或者单元的外观。在阅读本节前。请先理解如何控制Actor的颜色(详见54页“Actor颜色”一节)。
VTK中颜色映射主要由用户生成或者数据文件里的标量数据和vtkMapper实例执行颜色映射使用的颜色查询表来控制。也能够使用随意的数据数组通过ColorByArrayComponent()方法来控制。
假设没有指明,Mapper会生成一个默认的颜色查询表,你也能够自己创建(下例摘自VTK/Examples/Rendering/Tcl/Rainbow.tcl,执行结果如图5-1所看到的)。
vtkLookupTable lut lut SetNumberOfColors 64 lut SetHueRange 0.0 0.667 lut Build for {set i 0} {$i<16} {incr i 1} { eval lutSetTableValue [expr $i*16] $red 1 eval lutSetTableValue [expr $i*16+1] $green 1 eval lutSetTableValue [expr $i*16+2] $blue 1 eval lutSetTableValue [expr $i*16+3] $black 1 } vtkPolyDataMapper planeMapper planeMapper SetLookupTable lut planeMapper SetInputConnection [planeGetOutputPort] planeMapper SetScalarRange 0.197813 0.710419 vtkActor planeActor planeActor SetMapper planeMapper
图5-1 颜色映射
如该例所看到的,操作查询表有两种方式。一是指定一个HSVA范围然后在HSVA空间中插值计算颜色表中颜色(实际由build()函数计算颜色)。
另外一种方法是在依据颜色表的位置人为指定颜色。
注意颜色表中的颜色数目能够设置。本例中利用HSVA范围生成颜色表。然后利用SetTableValue()函数替换掉对应的颜色。
Mapper的SetScalarRange()函数控制标量值与颜色表的映射方式。大于最大值的全部标量值都被映射为最大值,小于最小值的全部标量值都映射为最小值。
标量范围设置,能够通过在某一数据范围内映射很多其它的颜色来达到“扩展”的目的。
非常多情况下标量数据就是颜色值。这样就不须要通过颜色查找表进行映射。
映射器Mapper提供了多种方法来控制颜色映射。
- SetColorModeToDefault()设置使用默认Mapper映射方法。默认方法直接将三个unsignedchar类型标量数据作为颜色值而不须要进行映射。而其它的类型数据则是通过颜色查找表映射。
- SetColorModeToMapScalars()通过颜色查找表来映射全部类型的标量数据。假设每一个元组由多于1个分量组成,那么通过第0个分量来运行颜色映射。
- vtkMapper还有一个重要特性就是控制运行颜色渲染使用的数据,比如点或者单元数据,或者是数据数组。该功能能够由例如以下方法完毕。
须要注意的是,这些方法会产生很不同的渲染结果:在渲染时点标量数据模式下。几何体表面全部的点数据通过插值后映射颜色,而单元标量数据模式下则将每一个单元渲染为一个颜色。 - SetScalarModeToDefault()触发默认Mapper行为。Mapper默认採用点标量数据渲染;假设点标量数据不可用,则採用可用的单元数据进行渲染。
- SetScalarModeToUsePointData()利用点标量数据进行颜色映射完毕渲染。假设没有点标量数据可用。那么标量数据将对物体颜色没有不论什么影响。
- SetScalarModeToUseCellData()利用单元标量数据进行颜色映射渲染物体。假设没有单元标量数据可用,那么标量数据将对物体颜色没有不论什么影响。
- SetScalarModeToUsePointFieldData()利用点属性数据中的数据数组。而不是点标量数据和单元标量数据。
该函数须要结合ColorByArrayComponent()函数来指定数据数组和作为标量数据的分量。 - SetScalarModeToUseCellFieldData()利用单元属性数据中得场数据。而不是点或者单元标量数据。该方法也须要结合ColorByArrayComponent()函数来指定数据数组和作为标量数据的分量。
正常情况下Mapper默认行为都会正常工作。除非点和单元标量数据都可用。这时。你须要显式指明用点或者面标量数据来对物体着色。
轮廓(Contouring)
还有一个常见的可视化技术是轮廓生成。轮廓是指具有同样标量数据的线或者面。VTK中vtkContourFilter执行轮廓生成,如以下的Tcl样例所看到的。
演示样例代码摘自VTK/Examples/VisualizationAlgorithms/Tcl/VisQuad.tcl。执行结果如图5-2所看到的。
#Create 5 surfaces in range specified vtkContourFilter contours contours SetInputConnection [ Sample GetOutputPort] Contours GenerateValues 5 0.0 1.2 vtkPolyDataMapper contMapper contMapper SetInputConnection [contours GetOutputPort] contMapper SetScalarRange 0.0 1.2 vtkActor contActor contActor SetMapper contMapper
图5-2 生成轮廓
有两种方式来指定轮廓值。
最简单的方式是通过SetValue()方法来指定轮廓数和相应的轮廓值(能够指定多个值)。
ContoursSetValue 0 0.5
演示样例代码中採用的是另外一种方法,即GenerateValues()方法。
通过该函数,你能够指定数据范围和该范围内要生成的轮廓个数(包括结束数据)。
注意在VTK中有多个对象来针对特定数据类型运行轮廓生成。如vtkSynchronizedTemplates2D和vtkSynchronizedTemplates3D。
假设利用vtkContourFilter的话。不须要直接实例化这些类型的对象,该Filter会依据数据类型自己主动生成对应的轮廓生成函数。
符号化(Glyphing)
符号化是一种利用字符或字形来表示数据的可视化技术(图5-3)。
这些符号能够简单。也能够复杂。
简单的如利用有向锥体来表示向量数据。复杂的有多元字形。如Chernoff faces(由数据来控制表情的人脸字形表示)。
VTK中利用vtkGlyph3D类来产生能够放缩、着色和具有方向的字形。输入数据的每一个点都会拷贝一个字形。字形本身则是通过该Filter的第二个输入函数接收(接收vtkPolyData类型数据)。以下代码说明了vtkGlyph3D的使用。(代码摘自VTK/Examples/VisualizationAlgorithms/Tcl/spikeF.tcl)
vtkPolyDataReader fran franSetFileName "$VTK_DATA_ROOT/Data/fran_cut.vtk" vtkPolyDataNormals normals normalsSetInputConnection [fran GetOutputPort] normalsFlipNormalsOn vtkPolyDataMapper franMapper franMapper SetInputConnection [normals GetOutputPort] vtkActor franActor franActor SetMapper franMapper eval[franActor GetProperty] SetColor 1.0 0.49 0.25 vtkMaskPoints ptMask ptMask SetInputConnection [normals GetOutputPort] ptMask SetOnRatio 10 ptMask RandomModeOn # In this case we are using a cone as a glyph. Wetransform the cone so # its base is at 0,0,0. This is the point whereglyph rotation occurs. vtkConeSource cone cone SetResolution 6 vtkTransform transform transformTranslate 0.5 0.0 0.0 vtkTransformPolyDataFilter transformF transformF SetInputConnection [cone GetOutputPort] transformF SetTransform transform vtkGlyph3D glyph glyph SetInputConnection [ptMask GetOutputPort] glyph SetSourceConnection [transformF GetOutputPort] glyph SetVectorModeToUseNormal glyph SetScaleModeToScaleByVector glyph SetScaleFactor 0.004 vtkPolyDataMapper spikeMapper spikeMapper SetInputConnection [glyph GetOutputPort] vtkActor spikeActor spikeActor SetMapper spikeMapper eval[spikeActor GetProperty] SetColor 0.0 0.79 0.34
图5-3 在表面法向量位置上显示符号
这段代码演示了如何用有向小锥体来表示曲面法向量。
首先读入并显示一个数据集(由Cyberware激光数字化系统採集)。然后vtkMastPoints用来对输入数据点进行降採样,採样结果(附带属性数据)作为vtkGlyph3D的输入。vtkConeSource用来生成字形实例。
注意锥体採用vtkTransformPolyDataFilter平移来使原点位于(0, 0, 0) (由于vtkGlyph3D绕原点旋转)。
vtkGlyph3D对象glyph设置用点属性法向量作为方法向量。(SetVcetorModeToUseVector()设置使用向量数据来替代法向量)它利用向量模值来对锥体进行放缩。(当然也能够利用SetScaleModeToScaleByScalar()函数设置通过标量数据对字形进行放缩或者利用SetScaleModeToDataScalingOff()函数关闭放缩功能。)
当然也能够利用标量数据、向量数据或者放缩因子对字形进行着色。还能够创建一个字形表,採用标量或者向量数据来尽力索引。參考在线文档来获取很多其它信息。