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

article/2025/9/16 15:07:19

转自: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://chatgpt.dhexx.cn/article/PCjHrgyS.shtml

相关文章

数学建模之MATLAB编程

EverydayOneCat 🌈 🐱🐱🐱 🍣🍣🍣🍣🍣✖️N 🌈📖📜 🥢 🐱 🐱 「Sushi shop!」 知识点 1.下载 链接:https://pan.baidu.com/s/1DbfysOOwIoSvt8HQUw0jhw 提取码:75mz 2.基本数学运算 2.1变量与数据操作 变量定义注意事项: 变量名区分字母…

数学建模-如何用matlab画出漂亮的图(一)

1 画图基本指令 hold on :保持打开的命令关闭图形保持功能hold off: title ( xx)命名 xlabel (xx’ ) x轴标注 ylabel (xx’ ) y轴标注 figure (x) 创建图窗 hidden on 将网格设为不透明 hidden off 将网格设为透明 legend (xx)加图例 grid on加网格线 subplot (2,2.4).显示第4…

数学建模-数学规划(Matlab)

目录 一、线性规划求解 二、非线性规划问题 三、整数规划&#xff08;包括0-1规划&#xff09; 四、最大最小化模型 五、多目标规划模型 注意&#xff1a;代码文件仅供参考&#xff0c;一定不要直接用于自己的数模论文中国赛对于论文的查重要求非常严格&#xff0c;代码雷同…

【数学建模】MATLAB

MATLAB 一、基本介绍二、矩阵运算三、编程基础四、常见函数以及其他五、二维图形绘制六、三维图形绘制七、运算符八、流程控制语句九、自定义函数十、画图进阶 一、基本介绍 MATLAB 是美国MathWorks公司出品的商业数学软件&#xff0c;用于算法开发、数据可视化、数据分析以及…

【数学建模】数学建模学习1---线性规划(例题+matlab代码实现)

1 线性规划 在人们的生产实践中&#xff0c;经常会遇到如何利用现有资源来安排生产&#xff0c;以取得最大经济效益的问题。此类问题构成了运筹学的一个重要分支—数学规划&#xff0c;而线性规划(Linear Programming 简记 LP)则是数学规划的一个重要分支。自从 1947 年 G. B. …

数学建模之matlab入门篇

随着美赛的推进&#xff0c;数学建模用到matlab的越来越多&#xff0c;然而对matlab不熟悉的还很多&#xff0c;在此写下matlab基础篇&#xff0c;一是帮助没有了解过matlab的同学入门&#xff0c;二是帮助自己巩固知识。 时间紧迫&#xff0c;直接开始&#xff01;此篇文章包…

数学建模-MATLAB三维作图

导出图片用无压缩tif会更清晰 帮助文档&#xff1a;doc 函数名 matlab代码导出为PDF 新建实时脚本或右键文件转换为实时脚本实时编辑器-全部运行-内嵌显示保存为PDF

数学建模专栏 | 开篇:如何备战数学建模竞赛之 MATLAB 编程

作 者 简 介 卓金武&#xff0c;MathWorks中国高级工程师&#xff0c;教育业务经理&#xff0c;在数据分析、数据挖掘、机器学习、数学建模、量化投资和优化等科学计算方面有多年工作经验&#xff0c;现主要负责MATLAB校园版业务。曾2次获全国大学生数学建模竞赛一等奖&#x…

MATLAB数学建模:常用建模函数

常用建模函数 本文将对常用于 MATLAB 建模的函数&#xff0c;如曲线拟合函数、参数估计函数、插值函数等&#xff0c;作详细介绍。 文章目录 常用建模函数1. 曲线拟合函数1.1 多项式拟合1.2 加权最小方差拟合1.3 非线性曲线拟合 2. 参数估计函数2.1 点估计2.1.1 最大似然法2.1…

【数学建模】数学建模学习2---整数规划(例题+matlab代码实现)

文章目录 1 概论1.1 定义1.2 整数规划的分类1.3 整数规划特点1.4 求解方法分类 2 分枝定界法3 0 −1型整数规划3.1 引入0 −1变量的实际问题3.1.1 投资场所的选定——相互排斥的计划3.1.2 相互排斥的约束条件3.1.3 关于固定费用的问题&#xff08;Fixed Cost Problem&#xff0…

