【无标题】CTK-Plugin框架在CAS程序中应用教程(翻译)

1.介绍

  日益增加的复杂性和安全性要求是实现现代临床有用的CAS应用程序的主要问题。为了解决这些问题,框架如IGSTK[1]、MITK使用[2]、CISST[3]等。它们提供了可重用的方法和代码框架,从而简化了开发。因此,标准方法的复杂重新实现,如可视化[4]、注册/分割[5]和导航[1],可以避免。插件框架[6][7][8]和状态机支持[1][6]也有助于处理复杂程度和提高可靠性。
  框架规模的增加可能会导致在初始培训中付出更大的努力,即使可能只使用很小的部分。特别是,这影响了原型和技术演示的开发,因为它们具有探索性,所以具有时间敏感性。为此,熟悉周期较短的轻量级框架(如公共工具包[7])非常适合。
  CTK是一个生物医学领域的开源工具包,它提供了一个基于服务的插件框架[7]。CTK框架的设计支持创建模块化CAS应用程序。到目前为止,在internet上只有一些关于CTK工具包的全面教程。尽管CTK在MITK框架[2]中使用,但是没有开放源码示例应用程序。这使得有效的开发更加困难。
  在本教程中,我们将考虑这个问题,并介绍CTK插件框架、它的最小工作示例,以及一个可作为标准导航模板的开放源码演示应用程序。此外,由于基于服务/事件的松散耦合,模块/插件可以很容易地重用。

2.CTK概述

  本节简要概述了通用工具包,并介绍了插件框架及其在计算机辅助手术环境中的使用。由此获得的知识为第3节中展示的演示应用程序奠定了基础。
  CTK框架包括一个全面的DICOM加载程序[9]、一个DICOM应用程序宿主系统、一组QtWidgets(与生物医学应用程序相关)、一个命令行接口和一个插件框架。单个元素基于c++和QtLibraries[6]。下面只讨论插件框架,因为它允许CAS应用程序的模块化设计,并且演示应用程序是在此基础上构建的。框架的其他部分仍然可以无限制地用于扩展演示应用程序。
  CTK插件是应用程序中的模块单元,代表常规分离的功能,如与跟踪器通信、准备GUI元素或读取数据。插件是单独编译的,并在运行时由插件框架加载。插件是由插件激活器类初始化的,为了并行执行,需要启动一个GUI or background-thread。每个插件都包含一个指向插件上下文的指针,它为所有加载的插件和服务提供一个访问点。因此,主程序只启动插件,并在需要时初始化图形用户界面的框架。插件之间的通信是通过服务提供的。服务是由CTK服务注册中心的插件注册的对象,它派生自CTK服务类。CTK服务注册中心是提供服务或访问服务的插件的中心点。服务由其接口(通常只包含虚拟方法的c++类)和属性(包含作为标准数据类型的属性名称和状态的c++哈希映射)唯一定义。因此,接口必须包含在提供服务的插件以及访问服务的插件中。插件通过将自己注册到CTK服务注册中心来提供服务。其他插件可以通过指定跟踪器的接口和属性来传递跟踪器对象(实现抽象类ctkServiceTracker的类的实例)。在添加、删除CTK服务注册中心的服务或更改服务的属性之后,将通知tracker对象。通知以同步方式进行,这就是为什么向服务对象传播更改的插件不应该具有锁的原因。另一种选择是,插件或主程序可以直接从服务注册中心访问服务对象,但是由于内存安全,应该避免多个插件对对象的操作。图1a显示了插件之间基于服务的通信。插件A提供了一个服务对象。插件B访问一个服务跟踪器对象,该对象在更改后被通知。插件C和主程序直接访问服务对象。
