背景:

DICOM3.0协议中有介绍关于worklist的部分。简而言之,worklist可以看做是放射科设备从医院RIS系统中自动读取患者信息的一种“通信协议”,可以指存储在RIS系统中的患者数据库,主要包括患者的基本信息(如年龄、性别、身高、体重、出生年月等),这与DCM文件信息头MetaInfo中的多数字段重合。因此从RIS系统中自动获取worklist是医院信息化的必要组成部分。下面简单的给出几个图像,形象的描述一下worklist的作用。

worklist的实例学习:

在简单的了解了worklist的作用后,下面我们利用DCMTK提供的工具包(wlmscpfs.exe和findscu.exe)来真实模拟一下该场景,从而更深刻的学习worklist的功能。

worklist简单的看做一种“通讯”,那么自然就存在着通讯的两端,暂且称作“服务端”和“客户端”。这里我们用wlmscpfs.exe来作为worklist通讯的服务端,即等待外部访问的终端;用findscu.exe来作为服务端,用来发起worklist访问。

首先简单的介绍一下工具包的指令及使用方式。wlmscpfs.exe是类似于DOS时代的命令,通过设定参数可以实现不同的目的。利用Win+R键开启操作系统的运行窗口,输入cmd后进入到命令提示行窗口。然后进入到DCMTK编译后的bin文件夹(我本机地址是C:\Program Files (x86)\DCMTK\bin)目录,此时直接输入wlmscpfs.exe就可以看到关于该命令工具的各种说明。

>cd C:\Program Files (x86)\DCMTK\bin

>wlmscpfs.exe

(注,此处如果为了省事,可以将bin文件夹路径添加到windows的环境变量中,如是就可以在任何目录下使用bin下的各种工具了)

由上图看到,wlmscpfs.exe工具至少需要给出port一个参数,即开启worklist服务的本机端口号。另外还需要输入worklist数据库文件的地址,用来供客户端查询、访问使用。下面我们正式开启worklist的服务端程序,至于开启全过程,可以跟大家推荐CSDN一位博主的精品文章(http://blog.csdn.net/pachleng/article/details/5800513),博文中给出了具体的操作步骤,这个实例是对DCMTK论坛中的补充和更新。大家可以动手试一下。

第一步:建立各级目录

以我的电脑为例,我在D盘创建了DCMWorklist文件夹,然后建立了两个子文件夹wlistdb和wlistqry。

第二步:准备worklist数据库文件,开启worklist服务端服务。

然后将worklist的数据库文件拷贝到wlistdb目录下,此处参见冷哥博文,记得建立OFFIS子目录。至于worklist数据库文件通常在dcmtk库的源码中已经给出了,默认目录是dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\datawlistdb(我用的是3.6.0版本)。但是源码中的文件通常是.dump扩展名的文件,也就是我们常见的文本文件(用记事本或者Notepad++等工具双击即可打开)。通过利用dcmtk工具包中的dump2dcm.exe可以将.dump文件转换成.wl文件,转换后的.wl文件就是worklist数据库文件。

>dcmp2dcm.exe .\dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\data\wklistdb\wlist1.dump d:\DCMWorklist\wlistdb\OFFIS\wlist.wl

>……

有了worklist数据库文件后,我们就可以开启worklist服务了,利用的工具就是前文提到的wlmscpfs.exe(从工具名称中的SCP就可以看出这应该是服务端开启服务的)。

>wlmscpfs.exe –d –dfr –dfp d:\DCMWorklist\wlistdb 104     (注:其中的-d是为了方便我们观察工具运行过程而开启的调试开关)

>……

第三步,准备查询文件,开启worklist查询。

服务端已经准备就绪,下面就是该发起worklist查询服务啦。利用的工具是findscu.exe(从工具名称中的SCU同样可以猜测出这是客户端)。dcmtk源码包中同样给我们提供了查询worklist的查询文件,默认目录是dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\data\wlistqry,打开后可以看到里面以.dump格式存在的文件,再次利用dump2dcm.exe将.dump转换为.wl文件。

>dcmp2dcm.exe .\dcmtk-3.6.0\dcmtk-3.6.0\dcmwlm\data\wklistqrt\wlistqry1.dump d:\DCMWorklist\wlistqry\wlistqry.wl

然后我们利用findscu发起查询请求,

>findscu.exe –d 127.0.0.1 104 d:\DCMWorklist\wlistqry\wlistqry.wl –aec OFFIS

其中aec代表的是被请求或者说被呼叫的应用端的名称,即我们wlistdb文件夹内的OFFIS子文件夹。

通过以上三步,我们就简单的利用dcmtk提供的wlmscpf.exe和findscu.exe工具包以及相应的wklistdb数据库文件和wklistqry数据库查询文件模拟了worklist服务开启及查询的整个流程。是不是很简单,很容易上手。

实际结果分析:

上一部分中提到了在使用wlmscpfs.exe和findscu.exe工具包的时候开启了-d调试模式,目的就是为了方便我们跟踪整个通讯的流程。另外为了方便查看,我们利用重定向技术,将wlmscpfs.exe和findscu.exe工具包的调试信息输出到txt文件,方便我们事后进行再次对比查看。下面将两个工具包的调试信息用Notopad++打开,对比分析一下,见下图:

从调试信息可以清晰的看到wlmscpfs.exe与findscu.exe之间的通信流程,该流程在DICOM3.0标准的第四部分(Service Class Specifications )和第八部分(Network Communication Support for Message Exchange)都有详细的介绍,上述的重定向生成的文本文档就是学习DICOM3.0第四、八部分最好的实例。因为dcmtk是开源的,所以这方便我们分析wlmscpfs.exe和findscu.exe两个工具包的源码。具体分析见下一节。

wlmscpfs.exe和findscu.exe工具包源码分析:

  wlmscpfs.exe findscu.exe
C/S worklist服务端 worklist客户端
源码文件 wlmscpfs.cc
wlcefs.cc
wlmactmg.cc
findscu.cc
内部函数 1)ConnectToDataSource();//开启连接
2)WlmActivityManager();
//函数内部利用WSAStartup()启动了Windows套接字服务
3)StartProvidingService();
//启动worklist管理服务,位于wlmactmg.cc文件。函数内部调用(a)(b)两个函数。
(a)ASC_initializeNetwork()函数
ASC_initializeNetwork函数调用了DICOM协议封装的TCP协议函数DUL_InitializeNetwork(该函数内部就会出现我们在套接字编程中常见的socket、setsockopt、bind和listen函数)
(b)WaitForAssociation();函数
WaitForAssociation函数调用了DICOM协议封装的TCP/IP协议函数receiveTransportConnectionTCP(该函数内部利用的是select端口模式,会出现套接字编程中常见的select、accept函数)
4)disconnectfromDataSource();
//断开连接的函数。
1)WSAStartup();//初始化套接字服务
2)DcmFindSCU::initializeNetWork();
//函数内部调用的也是ASC_initializeNetwork函数。
3)DcmFindSCU::performQuery();
//同样该函数内部封装了很多以DUL开头的协议操作函数。DUL是DICOM  Upper Layer 的缩写。
4)WSACleanup();

