vtk filter 的写法

来源:互联网 发布:微博发长图用什么软件 编辑:程序博客网 时间:2024/06/08 17:52

1.vtk filter 的编写要遵循以下几个原则:

从不修改输入数据:数据是静态的,修改了输入,其它滤波器也用这个输入数据,则会出问题。(因为数据会很大,应该用的都是同一份数据)

相关计算数据:不要复制,内存消耗会很大。

使用调试宏:当设置了对象的调试标记时,滤波器应该提供调试信息。使用在VTK/Commom/vtkSetGet.h中定义的VTK 的调试宏很方便。

取消/删除已分配的内存:滤波器写入器的一个共同的不足是引入了内存泄漏或者使用了额外的内存。通过对所有的VTK 对象成对地使用New()和Delete()方法来避免内存泄漏,而且new 和delete 方法适用于所有的固有的或者non-VTK 对象。其它降低内存使用的方法是使用所有数据对象都支持的Squeeze()方法。这个方法收回了一个对象可能在使用的额外内存。当只能估计数据对象的规模时使用Squeeze()方法。

写一个滤波器的诀窍是保证它被修改过的时间被适当地管理。你可能会记起, 被修改过的时间是每个对象包括的,与它内部状态有关的一个内部时间标签。Set

通常,一个滤波器被修改过的时间不需要干涉就可以维持。例如,如果你使用VTK/Commom/vtkSetGet.h 中定义的vtkSet/Get 宏来得到和设置实例变量值,被修改过的时间被适当地管理,而且继承方法vtkObject::GetMTime()返回正确的值。可是,如果你定义你自己的Set__()方法,或者包括了修改了对象内部状态的方法,你必须要在滤波器上调用Modified()(i.e,改变内部的修改时间)。而且,如果你的对象定义包括其它对象的相关,滤波器正确的修改过的时间包括滤波器和它依靠的对象。这需要重载GetMTime()方法。

一个对象的修改时间是其依赖的所有的对象的修改时间的最大值。

ProgressEvent 和AbortExecute:

ProgressEvent :反映filter执行的进度的类(例如进度条)。但是有些filter很难得到具体进度,这时通常会选择更新算法上关键点的滤波器

AbortExecute: 用于停止滤波器执行的一个标记当设置时,某些滤波器会中止它们的执行,向它们的输出发送它们执行结果(可能什么也没有)的某些部分

一个重要的指导原则:作为一个滤波器的实施者你不想过多地调用进程方法因为他们能够影响全部的执行。

2.如何写一个图象滤波器:

首先要考虑这个filter:

1. 源自哪个父类
2. 如何适当地安排被修改的时间,包括重载 GetMTime()方法和调用Modified();
3. 如何重载由流水线执行过程定义的方法
4. Execute()方法的创建//执行方法就是主方法?
5. 是否支持 Progress()和Abort()方法。
6. 滤波器实施所需要的实例变量,结构,或者方法。

简单filter:

简单滤波器是显示了典型行为的滤波器。这意味着它们有一个输入和/或输出,支持输入和输出数据类型的常用的结合,而且不需要重载GetMTime()方法。其关键点就是找到合适的抽象父类。

如果这个filter的作用是源,也就是source,那么这个filter可以选择以下某个合适的类作为父类:

vtkPolyDataSource——读来自一个文件的数据或者程序性地产生vtkPolyData //多边形数据

vtkStructuredPointsSource — — 读来自一个文件的数据或者程序性地产生vtkStructuredPoints//结构化点数据

vtkRectilinearGridSource — — 读来自一个文件的数据或者程序性地产生vtkRectilinearGrid//线形网格

vtkStructuredGridSource——读来自一个文件的数据或者程序性地产生vtkStructuredGrid//结构化网格

vtkUnstructuredGridSource — — 读来自一个文件的数据或者程序性地产生vtkUnstructuredGrid//非结构化网格

如果这个filter的作用就是过滤,就要考虑继承以下列出的这些类。这些抽象类的名字清楚地确定了滤波器的输入和输出数据类型:

 vtkDataSetToPolyDataFilter
 vtkDataSetToStructuredGridFilter
 vtkDataSetToStructuredPointsFilter
 vtkDataSetToUnstructuredGridFilter
 vtkPolyDataToPolyDataFilter
 vtkRectilinearGridToPolyDataFilter
 vtkStructuredGridToStructuredGridFilter
 vtkStructuredPointsToPolyDataFilter
 vtkStructuredPointsToUnstructuredGridFilter

如果这个filter是投射器(应该就是输出的意思)考虑继承以下列出的这些类。投射器通常不是简单的过程对象,除了写出filter外。甚至还需要写一个WriteData()方法,和SetInput()方法一样。你也必须提供实例变量来掌握输出文件名字,和操作它们的方法。

vtkWriter——特殊地抽象了写出器对象的接口

