背景:

Orthanc是博主发现的一个很完美的DICOM和HTTP服务端开源软件,前几篇分别介绍了Orthanc的基本使用。Orthanc从0.8.0版本之后给出了Plugin SDK,通过该SDK可以利用Orthanc内建的REST API实现WADO服务,下面就参照官网给出的说明介绍一下如何使用SDK实现WADO服务,并且对官网的实例进行更新,采用最新的方式直接实现WADO服务。

官方说明中文翻译:

1)简介

DICOM标准定义了文件格式以及医学影像网络传输协议。WADO,即Web Access to DICOM Persistent Objects,是DICOM3.0标准中制定的一种基于网络web服务访问医学图像的协议(具体在DICOM3.0第18部分)。通过WADO协议,专业的医生可以使用常见的浏览器(目前Orthanc貌似不支持IE)预览和下载医学图像。

本博文所附代码给出了一个实现简单WADO服务的示例。该示例可以返回原始的DICOM图像,或者JPEG格式图像;并可以作为Orthanc的插件来运行。借助于Orthanc的框架,可以通过简单的几行代码实现WADO服务。

2)背景:

Orthanc是一款开源的、轻型的、独立的,并支持脚本化的DICOM服务端。Orthanc主要用来精简临床就医流程和简化医学图像的管理。另外通过兼容常见的JSON、PNG格式和RESTful API,使得DICOM标准在计算机图像领域得到更广泛的应用。Orthanc隐藏了DICOM文件格式和DICOM协议的复杂性,因此医院普通的网络管理人员以及专业的医学图像自动分析软件开发人员都可以使用。Orthanc可以作为一个健壮的医学影像处理中心,为各个医院提供服务。

从0.8.0(2014 7月份发布)开始,Orthanc给外部开发人员 提供了插件开发SDK。利用SDK开发的动态库形式的插件可以被导入到Orthanc服务中,插件通过注册回调函数来响应浏览器的HTTP请求。回调函数反过来可以访问Orthanc数据库提取目标DICOM文件的信息。Orthanc Plugin SDK以C语言头文件形式给出,链接如下:https://code.google.com/p/orthanc/source/browse/Plugins/OrthancCPlugin/OrthancCPlugin.h?name=Orthanc-0.8.0,说明文档:http://www.codeproject.com/KB/webservices/797118/OrthancPluginDocumentation.zip

3)DICOM 和 WADO

本小节只概括介绍WADO协议,详细介绍参见DICOM3.0标准的第18部分。

DICOM协议里规定了如下标准:一个患者(Patient)可以做多次检查(Studies)。每一个检查(Study)包含一系列医学图像,即序列(Series)。举个例子:标准的PET-CT检查(Study)会包含两组序列,CT 序列和PET 序列。序列中通常对应人体的二维/三维/四维影像。每种影像会被分割成多个文件存储,即Instance(也就是我们看到的包含单幅图像的单个后缀为DCM的文件)。

通常,一个DICOM实例(Instance)可以看做是二维图像与存储了患者元信息(人口统计学信息,如姓名、年龄、身高、体重等等)结构的组合。患者元信息通常是包含了DICOM标签对应值的数组。每个标签由两个十六进制数表示。非常重要的是,每一级的检查(Study)、序列(Series)和图像(Instance)要求全局唯一。

例如(0x0020,0x000d)代表的是Study Instance UID,定义检查(Study)的唯一性;

…………

一个WADO请求就是一个简单的HTTP GET请求,请求中包含了Study、Series和Instance标识符。例如:

http://localhost/wado?
studyUID=1.2.840.113845.11.1000000001951524609.20121203131451.1457891&
seriesUID=1.2.840.113619.2.278.3.262930758.589.1354512768.115&
objectUID=1.2.840.113619.2.278.3.262930758.589.1354512768.116.1&
requestType=WADO

该WADO请求的响应会是与studyUID/seriesUID/objectUID对应的DICOM图像的JPEG格式。如果希望直接获取DICOM格式文件,应在WADO请求中添加contentType=application%2Fdicom,如下所示:

http://localhost/wado?
studyUID=1.2.840.113845.11.1000000001951524609.20121203131451.1457891&
seriesUID=1.2.840.113619.2.278.3.262930758.589.1354512768.115&
objectUID=1.2.840.113619.2.278.3.262930758.589.1354512768.116.1&
contentType=application%2Fdicom&
requestType=WADO
    

官方说明就简单的翻译到这里,下面采用结合具体事例的方式来进行。

Orthanc WADO Plugin的编辑及使用:

该示例代码依赖于下面四部分:Orthanc Plugin SDK(0.8.0版本之后);CImg Library(用于将DICOM图像转换成PNG格式);JsonCpp库,用于解析Orthanc服务返回的Json格式的文件;CMake,用于编译源码。

1)Orthanc WADO Plugin的编译:

下载官方说明中的源码(http://www.codeproject.com/KB/webservices/797118/WadoPluginSources.zip),解压后按照README.txt中的说明编译安装Orthanc WADO Plugin:

第一步:进入cmd命令行模式,创建编译目录,输入指令:mkdir Build

第二步:进入Build目录

第三步:启动Cmake,开始编译,输入cmake ..\WadoPluginSources(注:这里..意思是返回WadoPluginSources源码中CmakeList.txt所在的目录,README.txt中的写法是错误的

第四步:打开Build目录下的WadoPlugin.sln工程,利用VS进行编译,会在Build\Debug目录下看到WadoPlugin.dll,说明WADO插件生成成功。

2)Orthanc WADO Plugin的安装:

源码包中README.txt给出的安装说明有误,应该将WadoPlugin.dll全路径名添加到Configuration.json文件中Plugin对应的字段内,如下图所示:

注意:在Windows系统中输入的WadoPlugin.dll的路径应该使用上图中的【/】,或者输入"c:\\WadoPluginSources\\Build\\Debug\\WadoPlugin.dll”。否则会出现错误。

3)Orthanc WADO Plugin启动:

修改完Configuration.json文件后,准到Orthanc.exe所在目录,例如我本机为:C:\Orthanc-0.8.5\Debug>Orthanc.exe ../../Orthanc/Configuration.json。【注意:后面跟的是添加了WadoPlugin.dll的Configuration.json的路径,如果输入Orthanc.exe --config=Configuration.json,是生成默认Configuration.json的结果,而并不会启动WadoPlugin服务)。

4)实例测试:

按照前几篇博文方式,上传两幅测试图像,结果如下:

其中已知test1的各级UID为:

StudyInstanceUID=1.3.6.1.4.1.30071.6.176694098609799.4240639413125000;

SeriesInstanceUID=1.3.6.1.4.1.30071.6.176694098609799.4240639413125000.1;

SOPInstanceUID=2.16.840.114421.81623.9430067258.9493139258;

构造WADO请求,查询test1图像,请求连接为:http://localhost:8042/wado?studyUID=1.3.6.1.4.1.30071.6.176694098609799.4240639413125000&seriesUID=1.3.6.1.4.1.30071.6.176694098609799.4240639413125000.1&objectUID=2.16.840.114421.81623.9430067258.9493139258&requestType=WADO

浏览器结果如下所示,与test1.dcm原文件相同。

新版Orthanc WADO Plugin:

1)官方说明:

官方说明中有这样一段:(http://www.codeproject.com/Articles/797118/Implementing-a-WADO-Server-using-Orthanc)

当从WADO HTTP请求中解析出study/series/instance标识符后,需要在Orthance数据库中进行查询。为了实现查询定位,官方博文中给出的代码实例直接借用了Orthanc内建的RESTful API服务(而不是直接响应WADO HTTP 请求)。

首先需要定位study级,代码如下:

  1. static bool LocateStudy(Json::Value& study,
  2. const std::string& studyUID)
  3. {
  4. // Retrieve the list of the studies that are stored in Orthanc
  5. Json::Value listOfStudies;
  6. if (!OrthancContext::GetInstance().RestApiDoGet(listOfStudies, "/studies"))
  7. {
  8. return false;
  9. }
  10. // Retrieve information about each of these studies
  11. for (Json::Value::ArrayIndex i = 0; i < listOfStudies.size(); i++)
  12. {
  13. std::string studyUri = "/studies/" + listOfStudies[i].asString();
  14. if (OrthancContext::GetInstance().RestApiDoGet(study, studyUri))
  15. {
  16. // If the "StudyInstanceUID" of this study matches, we are done
  17. if (study["MainDicomTags"]["StudyInstanceUID"].asString() == studyUID)
  18. {
  19. return true;
  20. }
  21. }
  22. }
  23. return false;
  24. }

LocateStudy函数内部先构造出/studies形式的RESTful API的uri请求,查询出Orthanc中的所有study的UUID,然后再循环遍历每一个获得的studyUUID,构造出/studies/{id}形式的RESTful API请求,逐个对比返回JSON结果中的StudyInstanceUID标签,实现study定位;

其次定位sereis级,代码如下:

  1. static bool LocateSeries(Json::Value& series,
  2. const Json::Value& parentStudy,
  3. const std::string& seriesUID)
  4. {
  5. // Loop over the child series of the located study
  6. const Json::Value& listOfSeries = parentStudy["Series"];
  7. for (Json::Value::ArrayIndex j = 0; j < listOfSeries.size(); j++)
  8. {
  9. std::string seriesUri = "/series/" + listOfSeries[j].asString();
  10. // If the "SeriesInstanceUID" of this series matches, we are done
  11. if (OrthancContext::GetInstance().RestApiDoGet(series, seriesUri) &&
  12. series["MainDicomTags"]["SeriesInstanceUID"].asString() == seriesUID)
  13. {
  14. return true;
  15. }
  16. }
  17. return false;
  18. }

与study级类同;

最后是Instance级,代码如下:

  1. static bool LocateInstance(Json::Value& instance,
  2. const Json::Value& parentSeries,
  3. const std::string& objectUID)
  4. {
  5. // Loop over the child instances of the located series
  6. const Json::Value& listOfInstances = parentSeries["Instances"];
  7. for (Json::Value::ArrayIndex k = 0; k < listOfInstances.size(); k++)
  8. {
  9. std::string instanceUri = "/instances/" + listOfInstances[k].asString();
  10. // If the "SOPInstanceUID" of this series matches "objectUID", we are done
  11. if (OrthancContext::GetInstance().RestApiDoGet(instance, instanceUri) &&
  12. instance["MainDicomTags"]["SOPInstanceUID"].asString() == objectUID)
  13. {
  14. return true;
  15. }
  16. }
  17. return false;
  18. }

上述定位流程复杂,从0.8.0版本之后Orthanc提供了直接访问数据库中DICOM索引的函数,OrthancPluginLookupPatient(),OrthancPluginLookupStudy(), OrthancPluginLookupStudyWithAccessionNumber(), OrthancPluginLookupSeries()and OrthancPluginLookupInstance()。利用该类函数就需不要先定位study、再定位series、最后定位instance如此繁琐了,修改后的代码如下:

  1. //2014-12-07:zssure
  2. //利用新的Orthanc插件的接口,直接定位Instance
  3. //http://www.codeproject.com/Articles/797118/Implementing-a-WADO-Server-using-Orthanc
  4. static bool LocateInstance(Json::Value& instance, const std::string& objectUID)
  5. {
  6. char* instanceId = OrthancPluginLookupInstance
  7. (OrthancContext::GetInstance().GetContext(), objectUID.c_str());
  8. if (instanceId == NULL)
  9. {
  10. return false;
  11. }
  12. std::string instanceUri = "/instances/" + std::string(instanceId);
  13. OrthancPluginFreeString(OrthancContext::GetInstance().GetContext(), instanceId);
  14. return (OrthancContext::GetInstance().RestApiDoGet(instance, instanceUri) &&
  15. instance["MainDicomTags"]["SOPInstanceUID"].asString() == objectUID);
  16. }
  17. //zssure:end

2)新版Wado Plugin修改:

按照官方的说明Orthanc Plugin SDK是以C头文件格式给出,因此直接利用Orthanc-0.8.5中的OrthancCPlugin.h文件替换掉WadoPluginSources中的OrthancCPlugin.h后,发现WadoPlugin.cpp中我们新添加的LocateInstance函数中的GetContext()无法识别:

打开OrthancContext.h文件发现,文件中并不存在GetContext()函数,因此需要手动添加公有函数:

  1. public:
  2. OrthancPluginContext* GetContext()
  3. {
  4. return context_;
  5. }

修改完成后可以识别GetContext()函数了,但是编译后出现如下错误:

将#include "../../Resources/ThirdParty/VisualStudio/stdint.h"代码修改为#include "stdint.h"后即可消除上述错误。重新生成后可获得新版WadoPlugin.dll插件。重新输入WADO Request,得到测试结果如下:

至此Orthanc WADO Plugin的开发就讲解完成了。