从上述分析中我们基本可以看出,worklist的通讯是建立在TCP/IP这一现有协议之上的,可以说是对协议的二次封装。与我们平时进行套接字编程的基本流程相似,搞清楚了这一点,对于分析工具包、学习工具包的使用都会有很大的帮助。

worklist数据库文件或查询文件(*.wl)的生成:

1)问题提出:

参照冷哥博文(http://blog.csdn.net/pachleng/article/details/5800513、http://blog.csdn.net/pachleng/article/details/5827232)中的说明,我们可以很容易的利用DCMTK的工具包学习worklist操作。但是在实际应用模仿过程中,有的人可能会好奇为什么要把wklist.wl和wklistqry.wl文件分别放在不同目录?为什么同为以.wl为扩展名的文件,一个就可以作为worklist的数据库文件放在服务端,而另一个就是客户单的查询文件?如果想发起自己的查询,即C-FIND请求,我们怎么手动生成wklistqry.wl文件?

针对上述问题,在dcmtk的论坛中也曾经有人提到过,参见(http://forum.dcmtk.org/viewtopic.php?f=1&t=1475&hilit=wlmscpfs.exe%23p5016)。利用UltraEdit工具将wklist.wl和wklistqry.wl文件同时打开,对比如下:

从上图中可以看出,作为worklist客户端数据库文件的wklist.wl中是将患者真实信息以符合DICOM3.0标准字段的形式存储,而客户端发起查询请求的wklistqry.wl文件内部只是简单的需要查询的空字段,即各个字段的值域都为空。参考博文中给出了利用dump2dcm.exe工具包将.dump文件转换成wklistqry.wl文件(dump可以认为是普通的文本文件,可以利用记事本等工具进行直接编辑)。如下图所示:

例如作为测试用的wlist-2.dump文件中的患者ID为123456,生成后的wlist-2.wl文件中的(0010,0020)字段也是123456。

2)实际测试:

从dump2dcm.exe工具包的说明我们就可以知道,.wl文件其实就是dcm文件,只是该类文件中并不存在真实的像素信息。通常只包含信息头部分,主要指的是患者的各项信息。因此想利用dcmtk的库函数直接获取.wl文件,其实就是手动构造dcm文件的过程。参见http://support.dcmtk.org/docs/mod_dcmdata.html#Examples中的第二个例子,我们可以手动生成以“.wl”为后缀的dcm文件。具体的代码如下:

#include <stdio.h>
#include <tchar.h>
#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dctk.h"
#include "dcmtk/dcmdata/dcpxitem.h"
#include "dcmtk/dcmjpeg/djdecode.h"
#include "dcmtk/dcmjpeg/djencode.h"
#include "dcmtk/dcmjpeg/djcodece.h"
#include "dcmtk/dcmjpeg/djrplol.h"
using namespace std;int main()
{char uid[100];DcmFileFormat fileformat;DcmDataset *dataset = fileformat.getDataset();/************************************************利用下列语句可以生成worklist的数据库文件,即*不含有影像信息的dcm文件*************************************************/dataset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));dataset->putAndInsertString(DCM_PatientName, "Doe^John");OFCondition status = fileformat.saveFile("D:\\DcmWorklist\\worklist\\test.wl", EXS_LittleEndianExplicit);if (status.bad())cerr << "Error: cannot write DICOM file (" << status.text() << ")" << endl;return 0;
}

