VTK 初步 (2) ----- 基本数据结构

VTK 初步 (2) ----- 基本数据结构

前言: 解决是什么样的数据应用到 VTK 可以显示出模型的问题;夹杂大量的个人理解,可能有偏差。

参考资料:

《An Object-Oriented Approach to 3D Graphics》

《The VTK User’s Guide》

文章目录

  • VTK 初步 (2) ----- 基本数据结构
    • 1. 概述
      • 1.1 简单问题
      • 1.2 vtk 的解决方案
      • 1.3 简单例子
    • 2. 数据结构
      • 2.1 几何结构
      • 2.2 拓扑结构
      • 2.3 属性数据
      • 3.4 数据集
    • 写在后面

1. 概述

1.1 简单问题

上节实例中使用了vtk中自带类 vtkConeSource 通过设定参数实例化了一个锥体。锥体数据是什么样的?可否自己设定类似格式的数据,从而生成想要的实体?

从最简单问题入手,思考如何显示一个三角形 ?

首先需要定义三个点,三个点可以用坐标表示;其次把点按一定次序连接后就形成了三角形。坐标表示比较好办;按次序连接,次序如何规定?对于三角形而言,无非就是顺时针或者逆时针。为了方便,可以在设定点的时候为点设定一个 ID,在连接时,只需按 ID 逐次连接即可。

如果这是个类似于 *.stl 文件中的三角面片呢,即除了三角形结构外,每个三角形还有法向量数据(又如最简单的颜色数据、具有物理含义的数据等)。解决方案很简单,只需在定义好三角形点坐标和连接顺序后,在加入一个额外数据即可。而这三部分的数据内容就可生成一个可视化的模型了。

1.2 vtk 的解决方案

上述的一些过程,在 VTK 中有规范的名词描述。

定义一个个基础点坐标,在 VTK 中就是定义 几何结构(Geometry) ,在 VTK 中用点表示,使用 vtkPoints

定义点按顺序的连接,在 VTK 中就是定义 拓扑结构(Topology)。在 VTK 中用 拓扑结构类型节点 表示。

拓扑结构实际上分两个层次,一种是单元的拓扑结构(称作单元 Cell),一种是整个数据集的拓扑结构(用 CellArray 表示)。两者数据结构不同。

为什么会分两种,为何不直接用数据集拓扑结构表示?

一般模型(DataSet)都是由复杂的结构组成,这些复杂结构在 VTK 中不可能有直接对应的 拓扑结构类型,但却可以由一个个简单的拓扑结构(称作单元 Cell)堆积而成。常见简单拓扑结构(Cell)包括 点、线、三角形、四面体等,而这些简单拓扑结构(单元Cell)组合就形成了数据集的拓扑结构(CellArray)。

单元拓扑结构(Cell)与数据集拓扑结构(CellArray)数据储存方式也不同。

具体数据表达方式的异同,先按下不表,先将名词描述完毕,本节结尾以一个例子详细列出其储存方式。

几何结构 (点 points)拓扑结构 (单元集 CellArray) 组成了组织结构(没有属性数据的数据集)。如上所述,此时组织结构数据已经可以用于VTK,并最终渲染出模型了。

属性数据. 用于描述 点 或 单元(包含 Cell 和 CellArray)的属性, 一般用 标量或张量等。 常用类是 vtkPointData 和 vtkCellData。

组织结构属性数据 便组成了我们常用的数据集(DataSet)

整个数据集构建用如下图表示

VTK 初步 (2) ----- 基本数据结构_第1张图片

极其重要的两点认知:

  • Cell 根据类型以及点ID确定。不同单元类型产生不同单元的拓扑结构。

  • DataSet 是一系列包含组织结构数据和属性数据的集合,不同的组织结构数据需要使用不同类型的数据集。如对于二维和三维数据,其数据类型肯定是不一样的,因此也要指定与组织结构对应的数据集,从而接收到相应的组织结构和属性数据。最终才能准确无误的渲染出模型。

所以,下面(第二节中)也将有两种数据类型要介绍:单元(Cell)类型和数据集(DataSet)类型。

1.3 简单例子

根据以上理论,先实现一个简单例子: 构建一个点、线段、三角形,用 vtk 显示出来。

代码

./demo.cxx

#include 
#include 

#include 	// 点单元
#include 	// 线单元
#include 	// 三角形单元
#include 	// 四面体单元

#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include "vtkAutoInit.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2); // VTK was built with vtkRenderingOpenGL2
VTK_MODULE_INIT(vtkInteractionStyle);