在这里插入图片描述
  事件由CTKs自己的Eventadmin插件管理,该插件提供用于发送和接收事件的ctkEventAdmin服务。该服务允许插件(由属性模拟服务组成)在特定主题下发布事件(类型为QString)。要接收特定主题的事件,插件必须实现接口ctkEventHandler并在服务注册中心注册自己。事件可以同步或异步发送。图1b展示了Eventadmin插件和其他插件之间的关系。Plugin D发送一个特定主题下的对象,Eventadmin Plugin调用Plugin E的handleEvent方法(之前已经在主题下注册过)。
  在计算机辅助手术(CAS)的环境中,基于服务的通信特别适合于交换标准c++数据类型无法描述的数据,因为它们不受事件的支持,例如在插件之间或从Qt GUI元素到主程序的DICOM[9]数据的传输。相反,异步传输事件用于小的、频繁更新的数据,如用于实时导航的跟踪器坐标。
  数据交换的基于服务/事件的特性提供了单个插件之间的松散耦合,因此CAS应用程序的模块化设计如第3节所示。插件一旦创建,就可以很容易地为其他程序重用。每个插件作为一个独立的模块,其状态由定义的访问点(GUI输入、服务更新和事件)定义。因此,插件可以包含它们各自的状态机。由于CTK基于Qt[6],因此使用Qts自己的statemachine实现是合适的。不过,插件可以包含任何库,因此也可以使用其他实现,如IGSTK[1]。

3.创建新CTK插件

在这里插入图片描述
  在本节中,我们将详细讨论如何创建和管理项目和插件。在创建项目的第一步中,您需要一个顶级CMakeList (src/CMakeList.txt),以便能够使用CMake跨平台[11]编译项目及其插件。可能的项目布局如图所示。该项目包含一个应用程序CTK_Tutorial_bin和一个插件at.voxelmaster.emptyPlugin。
  顶级CMakeLists.txt文件负责查找已安装的CTK和Qt4库,并管理插件的创建。CTK Plugin integration[12]需要CTK和Qt4库。要找到CTK和Qt,需要在cmaklist .txt中添加以下CMake命令

FIND_PACKAGE(CTK)and FIND_PACKAGE(Qt4)

  为了能够在您自己的构建系统中利用CTKs复杂的依赖项检查系统,您需要编写一个名为GetMyTargetLibraries的小型CMake宏。它在CTKs自己的CMake宏和函数中用作回调函数,用于区分项目中构建的目标和项目外部的目标(例如来自CTK的目标)。它的工作方式是获取一个目标名称列表,并使用正则表达式过滤属于您自己的项目的名称。在_tmp_list和OUTPUT_VARIABLE之间,正则表达式的使用不受限制。注意,下划线(_)将被点()替换。

macro(GetMyTargetLibraries all_target_libraries varname)
set(re_ctkplugin "^at_voxelmaster_[azAZ09_]+$")
set(_tmp_list)
list(APPEND _tmp_list ${all_target_libraries})
ctkMacroListFilter(_tmp_list re_ctkplugin OUTPUT_VARIABLE ${varname})
endmacro()

这个宏可能的插件名是:

at.voxelmaster.testname
at.voxelmaster.newPlugin
at.voxelmaster.pluginName
at.voxelmaster.asdfjkasdf

  在下一步中,您应该使用CMake命令集为plugins目录中的每个插件创建一个包含一个条目的列表。每个条目由包含插件的目录名、双点(:)和默认构建选项值(ON/OFF)组成。这是在CMake项目中设置CTK插件的主要宏。

SET(plugins
plugins/at.voxelmaster.emptyPlugin:ON)

  ctkMacroSetupPlugins宏负责验证当前的插件构建选项集,启用和/或检查所需的插件,并处理插件依赖关系的所有方面。此外,它在每个给定的插件上调用add_subdirectory()。

ctkMacroSetupPlugins(${plugins}
)

  在CmakeList中集成我们的应用程序CTK_Tutorial.txt需要从CTKPluginFramework库调用方法,它通过include_directory包含目录,并将应用程序链接到CTKPluginFramework[13]。因此,您需要以下CMake命令

