DICOM:dcmqrscp.exe与storescu.exe中C-STORE服务的差别

背景:

专栏中曾分别写过dcmqrscp.exe与storescu.exe工具包的介绍,但是并未深究两者之间的差别。dcmqrscp.exe工具是一个mini版PACS,可分别响应C-FIND、C-MOVE、C-GET、C-STORE等各种DIMSE服务,而storescu.exe工具是C-STORE服务使用者,可以看出这两个工具包都提供C-STORE SCU服务。前几天博友的一个问题使得重新研究了一下两者在实现C-STORE SCU服务上的差别。通过源码分析发现问题根源出在Presentation
Context构建方式不同,下面通过具体示例引出问题,最后给出一个蹩脚的解决方案。

博友提出的问题:

起初看到提问后简单的猜想是“参数设置有误”,但是周末回来在自己电脑上尝试了多种参数组合方式也没能解决问题,因此决定从源码入手查找问题的根源。下面简单记录一下错误排查的整个过程,按照由浅入深的思路,分别从工具包官方文档配置说明、多工具包实际测试和源码分析三个方面来阐述。

DCMTK工具包配置官方文档:

dcmqrscp.exe工具:

之前在博文DICOM医学图像处理:DCMTK的wiki资料学习之PACS调试中曾详细介绍过关于dcmqrscp.exe工具的具体配置,由于本地测试数据都采用DICOM标准中默认支持的非压缩格式,即1.2.840.10008.1.2.1,Little Endian Explicit,因此当时并未涉及到DCM文件传输语义(Transfer
Syntax)的设置。而此次博友y317215133y遇到的问题多半与Transfer Syntax相关,因此需要重新学习官网中关于传输语义的说明。

