(高隽 黄伟 合肥工业大学计算机与信息学院 合肥 230009)
摘要 Visualization Toolkit 是一个面向对象的可视化类库,它为从事可视化应用程序开发的广大科研工作者提供直接的技术支持。VTK 具有及其强大的功能,不仅提供了强大的可视化功能,而且还提供了强大的图像处理以及有线元分析的功能,能够对标量场,矢量场以及张量场数据进行重建。对于广大从事可视化应用研究以及可视化应用程序与系统开发的科研工作者来说,VTK 具有非常重要的意义。本文介绍了VTK 的机制特点和框架,并简要介绍了一个VTK 在医学可视化应用中的实现。
关键词 科学计算可视化、面向对象程序设计、Visualization Toolkit、3D 重建、CT
1. 引言
科学计算可视化(Visualization in Scientific Computing) 是1987 年由B.H.McCormick等人根据美国国家科学基金会召开的“科学计算可视化研讨会”的内容撰写的一份报告中正式提出来的[7][8]。 随后,美国西欧日本各著名大学、研究所、超级计算机中心、各大公司纷纷进行科学计算可视化理论和方法的研究,科学计算可视化迅速成为计算机科学中一个热门的研究领域。
可视化是运用计算机图形学和图像处理技术,将科学计算过程中及计算结果的数据转换为图形及图像在屏幕上显示出来并进行交互处理的理论,方法和技术[11]。 近些年来随着计算机硬件水平的提高和可视化理论方法的不断完善,可视化在许多领域都得到了广泛的应用,如有限元分析,计算机断层扫描CT 及核磁共振MRI 数据的可视化等[9]。但是随着可视化技术在许多其它领域的广泛应用,也暴露出一些急待解决的问题。因为对于许多并不是专业从事可视化研究的科研人员来说,虽然他们对于可视化也有很强烈的需求,但是由于对可视化程序的开发并不熟悉,因而在其研究工作中面临着不少现实的困难。如果能有一个专门的可视化开发工具,能够简化可视化程序的开发,将会极大的提高我们的工作效率。
本文介绍了一个基于Windows/Unix 环境的面向对象的可视化开发工具VisualizationToolkit,它将一些常用的算法和在可视化程序的开发过程中会经常遇到的细节屏蔽起来,以类库的形式给我们的开发工作以直接的支持,极大的简化了我们的开发工作和提高了我们的工作效率。
2. 可视化开发工具Visualization Toolkit
随着可视化技术在各个学科领域的广泛应用,对于广大从事可视化研究领域的科研人员来说,迫切需要一种功能强大的可视化开发工具来给我们的研究工作提供强有力的支持。我们在本文中采用的是Will Schroeder, Ken Martin, Bill Lorensen 等人用C++语言开发的一个基于Windows 和Unix 环境的面向对象的可视化工具Visualization Toolkit。
Visualization Toolkit 把可视化开发过程中的一些细节屏蔽起来,以类库的形式给从事可视化程序开发的研究者提供支持,使我们可以方便快捷的开发出高性能的可视化应用程序。
2.1 Visualization Toolkit 的机制
Visualization Toolkit 是一个用于可视化应用程序构造与运行的支撑环境。它是在三维函数库OpenGL 的基础上,采用面向对象的设计方法发展起来的。它将我们在可视化开发过程中会经常遇到的细节屏蔽起来,并将一些常用的算法封装起来。比如Visualization Toolkit 将我们在表面重建中比较常见的Marching Cubes 算法封装起来,以类的形式给我们以支持。这样我们在对三维规则点阵数据进行表面重建时,就不必再重复编写Marching Cubes 算法的代码,而直接使用Visualization Toolkit 中已经提供的vtkMarchingCubes 类。
Visualization Toolkit 采用的是Pipeline 机制,几乎可以对任何类型的数据进行处理,并提供了许多相应的类对各种类型的数据进行转换或处理。根据所要处理的原始数据类型的不同和所使用的算法以及所要达到的结果,我们可以设计和建立起自己的可视化流程,并由此选择不同的数据处理和转换的类,用数据通道将这些类连接起来,将原始数据类型转换为所采用的算法模块可以直接进行处理的数据类型,最终得到我们所需要的可视化的结果。而且所有的类和算法模块都是可扩充的,用户可以将自己开发的类或模块转换成系统可以接受的形式,并可替换或扩充原有的类。所以Visualization Toolkit 是一个开放的系统,可以扩展到任何应用领域。
2.2 Visualization Toolkit 的特点
Visualization Toolkit 是给从事可视化应用程序开发工作的研究人员提供直接的技术支持的一个强大的可视化开发工具。它以用户使用的方便性和灵活性为主要原则,具有如下的特点:
1. 具有强大的三维图形功能Visualization Toolkit 既支持基于体素Voxel-basedrendering 的体绘制Volume Rendering [4] ,又保留了传统的面绘制,从而在极大的改善可视化效果的同时,又可以充分利用现有的图形库和图形硬件。
2. Visualization Toolkit 的体系结构使其具有非常好的流streaming 和高速缓存caching 的能力。在处理大量的数据时不必考虑内存资源的限制。
3. Visualization Toolkit 能够更好的支持基于网络的工具,比如Java 和VRML。 随着Web 和Internet 技术的发展Visualization Toolkit 有着很好的发展前景。
4. 能够支持多种着色如OpenGL 等。
5. Visualization Toolkit 具有设备无关性使其代码具有良好的可移植性。
6. Visualization Toolkit 中定义了许多宏。这些宏极大的简化了编程工作并且加强了一致的对象行为。
7. Visualization Toolkit 具有更丰富的数据类型,支持对多种数据类型进行处理。
8. 既可以工作于Windows 操作系统又可以工作于Unix 操作系统极大的方便了用户。
目前与Visualization Toolkit 类似的可视化系统还有AVS89 和Haber91。其中AVS89是第一个商业可视化系统。AVS89 和Haber91 都采用的是抽象的数据模型,这虽然使它们有更广泛的数据表达能力和更大的灵活性,但这也使得它们更容易导致计算机代码和接口的混乱,而且也会使得使用者由于误解而造成误操作。Visualization Toolkit 采用的是更具体的数据模型,一方面是为了是用户更容易掌握和使用,另一方面是因为VisualizationToolkit 数据模型已经足够支持我们的可视化实践。
3. Visualization Toolkit 的框架结构
用面向对象技术的对象模型图表示Visualization Toolkit 的框架结构如图1 所示:
Visualization Toolkit 采用的是流水线pipeline 的机制,根据所获得的原始数据的类型以及所要得到的显示结果,我们可以选择适当的算法,并构建起自己的可视化流程。在用可视化工具Visualization Toolkit 对体数据进行可视化的过程中,首先应按照所设计的可视化过程对原始数据进行处理,将其转换为适当的数据形式并映射为几何数据,然后将几何数据的属性告诉将要在窗口中显示的演员actor。
Visualization Toolkit 一个很特别的地方是对于任何进行可视化的数据,它都是通过演员在窗口中表现出来的。
vtkObject 是Visualization Toolkit 类库的基类,它为整个可视化流程提供基本的方法。vtkSource 是vtkObject 的派生类和vtkFilter 的父类,它为整个可视化流程的开始(比如读取数据等)定义具体的行为和接口。vtkFilter 是vtkSource 的派生类,它对数据进行各种处理,将原始数据经过各种filter 的处理后,转换为可以直接用某种算法模块对其进行处理的形式。vtkMapper 也是vtkObject 的派生类,它将经过各种filter 处理后的应用数据映射为几何数据,为原始数据与图像数据之间定义了接口。vtkActor 类用来表达绘制场景中的一个实体,也就是绘制场景中的演员,它通过SetMapper()方法将几何数据的属性告诉演员,然后通过vtkRender 类将结果在窗口中显示出来。
4. 利用Visualization Toolkit 对数据进行可视化的实例
使用Visualization Toolkit 对数据进行可视化十分方便,程序的结构也正如我们所设计的可视化流程一样,十分简洁明了。下面我们将利用Visualization Toolkit, 就可视化应用研究中一个十分热门的领域——计算机断层扫描CT, 简单剖析一下利用VisualizationToolkit 进行数据可视化的实现[1]~[3]。
我们将要进行处理的CT 数据是一个人体头部的切片数据,共有93 个切片。切片的间距是1.5mm ,每个切片由有12 个灰度级的间距为0.8 毫米的256*256 像素构成[6] 。我们打算由这些切片数据恢复出皮肤和骨骼的表面。为此我们首先应选取合适的算法,考虑到我们所处理的数据量是非常庞大的,超过了12 兆比特,而且我们只打算对表面进行重建,所以我们选择基于表面重建的经典算法Marching Cubes 算法。而对于Marching Cubes算法,Visualization Toolkit 中已经有封装好的vtkMarchingCubes 给予支持,这样就进一步简化了我们的工作。
Marching Cube 算法是Lorensen 等人于1987 提出的,是三维数据场等值面生成的经典算法,是体素单元内等值面抽取技术的代表,并一直沿用至今。
Marching Cube 算法的基本思想是在数据体中将位于两个相临切片上的8 个相临的体素构成一个立方体(cube) ,逐个处理数据场中的立方体,分类出与等值面相交的立方体,采用插值计算出等值面与立方体边的交点。根据立方体每一顶点与等值面的相对位置,将等值面与立方体边的交点按一定方式连接生成等值面,作为等值面在该立方体内的一个逼近表示[7][10]。
4.1 读取数据
首先,我们要做的事情是读取切片数据,并将其转换为我们的开发工具VisualizationToolkit 所支持的一种数据表达形式;然后根据其物理结构建立起相应的模型,我们给CT数据建立的是比较抽象的等值面模型;最后将物理组件与抽象的模型结合在一起来建立对CT 数据的可视化,以帮助用户正确理解数据。我们所要进行处理的是有结构点阵数据,其拓扑和几何都是隐含知道的,所以我们只需要知道数据的维数、数据源和数据空间。利用Visualization Toolkit 中的vtkVolume16Reader 我们可以很方便的读取切片数据,只需要告诉读取数据对象我们的CT 数据的一些参数,如切片之间的间距、切片上像素之间的间距以及所读取切片的起始段(如从第1 个切片到45 个切片),读取数据的代码如下所示:
Reader->SetDataDimensions(256,256); //设置数据的维数
Reader->SetDataByteOrderToLittleEndian ();
Reader->SetFilePrefix (".. /.. /.. /vtkdata/headsq/quarter"); //设置所读取切片数据文件的路径
Reader->SetImageRange(1, 93); //设置读取切片的起始段
Reader->SetDataSpacing (0.8, 0.8, 1.5); //设置切片之间的间距和像素之间的间距
4.2 提取等值面
下面我们就可以用Marching Cubes 算法对所读取的数据进行处理了。首先利用vtkMarchingCubes 类来提取出某一CT 值的等值面,但这时的等值面其实仍只是一些三角面片,还必须由vtkStripper 类将其拼接起来形成连续的等值面。这样就把读取的原始数据经过处理转换为应用数据,也即由原始的点阵数据转换为多边形数据然后由vtkPolyDataMapper 将其映射为几何数据,并将其属性赋给窗口中代表它的演员,将结果显示出来。
vtkMarchingCubes *skinExtractor = vtkMarchingCubes::New(); //建立一个Marching Cubes 算法的对象
skinExtractor->SetInput(Reader->GetOutput()); //获得所读取的CT 数据
skinExtractor->SetValue(0, 500); //提取出CT 值为500 的皮肤
vtkStripper *skinStripper = vtkStripper::New(); //建立三角带对象
skinStripper->SetInput(skinExtractor->GetOutput()); //将生成的三角片连接成三角带
vtkPolyDataMapper *skinMapper = vtkPolyDataMapper::New(); //建立一个数据映射对象
skinMapper->SetInput(skinStripper->GetOutput()); //将三角带映射为几何数据
vtkActor *skin = vtkActor::New(); //建立一个代表皮肤的演员
skin->SetMapper(skinMapper); //获得皮肤几何数据的属性
skin->GetProperty()->SetDiffuseColor(1, .49, .25); //设置皮肤颜色的属性
skin->GetProperty()->SetSpecular(.3); //设置反射率
skin->GetProperty()->SetSpecularPower(20); //设置反射光强度
利用同样的方法,我们也可以提取出骨骼的等值面。只是骨骼的CT 值是1150 左右而已。所以只要在SetValue()方法中将参数设置为1150 就可以了。而且Visualization Toolkit支持多表面重建,所以在实际应用中我们可以设置多个参数值,提取出多个等值面并同时显示出来。在这个应用实例中我们只对皮肤和骨骼地的等值面进行了重建。
4.3 显示结果
通过前面这些工作,我们基本上已经完成了对数据的读取处理映射等步骤,下面我们就要对数据进行显示了。
vtkRenderWindow *renWindow = vtkRenderWindow::New(); //建立绘制窗口
renWindow->AddRenderer(ren); //将绘制者加入绘制窗口
vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New(); //对绘制结果进行交互操作
iren->SetRenderWindow (renWindow); // 告诉绘制者将要在绘制窗口中进行显示的演员
ren->AddActor(skin); //皮肤
ren->AddActor(bone); //骨骼
5. 结束语
Visualization Toolkit 是采用C++语言设计的一个基于Windows/Unix 环境的面向对象对象的可视化类库,具有十分强大的可视化功能。与MFC 强大的图形界面功能相结合,可以方便快捷的开发出高性能的可视化程序,并且可以在不同的开发环境下使用,如Microsoft Visual C++ Borland C++等。对于一般的用户,它屏蔽了一些常见算法和复杂的数据处理过程,使不具备可视化程序开发经验的用户也可以方便快捷地编制可视化程序。同时对于具有可视化程序开发经验的用户,VTK也提供了相应的方法使用户可以对其进行改进,以支持对可视化程序的行优化,在开发可视化程序的过程中具有十分重要的意义。
参考文献
[1] Sabe P. A Rendering Algorithm for Visualization 3D Scalar Fields. Computer Graphics, August 1988,22(4):
51~58
[2] W.C.Lin, C.C.Liang, C.T.Chen, Dynamic elastic interpolation for 3D medical image reconstruction from
serial cross sections, IEEE Trans. On Medical Imaging, Vol.7, No.3, 1988,225~232
[3] G.T.Herman and H.K.Lin, Three-dimensional display of human organs from Computed Tomograms, CVGIP9, 1979, 1-21
[4] Krueger W, Volume rendering and feature enhancement, Computer Graphics, 1990,24(5): 21~26
[5] Max N. Area and Volume Coherence for Efficient Visualization of 3D Scalar Functions. Computer Graphics,
1990, 24(5): 27~33
[6] Will Schroeder, Ken Martin, Bill Lorensen, Visualization Toolkit 2nd Edition - An Object-Oriented
Approach to 3D Graphics, Prentice Hall, 1988
[7] 管伟光:《体视化技术及其应用》北京 电子工业出版社1998
[8] 管伟光:《体数据可视化及其在医学中的应用》中国科学院自动化所博士论文1995
[9] 吕维雪:《医学图像处理》北京 高等教育出版社1989
[10] 唐泽圣:《三维数据场可视化》北京 清华大学出版社1999
[11] 石教英、蔡立文:《科学计算可视化算法与系统》北京 科学出版社1996
【Jati 注】
//在我的VTK 5.0.2中,相应的测试程序代码如下:
#include "vtkVolume16Reader.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkMarchingCubes.h"
#include "vtkStripper.h"
#include "vtkActor.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
//读取RAW文件,提取等值面。
int main()
{
vtkVolume16Reader *reader=vtkVolume16Reader ::New();
reader->SetDataDimensions(64,64);
reader->SetDataByteOrderToLittleEndian();
reader->SetFilePrefix("D://headsq//quarter");
reader->SetImageRange(1,93);
reader->SetDataSpacing(3.2,3.2,1.5);
vtkMarchingCubes *boneExtractor=vtkMarchingCubes::New();
boneExtractor->SetInput((vtkDataObject *)reader->GetOutput());
boneExtractor->SetValue(0,500);
vtkStripper *boneStripper=vtkStripper::New();
boneStripper->SetInput(boneExtractor->GetOutput());
vtkPolyDataMapper *boneMapper=vtkPolyDataMapper::New();
boneMapper->SetInput(boneStripper->GetOutput());
vtkActor *bone=vtkActor::New();
bone->SetMapper(boneMapper);
bone->GetProperty()->SetDiffuseColor(.1,.94,.52);
bone->GetProperty()->SetSpecular(.3);
bone->GetProperty()->SetSpecularPower(20);
vtkRenderer *ren=vtkRenderer::New();
vtkRenderWindow *renWindow=vtkRenderWindow::New();
renWindow->AddRenderer(ren);
vtkRenderWindowInteractor *iren=vtkRenderWindowInteractor::New();
iren->SetRenderWindow(renWindow);
ren->AddActor(bone);
iren->Initialize();
iren->Start();
reader->Delete();
iren->Delete();
return 0;
}