Matlab数学建模(五):优化模型之标准模型

一、学习目标 &#xff08;1&#xff09;了解最优化模型。 &#xff08;2&#xff09;掌握线性规划的优化求解。 &#xff08;3&#xff09;掌握整数规划的优化求解。 &#xff08;4&#xff09;了解Matlab的图形化应用。 二、实例演练 1、谈谈你对最优化模型的了解。 最优…

Matlab 数学建模算法大全

Matlab 数学建模算法大全 第一章 线性规划…1 第二章 整数规划…16 第三章 非线性规划…32 第四章 动态规划…56 第五章 图与网络…68 第六章 排队论…118 第七章 对策论…154 第八章 层次分析法…167 第九章 插值与拟合…175 第十章 数据的统计描述和分析…201 第十一章 方差分…

数学建模专栏 | 第三篇:MATLAB数据建模方法(上) —常用方法

以数据为基础而建立数学模型的方法称为数据建模方法&#xff0c; 包括回归、统计、机器学习、深度学习、灰色预测、主成分分析、神经网络、时间序列分析等方法&#xff0c; 其中最常用的方法还是回归方法。 本讲主要介绍在数学建模中常用几种回归方法的 MATLAB 实现过程。 根据…

快速MATLAB学习,数学建模

文章目录 MATLAB1.简单加减乘除2.help的用法3.数据类型3.1整数3.2浮点数3.3复数3.4逻辑型3.5矩阵1 普通矩阵2转置矩阵3拉伸4逆矩阵6三维矩阵7等差矩阵8重复矩阵7全一的矩阵9结构体 4.变量的使用4.1特殊变量4.2>>iskeyword 5.矩阵的四则运算矩阵下标 6.程序结构6.1for循环…

利用MATLAB进行数学建模

一、用给定的多项式&#xff0c;如yx3-6x25x-3&#xff0c;产生一组数据(xi,yi&#xff0c;i1,2,…,n),再在yi上添加随机干扰(可用rand产生(0,1)均匀分布随机数,或用rands产生N(0,1)分布随机数)&#xff0c;然后用xi和添加了随机干扰的yi作的3次多项式拟合&#xff0c;与原系数…

【数学建模】常用模型算法及MATLAB代码汇总

大家好&#xff0c;我是程序员史迪仔。 这篇文章是在大学准备数学建模比赛时&#xff0c;整理的学习笔记&#xff0c;没想到阅读量、点赞量和收藏量还是可以的&#xff0c;很高兴我的文章能给大家带来帮助&#xff01; 一、蒙特卡洛算法二、数据拟合三、数据插值四、图论1、最…

2022数学建模国赛备赛阶段性记录(1-1)

数学建模国赛培训记录&#xff0c;主要使用软件为MATLAB&#xff0c;主要内容为在数学建模竞赛中常用的操作、数学与模型以及部分练习题的解析。 一、常规操作 1、基本运算 MATLAB内四则运算相当于计算机的加减乘除&#xff0c;对应输入数字、运算符&#xff0c;回车即可得出…

Matlab与数学建模

一、学习目标。 (1)了解Matlab与数学建模竞赛的关系。 (2)掌握Matlab数学建模的第一个小实例—评估股票价值与风险。 (3)掌握Matlab数学建模的回归算法。 二、实例演练。 1、谈谈你对Matlab与数学建模竞赛的了解。 Matlab在数学建模中使用广泛:MATLAB 是公认的最优秀的数…

MATLAB在数学建模中的应用

MATLAB在数学建模中的应用 一.预备知识 1.1.关于MATLAB软件 由于科学技术及计算机的飞速发展,各类数学软件不断涌现&#xff0c;这使在解决各类复杂的问题变得非常简单。常用的数学软件有Mathematica、MATLAB、SAS等软件。MATLAB是“Matrix Laboratory"的缩写&#xff…

数学建模——matlab基本使用

一&#xff1a;命令窗口两个常用基本命令 清除工作区&#xff1a;clear。清屏&#xff1a;clc。 二&#xff1a;变量与基本运算 圆周率表示&#xff1a;pi。lnx代码化&#xff1a;log(x)。e^x代码化&#xff1a;exp(x) x代表次数。sin(x):sin(x);cos(x):cos(x);tan(x):tan(x…