int main()
{
     
	/* 设定集合结构 */
	vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
	// 组成点单元的点
	points->InsertNextPoint(0,0,0);
	// 组成线单元的点
	points->InsertNextPoint(0,-1,0);
	points->InsertNextPoint(1,-1,0);
	// 组成三角形单元的点
	points->InsertNextPoint(1,0,0);
	points->InsertNextPoint(0,1,0);
	points->InsertNextPoint(0,0,1);

	/* 设定单元的拓扑结构 */
	// 设定点单元的拓扑结构
	vtkSmartPointer<vtkVertex> vertex = vtkSmartPointer<vtkVertex>::New();
	vertex->GetPointIds()->SetId(0,0);

	// 设定线单元的拓扑结构
	vtkSmartPointer<vtkLine> line = vtkSmartPointer<vtkLine>::New();
	line->GetPointIds()->SetId(0,1);
	line->GetPointIds()->SetId(1,2);

	// 设定三角形单元的拓扑结构
	vtkSmartPointer<vtkTriangle> triangle = vtkSmartPointer<vtkTriangle>::New();
	triangle->GetPointIds()->SetId(0,3);
	triangle->GetPointIds()->SetId(1,4);
	triangle->GetPointIds()->SetId(2,5);
	
	/* 所有单元组成数据集单元(数据集的拓扑结构) */
	vtkSmartPointer<vtkCellArray> cellArray = vtkSmartPointer<vtkCellArray>::New();
	// 方法1 : 直接插入上面构建的 cell 对象
	cellArray->InsertNextCell(vertex);
	//cellArray->InsertNextCell(line);
	//cellArray->InsertNextCell(triangle);
	// 方法2 : 前者为点数量,后者为点 id 列表
	vtkIdType pts[2] = {
     1,2};
	cellArray->InsertNextCell(2,pts);
	// 方法3 : 直接输入点 id 列表
	cellArray->InsertNextCell({
     3,4,5});

	/* 几何结构和拓扑结构,构成数据集(ps:没有设定属性数据) */
	vtkSmartPointer<vtkPolyData> polydata = vtkSmartPointer<vtkPolyData>::New();
	polydata->SetPoints(points);
	polydata->SetVerts(cellArray);
	polydata->SetLines(cellArray);
	polydata->SetPolys(cellArray);


	/* 以下,可视化管线进行显示 */
	vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
	mapper->SetInputData(polydata);

	vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
	actor->SetMapper(mapper);

	vtkSmartPointer<vtkRenderer> ren = vtkSmartPointer<vtkRenderer>::New();
	ren->AddActor(actor);

	vtkSmartPointer<vtkRenderWindow> win = vtkSmartPointer<vtkRenderWindow>::New();
	win->AddRenderer(ren);

	vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New();
	iren->SetRenderWindow(win);
	win->Render();
	iren->Start();

	ren->Delete();
	win->Delete();
	iren->Delete();
	
	return 0;
	}

结果

VTK 初步 (2) ----- 基本数据结构_第2张图片

几点说明

  • 在构建几何结构,插入点的时候,自动标定点 ID 从 0 开始。且如 points->InsertNextPoint(0,0,0) 是有返回值的,返回值就为点的 ID (注意:此处返回值 ID 不是 int 类型)。

  • 上面在构建数据集拓扑结构时,对于点、线、三角形,分别展示了三种不同方法。都可行。

    具体可参考:vtkCellArray Class Reference

    	/* 所有单元组成数据集单元(数据集的拓扑结构) */
    	vtkSmartPointer<vtkCellArray> cellArray = vtkSmartPointer<vtkCellArray>::New();
    	// 方法1 : 直接插入上面构建的 cell 对象
    	cellArray->InsertNextCell(vertex);
    
    	// 方法2 : 前者为点数量,后者为点 id 列表
    	vtkIdType pts[2] = {
           1,2};
    	cellArray->InsertNextCell(2,pts);
    
    	// 方法3 : 直接输入点 id 列表
    	cellArray->InsertNextCell({
           3,4,5});
    
  • 点、线段、三角形的单元表示,都被包含于多边形类型的数据集中,所以可以用一个数据集 vtkPolyData 来表示。事实上, PolyData 数据集类型,就是指包含点、线、多边形的数据集合,因此才有对应的下列函数

    polydata->SetVerts();	// 加入点
    polydata->SetLines();	// 加入线
    polydata->SetPolys();	// 加入多边形
    
  • 关于 CellCellArray

    先给出上述例子的 cellcellArray的数据结构

    cell topology:
    ---------
    Cell 0: Vertex | point ids: {0}
    Cell 1: Line | point ids: {1, 2}
    Cell 2: Triangle | point ids: {3, 4, 5}
    
    vtkCellArray (current):
    -----------------------
    Offsets:      {0, 1, 3, 6}
    Connectivity: {0, 1, 2, 3, 4, 5}
    

    cell 部分很好理解,就是指定类型后再加上相应点ID,然后顺次连接。

    cellArray 储存了所有的 cell。其中包含两个列表数据 offsetsconnectivity

    • connectivity:就是上述所有 cell 的点 ID 顺序地放到一个列表里。但仅仅这样是不行的,因为不能辨认出那些点组合形成了什么cell,这就是 offsets 的功能了。
    • offsets:每个 cell 起始点对应在 connectivity 列表中索引。如上,offsets 还多出了一个数。最后一个数,是 connectivity 的长度值。

    具体依旧可参考:vtkCellArray Class Reference

2. 数据结构

按照上面总结的图片,依次说明

VTK 初步 (2) ----- 基本数据结构_第3张图片

2.1 几何结构

几行代码即可说明,就是通过指定点坐标确定一系列点。使用 vtkPoints

#include 

vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
// 获取返回值,即点的 ID
vtkIdType pid[1];
pid[0] = points->InsertNextPoint(0,0,0);
// 或者不使用返回值,默认 ID 从 0 编号
points->InsertNextPoint(0,0,0);

2.2 拓扑结构

这里主要是单元的拓扑结构,即 Cell。前面已经指出单元拓扑结构是由单元类型和点ID组成。

上面已经使用了点、线、三角形的 Cell 类型,并给出了如何指定点 ID 的方法。如上述中对三角形而言

vtkSmartPointer<vtkTriangle> triangle = vtkSmartPointer<vtkTriangle>::New();
triangle->GetPointIds()->SetId(0,3);
triangle->GetPointIds()->SetId(1,4);
triangle->GetPointIds()->SetId(2,5);

SetId(Num1, Num2) 中, Num1指明点在三角形中的编号,Num2指明使用vtkPoints点的 id。

不同的单元类型,通过给定点,生成不同结构。因此,重点在于都有那些单元(cell)类型上。事实上,VTK中单元类型可以分为线性非线性两大类。以下图均来自于官方指导书 《An Object-Oriented Approach to 3D Graphics - Chapter5》

另外一个重要点,前述,对于一个三角形而言,事实上有两种连接顺序,即顺时针和逆时针。VTK 三角形类型单元使用了逆时针连接。如下图中 © 所示。所以当指明了一个 Cell 类型,其点的顺序在 VTK 中已经按一定顺序确定了。

下面是VTK中线性类型 单元

VTK 初步 (2) ----- 基本数据结构_第4张图片

以及非线性类型 单元

VTK 初步 (2) ----- 基本数据结构_第5张图片

各单元的具体属性,同样可参考官方书《An Object-Oriented Approach to 3D Graphics - Chapter5》

可以看出,非线性往往是在线性的基础上增加了点,使得线变弯曲。严格说,两者是在插值上的区别。

2.3 属性数据

本质上就是在一些点、单元上的一个值。使用相应的函数,在特定点(可根据ID确定)添加,读取属性值即可。属性值类型及应用常分为以下几种

VTK 初步 (2) ----- 基本数据结构_第6张图片

3.4 数据集

如前所述,数据集是可以输入到 VTK 可视化管线中,从而渲染出具体模型。

而对于不同类型的单元,适用的数据集不同。因此需要选择适当的数据集。在 VTK 中常用数据集有下

VTK 初步 (2) ----- 基本数据结构_第7张图片

对于各个数据集的具体描述,同样见官方指导书。或使用时,具体查询。较为常用的是 ImageData、PolyData ,即用于表示图片数据和多边形数据。

写在后面

  • 考虑 vtkPointsvtkVertex 的区别。
  • 可以预想的是,对于 Unstructured Grid 类型的数据,相比于规则形状的数据,需要占据更多的空间,计算也需要更多的资源,所以应尽量避免之。
  • 实例仅生成了比较简单的多边形数据,考虑生成更复杂的模型。
  • 可以使用 VTK 进行点云的显示。

你可能感兴趣的:(VTK,&,ITK,数据结构,可视化,机器学习,python)