PROJECT(CTK_Tutorial)
SET(CTK_Tutorial_SRCS
src/main.cxx
)
INCLUDE_DIRECTORIES(
${CTK_Tutorial_SOURCE_DIR}
${CTK_Tutorial_BINARY_DIR}
${CTK_Tutorial_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
SET(my_libs
CTKPluginFramework
)
ADD_EXECUTABLE(CTK_Tutorial ${CTK_Tutorial_SRCS})
TARGET_LINK_LIBRARIES(CTK_Tutorial {QT_LIBRARIES} ${CTK_LIBRARIES} ${my_libs}
)

  提示1:在这个链接下面找到示例
https://sourceforge.net/p/ctkcas/code/ci/master/tree/CTK_Tutorial/CMakeLists.txt
  要在应用程序中创建自己的插件,需要在主项目中创建plugindirectory插件。在这个目录中,可以放置插件目录,例如at.voxelmaster.emptyPlugin。插件至少应该包含以下文件:pluginName.cpp,pluginName.h, CmakeList.txt, manifest_headers.cmake target_libraries.cmake。要生成此插件文件,可以使用位于…中的CTKs插件生成器应用程序CTKPluginGenerator。/yourCTKBuildFolder/CTKbuild/bin或复制emptyPlugin插件
(https://sourceforge.net/p/ctkcas/code/ci/master/tree/CTK_Tutorial/plugins/at.voxelmaster.emptyPlugin/)
外部插件(没有可以在不同项目中使用的主应用程序的插件)的构建方式与CTK项目本身中的插件完全相同。因此,插件的CmakeList应该包含以下CMake命令,以便将插件集成到应用程序中(这只是一个示例CMakeLists文件)

PROJECT(at_voxelmaster_myNewPlugin)
SET(PLUGIN_export_directive "at_voxelmaster_myNewPlugin_EXPORT")
SET(PLUGIN_SRCS
emptyPlugin.cpp
)
SET(PLUGIN_MOC_SRCS
emptyPlugin.h
)
ctkFunctionGetTargetLibraries(PLUGIN_target_libraries)
ctkMacroBuildPlugin(
NAME ${PROJECT_NAME}
EXPORT_DIRECTIVE ${PLUGIN_export_directive}
SRCS ${PLUGIN_SRCS}
MOC_SRCS ${PLUGIN_MOC_SRCS}
UI_FORMS ${PLUGIN_UI_FORMS}
RESOURCES ${PLUGIN_resources}
TARGET_LIBRARIES ${PLUGIN_target_libraries}
)

提示2:项目和插件创建的额外帮助可以在
http://www.commontk.org/index.php/Documentation/CTK_Plugin_Framework:_Setting_up_a_project
  提示3:由于某些原因,在更改插件的CMakeLists.txt并重新构建它之后,可能会出现分割错误或类似的异常。在这种情况下,删除构建目录中的所有内容,使用CMake创建目录并再次构建应用程序。
  提示4:您可以在TARGET_LIBRARIES ${PLUGIN_target_libraries}之后添加其他库。此外,在链接到一个共享库(第一个库)之后,我们还发现了一些奇怪的行为,这个库链接到另一个共享库(第二个库)。它无法在运行时找到第二个共享库。因此必须使用QLibrary类

QLibrary myLib("first library.so");
library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
library.load();
typedef void (*MyPrototype)();
MyPrototype myFunction = (MyPrototype) myLib.resolve("mysymbol");
if (myFunction)
myFunction();

在最后一步中,您需要在主应用程序中找到并启动创建的插件。在加载插件之前,需要将插件目录路径添加到ctkPluginFrameworkLauncher

ctkPluginFrameworkLauncher::addSearchPath("plugins",true);

之后你可以用with开始这个插件

ctkPluginFrameworkLauncher::start(“name of plugin”);

4.创建通信

  根据一般的插件间通信机制,插件之间可以进行[14]通信。通信符合流行的发布/订阅范式,可以以同步或异步方式执行。插件之间的通信可以采用两种模式:基于事件的模式和基于服务的模式。
  发布/订阅通信中的主要组件是:事件发送方:发送与特定主题相关的事件或消息。事件接收者(或处理程序):表示对一个或多个主题感兴趣,并接收属于这些主题的所有消息。

1)基于事件通信

  通过基于事件的通信[14],事件由两个属性组成:定义事件性质的主题和描述事件的属性集。在下面,您可以找到一个创建了属性的事件,并使用eventAdmin将其发送到接收方。

// sendEventToReceiver located in
//plugins/at.voxelmaster.eventBasedCommunicationSenderPlugin/eventBasedCommunicationSenderPlugin.cpp
ctkServiceReference reference=
context->getServiceReference<ctkEventAdmin>();
ctkEventAdmin* eventAdmin=context->getService<ctkEventAdmin>(reference);

ctkDictionary properties;
properties["sendText"]= sendText;
properties[sendText];
ctkEvent reportGeneratedEvent("SendEventUpdated", properties);
eventAdmin->
sendEvent(reportGeneratedEvent); //for synchronous sending
eventAdmin->
postEvent(reportGeneratedEvent); //for asynchronous sending

  为了接收事件通知,接收者创建一个事件处理程序(事件处理程序是实现ctkEventHandler接口的类,该接口注册为服务对象),并将其注册为ctk服务注册中心的服务。EVENT_TOPIC属性描述事件处理程序感兴趣的主题列表。

//receiveEventFromSender located in
//plugins/at.voxelmaster.eventBasedCommunicationReceiverPlugin/eventBasedCommunicationRec
eiverPlugin.cpp
ctkDictionary props;
props[ctkEventConstants::EVENT_TOPIC] = "SendEventUpdated";
context->registerService<ctkEventHandler>(instance, props);
receiverText= event.getProperty("sendText").toString();

  提示5:在启动sender插件之前,您应该在插件的两种通信模式(eventor service - based)中启动receiver插件。否则,receiver插件将无法接收发送方发送的事件(在接收方激活之前)。
  提示6:查找基于事件的通信的附加信息
http://www.commontk.org/docs/html/PluginFramework_EventAdmin_Page.html

2)基于服务的通信

  要创建基于服务的通信,发送方需要初始化一个接口并使用其属性将自己注册到服务

//sendServiceToReceiver located in
//plugins/at.voxelmaster.serviceBasedCommunicationSenderPlugin/serviceBasedCommunicationSe
nderPlugin.cpp
ctkDictionary properties;
interfaceInstance = new serviceInterface();
interfaceInstance>
sendText= "Send Service Text ";
serviceRegistrationReference =
context>
registerService<serviceInterface>(interfaceInstance,
properties);
properties["serviceEventID"]=0;
serviceRegistrationReference.setProperties(properties);

  要将服务跟踪器设置为所有serviceInterace服务的侦听器,可以用这种方式初始化它。

//initializeServiceTracker located sendServiceToReceiver.cxx
serviceTracker* sTracker = new serviceTracker(context,this);
ctkServiceTracker<serviceInterface*,serviceInterface*>* tracker = new
ctkServiceTracker<serviceInterface*,serviceInterface*>(context,sTracker);
tracker->open();

  服务跟踪器类是通过指定其接口和属性实现抽象类ctkServiceTracker的类的实例。服务跟踪器可以访问跟踪器对象
  serviceBasedCommunicationReceiverPluginObject。在CTK服务注册中心添加、删除或修改服务后,接收通知。

//serviceTracker located in
//plugins/at.voxelmaster.serviceBasedCommunicationReceiverPlugin/serviceTracker.h
serviceInterface* addingService (const ctkServiceReference &reference)
{
modifiedService(reference,context>
getService<serviceInterface>(referenc
e));
return context>
getService<serviceInterface>(reference);
}
void modifiedService (const ctkServiceReference &reference,
serviceInterface* service)
{
serviceBasedCommunicationReceiverPluginObject>
receiveServiceFromS
ender(service->sendText);
qDebug()<< "Receive Service From Sender \n"<< sendText;
}

  提示7:通常,modifiedService方法用于通信。要触发修改,只需从服务注册引用调用setProperty方法。
}

