VTK修炼之道82:VTK管线机制_信息对象类VTKInformation

来源:互联网 发布:喇叭仿真软件 编辑:程序博客网 时间:2024/05/04 23:48

1.VTK管线机制

VTK中通过管线机制来实现组合各种算法处理数据。每一种算法是一个Filter,多个Filter连接在一起形成VTK管线。每个Filter可以分为两个组成部分:一个是算法对象,继承自vtkAlgrithm,主要负责处理输入的数据和信息;另一个是执行对象,继承自vtkExecute(),负责通知算法对象何时运行以及传递需要处理的数据和信息。Filter类继承自vtkAlgrithm及其子类,实例化时,内部会生成一个默认的Executive()对象,用于管理执行管线。
数据和信息通过端口在Filter中传递,根据数据流的方向,分为输入端口和输出端口,如下图所示:

Filter的输入数据与信息存储在输入端口中。一个Filter可能有0个输入端口(例如Reader对象);也可能有一个或多个输入端口(例如,vtkGlyph3D类需要两个输入端口,每个输入端口可以建立多个连接)。
一个Filter可能有1个或多个输出端口,每个输出端口对应一个逻辑输出。例如vtkExtractVectorComponents类,从一个三维向量数据中提取每个分量数据,该Filter需要一个输入端口接受向量数据,三个输出端口用于输出三个分量数据,端口号分别为0,1,2。
Filter之间通过端口(Port)建立连接(Connection)。例如一个标准的连接代码如下:
Filter2->SetInputConnection( Filter1->GetOutputPort() );
该句代码将Filter1的输出端口与Filter2的输入端口建立连接,连接中只涉及了一个输入端口和一个输出端口。而VTK中还有许多Filter可能需要多个输入,例如vtkGlyph3D,该类需要两个输入数据并生成一个输出数据。因此这里需要建立两个连接,相应的函数分别为SetInputConnection()和SetSourceConnection(),其中,SetInputConnection()输入的是几何点集数据,对应输入端口0,SetSourceConnection()输入的是Glyph图形数据,对应输入端口1。vtkGlyph3D中输入的两个数据具有不同的意义,因此建立了两个不同的输入端口。另外,对一个Filter的多个输入数据具有相同意义时,则只需要建立一个输入端口,并使用AddInputConnection()来添加新的连接。例如vtkAppendFilter类实现数据的合并,其多个输入数据具有相同意义,而不像vtkGlyph3D的两个输入表示不同的对象,因此其连接建立如下:
apeend = vtkAppendFilter::New();append->AddInputConnection( foo->GetOutputPort );append->AddInputConnection( bar->GetOutputPort );
下图显示了Filter之间建立连接的示意图:
在一个连接中,上游Filter(也称为Producer)的输出端口与下游Filter(也称为Consumer)的输出端口建立连接。上游Filter为下游Filter提供数据和信息以待处理。

2.完整的VTK管线

一个完整的VTK管线通常包含Source对象、Filter对象和Mapper对象。
  • Source对象时一个管线的起点,主要负责读取文件或者根据参数生成管线处理的数据,例如,vtkBMPReader、vtkSphereSource等。读取文件的Source通常称为Reader。
  • Filter对象是处理数据的算法类,需要一个或者多个输入类生成一个或者多个输出,例如,vtkImageCast、vtkCurvatures等。
  • Mapper对象负责将数据转换为图元,但也可能将数据写入文件或者其他软件系统,写文件的Mapper通常称为Writer。

2.1 执行管线的连接和控制是如何实现的?

一般情况下,Source对象、Filter对象、Mapper对象都统称为Filter。VTK中存在多种数据结构,如果每种数据类型都要定义一种Filter,那么将会产生非常庞大的Filter群,不利于定义一种通用的VTK执行管线。
管线的接口是通过逻辑端口(Logical Port)而不是数据流实现的,因此在形成连接的过程中不需要知道实际的数据类型,而是在执行时进行数据类型检查,以决定管线是否执行
vtk定义了一个vtkInfomation类,用于存储和传递管线执行过程中的信息、请求和数据,实现执行管线的连接和控制。

3.信息对象类vtkInformation

