当前位置: 首页 > news >正文

VTK开发精要:数据与管线机制

VTK开发精要

目录

一、 数据存储

2.1 vtkDataArray

2.2 vtkFieldData

2.3 vtkDataObject

2.4 vtkDataSet

2.5 vtkMultiBlockDataSet

二、 管线机制

2.1 管线

2.2 Filter

三、 vtkExcutive

3.1 关联算法

3.2 端口信息

3.3 数据流的传递

3.4 算法执行

四、 vtkDemandDrivenPipeline

4.1 请求触发

4.2 REQUEST_DATA_OBJECT

4.3 REQUEST_INFROMATION

4.4 REQUEST_DATA

五、 vtkStreamingDemandDrivenPipeline

六、 vktCompositeDataPipeline

附录A: vtkDataObject子类

附录B:vtkExecutive子类

参考资料

  • 一、数据存储

图形图像处理与科学计算数据可视化的数据主要由几何信息、拓扑信息、属性信息等三类数据组成。VTK定义一套数据结构用于存储这三类信息。

  1. vtkDataArray

vtkDataArray派生于vtkAbstractArray,实际上一个Tuple元素的动态数组,每个Tuple包含vtkAbstractArray::NumberOfComponents个分量,总共包含vtkAbstractArray::MaxId个Tuples。

  1. vtkFieldData

vtkFiledData实际上是一组vtkDataArray (A group of vtkDataArray objects)。不同vtkDataArray可以有不同的Tuple数目,而且不同vtkDataArray中Tuple的分量大小也可以不同。可以看到,可以将速度、压力、温度等场变量存储到vtkFiledData内部的vtkDataArray中。

另外,从vtkFieldData派生的vtkPointData与vtkCellData分别用于存储定义在节点、单元中心的多个场数据(速度、压力、温度等)。

  1. vtkDataObject

vtkDataObject包含一个vtkFieldData类型的成员变量FieldData,同时vtkDataObject定义了数据可视化过程中通过vtkInformation、vtkInformationVector等信息对象来控制成员变量vtkDataObject::FieldData的接口。

VTK中定义了大量的派生于vtkDataObject的类,参见附录A。

  1. vtkDataSet

vtkDataSet直接派生于vtkDataObject,在vtkDataObject基础之上,其内部增加了vtkPointData、vtkCellData用于存储定义在节点、单元中心的场数据。vtkDataSet关联了几何、拓扑与属性信息,可以方便地进行可视化。

  1. vtkMultiBlockDataSet

vtkMultiBlockDataSet间接派生于vtkDataObject,提供类树状结构,每个树节点包含一个vtkDataObject指针。在CFD中,可以将计算结果按照空间或者时间存储到vtkMultiBlockDataSet的不同blocks内。

处理vtkMultiBlockDataSet这种混合数据的基本流程就是将树状结构中的每一个vtkDataObject进行处理,然后将所有的处理结果进行合并。

  • 二、管线机制

VTK使用管线机制来完成数据与信息的处理,管线是由多个Filter顺序组合而成的逻辑上的管线状的结构。目前,VTK提供了vtkDemandDrivenPipeline、vtkStreamingDemandPipeline、vtkCompositeDataPipeline等三种管线实现(附录B)。

  1. 管线

VTK管线用于将多个Filter组合在一起以处理数据与信息,构成了逻辑上“线状”的结构。

  1. Filter

Filter指的是派生于vtkAlgorithm的类,用于处理数据与算法。Filter由输入端口、算法、执行对象(vtkExcutive及其子类)、输出端口、信息对象等组成。

每个输入端口可以包含多个连接,而每个输出端口只有一个连接。当Filter输入端口个数为0,这样的Filter也被成为Reader或者Source;当Filter的输出端口个数为0,这样的Filter被称作Writer或者Sink。

Filter内部的算法通常是在重写vtkAlgorithm::ProcessRequest()函数,或者派生于vtkAlgorithm子类,然后重写对应的请求处理函数。例如可以派生vtkMultiBlockAlgorithm,然后通过重写RequestData()函数响应REQUEST_DATA_OBJECT请求。

每个Filter内部都有一个执行对象(vtkExcutive及其子类),用于控制管线的运行。当每次调用Filter的Update()函数更新数据时,实际上是通过执行对象生成多个请求,按照指定流向与顺序,在上游或者下游的Filter及其对应的执行对象内依次执行。