serviceRegistrationReference =
context->
registerService<serviceInterface>(interfaceInstance,
properties);
serviceRegistrationReference.setProperties(properties);
//serviceInterface located in
//plugins/at.voxelmaster.serviceBasedCommunicationSenderPlugin/serviceInterface.h
//and
//plugins/at.voxelmaster.serviceBasedCommunicationReceiverPlugin/serviceInterface.h
class serviceInterface : public QObject
{
Q_OBJECT
public:
QString sendText;
};

5.CAS演示程序

  为了验证CTK在计算机辅助外科手术中的应用,并便于对该框架的熟悉,开发了一个演示应用程序。它提供了加载DICOM[9]图像、集成跟踪数据、基于标记的CT数据注册、导航和(立体声)视频流可视化的插件。
  应用程序的结构可以分为三个子组:

1)主程序

  主程序:主程序处理插件的加载,并提供一个基本的Qt GUI框架(图2a)。GUI是从可视化插件注册的服务对象动态加载的。右边的框架包含当前选择的插件的可视化,左边的框架包含所有插件的控件。通过点击插件标题,可以选择一个活动插件。

2)可视化插件

  可视化插件通过服务向主程序提供GUI元素。它们包括一个可视化元素(VE,图2a,红色),它处理实际的图形表示,以及一个控制元素(KE,图2a,蓝色)。对于演示应用程序,创建了三个可视化插件
  DICOM加载器:KE用于加载CT DICOM数据[9]。在VE中,数据以冠状矢状面、轴向面和a相结合的形式呈现。因此,使用IGSTK DicomReader[1],使用QT Designer[6]创建GUI元素。DICOM数据上的指针存储在服务对象中,服务对象在修改后通知导航插件。
  导航:导航插件实现了DICOM[9]数据的基于标记的注册、可视化和导航。可视化的执行类似于DICOM加载器插件(图2a)。跟踪数据通过trackercommunications插件的异步事件接收。由于松耦合,可以随意替换trackercommunication插件。
  显微可视化:作为一个技术演示,显微镜插件对导航没有任何好处。KE不包含任何控制元素,VE显示立体声摄像机设置的两个视流。该流作为GUI元素被显微镜通信插件的服务接收。为了启用stereomicroscope导航,可以从tracker通信插件接收事件,并重用导航GUI元素。为简单起见,尚未实现此功能。

