您的位置:首页 > 其它

VTK经验分享 4. VTK数据集实例

2011-09-03 22:35 423 查看
建立数据集的方式多种多样,我们可以用编程的方式从无到地建立起一个数据集,也可以利用vtk的io功能直接从文件读取数据集。

我们首先看编程的方式,再说读文件的方式。

4.1 编程方式建立数据集

在开始建立数据集之前,我们一定要清楚:数据集有六种(请见前篇介绍),我们即将建立的这个数据集最符合哪一种?之后,我们就使用对应的vtk数据集类,运用相关api,开始建立数据集。

无论我们使用何种数据集类型,如何编程,最终的目的都是一样的:直接或者间接地指明这个数据集的几何结构、拓扑结构,和数据属性(Data Attribute)。具体来说,就是:

①. 在正交坐标系空间中,用一系列3维坐标点表示出数据集的几何结构。

②. 将这些点以某方式联系起来,从而在这个空间中划分出若干基元,表示出数据集的拓扑结构。

③. 在①步骤中指定的3维坐标点,或者②步骤中划分出的基元上,绑定有物理意义的数据(Data Attributes),这些数据可以是标量、矢量、或是张量

4.1.1 Demo1:麻雀虽小五脏俱全 -- 四个点,一个基元,也是数据集:)

对于demo来说,当然是越简单越好,越能说清楚事情越好。于是我们将建立一个仅由四个点的组成的数据集,它唯一的基元便是这四个点围起来形成的。那它属于那种数据集呢?进一步分析一下:这个数据集实际上就是一个四边形,也就是说,它是一个最基本的图元(参考前篇3.1.2),再回顾一下PolyData,它的定义是“a collection of graphics primitives”,即它是图元的集合,那我们这个数据集就是polyData了。

ps:请多注意下polyData这类数据集,虽然它的几何、拓扑结构都是不规则的,但是它本身就是由图元组成,而图元是可以直接渲染的。所以这种数据集实际上是最简洁、高效的数据集。

全部代码如下所示,关键地方请看注释。

package linke;

import vtk.vtkActor;
import vtk.vtkCellArray;
import vtk.vtkDoubleArray;
import vtk.vtkFloatArray;
import vtk.vtkIntArray;
import vtk.vtkInteractorStyle;
import vtk.vtkInteractorStyleTrackballCamera;
import vtk.vtkPoints;
import vtk.vtkPolyData;
import vtk.vtkPolyDataMapper;
import vtk.vtkRenderWindow;
import vtk.vtkRenderWindowInteractor;
import vtk.vtkRenderer;

public class Demo1 {

static {
System.loadLibrary("vtkCommonJava");
System.loadLibrary("vtkFilteringJava");
System.loadLibrary("vtkRenderingJava");
}

public static void main(String[] args) {
//vtk[Int/Float/Double]Array类代表了一类有规律、有意义的数据组,一般有两个作用:
//①.装载表示数据集的几何结构的点集的坐标数据  —— 3Component per Tuple(点)
//②.装载描述数据集的几何结构或拓扑结构的数据属性(Data Attributes)的数据 —— 标量(1Component/Tuple)、矢量(3Comps/Tuple)、张量(9)
vtkFloatArray pcoords = new vtkFloatArray(); // 作用①:表示点集 —— 几何结构
pcoords.SetNumberOfComponents(3); // 因为是坐标数据,所以Tuple就代表了“点”,一个Tuple显然得有3个基础数据(Component),来对应点的x、y、z了
pcoords.SetNumberOfTuples(4); // 这句话是说一共有4个元组(点)
float pts[][] = { // 我们的数据集中的四个点
{0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f},
{1.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 0.0f}
};
for (int i = 0; i < pts.length; i++) { // 将这四个点按顺序放入vtkFloatArray
// pcoords.SetTuple(id0, id1, id2)
pcoords.SetTuple3(i, pts[i][0], pts[i][1], pts[i][2]);
}
// This can work, too:
// float pts1[] = new float[]{0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f};
// pcoords.SetJavaArray(pts1);
vtkPoints points = new vtkPoints(); // 这个类就是vtk表示“几何结构”的封装类
points.SetData(pcoords);

vtkCellArray strips = new vtkCellArray(); // 代表拓扑结构
strips.InsertNextCell(4);//表示下一个Cell由4个点组成,然后【按顺序】指明是哪四个点
strips.InsertCellPoint(0);
strips.InsertCellPoint(1);
strips.InsertCellPoint(2);
strips.InsertCellPoint(3);//InsertCellPoint(x)的x对应的是上面“pcoords ”所表示的点的index,不防把顺序由0,1,2,3改为0,3,2,1看看是什么效果,再想想为何?

vtkIntArray temperature = new vtkIntArray(); // 作用②,代表点(几何结构)上的标量数据了
temperature.SetName("Temperature"); // 嗯,“温度”数据
temperature.InsertNextValue(10);
temperature.InsertNextValue(20);
temperature.InsertNextValue(30);
temperature.InsertNextValue(40);

// 经过一系列准备,终于可以让vtkPolyData闪亮登场了
vtkPolyData polydata = new vtkPolyData();
// 设定点和基元
polydata.SetPoints(points);
polydata.SetStrips(strips);
// 设定几何结构上的标量数据
polydata.GetPointData().SetScalars(temperature);

// 将生成的数据集加入pipeline
vtkPolyDataMapper mapper = new vtkPolyDataMapper();
mapper.SetInput(polydata);
mapper.SetScalarRange(0, 40);

vtkActor actor = new vtkActor();
actor.SetMapper(mapper);

vtkRenderer ren = new vtkRenderer();
ren.AddActor( actor );
ren.SetBackground( 0, 0, 0 );

//Add renderer to renderwindow and render
vtkRenderWindow renWin = new vtkRenderWindow();
renWin.AddRenderer(ren);
renWin.SetSize(600, 600);

vtkRenderWindowInteractor iren = new vtkRenderWindowInteractor();
vtkInteractorStyle style = new vtkInteractorStyleTrackballCamera();
iren.SetInteractorStyle(style);
iren.SetRenderWindow(renWin);
// renWin.Render();
iren.Initialize();
iren.Start();
}

}


