3D Slicer 数据类型MRML

1.Slicer MRML数据类型综述

  • MRML提供API(应用程序接口)管理医学图像数据类型(Volume、Model、Transform、Fiducial、Camera等)和它们的可视化;
  • 每种数据类型都用一个特定的MRML节点表示;
  • MRML场景就是MRML节点的集合;
  • Slicer的MRML数据模型(data Model)独立于Slicer系统的可视化组件Visualization和算法组件Algorithm;
  • 其他的Slicer组件,Logic和Qt Widget观测MRML场景和每个节点的变化。

2.MRML 场景

  • MRML场景管理MRML节点:添加、删除、寻找等;
  • MRML Scene provides persistence of MRML nodes (reading/writing to/from XML file)

3.MRML 节点

  • MRML节点用于存贮Slicer应用的状态,包括原始数据和可视化、参数。一组存储核心Slicer Module状态的的核心MRML节点如下所示:
  • MRML节点以C++类等级的形式被组织,所有的子类均继承vtkMRMLNode;
  • 所有的MRML节点必须应用某种标准的API:ReadAttributes、WriteAttributes、Copy等;
  • 相同的数据可以用不同的方式进行可视化,并储存成不同的类型。因此,信息分别以三种不同节点类型进行存储:当一个数据节点被创建的时候,我们必须创建相应的显示节点与数据存储节点,方便进行合理的可视化和保存文件。MRMLLogic会提供函数——创建一个节点并与显示节点&数据节点关联。注意:在一些情况下,Slicer会检测是否显示节点与存储节点遗失,并尝试创建默认节点,但是作为开发人员我们不应该依赖这种错误校正机制。
    • 数据节点:存储原始数据,例如vtkMRMLScalarVolumeNode存储体素、spacing、位置、方向
    • 显示节点:描述了数据怎么样被可视化,对于相同的原始数据可能会有很多的显示节点,例如一个节点进行体绘制、另外一个节点用于显示一张图像
    • 存储节点:描述数据应该如何永久性存储到磁盘中,文件格式、文件名

4.MRML节点属性

MRML节点可以成对存储自定义属性:属性名+属性值。
To avoid name clashes custom modules that adds attributes to nodes should prefix the attribute name with the module's name and the '.' character. Example: the DoseVolumeHistogram module can use attribute names such as DoseVolumeHistogram.StructureSetName, DoseVolumeHistogram.Unit, DoseVolumeHistogram.LineStyle.

5.MRML节点引用

这里有两种机制来指定节点间是相关的:层次结构节点&节点引用。相比之下,节点引用更简单。
MRML节点可以利用节点引用API引用或者观测其他的MRML节点。这个框架会自动处理:
  • 节点引用的读、写、复制
  • 场景导入更新引用
  • 增加或删除节点
一个节点可能引用很多节点,每个节点都扮演了不同的角色,并且每个节点都有唯一的字符串进行处理。
数据节点可以选择性地应用接口:如Displayable、Storable、Transformable。每一个接口都有自己的需求。例如,变换模块接口期待与变换节点相关联。这种关联就是通过创建MRML Reference完成的。

5.1 MRML Reference:API描述

上面的节点应用API功能是在vtkMRMLNote基类中实现的。
The only thing that needs to happen in thederived MRML node class(usually in the constructor of the class) is a call tovtkMRMLNode::AddNodeReferenceRole(const char *referenceRole, const char *mrmlAttributeName) that takes a unique string defining the reference role between this node and the referenced node, and a MRML attribute name for storing the reference in the .mrml file.
The only other call that is needed is either:
vtkMRMLNode* SetAndObserveNodeReferenceID(const char* referenceRole , const char* referencedNodeID, vtkIntArray *events=0);
or
vtkMRMLNode* AddAndObserveNodeReferenceID(const char* referenceRole , const char* referencedNodeID, vtkIntArray *events=0);
vtkMRMLNode也提供了虚回调函数,可以在继承类中拓展:
1 OnNodeReferenceAdded(vtkMRMLNodeReference *reference)
2 OnNodeReferenceRemoved(vtkMRMLNodeReference *reference)
3 OnNodeReferenceModified(vtkMRMLNodeReference *reference)
默认情况下,这些方法生成一下事件:
1 vtkMRMLNode::ReferenceAddedEvent
2 vtkMRMLNode::ReferenceRemovedEvent
3 vtkMRMLNode::ReferenceModifiedEvent
在继承类中,这些方法可以采用VTKMRMLNode拓展,以允许查询节点的引用。
vtkMRMLNode成员的完整列表参考如下网址,包括所有继承的成员:
http://apidocs.slicer.org/master/classvtkMRMLNode-members.html
目前,下述的几个MRML节点是采用MRML节点引用API实现的:
  • vtkMRMLStorableNode
  • vtkMRMLTransformableNode
  • vtkMRMLDisplayableNode
其他引用MRML节点,例如vtkMRMLColorTableNode, vtkMRMLDiffusionTensorDisplayPropertiesNode,目前还没有只用这种API。

5.2 MRML Reference:示例