dcmqrscp.exe工具包官网中关于Transfer Syntax传输语义的主要配置参数如下图(详情参见http://support.dcmtk.org/docs/dcmqrscp.html。):

注意观察上图中矩形框标记的两部分,其内部的含义大多是重复的,那么为什么会分为两部分呢?并且分别以prefer和propose为前缀?其中的incoming association和outgoing association又分别代表什么呢?因为dcmqrscp.exe充当的是miniPACS,其中的DIMSE服务既有SCU端,也有SCP端,例如在C-MOVE服务中,dcmqrscp.exe工具同时扮演了C-MOVE SCP和C-STORE SCU两种角色。由此可以猜测上图中的传输语义分成两部分的原因应该是分别对应SCP和SCU端。为了验证这一想法,让我们继续看一下其他工具的参数配置。

storescu.exe工具:

关于storescu.exe工具的使用可阅读之前的两篇博文DICOM医学图像处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求DICOM医学图形处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求(续),这里我们只关注storescu.exe工具包的参数配置,浏览官网文档(http://support.dcmtk.org/docs/storescu.html)得到如下结果:

通过上图基本验证了上一小节的猜想,propose与prefer分别用于控制SCU和SCP两端的传输语义,因此storescu.exe工具配置项中只有以propose为前缀的参数。为了确定我们的猜想,继续看一下storescp.exe工具的配置。

storescp.exe工具:

不罗嗦,直接贴出storescp.exe官方的配置说明(http://support.dcmtk.org/docs/storescp.html),如下图所示:

因为storescp.exe在DIMSE服务中充当SCP角色,因此其配置项中只存在以prefer为前缀的参数,至此验证了我们的猜测。

问题本地测试:

那么现在可以回忆一下博文开始时博友y317215133y提出的问题,它开启了dcmqrscp.exe充当PACS服务器,然后系统利用movescu.exe工具从dcmqrscp.exe服务端同时查询并下载非压缩Little Endian Explicit和JPEG无损压缩两种数据。

首先按照上文的分析,我们知道dcmqrscp.exe和movescu.exe工具包的配置参数中都包含prefer和propose两类。以C-MOVE服务为例,分析两个工具在具体实施过程中充当的角色,如下表:

工具包 C-MOVE C-STORE
dcmqrscp.exe SCP SCU
movescu.exe SCU SCP

因此从参数配置来看想要同时查询并下载到压缩和非压缩两种数据,我们应该分别配置C-MOVE子服务C-STORE的两端。如是的话,大致配置过程如下表:

C-STORE SCP movescu.exe +xa,此处的意思是movescu.exe工具开启的C-STORE SCP服务允许接收存储各种类型的数据
C-STORE SCU dcmqrscp.exe –xs,通过添加-xs指出dcmqrscp.exe工具包中开启的C-STORE SCU服务允许读取并发送JPEG无损压缩的数据

dcmqrscp.exe -x=,通过添加-x=指出dcmqrscp.exe工具包中开启的C-STORE SCU服务允许读取并发送默认非压缩格式的数据

【注】:工具包中对于传输语义(Transfer Syntax)的参数配置是单选方式,不能通过设置多个参数来完成传输语义的任意组合,例如上述如果同时开启-xs,-x=,那么按照先后顺序-xs会覆盖-x=的设置,导致-xs设置无效。

默认非压缩数据的本地测试:

进入命令行模式,输入下述指令开启服务端,

dcmqrscp.exe –d –c d:\DcmScuScp\dcmqrscp.cfg

客户端使用STUDY级别查询来下载数据,指令如下:

movescu.exe -d -S -aec ACME_STORE -aet ACME1 -aem ACME1 --port 12345 -od d:\ localhost 11110 -k QueryRetrieveLevel=STUDY -k StudyInstanceUID=1.3.6.1.4.1.30071.6.116521528759.4534135570203453

movescu.exe指令中的参数具体数值,例如ACME_SOTRE、ACME1,都是参照之前博文中的dcmqrscp.cfg配置文件来的,具体的可阅读博文DICOM医学图像处理:DCMTK的wiki资料学习之PACS调试。实际运行结果图如下:

从dcmqrscp.exe和movescu.exe的输出结果可以看出,利用StudyInstanceUID=1.3.6.1.4.1.30071.6.116521528759.4534135570203453查询获取到了两组数据,其中传统的非压缩数据,即传输语义为Little Endian Explicit,已经成功保存到D:\(如下图所示),而另一组JPEG无损压缩数据传输失败,具体提示错误如上图白色框所示。

JPEG无损压缩数据的本地测试:

按照之前的分析,要想下载JPEG LossLess无损压缩数据,需要C-STORE的SCU和SCP两端开启对应的传输语义,即JPEG lossless TS。上一节已经说明了dcmqrscp.exe在C-STORE服务中充当的是SCU角色,movescu.exe充当的是SCP角色。重新设置SCU和SCP如下:

输入下述指令,开启dcmqrscp.exe服务端,

dcmqrscp.exe –d –c D:\dcmscuscp\dcmqrscp.cfg
–xs

输入下述指令,重新开启movescu.exe客户端,

movescu.exe -d -S -aec ACME_STORE -aet ACME1 -aem ACME1 --port 12345 -od d:\ localhost 11110 -k QueryRetrieveLevel=STUDY -k StudyInstanceUID=1.3.6.1.4.1.30071.6.116521528759.4534135570203453
+xs

实际测试结果与非压缩数据基本类似,如下图:

修改完传输语义后,这次在D盘根目录下只看到了JPEG LL无损压缩的数据(如下图),

对比两次dcmqrscp.exe服务端的输出错误提示,可以发现在C-STORE SCU和SCP双方都为对JPEG Lossless传输语义进行单独设置时,movescu只能下载非压缩数据,对于JPEG Lossless压缩数据服务端提示不能由JPEG Lossless, Non-hierarchical, 1st Order Prediction语义转换成Little Endian Explicit语义;而单独设置完JPEG
Lossless语义参数后,对于非压缩数据dcmqrscp.exe服务端提示不能由Little Endian Explicit语义转换成JPEG Lossless, Non-hierarchical, 1st Order Prediction语义——这也就出现了前文中博友遇到的实际问题。

问题分析:

追根溯源:

让我们从根源来重新屡一下,movescu查询分别有Patient、Study、Series三个级别,是什么情况下才会同时查询到目标数据且分别包含压缩和非压缩两种类型呢?Patient级别下可以包含多个Study检查,而每个检查通常对应不同的影像设备,因此以Patient级别的查询有很大概率出现这种情况,如博文中给出的一个实例如下图:

上面我们一直使用的是Study级别的查询,那么会不会是查询级别出现问题呢?通过再次上传两组同一Patient的两套压缩和非压缩数据测试后,我可以明确地说不是由于查询级别导致的问题,那么问题究竟出在那里呢?是服务端dcmqrscp.exe?还是客户端movescu.exe?

从测试数据构造入手:

突然想到,起初为了复现博文中的问题,是通过手动修改同一Study下的同一Sereis中的两张图片,然后通过两次storescu.exe来存储到dcmqrscp.exe服务端来构造测试数据的。当时两次storescu.exe工具在提交不同格式数据时也用到了传输语义参数设置,让我们再回顾一下:

构造非压缩数据:

服务端:dcmqrscp.exe –d –c d:\dcmscuscp\dcmqrscp.cfg

客户端:storescu.exe -d localhost 11110 -aec ACME_STORE -aet ACME1 c:\test.dcm

本地测试结果如下,顺利完成测试数据上传

构造JPEG Lossless无损压缩数据:

服务端:dcmqrscp.exe –d –c d:\dcmscuscp\dcmqrscp.cfg

客户端:storescu.exe -d localhost 11110 -aec ACME_STORE -aet ACME1 c:\testLS.dcm
–xs

如是输入,发现出现了我们很熟悉的错误,无法从JPEG Lossless, Non-hierarchical, 1st Order Prediction语义转换成Little Endian Explicit语义,如下图:

按照之前的分析来看,应该是服务端配置有误,dcmqrscp.exe中的C-STORE SCP需要开启+xs

重新开启服务端:dcmqrscp.exe –d –c d:\dcmscuscp\dcmqrscp.cfg
+xs

客户端:storescu.exe -d localhost 11110 -aec ACME_STORE -aet ACME1 c:\testLS.dcm
–xs

利用storescp.exe排查:

在构造完成测试数据时,当下认为无法解答博友的问题,只能分别开启不同的服务来实现同时下载压缩和非压缩数据到本地。因为通过构造测试数据的整个过程已经很清楚的了解和掌握了工具包中关于传输语义的设置,却并未找到问题。利用源码调试也没有梳理出任何头绪,难道问题就这样终止了?

静下来想了想,dcmqrscp.exe是一个miniPACS服务端,功能比较复杂,在调试源码时不方便,那么能不能利用之前介绍C-STORE的博文,使用storescp.exe和storescu.exe两个工具包来调试查看一下具体的传输语义设置呢?

输入指令单独开启C-STORE SCP服务端:

storescp.exe –d 11110 –od c:\ +xa,此处为了省事直接使用+xa设置允许客户端的任何传输语义

输入指令开启C-STORE SCU客户端:

storescu.exe –d localhost 11110 c:\test.dcm

stroescu.exe –d localhost 11110 c:\testLS.dcm –xs

storescu.exe –d localhost 11110 c:\test.dcm –xs

在实际测试过程中发现服务单storescu.exe在开启-xs开关后,竟然可以同时实现非压缩和JPEG Lossless无损压缩两组数据

storescu.exe与dcmqrscp.exe源码对比分析:

既然已经验证了storescu.exe工具可以同时上传非压缩和压缩数据到服务端,那么问题应该出现在dcmqrscp.exe工具内部。在实现C-MOVE功能时,dcmqrscp.exe工具需要提供C-STORE SCU服务,因此猜测可能两者开启C-STORE SCU服务的方式不同,导致出现了博文中不能同时传输两种格式数据的问题。

分别找到storescu.exe工具和dcmqrscp.exe工具中关于开启C-STORE SCU服务的代码,主要查看关于Presentation Context设置部分(关于PresentationContext的介绍可参照博文DICOM医学图像处理:DICOM网络传输

通过上图可以看出在storescu.exe工具包中对于PresentationContext的设置的鲁棒性比较强,例如在添加了-xs参数后,storescu.exe的addStoragePresentationContexts函数会添加两种PresentationContext,一种是包含JPEGLossLess压缩传输语义的(即下图中的preferredTransferSyntax),一种是不包含JPEGLossLess压缩传输语义的(fallbackSyntaxes),当然还有一种是两中语义都包含的,即combinedSyntaxes

而dcmqrscp.exe通过moveCallback开启的sub operation(C-STORE SCU),在添加PresentationContext时虽然根据-xs选项也构造了TransferSyntax数组,包含下面四种传输语义(如下图),但是在添加PresentationContext时并未针对UID_JPEGProcess14SV1TransferSyntax和UID_LittleEndianExplicitTransferSyntax分别添加,因此导致当开启了-xs开关后,CTImageStorage对应的PresentationContext只能接受UID_JPEGProcess14SV1TransferSyntax无损压缩传输语义了。

为了验证上述猜测,我们利用命令行重定向工具将dcmqrscp.exe和storescu.exe配合构造测试数据时的调试信息写入到文件中,对比dcmqrscp.exe和movescu.exe工具查询下载数据的调试信息,看一下两者中C-STORE SCU服务对应的PresentationContext是否不同,详情如下图所示:

讲到这里博文中的问题的根源就算找到了,那么其对应的依据又在哪里呢?之前博文也提到过DICOM3.0标准第7部分附录D中有关于DICOM Association建立的具体细节,其中有这么一段话:

从中我们可以看出,对于同一种服务也就是PresentationContext中提到的AbstractSyntax,即本博文中的CTImageStorage,要想实现多种传输语义,就需要针对每种语义分别构造PresentationContext,如是才能实现同时兼容多种传输语义的目的。

修改尝试:

既然找到问题根源了,最后该是给出具体解决方案的时刻了,最恰当的修改方案应该是修改dcmqrscp.exe工具包中对应的addAllStoragePresentationContexts函数(具体代码在dcmqrcbm.cc文件的516行),在添加PresentationContext时根据传输语义状况分别添加PresentationContext,具体添加代码可仿照storescu.exe工具包中的addStoragePresentationContexts函数(具体代码在storescu.cc文件的1167行)内的1263-1280行。

这里由于时间关系我就不具体介绍修改过程了,为了演示本博文的分析,这里有一个简单的方法。当然该方法由于影响面较广,修改后对整个DCMTK库都有影响,因此不推荐使用。简单的方法就是直接修改dimse.cc中的DIMSE_sendMessage函数,如下图所示:

既然由于期望的传输语义(propose)与具体文件的实际传输语义(TransferSyntax UID)不匹配导致无法发送文件,那么在DIMSE_sendMessage函数内部当检测到两者不匹配时默认采用DCM文件原始的传输语义发送,需要注意的是此时在movescu.exe端需要开启+xa服务,允许接收所有传输语义。修改完成上述代码后,重新编译dcmnet工程,生成dcmnet.lib文件后即可实现同时下载两种传输语义的数据了,下图是我本机的测试结果:

最后记得修改完后,在还原回来。最靠谱的方法是修改dcmqrscp.exe工程中的代码,不要修改DCMTK库的基础文件(dimse.cc)代码。

        (本文完)

作者:[email protected]

时间:2015-01-17

时间: 2024-10-05 21:15:47

DICOM:dcmqrscp.exe与storescu.exe中C-STORE服务的差别的相关文章

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

背景: 上一篇专栏博文中针对PACS终端(或设备终端,如CT设备)与RIS系统之间worklist查询进行了介绍,并着重对比分析了DICOM3.0中各部分对DICOM网络通讯服务的定义.此次通过结合早些时间的博文DICOM医学图像处理:基于DCMTK工具包学习和分析worklist,对DCMTK开源库中提供的storescp.exe和storescu.exe工具的源码进行剖析,从底层深入了解C-STORE服务的触发及响应. 分析思路: storescp.exe和storescu.exe分别充当着

DICOM医学图形处理:storescp.exe与storescu.exe源码剖析,学习C-STORE请求(续)

背景: 上一篇博文中,在对storescp工具源文件storescp.cc和DcmSCP类的源文件scp.cc进行剖析后,得出了两者都可以实现响应C-ECHO和C-STORE(需要对DcmSCP类进行扩展)请求的功能.但是在对DcmSCP类进行扩展,期望模拟实现自己的storescp.exe工具时遇到了问题,客户端提示服务中断链接,而服务端显示保存失败,如下图所示.此次博文通过排除该问题再一次对storescp.cc和scp.cc进行对比,主要从Presentation Context.Abst

DICOM:DCMTK工具包分析之dcmqrscp.exe、dcmqridx.exe、dcmqrti.exe

背景: 新的一年开始,专栏会坚持下去,继续与大家相互交流学习.本篇博文再一次分析DCMTK自带的工具包dcmqrscp.exe(类似于一个单机版miniPACS),此次主要关注dcmqrscp.exe的数据库部分,通过使用dcmqridx.exe手动操作dcmqrscp.exe的数据库文件,直观了解数据库的记录内容:然后利用dcmqrti.exe查询dcmqrscp.exe的数据库文件,学习如何操作dcmqrscp.exe数据库. 单机版数据库: 单机版数据库就是只能运行在单机上,不提供网络功能

httpd.exe你的电脑中缺失msvcr110.dll怎么办(WIN2008服务器环境装WAMP2.5出现的问题)

httpd.exe你的电脑中缺失msvcr110.dll怎么办 去微软官方下载相应的文件 1 打开上面说的网址 Download and install, if you not have it already, from: http://www.microsoft.com/en-us/download/details.aspx?id=30679 点击下载,记得选择中文简单然后点击 下载 2 选择相应的文件类型进行下载,如果 你的电脑 是32位就下载x86版. 如果 你的明心是64位就下载x64版

DICOM医学图像处理:全面分析DICOM3.0标准中的通讯服务模块

背景: 最近在做关于PACS终端与RIS系统之间进行worklist查询的相关调试工作,因此又重新对DICOM3.0标准中关于网络传输的部分进行了阅读,在此将本周的工作进行一下总结,以加深对DICOM3.0标准的认识,从底层更加清晰的了解worklist查询.C-STORE.C-FIND等各种服务. 要点: 1)名词简称 该部分中会出现很多的常见名词的缩写,因此为了更好的理解其含义,先给出各个名词所对应的全称,这里没有用中文进行翻译原因有二,其一是因为英文很简单,而且表意很清楚,其二是因为目前D

Windows10下python3和python2同时安装 python2.exe、python3.exe和pip2、pip3设置

1.添加python2到系统环境变量 打开,控制面板\系统和安全\系统,选择高级系统设置,环境变量,选择Path,点击编辑,新建,分别添加D:\Python\python27和D:\Python\python27\Scripts到环境变量. 注意:python3安装时可以选择自动添加到系统环境变量,如未选择,方法和python2添加过程相同. 2.修改python.exe名字为python2.exe和python3.exe 找到python2和python3的安装目录,修改python2.7.9

在64位windows下使用instsrv.exe和srvany.exe创建windows服务

在64位windows下使用instsrv.exe和srvany.exe创建windows服务 在32位的windows下,包括windows7,windows xp以及windows 2003,都可以使用instsrv.exe和srvany.exe来创建自定义的windows服务.比如,我们有一个bat文件,用于将指定的程序作为服务进行启动,使用一般的工具都不可以进行此类工作,而使用由windows 2003的资源工具包windows toolkit中所带的instsrv就可以. 详细的用法这

使用 fslex.exe 和 fsyacc.exe

在这一节,我们将学习如何使用fslex.exe 和 fsyacc.exe 两个工具,它是由 F# 提供的,用来为 F# 语言创建解析器. 注意 fslex.exe 和 fsyacc.exe 是以 ocamllex.exe和 ocamlyacc.exe 为基础的,它们是随 O'Caml 发布的工具. 正如前一章所讨论的,创建语言可以分成两个步骤:解析用户的输入,然后,根据输入而行动.这分别被称为前端和后端,本章重点关注前端,但是,回忆一下前一章中有关抽象语法树,还是值得的,像这样: module

使用PageHeap.EXE或GFlags.EXE检查内存越界错误 (转)

2011-05-27 20:19 290人阅读 评论(0) 收藏 举报 microsoftdebuggingstructureoutputimagefile 必先利其器之一:使用PageHeap.EXE或GFlags.EXE检查内存越界错误 Article last modified on 2002-6-3 ---------------------------------------------------------------- The information in this articl