这个例子是Java代码,是在C++的例子的基础上改写的。运行成功后,出来的是这样的效果:



我们可以看到,这个四边形被染上了颜色。这些颜色是根据我们设置的标量,以及颜色对应表(vtkLookupTable)决定的。vtkLookupTable这里不多讲,可以认为它是标量数值 -- 颜色的对应表。有兴趣的同学去看看api就好了,比较容易的。

我们接下来可以看着这个结果,想想这个结果和我们设置的数据的对应关系。试着改变一下数据,然后再对比结果。应该会有所收获的。

4.1.2 Demo2 :找出数据规律,借助相关API,“隐式”地快速建立数据集

Demo1几乎是一个最简单的数据集,我们就已经写了不少代码,真实的数据往往是几何级别,像Demo1那样建立数据集是很麻烦的,尤其是要建立完整的cell来表示拓扑结构,几乎不可能。

不过好在真实的数据集大多都是有规律的,我们可以像4.1节开篇说的,找出真实数据能够对应的数据集类型,再利用vtk相应的API建立之。下面是一个建立线性网格数据集(RectilinearGrid,关于这种数据集的特点,请见前篇的介绍)的Demo,来自于一个C++例子,翻译为Java的了:

package linke.dataset;

import vtk.vtkActor;
import vtk.vtkFloatArray;
import vtk.vtkInteractorStyle;
import vtk.vtkInteractorStyleTrackballCamera;
import vtk.vtkPolyDataMapper;
import vtk.vtkRectilinearGrid;
import vtk.vtkRectilinearGridGeometryFilter;
import vtk.vtkRectilinearGridWriter;
import vtk.vtkRenderWindow;
import vtk.vtkRenderWindowInteractor;
import vtk.vtkRenderer;