vtkInformation是实现VTK执行管线的一个非常重要的类。此类实际上是一个Map容器,采用Key-Value的映射方式,通过索引(Key)的类型来决定其对应的数据,用于存储管线中的各种信息和数据。
使用vtkInformation类较好地增强了VTK执行管线的灵活性,对于管线接口而言,不需要知道数据和信息的实际类型,从而在不改变VTK执行管线类接口的情况下,方便地为Filter的端口添加新的数据。
vtkInformation类中索引Key的类型为vtkInformationKey的子类,VTK定义了大量的索引类型,这些类都继承自vtkInformationKey。比如:vtkObjectData类中定义的静态函数:
static vtkInformationDataObjectKey*  DDATA_OBJECT();
用于获取一个映射vtkDataObject类型数据的vtkInformationDataObjectKey类型索引对象,可以方便的保存和获取一个vtkDataObject数据,该函数在CPP文件中实现如下:
vtkInformationKeyMacro( vtkDataObject, DATA_OBJECT, DataObject);
vtkInformationKeyMacro是一个宏,其代码如下:
#define vtkInformationKeyMacro(CLASS, NAME, Type);{  static vtkInformation##type##Key*  CLASS##_##NAME =new vtkInformation##type##Key(#NAME, #CLASS);  return CLASS##_##NAME;}
从该宏的定义可知,需要三个参数:一个是调用宏的类名CLASS,一个是函数名字NAME,另外一个是索引类型Type。CLASS与NAME仅在内部使用,而type决定了返回的Key的类型,如以上的vtkInformationDataObjectKey传入的type为DataObject。所以一个Key类型结构为vtkInformation##type##Key,输入不同的type,即可得到不同的Key。
  • Key的约束
当一个Key类型的构造函数需要额外的信息来限制Key时,需要使用vtkInformationKeyRestrictedMacro宏:
#define vtkInformationKeyRestrictedMacro( CLASS, NAME, type, required)  \{static vtkInformation##type##Key*  CLASS##_##NAME =new vtkInformation##type##Key( #NAME, #CLASS, required);return CLASS##_##NAME;}
例如,vtkDataObject中静态函数ORIGIN()用于返回映射三维向量的索引Key:
static vtkInformationDoubleVectorKey* ORIGIN();
其实现如下:
vtkInformationKeyRestrictedMacro( vtkDataObject, ORIGIN, Double Vector, 3);
以上返回的是一个vtkInformationDoubleVectorKey类型,由于使用时需要限定维数大小,因此使用vtkInformationKeyRestrictedMacro,并将为数作为第四个参数传入来限定向量大小,其类型参数为DoubleVector。使用上述方式可以方便地自定义Key类型和访问函数。

3.1 管线信息对象