既然服务端需要的worklist数据库文件和客户端需要的查询文件都是.wl文件,唯一的区别就是一个有值域,一个没有。因此利用上述代码我们既可以生成worklist数据库文件,也可以生成worklist查询文件。

3)测试结果:

查看findscu.exe工具包,我们可以找到-k选项,也就是在提供了查询文件wklistqry.wl的同时也可以指定限定的查询字段,例如

>findscu 127.0.0.1 104 -v -k 0010,0020="123456" -aec OFFIS wlistqry.wl

如果不添加-k 0010,0020=“123456”限定选项,查询结果如前文中重定向的结果相同,而添加了限定字段后,我们能够查询到的就只有数据库端中满足PatientID字段为123456的患者数据。具体结果如下:

从中我们可以看到服务端给我们的反馈是PatientID为123456的患者信息,所返回的信息都是wlistqry.wl文件中要求的字段,其中通过-k 0010,0020=“123456”限定项来限定了查询的结果,在服务端的反馈是两个患者中表明有一个匹配的worklist数据库文件,如上图中黄色区域所示。

     猜想:既然我们可以利用dcmtk自由生成客户端的.wl查询文件,而-k 0010,0020=”123456”就是对该查询文件的补充,那么是不是如果我们直接把123456写入到wlistqry.wl中的(0010,0020)字段的值域,而直接利用修改后的查询文件也会得到相同的结果呢?此处利用自己的代码将0010,0020字段的值域填充为123456

#include <stdio.h>
#include <tchar.h>
#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dctk.h"
#include "dcmtk/dcmdata/dcpxitem.h"
#include "dcmtk/dcmjpeg/djdecode.h"
#include "dcmtk/dcmjpeg/djencode.h"
#include "dcmtk/dcmjpeg/djcodece.h"
#include "dcmtk/dcmjpeg/djrplol.h"
using namespace std;int main()
{char uid[100];DcmFileFormat fileformat;DcmDataset *dataset = fileformat.getDataset();/***********************************************【猜测一】:*利用下列语句可以生成worklist的查询文件*即,* 各个字段数据都为空的dcm文件************************************************/dataset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));dataset->putAndInsertString(DCM_ImplementationVersionName,"OFFIS_DCMTK_361");dataset->putAndInsertString(DCM_SpecificCharacterSet,"");dataset->putAndInsertString(DCM_PatientName, "");dataset->putAndInsertString(DCM_PatientID,"123456");dataset->putAndInsertString(DCM_PatientBirthDate,"");dataset->putAndInsertString(DCM_PatientSex,"");OFCondition status = fileformat.saveFile("D:\\DcmWorklist\\worklist\\testqry.wl", EXS_LittleEndianExplicit);if (status.bad())cerr << "Error: cannot write DICOM file (" << status.text() << ")" << endl;return 0;
}

然后利用

>findscu 127.0.0.1 104 -v -aec OFFIS testqry.wl 指令直接发起查询。

查询结果反馈如下图所示:

        上图证明了我们的猜想通过写入wlistqry.wl的相关字段的值域,就等同于在findscu.exe指令中添加-k XXXX,XXXX限定选项

至此我们详细的介绍了如何模拟worklist的双端服务,如何开启服务端服务、发起客户端查询,关键是对如何利用dcmtk的库函数来生成自定义的查询端.wl文件进行了补充设实例测试。

(完)

作者:zssure@163.com

时间:2014-08-23