public class RectilinearGridDemo {

static {
System.loadLibrary("vtkCommonJava");
System.loadLibrary("vtkFilteringJava");
System.loadLibrary("vtkRenderingJava");
System.loadLibrary("vtkGraphicsJava"); // vtkRectilinearGridGeometryFilter
System.loadLibrary("vtkIOJava");
}

public static void main(String[] args) {
// 假设我们已经从符合RectilinearGrid结构特点的真实数据中提取出了所需要的各个坐标,如下:
double x[]={ // 47个
-1.22396, -1.17188, -1.11979, -1.06771, -1.01562, -0.963542,
-0.911458, -0.859375, -0.807292, -0.755208, -0.703125, -0.651042,
-0.598958, -0.546875, -0.494792, -0.442708, -0.390625, -0.338542,
-0.286458, -0.234375, -0.182292, -0.130209, -0.078125, -0.026042,
0.0260415, 0.078125, 0.130208, 0.182291, 0.234375, 0.286458,
0.338542, 0.390625, 0.442708, 0.494792, 0.546875, 0.598958,
0.651042, 0.703125, 0.755208, 0.807292, 0.859375, 0.911458,
0.963542, 1.01562, 1.06771, 1.11979, 1.17188};
double y[]={ // 33个
-1.25, -1.17188, -1.09375, -1.01562, -0.9375, -0.859375,
-0.78125, -0.703125, -0.625, -0.546875, -0.46875, -0.390625,
-0.3125, -0.234375, -0.15625, -0.078125, 0, 0.078125,
0.15625, 0.234375, 0.3125, 0.390625, 0.46875, 0.546875,
0.625, 0.703125, 0.78125, 0.859375, 0.9375, 1.01562,
1.09375, 1.17188, 1.25};
double z[]={ // 44个
0, 0.1, 0.2, 0.3, 0.4, 0.5,
0.6, 0.7, 0.75, 0.8, 0.9, 1,
1.1, 1.2, 1.3, 1.4, 1.5, 1.6,
1.7, 1.75, 1.8, 1.9, 2, 2.1,
2.2, 2.3, 2.4, 2.5, 2.6, 2.7,
2.75, 2.8, 2.9, 3, 3.1, 3.2,
3.3, 3.4, 3.5, 3.6, 3.7, 3.75,
3.8, 3.9};

// Create a rectilinear grid by defining three arrays specifying the
// coordinates in the x-y-z directions.
int i;
vtkFloatArray xCoords = new vtkFloatArray();
for (i=0; i<47; i++) xCoords.InsertNextValue(x[i]);

vtkFloatArray yCoords = new vtkFloatArray();
for (i=0; i<33; i++) yCoords.InsertNextValue(y[i]);

vtkFloatArray zCoords = new vtkFloatArray();
for (i=0; i<44; i++) zCoords.InsertNextValue(z[i]);

// The coordinates are assigned to the rectilinear grid. Make sure that
// the number of values in each of the XCoordinates, YCoordinates,
// and ZCoordinates is equal to what is defined in SetDimensions().

vtkRectilinearGrid rgrid = new vtkRectilinearGrid();
rgrid.SetDimensions(47,33,44);
rgrid.SetXCoordinates(xCoords);
rgrid.SetYCoordinates(yCoords);
rgrid.SetZCoordinates(zCoords);

vtkRectilinearGridWriter writer = new vtkRectilinearGridWriter();
writer.SetFileName("vtkRectilinearGrid.vtk");
writer.SetInput(rgrid);
writer.Write();

// 这个filter顾名思义,就是展现出RectilinearGrid的几何结构
vtkRectilinearGridGeometryFilter plane = new vtkRectilinearGridGeometryFilter();
plane.SetInput(rgrid);
plane.SetExtent(0,46, 0,32, 0,43);
// 接下来是按部就班的pipeline
vtkPolyDataMapper rgridMapper = new vtkPolyDataMapper();
rgridMapper.SetInputConnection(plane.GetOutputPort());

vtkActor actor = new vtkActor();
actor.SetMapper(rgridMapper);

vtkRenderer ren = new vtkRenderer();
ren.AddActor(actor);
ren.SetBackground(0, 0, 0);

vtkRenderWindow renWin = new vtkRenderWindow();
renWin.AddRenderer(ren);
renWin.SetSize(600, 600);

vtkRenderWindowInteractor iren = new vtkRenderWindowInteractor();
vtkInteractorStyle style = new vtkInteractorStyleTrackballCamera();
iren.SetInteractorStyle(style);
iren.SetRenderWindow(renWin);
// renWin.Render();
iren.Initialize();
iren.Start();
}
}


4.2 从文件读取数据集

上面的例子中,有关于vtkRectilinearGridWriter的片段,它属于vtk的IO API,用来把内存中的vtk数据集持久化为磁盘文件,或者从磁盘文件将数据集读入内存。在运行了上面的例子以后,Java工程的目录下应该会多个文件vtkRectilinearGrid.vtk,打开之后是这个样子:

# vtk DataFile Version 3.0
vtk output
ASCII
DATASET RECTILINEAR_GRID 【数据类型】
DIMENSIONS 47 33 44 【各个方向维度】
X_COORDINATES 47 float 【x轴坐标】
-1.22396 -1.17188 -1.11979 -1.06771 -1.01562 -0.963542 -0.911458 -0.859375 -0.807292
-0.755208 -0.703125 -0.651042 -0.598958 -0.546875 -0.494792 -0.442708 -0.390625 -0.338542
-0.286458 -0.234375 -0.182292 -0.130209 -0.078125 -0.026042 0.0260415 0.078125 0.130208
0.182291 0.234375 0.286458 0.338542 0.390625 0.442708 0.494792 0.546875 0.598958
0.651042 0.703125 0.755208 0.807292 0.859375 0.911458 0.963542 1.01562 1.06771
1.11979 1.17188
Y_COORDINATES 33 float 【y轴坐标】
-1.25 -1.17188 -1.09375 -1.01562 -0.9375 -0.859375 -0.78125 -0.703125 -0.625
-0.546875 -0.46875 -0.390625 -0.3125 -0.234375 -0.15625 -0.078125 0 0.078125
0.15625 0.234375 0.3125 0.390625 0.46875 0.546875 0.625 0.703125 0.78125
0.859375 0.9375 1.01562 1.09375 1.17188 1.25
Z_COORDINATES 44 float 【z轴坐标】
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.75
0.8 0.9 1 1.1 1.2 1.3 1.4 1.5 1.6
1.7 1.75 1.8 1.9 2 2.1 2.2 2.3 2.4
2.5 2.6 2.7 2.75 2.8 2.9 3 3.1 3.2
3.3 3.4 3.5 3.6 3.7 3.75 3.8 3.9


关于使用vtkIO类的例子这里不再举例,因为这些API的接口都非常简单,一看就会了。只要我们记住这些就够了:
1. VTK中的每种数据类型,都有对应的IO类(vtkXXXReader,vtkXXXWriter),以及对应的文件格式来记录其内容。

2. Reader类是代表pipeline起始的算法(Algorithm)类,Writer是代表pipeline结束的算法类。

只要明白前面这些,清楚Reader类和Writer类在pipeline中的位置,就可以了。

我们不妨自己把工作中接触到的数据集都用对应的IO类写成文件,然后打开来看看它结构,就可以更快地理解vtk以文件保存数据集的方式了。


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: