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

转自:https://blog.csdn.net/zssureqh/article/details/38775315

背景:

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

clip_image001clip_image002

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

image

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

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

第一步:建立各级目录

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

image

第二步:准备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++打开,对比分析一下,见下图:

image

        从调试信息可以清晰的看到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.exefindscu.exe
C/Sworklist服务端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文件同时打开,对比如下:

image

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

clip_image002[1]

         例如作为测试用的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的患者数据。具体结果如下:

image

        从中我们可以看到服务端给我们的反馈是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 指令直接发起查询。

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

image

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

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

(完)

 

作者:zssure@163.com

时间:2014-08-23

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/436038.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

libc.so.6linux查找,Linux中提示:/lib64/libc.so.6: version `GLIBC_2.17' not found 的解决办法...

昨天在服务器上安装好node之后&#xff0c;提示这个错误&#xff1a;./node: /lib64/libc.so.6: version GLIBC_2.17 not found (required by ./node)&#xff0c;今天把解决过程整理一下一、查看系统中可使用的glibc版本//使用strings命令查看strings /lib64/libc.so.6 |grep …

linux将汇编转为机器码,汇编语言 高级语言 机器语言 本地代码

不管是什么语言&#xff0c;最终都会转化为机器语言(本地代码)(机器码)&#xff0c;计算机程序的运行最终仍是以机器语言(本地代码)(机器码)运行的。java汇编语言&#xff1a;linux汇编语言是低级编程语言&#xff0c;不像高级语言有跨平台性&#xff0c;首先&#xff0c;CPU的…

【转】关于DCMTK中像素存储以及getoutdata()函数的使用

转自&#xff1a;https://blog.csdn.net/ancewer/article/details/73277895 有删改 当你看这个帖子的时候&#xff0c;假设你也因为这个问题而感到困惑。 在使用这个函数的时候纠结了很久&#xff0c;各种google、百度都没查到相关资料&#xff0c;测试了好多次&#xff0c;并…

【Visual C++】游戏开发笔记三十五 站在巨人的肩膀上:游戏引擎导论

看到在留言中很多朋友提到不太清楚DirectX与游戏引擎的区别的问题&#xff0c;在这里浅墨就专门把自己对游戏引擎的一些理解写成一篇文章&#xff0c;作为我们《Visual C游戏开发》专栏的游戏引擎导论&#xff0c;也希望能通过这篇文章&#xff0c;能让大家有所启发&#xff0c…

String ... String 三个点 jdk1.5的特性.才知道

String ... String 三个点 jdk1.5的特性.才知道 String... excludeProperty表示不定参数&#xff0c;也就是调用这个方法的时候这里可以传入多个String对象。public static void main(String[] args) {//测试&#xff0c;传入多个参数test("hello", "world"…

c语言大数相加oj,郑州轻工业大学oj题解(c语言)论如何正确的提高正确率:水题合集(四)...

好像离上一篇水题篇过去已经快半个月了~也是好久没有发过这些简单又有趣的题目了呢&#xff0c;今天想起来就总结几道吧。今天是看龙族的一天~ 花了一下午时间把《龙族2》看了2/3&#xff0c;看书的时间真的过的飞快&#xff0c;听着歌翻着书&#xff0c;一会4.5个小时就过去了…

【转】DICOM:DICOM Print服务中PresentationContext协商之 MetaSOPClass与SOPClass对比分析!!!!!!!!

转自&#xff1a;https://zssure.blog.csdn.net/article/details/45119841 背景&#xff1a; 最近项目中遇到的实际问题较多&#xff0c;且大多是较隐蔽的、不易被发现的错误。究其根源来看&#xff0c;还是对DICOM3.0协议中的细节掌握不够仔细&#xff0c;因而导致在实际编码…

2008秋季-计算机软件基础-未交实验报告名单

实验四 序号&#xff1a;108 实验五 序号&#xff1a;108&#xff0c;225&#xff0c;226 351 实验二 353 实验一 356 实验二 360 实验三 365 实验三 367 实验三 368 实验二 372 实验五 转载于:https://www.cnblogs.com/emanlee/archive/2008/11/02/1324910.html

操作系统真实的虚拟内存是什么样的

1. 内存及虚存基本布局 提起虚存&#xff0c;大都能说出几条来。 对于32位系统&#xff0c;大多数操作系统都会将4GB的内存空间的一部分挪给内核使用&#xff0c;应用程序无法直接访问这一段内存&#xff0c;这部分内存空间称为内核空间。Windows默认情况下会将高地址的2GB分配…

约瑟夫问题c语言链表解法,约瑟夫环问题 --链表 C语言

总共有m个人在圆桌上&#xff0c;依次报名&#xff0c;数到第n个数的人退出圆桌&#xff0c;下一个由退出人下一个开始继续报名&#xff0c;循环直到最后一个停止将编号输出#include #include typedef struct Head * PHead;typedef struct Node * PNode;struct Head{PNode Next…

【转】用fo-dicom实现print scu的注意事项!!!!!!!!!

转自&#xff1a;https://blog.csdn.net/tvsofa2008/article/details/50245357 用fo-dicom实现print scu的注意事项 fo-dicom是一个开源的协议库&#xff0c;开发语言是c#。网上针对fo-dicom的分析也有不少&#xff0c;但是专门针对dicom print的文章还是太少了。 近几天需要…

DELPHI串口通讯编程

spcomm更加简单 转贴 Delphi中串口通信的实现 河南省计算中心 张海航 -------------------------------------------------------------------------------- Delphi是一种具有功能强大、简便易用和代码执行速度快等优点的可视化快速应用开发工具&#xff0c;它在…

设计模式C++实现 ——观察者模式

观察者模式&#xff1a;定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。它还有两个别名&#xff0c;依赖(Dependents)&#xff0c;发布-订阅(Publish-Subsrcibe)。可以举个博客订阅的例子&…

【转】DCMTK开源库的学习笔记1:将DCM文件保存成BMP文件或数据流(即数组)

转自&#xff1a;https://blog.csdn.net/zssureqh/article/details/8784980 DCMTK开源库介绍&#xff1a; DCMTK是目前最全面实现DICOM3.0标准的开源库&#xff0c;通过结合DCMTK开源库和CxImage图像开源库&#xff0c;能够很方便的开发属于自己的DCM文件编辑浏览软件。 DCMT…

如何将C语言翻译成汇编语言,如何把汇编语言转换成C语言

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼程序:ORG 0000HLJMP MAINORG 000BHLJMP TIMEORG 1000HHOUR1 EQU 10hHOUR2 EQU 12hMIN1 EQU 14hMIN2 EQU 16hSEC1 EQU 18HCOUNT EQU 20HNUM1 EQU 22HNUM2 EQU 24HLL4 EQU 26Hs1 bit P1.0s2 bit P1.1s3 bit P1.2s4 bit P1.3A1 EQU 36…

数据库-Oracle【Oracle数据库设置默认表空间问题及Oracle,SQL,MySQL的自增变量设置】...

一、 Oracle数据库设置默认表空间问题 DBA们经常会遇到一个这样令人头疼的问题&#xff1a;不知道谁在Oracle上创建了一个用户&#xff0c;创建时&#xff0c;没有给这个用户指定默认表空间&#xff0c;所以这个用户就会采用默认的表空间——system表空间。导致系统表空间迅速被…

设计模式——Iterator模式实现研究

导读&#xff1a;软件设计模式是一种表达、记录和重用软件设计结构和设计经验的新方法&#xff0c;它对反复出现的设计结构的关键特征进行识别、抽象和命名&#xff0c;使重用成功设计和结构更加容易。本文介绍了软件设计模式的特点、描述方式以及在设计中使用模式带来的好处&a…

【转】DCMTK 开源库的学习笔记2:直接操作dcm文件中像素数据的尝试

转自&#xff1a;https://blog.csdn.net/zssureqh/article/details/8785132 DCMTK官网给出了JPEG格式压缩的DCM文件解压缩的方法&#xff08;http://support.dcmtk.org/docs/mod_dcmjpeg.html&#xff09;&#xff0c;代码摘录如下&#xff1a; DJDecoderRegistration::regist…

R语言处理非线性回归模型C-D方程,使用R语言进行多项式回归、非线性回归模型曲线拟合...

对于线性关系&#xff0c;我们可以进行简单的线性回归。对于其他关系&#xff0c;我们可以尝试拟合一条曲线。曲线拟合是构建一条曲线或数学函数的过程&#xff0c;它对一系列数据点具有最佳的拟合效果。使用示例数据集#我们将使Y成为因变量&#xff0c;X成为预测变量#因变量通…

认识IL

1.要编译的代码如下&#xff1a; using System; using System.Collections.Generic; using System.Text; namespace HellowWorld { class Program { static void Main() { Console.Write("Hello World!"); } } } 2…