3) 通信插件

  通信插件通过服务和事件为可视化插件提供信息。它们作为硬件的接口。这些插件可以在任何时候替换(例如与其他跟踪器通信),只要它们实现相同的服务接口或发送相同的事件。
  跟踪器通信:Tracker-communication插件建立到特定跟踪器的连接。在演示应用程序中,从Optotrak Certus Tracker (NDI, Germany)读取IGSTK[1]标记坐标(通过Eventadmin插件)到导航插件。要集成其他跟踪器(例如通过IGSTK[1]或OpenIGTLink[10]),创建一个新的TrackerCommunication插件并将事件发送到Eventadmin插件就足够了。
  显微镜通信:这个插件实现了与stereocamera设置的连接,stereocamera设置表示stereomicroscope的摄像机。videostream作为Qt GUI元素由StereoMicroscopeVisualization插件的服务提供。这个插件提供了显微镜的所有参数(缩放、焦点、位置等)。
  图2b显示了主程序、可视化插件、通信插件和Eventadmin插件之间的通信,以及它们的服务。主程序启动所有插件。这可以按照任何顺序完成,因为每个插件都有自己的状态机。只要所需的服务还没有初始化,每个插件就以“等待”状态等待。主程序在启动可视化插件后加载GUI框架中的GUI元素。然后,DICOM加载器可以加载DICOM数据,它将通知导航插件的服务对象跟踪器。如果导航插件接收到跟踪器坐标事件(即如果跟踪器通信已成功启动),则可以初始化注册和后续导航。与这些进程无关,StereoMicroscopeVisualization插件在加载microscopecomcommunication之后,将视讯流呈现为GUI元素。