信息对象vtkInformation实际上VTK实现的一个Map数据结构,而vtkInfromationVector是一个vtkInformation类型数组。vtkInformation /vtkInfromationVector用于存储管线相关的信息,包括输入输出端口参数、算法数据等。

  • 三、vtkExcutive

VTK中的管线机制的实现比较复杂,大体上主要涉及vtkExcutive及其子类、vtkAlgorithm、vtkInformation等相关类型。本节对vtkExcutive的实现做分析。

  1. 关联算法

vtkExecutive内定义了vtkAlgorithm指针,用于关联对应的vtkAlgorithm对象,

// The algorithm managed by this executive. vtkAlgorithm* Algorithm;

在vtkAlgorithm::SetExecutive函数中,完成vtkAlgorithm与vtkExecutive的关联,

void vtkAlgorithm::SetExecutive(vtkExecutive* newExecutive) { vtkExecutive* oldExecutive = this->Executive; if(newExecutive != oldExecutive) { if(newExecutive) { newExecutive->Register(this); vtkAlgorithmToExecutiveFriendship::SetAlgorithm(newExecutive, this); } this->Executive = newExecutive; if(oldExecutive) { vtkAlgorithmToExecutiveFriendship::SetAlgorithm(oldExecutive, nullptr); oldExecutive->UnRegister(this); } } }

可以看到,vtkAlgorithm是通过vtkAlgorithmToExecutiveFriendship::SetAlgortihtm来调用vtkExecutive::SetAlgorithm设置。

class vtkAlgorithmToExecutiveFriendship { public: static void SetAlgorithm(vtkExecutive* executive, vtkAlgorithm* algorithm) { executive->SetAlgorithm(algorithm); } };
  1. 端口信息

vtkExecutive内定义了类型为vtkInformationVector的OutputInformation来存储每个输出端口的信息,

// Store an information object for each output port of the algorithm. vtkInformationVector* OutputInformation;

vtkInformationVector是vtkInformation的数组,OutputInformation的每个vtkInformation元素对应输出端口的信息。

由于每个输入端口可以有多个连接,vtkExcutive定义了vtkExecutiveInternals类型的ExecutiveInternal来存储输入端口的信息,

// Internal implementation details. vtkExecutiveInternals* ExecutiveInternal;

vtkExecutiveInternals实际上一个vtkInfromationVector的列表,可以看成一个vtkInformation的不等列表格,每一行表示一个输入端口,对应的列数等于端口上连接数目。

class vtkExecutiveInternals { public: std::vector<vtkInformationVector*> InputInformation; vtkExecutiveInternals(); ~vtkExecutiveInternals(); vtkInformationVector** GetInputInformation(int newNumberOfPorts); };
  1. 数据流的传递

请求会通过ProcessRequest()下发,在这里,ProcessRequest()实际上起到分发请求的作用,即将请求交给不同的响应代码进行处理。

在vtkExcutive::ProcessRequest函数中,会根据FORWARD_DIRECTION设置的选项决定请求是向上(vtkExcutive::RequestUpstream)还是向下(vtkExcutive::RequestDownstream)传递,可以看到如果指定向上传递请求,则会调用ForwardUpstream这个虚函数将请求向上游传递。

vtkExecutive::ProcessRequest函数会根据ALGOTIYHM_BEFOR_FORWARD与ALGORITHM_AFTER_FORWARD来决定本Filter内部算法执行数据处理是发生在传递请求之前还是传递请求之后。