一旦选择了正确的父类,则需要实现 Execute()方法。执行方法应该访问合适的输入数据,执行错误计策(debug方法?)(如果必要),实现一个算法(i.e,产生合适的数据对象),而且把数据对象送给数据。

一个像 vtkShrinkPolyData 的简单滤波器派生自父类vtkPolyDataToPolyDataFilter。这个类依次派生自父类vtkPolyDataSource,vtkSource,和vtkProcessObject。这些类执行以下的函数。
 vtkProcessObject 被认为调用了StartEvent,ProgressEvent,和EndEvent 事件;因此它定义了同时也接口了AbortExecute 和Progress 实例变量。它也定义了输入实例变量,这是滤波器的一个输入列。也有设置和管理输入的特殊的方法。
 vtkSource 为流水线执行过程定义了API 而且实施了流水线执行过程;与vtkDataObjectReleaseData 标记接口。它也创建了被保护的实例变量输出,这是滤波器的一系列的输出。滤波器的执行时间用InformationTime 实例变量来追踪。
 vtkPolyDataSource 定义GetOutput()方法作为返回vtkPolyData 的一个指针的方法
 vtkPolyDataToPolyDataFilter 定义了SetInput()方法来接受vtkPolyData 的一个指针
 最后,vtkShrinkPolyData 重载了Execute()方法来实际上做一些有用的事情

复杂filter:

如果你希望写一个复杂滤波器,你需要做的是提供包括在对象组合中的功能性。

抽象图象filter:

有许多产生例如 vtkDataSet 或vtkPointSet 抽象数据集输出类型的VTK 滤波器。因为抽象数据集类型不能被例示(因为纯虚函数的展示),你可能怀疑这是可能的,或者为何这样的滤波器会是更有用的。答案是(1)我们对输入数据集的结构做了一个复制而且把它放在输出,(2)许多滤波器管理属性数据(e.g,标量或者向量)而且没有修改数据集的结构。因此,可以完全传递结构而且修改或者产生属性数据的滤波器是toolkit 的有用附加。

如果你想要创建图象滤波器有两个父类可以使用。它们是

 vtkDataSetToDataSetFilter——输入任意数据集类型而且输出同样的数据集类型
 vtkPointSetToPointSetFilter——输入vtkPointSet(i.e,显式地展示它们点的数据集)类型的任意数据集而且输出一个vtkPointSet

主要还是对Execute方法的重载。

可编程filter:

使用 C++开发一个滤波器的一个选择是使用可编程的过程对象。这些对象允许你创建一个在过程对象执行时(i.e,在Execute()方法时)被调用的函数。可编程滤波器的一个优点是你不必重新编译VTK 库,甚至不使用C++。事实上,你可以使用被支持的解释语言Tcl,Java,和Python 来创建一个滤波器!

可编程的源和滤波器是使你在允许时创建新滤波器的过程对象。没有必要创建一个C++类或者重新编译对象库。可编程的对象注意把hooking 的voerhead 放入可视化流水线中(i.e,管理被修改过的时间检验,适当地调用Update()方法和Execute()方法,而且提供了设置输入和/或得到滤波器输出的方法),只需要你写滤波器的Execute()方法的主体。

新 对 象vtkProgrammableSource , vtkProgrammableFilter , 和vtkProgrammableAttributeDataFilter。vtkProgrammableSource 是支持而且可以产生任意VTK数据集类型输出的一个源对象。vtkProgrammableFilter 允许你设置输入并且检索任意数据集类型(e.g,GetPolyDataOutput())的输出。滤波器vtkProgrammableAttributeDataFilter 允许同样或者不同类型的一个或多个输入,而且可以产生任意数据集类型的输出。

重载流水线执行方法:

当写一个新滤波器时你承当的最困难的工作可能是重载一个或多个流水线执行方法,这在的“执行过程中”有所描述。这个任务的难点在于流水线执行过程如何工作。只有你
需要你的滤波器流动时这样做。只有相对很少的方法你需要考虑重载。这些方法在这里描述,包括你必须做的和它们如何影响执行过程。

ExecuteInformation()这个方法给子类一个配置输出的机会。例如,WholeExent,间距,原点,标量类型,和成分的数目都可以被修改。默认地,vtkSource 简单地把来自第一个输入的基本范围信息复制到它所有的输出。如果对你的滤波器这不是正确的行为,那么你必须重载这个方法。

EnlargeOutputUpdateExents()。这个方法给子类一个机会,说明它会提供比输出所需要的更多的数据。这个会发生,例如,当滤波器只能产生整个输出时。尽管这个为一个特殊的作为一个参数传递的输出调用,源可能需要放大所有的输出。默认的实施是不做任何事(不放大输出)如果你的滤波器需要输出更新范围比要求的大(例如,你的滤波器只能产生整个输出),那么你必须重载这个方法。