在这里插入图片描述

6.讨论

  与CAS领域中的全面工具包相比,通用工具包提供了较短的熟悉期。例如,虽然MITK[2]是基于CTK的,但它的广泛功能却与高强度的工作相关。相反,CTK插件框架支持有效地开发原型和演示。基于服务的通信可以很容易地替换或重用插件。任何额外需要的功能(如分割/注册,状态机)可以通过使用著名的库,如ITK[5]或QT[6]来集成。
  引入的演示应用程序代表了一个使用公共工具包的标准导航的简单示例。实践证明,CTK可以应用于CAS领域。由于是公共源代码,该演示为CTK插件框架的开发提供了帮助。

7.摘要

  在本文中,我们将考虑CAS应用程序日益增加的复杂性和安全性需求,并介绍CTK插件框架。
  这允许创建模块化应用程序和集成外部库来扩展功能。因此,可以避免使用广泛的框架。在第2节中,已经描述了插件的创建,以及插件之间的服务通信。为了验证CTK在计算机辅助外科手术中的应用,以及便于熟悉该框架,开发了一个演示应用程序,如第3节所述。

8.附加信息

  演示应用程序的源代码,插件的最小示例和教程都是免费的
https://sourceforge.net/projects/ctkcas/ and http://www.wopsys.com
  如果您在工作中使用本教程或应用程序,请引用我们的论文:“计算机辅助手术中的通用工具包CTK演示应用程序”,F. Ganglberger, Y. Ozbek, W. Freysinger,中国医学科学院2013年第12届年会

9.参考文件

[1] Cleary K, Cheng P, IGSTK: The Book, Insight (2007)
[2] Nolden M, Zelzer S, Seitel A, Wald D, Müller M, Franz AM, Maleike D, Fangerau M, aumhauer M,MaierHein L, MaierHein KH, Meinzer HP and Wolf I, The Medical Imaging Interaction Toolkit: challenges and advances, International Journal of Computer Assisted Radiology and Surgery (2013)
[3] Deguet A, Kumar R, Taylor R, Kazanzides P, The CISST libraries for computer assisted intervention systems,Insight 18 (2008)
[4] Schroeder WJ, Martin K, Lorensen WE, The Visualization Toolkit: An ObjectOriented
Approach to 3D Graphics, Third edition, ISBN 1930934076, Kitware, Inc. (formerly PrenticeHall) (2003)
[5] L. Ibanez and W. Schroeder. The ITK Software Guide. Kitware, Inc. ISBN 1930934106, http://www.itk.org/ItkSoftwareGuide.pdf (2003)
[6] Dalheimer M, Programming with QT, Second edition, ISBN 9780596000646, O’Reilly Media (2002)
[7] http://www.commontk.org
[8] Pieper S, Halle M, Kikinis R, 3D SLICER. Proceedings of the 1st IEEE International Symposium on Biomedical Imaging: From Nano to Macro 632635 (2004)
[9] Bidgood WD, Horii, SC Introduction to the ACRNEMA DICOM standard. RadioGraphics 12, 345355 (1992)
[10] Tokuda J, Fischer GS, Papademetris X, Yaniv Z, Ibanez L, Cheng P, Liu H, et al., OpenIGTLink: an open network protocol for imageguided therapy environment. The international journal of medical robotics
computer assisted surgery MRCAS 5, 423434 (2009)
[11] CMake 2.8.4 Documentation, http://www.cmake.org/cmake/help/v2.8.4/cmake.html
[12] Documentation/CTK Plugin Framework: Setting up a project, http://www.commontk.org/index.php/Documentation/CTK_Plugin_Framework:_Setting_up_a_project, 18
November 2011
[13] Documentation/CTK Plugin Framework: Introduction,
http://www.commontk.org/index.php/Documentation/CTK_Plugin_Framework:_Introduction, 31 January 2011
[14] CTK Event Admin Service,
http://www.commontk.org/docs/html/PluginFramework_EventAdmin_Page.html

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值