DICOM医学图像处理:基于DCMTK工具包学习和分析worklist相关推荐

  1. 【转】DICOM医学图像处理:基于DCMTK工具包学习和分析worklist

    转自:https://blog.csdn.net/zssureqh/article/details/38775315 背景: DICOM3.0协议中有介绍关于worklist的部分.简而言之,work ...

  2. DICOM医学图像处理:Dcmtk与fo-dicom保存文件的不同设计模式之“同步VS异步”+“单线程VS多线程”...

    2019独角兽企业重金招聘Python工程师标准>>> 一.背景: 最近一直在做DCM相关的编程工作,以前项目使用C++居多,所以使用DCMTK开源库,而目前团队使用C#居多,所以需 ...

  3. DICOM医学图像处理:二零一四▪DICOM专栏一览

    题记 二零一四刚过,新年伊始就发生了"外滩踩踏"恶性事件,告诫我们要珍爱生命,为自己更为家人-- 回顾去年,有些许收获.有些许感慨-- 曾经听过一个段子说两个在公司工作多年的老员工 ...

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

    转自:https://www.cnblogs.com/mfrbuaa/p/4004114.html 有修订 背景介绍: 近期项目需求,需要使用C#进行最新的UI和相关DICOM3.0医学图像模块的开发 ...

  5. DICOM医学图像处理:DICOM存储操作之“多幅BMP图像数据存入DCM文件”

    背景: 本专栏"DICOM医学图像处理"受众较窄,起初只想作为自己学习积累和工作经验的简单整理.前几天无聊浏览了一下,发现阅读量两极化严重,主要集中在"关于BMP(JPG ...

  6. DICOM医学图像处理:DICOM存储操作之 “多幅JPG图像数据存入DCM文件”

    背景: 续上篇,继续介绍如何将多幅JPG图像数据存入DCM文件.即将有损压缩数据直接写入DCM文件,存储为Multi-frame形式. 多幅JPG图像数据存入DCM文件: 为了避免引起歧义,这里着重说 ...

  7. 【转】DICOM医学图像处理:DICOM网络传输

    背景: 专栏取名为DICOM医学图像处理原因是:博主是从医学图像处理算法研究时开始接触DICOM协议的.当初认识有局限性,认为DICOM只是一个简单的文件格式约定,简而言之,我当时认为DICOM协议就 ...

  8. php pacs,DICOM医学图像处理:WEB PACS初谈四,PHP DICOM Class – 只要踏出一步,路就在前方——zssure – CSDN博客...

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

  9. DICOM医学图像处理:fo-dicom网络传输之 C-Echo and C-Store

    背景: 上一篇博文对DICOM中的网络传输进行了介绍,主要参照DCMTK Wiki中的英文原文.通过对比DCMTK与fo-dicom两个开源库对DICOM标准的具体实现,对理解DICOM标准有一个更直 ...

  10. DICOM医学图像处理:BMP转DCM、DCM转BMP、多张BMP转DCM、JPG转DCM,,多张JPG转DCM。

    使用DCMTK库实现BMP转DCM的互相转换以及JPEG转DCM.使用DCMTK库其中在编译的时候错了好几次,在这有编译好的库给大家分享在此附上连接只需要1积分(DCMTK编译好的库-C/C++其他资 ...

最新文章

  1. “元宇宙”数字化理解
  2. python set过滤
  3. 图解SQL Server 2008入门总结
  4. 为什么重写equals()和hashcode()
  5. gm怎么刷东西 rust_RUST:2020年7月第三周 修补和更新
  6. zookeepe安装配置_Zookeeper安装配置及简单使用
  7. python第六篇文件处理类型
  8. Java 程序员最爱 Kotlin?
  9. 如何建设一个开源图形引擎的文档网站
  10. 千方百剂创建账套服务器文件,千方百剂辅助工具使用手册(图解).doc
  11. 网站建设开发策划书应该怎么写?
  12. 分析保镖人才网网站优化与搜索引擎相关建议
  13. 理科生学计算机数字媒体方向,文科生,理科生,分别可以报哪些专业?5分钟了解文理报考大方向...
  14. access字段相加_access如何对一行的几列求和
  15. win10 文件夹设置区分大小写
  16. Cisco-小型网络拓扑(DNS、DHCP、网站服务器、无线路由器)
  17. 如何用Xinstall来做一款App运营推广?
  18. Oracle数据库账户被锁定解决方案
  19. 小程序如何实时音视频聊天
  20. 在C:\Windows\Fonts下复制自己的字体

热门文章

  1. 计算机怎么看c盘用户名,如何查看c盘的软件 如何查看电脑上安装在C盘的软件...
  2. Qt 5.9.0安装教程
  3. REST(三)Restlet实现REST
  4. python根据url下载视频_Python爬取某视频并下载
  5. 关于微博api返回内容不全内容
  6. IP地址资源的分配和管理
  7. java微信web支付开发_java实现微信H5支付方法详解
  8. Android开发学习总结——appcompat_v7项目说明
  9. python UI自动化(一)
  10. VFIO PassThrough