管线信息对象用于存储执行管线的执行信息,存储在Filter的执行对象中。每个输出端口对应一个管线信息对象,通过vtkExecutive::GetOutputInformation()函数获取。输出端口的管线信息对象中包含了输出端口的vtkDataObject数据,可以通过相应的索引Key值来获取;与此同时,每个输出端口的vtkDataObject数据中也存储了对应端口的管线信息对象指针,通过vtkDataObject::GetPipelineInformation()来访问。另外,每个输出端口连接也对应一个管线对象,通过vtkExecutive::GetInputInformation()函数获取。输入端口连接对应的管线对象实际上为该链接对应的上游Filter的输出端口管线对象
SetInputConnection(int potr, vtkAlgrithmOutput* input)用于建立连接,其中input是一个vtkAlgrithmObject类型参数,定义了一个端口Index和算法对象Producer,通过函数GetIndex()和GetProducer()可以直接获取。因此在SetInputConnection()函数中,可以通过如下代码获取上游Filter(producer)的端口信息对象来建立连接。
vtkExecutive* producer =( input && input->GetProducer() ) ?  input->GetProducer()->GetExecutive():0;int producerPort = producer?  intput->GetIndex():0;vtkInformation*  newInfo =producer ? producer->GetOutputInformation(producerPort):0;
GetOutputPort()函数用于获取上游Filter的输入端口信息,即SetInputConnection()中的input对象。
GetOutputPort()函数实现代码如下:
vtkAlgrithmOutput* vtkAlgrithm::GetOutputPort(int port){    if( !this->OutputPortIndexInRange(port, "get") ){    return 0;}    //Create the vtkAlgrithmOutput proxy object if there is not one    if( !this->AlgrithmInternal->Outputs[port] ){    this->AlgrithmInternal[port] =vtkSmartPoint<vtkAlgorithmOutput>::New();    this->AlgorithmInternal[port]->SetProducer(this);    this->AlgorithmInternal[port]->SetIndex[port];}    //return the proxy pbject instance    return this->AlgorithmInternal->Outputs[port];}
一个Filter可能会有多个输出端口,这就对应着多个管线信息对象,VTK中使用VTKInformationVector类表示信息对象的集合。在vtkExecutive中即使用该类来定义管线信息对象集合。使用该类可以方便地对信息对象进行操作,例如GetNumberOfInformationObjects()/SetNumberOfInformationObjects()用于获取或者设置信息对象数目;SetInformationObject()/GetInformationObject()用于设置或获取某个信息对象;Append()/Remove()用于追加或者删除信息对象。在vtkAlgrithm中,也是该类来定义端口信息对象集合。

3.2 端口信息对象

端口信息对象存储在vtkAlgrithm类中,每个输入端口和每个输出端口都对应一个端口信息对象。端口信息对象的作用是指定输入/输出数据的类型,管线执行时需要根据输入/输出数据类型来判断和生成相应的数据。vtkAlgoorithm::GetInputPortInformation()用于获取输入端口信息对象。在vtkAlgrithm中,使用PORT_REQUIREMENTS_FILLED索引来标识是否设置端口的需求信息。因此,在GetInputPortInformation()中需要判断是否设置输入端口的需求信息。如果还未设置,则调用vtkAlgrithm::fillInputInformation()函数来设置输入端口需求信息。
vtkAlgrithm也是一个很基本的基类。所以vtkAlgorithm::FillInputPortInformation()仅实现了一个空的虚函数。在vtkAlgrithum子类中需要对该虚函数进行覆盖。例如vtkPolyDataAlgorithm中需要输入端口的数据类型为vtkPolyData,那么相应的FillInputPortInformation()实现如下:
int vtkPolyDataAlgrithm::FillInputPortInformation( int vtkNotUsed(port), vtkInformation* info){    info->Set( vtkAlgrithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");    return 1;}
上面的代码就是使用了INPUT_REQUIRED_DATA_TYPE索引来设置输入数据类型的值。

3.3 算法信息对象

在vtkAlgrithm算法对象中,逼近定义了端口信息对象,还定义了一个算法信息对象。算法信息对象存储了关于算法对象的相关信息,可以通过vtkAlgrithm::GetInformation()获取。
例如:
vtkShirinkFilter::vtkShrinkFilter(){    this->ShrinkFactor = 0.5;    this->GetInformation()->Set( vtkAlgorithm::PRESERVES_RANGES(), 1);    this->GetInformation()->Set( vtkAlgorithm::PRESERVES_BOUNDS(), 1);}

3.4 请求信息对象

VTK管线是通过一系列的请求完成的。请求被封装为vtkInformation对象在管线中传递。例如在vtkDemandDrivenPipeline::UpdateData()中定义请求如下:
this->DataRequest = vtkInformation::New();this->DataRequest->Set(REQUEST_DATA());//the request is forwarded upstream through the pipelinethis->DataRequest->Set(vtkExecutive::FORWARD_DIRECTION(), vtkExecutive::RequestUpstream);//Algorithms process this request after it is forwarded.this->DataRequest->Set( vtkExecutive::ALGORITHM_AFTER_FORWARDED, 1);
DataRequest是一个vtkInformation对象,通过vtkInformation::New()定义。REQUEST_DATA是一个重要的请求类型。REQUEST_DATA()函数返回一个VTKInformationRequestKey索引对象,其中报损了请求的名字为REQUEST_DATA。vtkInformationRequestKey用于表示一个索引请求。

3.5 数据信息对象

每个vtkDataObject数据对象中都保持了一个vtkInformation信息对象,用于存储当前数据对象中的逐句类型,可以通过函数vtkDataObject::GetInformation()来获取。

4.参看资料

1.《C++ primer》
2.《The VTK User’s Guide – 11thEdition》
3.  张晓东, 罗火灵. VTK图形图像开发进阶[M]. 机械工业出版社, 2015.
0 0
原创粉丝点击