vtkMRMLTransformableNode implementation:
http://viewvc.slicer.org/viewvc.cgi/Slicer4/trunk/Libs/MRML/Core/vtkMRMLTransformableNode.h?view=log
vtkMRMLDisplayableNodeimplementation:
http://viewvc.slicer.org/viewvc.cgi/Slicer4/trunk/Libs/MRML/Core/vtkMRMLDisplayableNode.h?view=log
vtkMRMLStorableNodeimplementation:
http://viewvc.slicer.org/viewvc.cgi/Slicer4/trunk/Libs/MRML/Core/vtkMRMLStorableNode.h?view=log

6.MRML事件与观测者Event-Observe

  • MRML场景和各个节点的改变会传递给其他观测者节点,GUI和Logic对象通过vtk事件、命令-观测者机制。
  • 使用vtkAddObserver()和InvokeEvent()方法。vtkSetMicro生成ModifiedEvent。
  • 对于MRML,命令-观测者机制采用助手vtkObserverManager、类、MRML观测者宏、ProcessMRMLEvent方法实现。
  • 观测者应该存储一个注册的指针,该指针指向MRML节点,防止一个已经删除对象的回调函数。
  • MRML 观察者宏定义位于 Libs/MRML/vtkMRMLNode.h
  • vtkSetMRMLObjectMacro - 注册 MRML节点
  • vtkSetAndObserveMRMLObjectMacro - 注册 MRML 节点,并为vtkCommand::ModifyEvent增加一个观察者
  • vtkSetAndObserveMRMLObjectEventsMacro - 注册MRML节点并为一个指定的事件集合设置观测者
  • SetAndObserveMRMLScene[Events]() 方法在GUI/Logic中使用,用于观察变化、新场景、增加节点等事件
  • ProcessMRMLEvents method在MRML节点、Logic、GUI类中实现,在观察节点中处理事件

7.创建自定义的MRML节点类

  • 自定义MRML节点为模块参数提供了永久性存储
  • 自定义MRML节点应该使用RegistraterNodeClass()在MRML场景中注册,因此他们可以从一个场景文件中恢复或者保存
  • 这个类应该实现以下方法:
    • CreatNodeInstance()-与VTK的New()相似,只不过不是静态的
    • GetNodeTagName()-为这个节点返回一个独一无二的XML标签
    • ReadXMLAttributes()-从XML文件中读取节点属性,如name-value
    • WriteXML()-将节点属性写入到输出流
    • Copy()-拷贝节点属性
  • 如果该节点已经引用其他的节点,那么下面的方法也应该实现:向MRML场景中添加新的节点
    • UpdateReferenceID()-更新对另一个节点已经存储的应用
    • UpdataScene()-依赖这个节点更新场景中的其他节点
一个实现自定义MRML节点的例子:
vtkMRMLGradientAnisotropicDiffusionFilterNode inModules/GradientAnisotropicDiffusionFilter directory.

8.MRML层次结构节点

http://apidocs.slicer.org/master/classvtkMRMLHierarchyNode.html

9.MRML:Developer FAQ

9.1 如何在场景中增加一个MRML节点?

  • 通用的模式
1 vtkNew<vtkMRML???Node> nodeToAdd;
2 ...
3 mrmlScene->AddNode(nodeToAdd.GetPointer());
  • 在场景中添加一个多边形数据?
1 vtkNew<vtkMRMLModelNode> modelNode;
2 modelNode->SetPolyData(polyData);
3 mrmlScene->AddNode(modelNode.GetPointer());
  • 从文件中加载一个多边形数据?
1 vtkSlicerModelsLogic* modelsLogic = ...;
2 //modelsLogic->SetMRMLScene(mrmlScene);
3 modelsLogic->AddModel(polyDataFileName);

9.2 加载一个MRML场景时,如果碰到以下信息该怎么办?

 1 ERROR: In /path/to/VTK/Imaging/vtkImageMapToColors.cxx, line 153
 2 vtkImageMapToColors (0x268f190): RequestInformation: No LookupTable was set but number of components in input doesn't match OutputFormat, therefore input can't be passed through!
 3  
 4 ERROR: In /path/to/VTK/Imaging/vtkImageExtractComponents.cxx, line 239
 5 vtkImageExtractComponents (0x26947e0): Execute: Component 1 is not in input.
 6  
 7 ERROR: In /path/to/VTK/Imaging/vtkImageExtractComponents.cxx, line 239
 8 vtkImageExtractComponents (0x26947e0): Execute: Component 1 is not in input.
 9  
10 [...]
确保每一个VolumeDisplay节点都设置了ColorNodeRef属性。

9.3 如何在2D视窗中改变立体?

1 appLogic = slicer.app.applicationLogic()
2 selectionNode = appLogic.GetSelectionNode()
3 selectionNode.SetReferenceActiveVolumeID(bg)
4 selectionNode.SetReferenceSecondaryVolumeID(fg)
5 appLogic.PropagateVolumeSelection()
原文地址:https://www.cnblogs.com/ybqjymy/p/14235434.html