06-VTK基本数据结构(5)

6.6 数据集的存储与表达

可视化数据自身的特点决定了数据对象内存的分配与管理必须谨慎处理才有可能创建出高效的可视化系统。VTK中对绝大多数的数据对象的内存分配采用连续内存,连续内存的结构可被快速地创建、删除和遍历,称为Data Array (数据数组),用类vtkDataArray实现。

6.6.1 vtkDataArray

数据数组的访问是基于索引的,从零开始计数。我们以vtkFloatArray类来说明如何在VTK中实现连续内存的数据数组。如图6.13所示,变量Array是一个指向浮点型数组的指针,数组的长度由变量Size指定,由于数组的长度是动态地增加的,所以当存储数据的数组长度超出指定的长度时,会自动触发Resize()操作来调整数组的长度,使数组的长度变成原来的两倍,MaxId是一个整型的偏移量,用来定义最后一个插入的数据的索引。如果没有数据插入,MaxId等于-1,否则,MaxId的值介于0到Size之间,即0≤MaxId<Size。

 

图6.13连续数组的实现

6.6.2 Tuple(元组)的概念

许多可视化数据是由多个数据分量组成的,如RGB颜色数据由红、绿、蓝三个分量组成,为了在连续数组中表达这一类数据,VTK引入了元组(Tuple)的概念。元组是数据数组的子数组,用于存储数据类型相同的分量数据,图6.13所示的NumberOfComponents,表示的就是数据数组里元组的个数。元组的大小在给定后不会改变,图6.14所示的数据数组由n个元组组成,每个元组存储三个分量数据。

 

图6.14数据数组结构

vtkDataArray存储的是数值数据,如属性数据(Attribute Data)和点数据(Point)等。有些属性数据,如点、矢量、法向量和张量等,在定义时就需要指定元组的大小。例如,点、矢量和法向量等属性数据,元组的大小是3,而张量属性数据的元组大小是9 (即3×3的矩阵),标量属性数据对于元组的大小则没有任何要求,对于处理标量属性数据的算法,通常都是只处理标量每一个元组数据的第一个分量。VTK提供了将多分量的数据数组分离成单一分量的数据数组,以及将单一分量的数据数组合并成多分量的数据数组的类,即vtkSplitField和vtkMergeFields。

下列代码演示了如何创建固定长度及动态的数据数组,以加深对Data Array及Tuple概念的理解:

 

/**************************************************************************

固定长度的数据数组(DataArray)。下列代码创建了容量为20个元组(Tuple)的数据数组,

每个元组的分量个数为1,通过方法SetComponent()和GetComponent()设置及获取相应

的元组的值。

**************************************************************************/

vtkSmartPointer<vtkFloatArray > arr = vtkSmartPointer< vtkFloatArray >::New();

arr->SetNumberOfComponents(1);//设置元组的分量个数为1

arr->SetNumberOfTuples(20);//指定数据数组的长度为20个元组

arr->SetComponent(10,0, 10.0); //指定第10个元组的第0个分量的值为10.0

arr->SetTuple1(11,9.0); //指定第11个元组的值为9.0

doubleb = arr->GetComponent(10, 0); //获取第10个Tuple的第0个分量的值

 

/**************************************************************************

动态长度的数据数组。下列代码创建了一个具有动态长度的数据数组,每个元组的分

量个数为1,通过方法InsertNextTuple1()插入一个单分量的元组。与InsertNextTuple1()

类似的还有InsertNextTuple2()/InsertNextTuple3()/InsertNextTuple4()/InsertNextTuple9()等。

**************************************************************************/

vtkSmartPointer<vtkFloatArray > arr = vtkSmartPointer< vtkFloatArray >::New();

arr->SetNumberOfComponents(1);//设置元组的分量个数为1

arr->InsertNextTuple1(5);  //插入一个单分量的元组,其值为5

arr->InsertNextTuple1(10);

doubleb = arr->GetComponent(1, 0);

6.6.3 抽象/具体数据数组对象

可视化数据有各种各样的类型,如简单的浮点型、整型、字节型和双精度型等,再复杂一点的类型,如特征字符串和多维标识符等。既然有这么多种数据类型,那么数据数组是如何操作和表达这些数据的呢?VTK通过对数据对象的抽象(AbstractData Object)提供运行时解决方案以及使用C++编译时动态绑定的方法(模板类)来解决这个问题的。如图6.15所示,vtkDataArray是一个抽象基类,其子类实现特定类型的数据数组及相关操作。

 

图6.15 数据数组对象(只列出部分数据数组类)

抽象数据对象通过动态绑定的方式使用统一的接口来创建、操作和删除数据,C++中用virtual关键字来声明动态绑定方法。动态绑定允许我们通过操作抽象父类的方法来调用具体子类对象的实现。以vtkDataArray为例,我们可以调用该抽象父类的方法GetTuple1(129),来获取ID为129的点数据值。因为GetTuple1()方法是在抽象父类vtkDataArray中定义的虚函数,且返回的数据类型是double,因此每一个从vtkDataArray派生的子类,都必须实现该方法,返回一个double型的数据。

虚函数的使用有其特定的优势之处,例如不用考虑具体的数据类型,而写出更加通用的可视化算法。但大量虚函数使用会导致程序性能的下降。为了处理各种各样的数据类型,vtkDataArray还采用了模板类的方法。