int vtkExecutive::ProcessRequest(vtkInformation* request, vtkInformationVector** inInfo, vtkInformationVector* outInfo) { if(request->Has(FORWARD_DIRECTION())) { // Request will be forwarded. if(request->Get(FORWARD_DIRECTION()) == vtkExecutive::RequestUpstream) { if(this->Algorithm && request->Get(ALGORITHM_BEFORE_FORWARD())) { if(!this->CallAlgorithm(request, vtkExecutive::RequestUpstream, inInfo, outInfo)) { return 0; } } if(!this->ForwardUpstream(request)) { return 0; } if(this->Algorithm && request->Get(ALGORITHM_AFTER_FORWARD())) { if(!this->CallAlgorithm(request, vtkExecutive::RequestDownstream, inInfo, outInfo)) { return 0; } } } if(request->Get(FORWARD_DIRECTION()) == vtkExecutive::RequestDownstream) { vtkErrorMacro("Downstream forwarding not yet implemented."); return 0; } } else { // Request will not be forwarded. vtkErrorMacro("Non-forwarded requests are not yet implemented."); return 0; } return 1; }
  1. 算法执行

在ProcessRequest中,会调用vtkExcutive::CallAlgorithm来执行本Filter的数据处理。

int vtkExecutive::CallAlgorithm(vtkInformation* request, int direction, vtkInformationVector** inInfo, vtkInformationVector* outInfo) { // Copy default information in the direction of information flow. this->CopyDefaultInformation(request, direction, inInfo, outInfo); // Invoke the request on the algorithm. this->InAlgorithm = 1; int result = this->Algorithm->ProcessRequest(request, inInfo, outInfo); this->InAlgorithm = 0; // If the algorithm failed report it now. if(!result) { vtkErrorMacro("Algorithm " << this->Algorithm->GetClassName() << "(" << this->Algorithm << ") returned failure for request: " << *request); } return result; }

可以看到vtkExecutive会根据数据流的方向,调用CopyDefaultInformation将数据流从输出端口(输入连接)复制到输入连接(输出端口),然后调用虚函数vtkAlgorithm::ProcessRequest完成本Filter内实际的数据处理。

  • 四、vtkDemandDrivenPipeline

vtkDemandDrivenPipeline在vtkExcutive基础之上,增加请求触发功能,同时增加对REUQEST_DATA_OBJECT、REQUEST_INFROMATION、REUQEST_DATA等请求的支持。

  1. 请求触发

如果管线采用vtkCompositeDataPipeline,则当调用vtkAlgorithm::Update函数之后,会调用vtkDemandDrivenPipeline::Update函数,其内部则是调用了两个虚函数UpdateInformation与UpdateData来完成主要业务。

int vtkDemandDrivenPipeline::Update(int port) { if(!this->UpdateInformation()) { return 0; } if(port >= -1 && port < this->Algorithm->GetNumberOfOutputPorts()) { return this->UpdateData(port); } else { return 1; } }
  1. REQUEST_DATA_OBJECT

在vktCompositeDataPipeline::UpdateInformation中,首先调用UpdateDataObject虚函数生成REQUEST_DATA_OBJECT请求,并下发到ProcessRequest中

int vtkDemandDrivenPipeline::UpdateDataObject() { // The algorithm should not invoke anything on the executive. if(!this->CheckAlgorithm("UpdateDataObject", nullptr)) { return 0; } // Update the pipeline mtime first. if(!this->UpdatePipelineMTime()) { return 0; } // Setup the request for data object creation. if (!this->DataObjectRequest) { this->DataObjectRequest = vtkInformation::New(); this->DataObjectRequest->Set(REQUEST_DATA_OBJECT()); // The request is forwarded upstream through the pipeline. this->DataObjectRequest->Set(vtkExecutive::FORWARD_DIRECTION(), vtkExecutive::RequestUpstream); // Algorithms process this request after it is forwarded. this->DataObjectRequest->Set(vtkExecutive::ALGORITHM_AFTER_FORWARD(), 1); } // Send the request. return this->ProcessRequest(this->DataObjectRequest, this->GetInputInformation(), this->GetOutputInformation()); }
  1. REQUEST_INFROMATION

在vktCompositeDataPipeline::UpdateInformation中,处理完REQUEST_DATA_OBJECT请求之后,会进一步创建REQUEST_INFORMATION请求,并将其下发到ProcessRequest处理。

int vtkDemandDrivenPipeline::UpdateInformation() { // The algorithm should not invoke anything on the executive. if(!this->CheckAlgorithm("UpdateInformation", nullptr)) { return 0; } // Do the>处理完毕REQUEST_INFROMATION请求之后,vtkDemandDrivenPipeline::Update函数调用UpdateData虚函数,在vtkDemandDrivenPipeline::UpdateData中会创建REQUEST_DATA请求,并下发到ProcessRequest去处理。

int vtkDemandDrivenPipeline::UpdateData(int outputPort) { // The algorithm should not invoke anything on the executive. if(!this->CheckAlgorithm("UpdateData", nullptr)) { return 0; } // Range check. if(outputPort < -1 || outputPort >= this->Algorithm->GetNumberOfOutputPorts()) { vtkErrorMacro("UpdateData given output port index " << outputPort << " on an algorithm with " << this->Algorithm->GetNumberOfOutputPorts() << " output ports."); return 0; } // Setup the request for data. if (!this->DataRequest) { this->DataRequest = vtkInformation::New(); this->DataRequest->Set(REQUEST_DATA()); // The request is forwarded upstream through the pipeline. this->DataRequest->Set(vtkExecutive::FORWARD_DIRECTION(), vtkExecutive::RequestUpstream); // Algorithms process this request after it is forwarded. this->DataRequest->Set(vtkExecutive::ALGORITHM_AFTER_FORWARD(), 1); } // Send the request. this->DataRequest->Set(FROM_OUTPUT_PORT(), outputPort); return this->ProcessRequest(this->DataRequest, this->GetInputInformation(), this->GetOutputInformation()); }
  • 五、vtkStreamingDemandDrivenPipeline

vtkStreamingDemandDrivenPipeline在vtkDemandDrivenPipeline提供的流水线功能基础之上,主要是增加对局部数据(分片数据)更新的功能。这一部分比较复杂,暂时不予以研究。

  • 六、vktCompositeDataPipeline

vktCompositeDataPipeline不仅可以支持非混合数据,同时提供了对等混合数据处理算法的支持,混合数据算法指的是能够处理vtkMultiBlockDataSet、vtkUniformGridAMR等数据的vtkAlgorithm派生类。

vktCompositeDataPipeline是最新VTK默认使用的管线。(笔者机器上使用的VTK版本是VTK-8.2.0)

参考资料

  1. 张晓东, 罗火灵. VTK图形图像开发进阶[M]. 机械工业出版社, 2015.
  2. VTK官网
http://www.jsqmd.com/news/826126/

相关文章:

  • Cursor AI代码优化工具:自动检测与重构冗余API调用
  • Coding Agent 正在偷走你的控制权?慢下来,守住开发者的核心地位!
  • Augustus核心功能深度解析:路障、劳动力池与仓库管理
  • Jdbc手动实现事务管理
  • 深入PEX8796:从Serdes到Virtual Switch,图解PCIe交换芯片的三种工作模式
  • FPGA开发板GT远端环回测试:原理、配置与调试实战指南
  • RAG是什么?为什么Agent必须用RAG?
  • pgwatch2在Kubernetes中的部署:Helm Chart完全解析
  • Cursor AI编程助手规则文件(.cursorrules)配置指南与最佳实践
  • AI+Web3开发实战:Helius Core-AI如何赋能Solana智能体应用
  • 大语言模型可解释性实战:从注意力可视化到特征归因的深度解析
  • SDLPAL资源文件格式详解:从RIX到YJ1的压缩技术
  • 产品经理面试与求职攻略:Awesome Product Management 职业转型成功案例
  • Spoolman与主流3D打印软件的完美集成:OctoPrint、Klipper、Moonraker详细配置教程
  • 亲身经历从申请密钥到成功调用Taotoken API的全流程耗时与难易度
  • 上下文工程:从提示词到智能体,高效管理AI交互的核心方法论
  • AlphaAvatar:从单目视频重建可驱动3D数字人的混合表示框架
  • Veyra Forms:React生态下声明式、类型安全的复杂表单状态管理框架
  • AI Gateway:统一调度多模型API,实现成本优化与性能监控
  • VSCode插件开发利器:cursor_info库实现光标上下文精准解析
  • 200类鸟类图像分类数据集
  • t-io HTTP服务器实现:如何替代Tomcat和Jetty的完整指南
  • 本地大模型运行、训练、微调全搞定,4GB RAM轻松运行4B模型!
  • msphpsql高级功能深度解析:Always Encrypted、数据分类和表值参数全面指南
  • Python-ADB协议实现原理:深入理解ADB和Fastboot通信机制
  • 构建个人知识库:从代码仓库到第二大脑的实践指南
  • FS8024A芯片实现USB-C PD诱骗:打造TYPE-C转DC电源转接头方案
  • AI LED调光驱动电源智能功率 MOSFET 完整选型方案
  • Blender FLIP Fluids域设置详解:如何优化模拟精度与性能
  • AI智能体钩子模式:用JSON Schema构建标准化交互协议