DICOM医学图像处理:Orthanc Plugin SDK实现WADO服务的更多相关文章

  1. DICOM医学图像处理:深入剖析Orthanc的SQLite,了解WADO & RESTful API

    背景: 上一篇博文简单翻译了Orthanc官网给出的CodeProject上“利用Orthanc Plugin SDK开发WADO插件”的博文,其中提到了Orthanc从0.8.0版本之后支持快速查询 ...

  2. DICOM医学图像处理:DIMSE消息发送与接收“大同小异”之DCMTK fo-dicom mDCM

    背景: 从DICOM网络传输一文开始,相继介绍了C-ECHO.C-FIND.C-STORE.C-MOVE等DIMSE-C服务的简单实现,博文中的代码给出的实例都是基于fo-dicom库来实现的,原因只 ...

  3. DICOM医学图像处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求

    转载:http://blog.csdn.net/zssureqh/article/details/39213817 背景: 上一篇专栏博文中针对PACS终端(或设备终端,如CT设备)与RIS系统之间w ...

  4. [转]DICOM医学图像处理:Deconstructed PACS之Orthanc

    转载:http://blog.csdn.net/zssureqh/article/details/41424027 背景: 此篇博文介绍一个开源的.基于WEB的DICOM Server软件.该开源软件 ...

  5. DICOM医学图像处理:Deconstructed PACS之Orthanc

    背景: 此篇博文介绍一个开源的.基于WEB的DICOM Server软件.该开源软件完全使用C++编写,不依赖于第三方数据库(内置了SQLite数据库)或其他框架,支持RESTful API设计模式. ...

  6. DICOM医学图像处理:Deconstructed PACS之Orthanc,Modification & Anonymization

    背景: 上篇博文为引子,介绍了一款神奇的开源PACS系统——Orthanc.本篇开始解读官方Cookbook中的相关内容,对于简单的浏览.访问和上传请阅读前篇博文.在常规的PACS系统中还未出现对于D ...

  7. DICOM医学图像处理:开源库mDCM与DCMTK的比較分析(一),JPEG无损压缩DCM图像

    背景介绍: 近期项目需求,须要使用C#进行最新的UI和相关DICOM3.0医学图像模块的开发.在C++语言下,我使用的是应用最广泛的DCMTK开源库,在本专栏的起初阶段的大多数博文都是对DCMTK开源 ...

  8. DICOM医学图像处理:WEB PACS初谈

    背景: 周末看到了一篇原公司同事的文章,讲的是关于新的互联网形势下的PACS系统.正好上一篇专栏文章也提到了有想搭建一个worklist服务器的冲动,所以就翻箱倒柜将原本学生时代做课题时搭建的简易We ...

  9. DICOM医学图像处理:WEB PACS初谈四,PHP DICOM Class

    背景: 预告了好久的几篇专栏博文一直没有整理好,主要原因是早前希望搭建的WML服务器计划遇到了问题.起初以为参照DCMTK的官方文档wwwapp.txt结合前两天搭建的WAMP服务器可以顺利的实现WM ...

随机推荐

  1. luogu2564 [SCOI2009]生日礼物

    排序枚举左端点,则右端点必定不降 #include <algorithm> #include <iostream> #include <cstring> #incl ...

  2. oracle整体结构-内存结构、物理结构、逻辑结构、进程

    Oracle的体系结构大体上分为两部分:Instance(实例)和Database(数据库). Instance(实例) :在Oracle Instance中主要包含了SGA以及一些进程(例如:PMO ...

  3. webdriver高级应用- 右键另存为下载文件

    1.要使用右键另存,需要先按照第三方工具AutoIt: 链接: https://pan.baidu.com/s/12aBBhOOTmyQpH9hukt0XGA 密码: fcdk 2.创建一个名为loa ...

  4. mysql primary partition分区

    尝试把数据库一个表分区 ALTER TABLE user PARTITION BY RANGE(TO_DAYS(`date`)) ( PARTITION p1004 VALUES LESS THAN  ...

  5. 在线安装ipa,超链接下载ipa

    在线安装ipa包其实是OTA实现,先粘一下OTA解释 OTA OTA即Over-the-Air,简单来说就是通过无线的方式发送指令给设备,具体针对iOS的设备,比如iphone .ipad等,让开发者 ...

  6. 《人月神话》读书笔记(2)-week3

    为了确保团队中的每个人都能保持系统概念上的完整性,关于项目的书面规格说明是必不可少的.手册要描绘用户可见的一切,但不应支配实现的过程.光有规格说明也是不够的,会议也是必要的.书中提到的周例会会迅捷地给 ...

  7. 2016湖南省赛----G - Parenthesis (括号匹配)

    2016湖南省赛----G - Parenthesis (括号匹配)   Bobo has a balanced parenthesis sequence P=p 1 p 2…p n of lengt ...

  8. Vmware占用宿主机硬盘越来越大

    Vmware占用宿主机硬盘越来越大 root /usr/bin/vmware-toolbox-cmd disk shrink /

  9. ACM程序设计选修课——1024: 末位零(求末尾0的方法+可有可无的快速幂)

    1024: 末位零 Time Limit: 1 Sec  Memory Limit: 32 MB Submit: 60  Solved: 11 [Submit][Status][Web Board] ...

  10. 模拟tap事件和longTap事件

    移动端模拟tap和longTap事件,基本原理就是在touchstart和touchend事件中,计算触摸的位移和时间差,位移在一定范围内(轻微滑动),时间小于150ms为tap事件,时间大于300m ...