要使用模板类,需要知道原始数据的类型以及指向该数据的指针。一般vtkDataArray使用类似以下的代码:

switch(outData->GetScalarType() )

{

    case VTK_CHAR

    { typedef char VTK_TT;

       func (arg1, arg2, arg3, VTK_TT* arg4,VTK_TT* arg5); }

    break;

    case VTK_UNSIGNED_CHAR

    { typedef unsigned char VTK_TT;

      func (arg1, arg2, arg3, VTK_TT* arg4, VTK_TT* arg5); }

    break;

    … for all types …

}

实际的VTK代码使用了宏以及C++的操作符static_cast<>来进行类型的转换。注意以上代码中的func()是一个模板函数。例如(摘自vtkImageCursor3D.cxx127-134行):

  switch (outData->GetScalarType())

    {

    vtkTemplateMacro(

      vtkImageCursor3DExecute(this,outData,static_cast<VTK_TT *>(ptr)));

    default:

      vtkErrorMacro(<< "Execute:Unknown ScalarType");

      return 1;

    }

模板函数的使用相较于虚函数而言,虽然能在一定程度上提高程序的性能,但同时也增加了代码的复杂性。

6.6.4 数据对象和数据集的表达

vtkDataArray及其子类是建立VTK数据对象(DataObject)的基础。以vtkPolyData为例,该类含有存储几何结构的数据数组(在vtkPoints类内),拓扑结构(存储在vtkCellArray内)和属性数据(vtkField,vtkPointData和vtkCellData类内)等同样有数据数组。vtkDataObject是一种通用的可视化数据的表达形式,内部封装了与可视化管线的执行相关的变量和方法,在vtkDataObject内部有一个vtkFieldData(场数据)的实例,负责对数据的表达。如图6.16所示,场数据(FieldData)可以看作是数组的数组,数组里的每一个元素都一个数组,数组的类型、长度、元组的大小和名称等都可以各不相同。VTK里的可视化算法很少有直接对vtkDataObject作处理,大多数的算法更关心的是待处理数据的组织结构(OrganizingStructure)等信息。

 

图6.16vtkDataObject数据对象的表达

图6.17是类vtkFieldData的继承图,从类的名字我们能够推断出,vtkFieldData存储的数据是与数据对象的属性数据相关的。以vtkPolyData为例,vtkPolyData内部存储了三种类型的数据,分别是vtkPointData、vtkCellData和vtkFieldData。vtkPointData是与每一个点相关联的数据,如某点上的温度值;vtkCellData是与每一个单元相关联的数据,如某个三角形单元的面积;除点和单元数据以外的数据,应该使用vtkFieldData,如数据的质心(Centerof mass)。

 

图6.17vtkFieldData类的继承图

关于vtkFieldData的使用,请参考下列代码。

int main(int, char *[])

{

 vtkSmartPointer<vtkSphereSource> source =vtkSmartPointer<vtkSphereSource>::New();

  source->Update();

 

  // Extract thepolydata

  vtkSmartPointer<vtkPolyData>polydata = vtkSmartPointer<vtkPolyData>::New();

 polydata->ShallowCopy(source->GetOutput());

 

  vtkSmartPointer<vtkDoubleArray>location = vtkSmartPointer<vtkDoubleArray>::New();

 

  // Create the data tostore (here we just use (0,0,0))

  doublelocationValue[3] = {0,0,0};

 location->SetNumberOfComponents(3);

 location->SetName("MyDoubleArray");

 location->InsertNextTuple(locationValue);

 

  // The data is addedto FIELD data (rather than POINT data as usual)

 polydata->GetFieldData()->AddArray(location);

 

  vtkSmartPointer<vtkIntArray>intValue = vtkSmartPointer<vtkIntArray>::New();

 intValue->SetNumberOfComponents(1);

 intValue->SetName("MyIntValue");

 intValue->InsertNextValue(5);

 

 polydata->GetFieldData()->AddArray(intValue);

 

  return 0;

}

 

这一章有几个概念比较容易混淆,包括数据对象(vtkDataObject)、数据集(vtkDataSet)、数据数组(vtkDataArray)等,理解它们之间的关系,有助于我们更容易地看懂VTK的代码,写出高效的VTK可视化程序。其中数据集DataSet与数据数组DataArray之间的关系,在《TheVTK User’s Guide – 11th Edition 》这本书的第16章第1段有如下的内容:“In this section we provide detailed information describingthe interface to the many data objects in VTK. This ranges fromdatasets,which are processed by the filterobjects in visualization pipelines, todata arrays, whichare used to represent a portion of a dataset (e.g., the scalar data).”

从上面这段话我们大概能够搞清楚Dataset (数据集)与DataArray (数据数组)这两个概念的区别。简单地理解为,数据集是VTK可视化管线所处理的对象;而数据数组是用于表达数值数据的内存组织形式,比如用数据数组来表示数据集里的标量数据。后续章节将会以程序示例的形式来加深对这些概念的理解。

==========欢迎转载,转载时请保留该声明信息==========

版权归@东灵工作室所有,更多信息请访问东灵工作室


教程系列导航:http://blog.csdn.net/www_doling_net/article/details/8763686

================================================

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容