ComputeInput UpdateExents()。这个方法给子类一个机会,请求输入的一个更大的范围。例如,当一个滤波器需要“内部”边缘更多的数据来产生边缘值时——例如一个通过对周围的输入值核心应用一些操作来派生一个新的像素值的图象滤波器,这是必要的。默认的实施是需要整个输入来产生输出。这是不被考虑的,例如,在vtkImageToImageFilter 中要求同样的输入更新范围作为输出更新范围。它又在vtkImageSpatialFilter 中被重新定义以请求一个有一点大的输入更新范围,因为根据滤波器的核心规模需要额外的像素。你应该仔细看你滤波器的层次来看默认的实施是否是哪个正确的。

Execute()。Execute()方法通常被重载。这个准则的主要例外是图象滤波器,多数成像滤波器继承自vtkImageToImageFilter,它实施了一个依次调用ThreadedExecute()的一个Execute()方法。ThreadedExecute()是支持多线程的Execute()的一个特殊版本。

3.如何写一个成像滤波器

图象流水线和成像流水线共享一个统一的执行过程。如果你理解了前面描述的流水线执行过程,你也理解了成像滤波器。可是,在成像滤波器和其它类型的滤波器之间还有一些重要的不同。

1. 成像算法通常是局部的,输入和输出类型是一样的
2. 多数成像滤波器只处理标量数据属性(1 到4 个成分的)其它信息,例如向量,法线,etc,不被处理。
3. 成像滤波器使用模板化的函数来实施它们的 Execute()方法。
4. 几乎所有的成像滤波器已经被写入流数据。
5. 几乎所有的成像滤波器是多线程的。

实施一个成像滤波器

给出了一个执行一个在每个输入像素上执行简单缩放和旋转操作的成像滤波器的例子。滤波器——vtkImageShiftScale——是vtkImageToImageFilter的一个子类,多数成像滤波器也是如此。

在这个例子中:


主要是重载了这三个方法。

首先是对ThreadedExecute()方法的实现;


可以看到,在这个方法中,ThreadedExecute()实际上被父类vtkImageToImageFilter 在它的Execute()方法中调用。ThreadedExecute()意味着用多个线程来处理滤波器(i.e,一个共享的内存实施)这个代码的一个重要特征社 switch 声明。这个代码在输入数据类型之间转换而且调用了一个模板化的函数。这个模板化的函数,vtkImageShiftScaleExecute1(),依次调用了其它的模板化的函数vtkImageShiftScaleExecute()(在输出数据类型之间转换)。两个模板是必要的因为输入和输出类型是不同的。在这里我们可以看到在这个模板中,inPtr变为了(VTK_TT*)并且在下一个模板中:


它在模板中的类型变为了T,T代表的是某种类型。这里使用的模板化的函数没有在头文件中声明因为它只被这一个类使用。它也不是一个类方法,只是一个简单的C++模板化的函数。它通常应该在使用之前出现在源代码中。因此通常模板化的执行函数正好在Execute()(或者ThreadedExecute())方法之前。函数定义前面的模板关键字意味着它是一个模板化的函数,而且它是用类型T 实施的。你可以把T 看作是在编译时被int,short,float,etc 代替的一个字符串。注意T 被作为inPtr 和outPtr 的数据类型来使用。因为它是代替一个方法的一个函数,它没有访问this 指针和它相关的实例变量。因此我们传递进this 指针作为一个称做self 的一个变量。Self 可以被接着使用来访问函数第一行的类的值。这里我们从实例得到了Scale 的值而且把它放入一个称为scale 的局部变量。剩下的行声明了用来在数据之间穿越的局部变量。最后,算法的实际工作使用 vtkImageShiftScaleExecute()方法来执行:注意AbortExecute实例变量和UpdateProgress()方法的使用,如我们在前面滤波器的实施中看到的那样。


我们可以看到最后*inPtr的类型为IT,*outPtr的类型是OT,从而得到两种类型来操作。在这个函数中:最初我们计算了一行像素的长度。这是像素的数目乘以每个像素的成分数目(e.g,对于RGB来说有3 个,每一个对应一个灰度)第二行和第三行计算y 和z 维。接下来的两行得到连续的对于inData 和outData 的连续的增长。有两种可以穿越数据步进的增长类型:规则的和连续的。规则的增长定义了在内存中你需要移动多少个单元来操纵图片。规则的x 增长定义了从一个像素到另外一个要移动多少单元。规则的y 和z 增长分别对行和片段做了同样的定义。

这个滤波器的另外一个重要特征是 ExecuteInformation()被重载。


这个方法的目的是重载对输出类型设置了与父类同一类型的默认父类行为。在这个特殊的滤波器中,用户可能选择性地把输出类型设置为一个不同类型,因此需要重载这个方法。我们希望这章帮助你使用 VTK 写你自己的滤波器。你可能希望通过学习其它滤波器的源代码来构建。如果你能找到一个你可以理解的算法,接着看VTK 如何实施它,那会更有用。

0 0
原创粉丝点击