背景:
上篇博文为引子,介绍了一款神奇的开源PACS系统——Orthanc。本篇开始解读官方Cookbook中的相关内容,对于简单的浏览、访问和上传请阅读前篇博文。在常规的PACS系统中还未出现对于DCM图像的修改和匿名化操作,因此此次重点介绍Orthanc利用REST API实现对DCM医学图像的修改(modification)和匿名化(anonymization)。对于官方Cookbook中的实例进行示范和调试,通过Orthanc源码分析确保示例在本机良好运行。注意:官方Cookbook中的示例在Windows下会有错误,详情见博文。
Orthanc介绍:
取名为Orthanc源自于J.R.R. Tolkien’s(托尔金)的小说。Orthanc是艾辛格(Isengard)要塞中的黑塔,初建于第二世纪,用于储存收纳南方王国的真知晶石——palantíri,一种圆形且能够看见远方的石头,透过palantíri可以跟远方使用palantíri的人进行交流。Orthanc Server正是取palantíri的此层含义,设计出一种可在整个医院DICOM拓扑网络中便捷、透明以及可编程访问医学图像的系统(可参照wiki百科的介绍:http://en.wikipedia.org/wiki/Isengard)。
另外,Orthanc中同时包含了“RTH“,即Radiotherapy。其实Orthanc本身源自于法国de Liège大学中心医院(Centre Hospitalier Universitaire)对于放射治疗服务的研究。
Orthanc之Modification & Anonymization
Anonymization:
Orthanc从0.5.0版本之后引入了对DICOM资源的匿名化操作,可对患者(patients)、检查(studies)、序列(series)和图像(instances)多个级别进行匿名化处理。为了方便示范,此处以instances级别为例进行介绍:
1)按照上篇博文上传两幅测试图像到Orthanc Server,如下图所示:
2)利用curl命令行查看一下上述两个instances,获取ID号,结果如下:
此处获取的instance ID号为:c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c
3)按照Orthanc官方Cookbook的说明,进行匿名化操作
输入指令:curl http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/anonymize -X POST -d '{}' > c:orthanc-anonymize.dcm
但是并未获得如期结果,打开c盘发现orthanc-anonymization.dcm文件大小为0KB。开启curl的verbose模式,
curl –v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/anonymize -X POST -d '{}' > c:orthanc-anonymize.dcm
查看输出信息如下:
发现Orthanc Server中HTTP 服务返回值为404。
通过自己查看官方Cookbook给出的指令,除了对应instance的UID不同外,并未找到问题,暂且跳过,尝试一下Modification。
Modification:
1)上传DCM文件到Orthanc Server,同Anonymization中相同;
2)利用curl http://localhost:8042/instances获取指定instance的ID号;
3)参照官方Cookbook进行Modification处理
输入如下指令,此次为了方便,直接使用curl的verbose模式:
curl –v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/modify –X POST –d ‘{"Replace”:{"PatientName”:"hello”,"PatientID”:"world”}}' >c:orthanc-modify.dcm
输出结果如下:
目前为止,参照Orthanc官方的Cookbook说明,给出的两个示例都在本机无法顺利完成。因此猜测官网中给出的代码有可能是Linux系统的,在Windows系统下应该做出适当的调整,但是该怎样调整呢?该调整哪一部分呢?请继续往下看……
调试Orthanc官方Cookbook示例:
0)Orthanc REST API资料查询:
在调试之前,我们先要搞清楚出现问题的大致原因,排除一些常见的错误,比如软件版本错误、指令拼写错误等等。上面介绍的Anonymization和Modification都是利用了Orthanc的REST API功能,那么Orthanc各版本对于REST API的支持程度如何呢?我们查看一下官方的说明,如下图所示:
从上图可以看出,Orthanc从0.5.0版本之后就支持多级别的修改和匿名化操作,如上一篇博文所述,我本机安装的是最新的0.8.5版本。因此可以确保软件版本无误,另外在上述示例仿真过程中我们也对比排查了指令拼写错误。
接下来使出我们的杀手锏吧:“启动C:Orthanc-0.8.5Orthanc.sln工程,进入调试模式,查看Orthanc的源码”。打开Orthanc.sln解决方案,右键Orthanc工程开启调试模式,在关于REST API的几个核心类中插入断点,初次尝试插入的断点如下:
1)Anonymization源码调试:
重新按照上一节中的步骤,首先利用Orthanc Explorer向调试模式下的Orthanc Server添加DICOM文件,此时直接按F5跳过调试,因为图像加载过程中我们并未遇到问题。(注:此步操作必须重新添加,因为调试模式下数据的存储目录是C:Orthanc-0.8.5OrthancStorage,与二进制安装包默认的C:OrthancStorage不同,上一节中我们添加的图像在调试状态下是看不到的)
输入指令查看ID:
curl http://localhost:8042/instances
开始输入上一节的Anonymization指令:
curl -v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/anonymize -X POST -d '{}' > c:orthanc-anonymize.dcm
Orthanc工程首次停在了断点RestApi.cpp中的Visit函数内,如下图所示:
利用F11单步调试,随后进入到解析Anonymization请求操作的函数ParseAnonymizationRequest内部,可以看到在该函数内给出了一个示例与我们输入的格式相同
继续单步调试,最后发现在解析Json格式的AnonymizationRequest指令,即我们输入的'{}’,json_reader.cpp中的readToken返回值为tokenError。
至此想必我们找到了Anonymization指令出错的原因的:输入的Json格式不正确,虽然我们是按照Orthanc官方的Cookbook来进行的。为了确定Json格式是否有误,在在线Json格式检查网站(http://www.bejson.com/)测试一下,结果为:
按照提示,去掉指令中Json部分的单引号,输入:
curl -v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/anonymize -X POST -d {} > c:orthanc-anonymize.dcm
curl调试输出的结果为:
利用DICOM看图软件,打开可以发现orthanc-anonymize.dcm文件中的患者姓名已经被隐藏掉了。
【总结】:正确的指令应该是外边不添加单引号,如下所示:
curl -v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/anonymize -X POST -d {} > c:orthanc-anonymize.dcm
2)Modification源码调试:
既然已经找到了Anonymization中代码出错的问题,让我们去掉Modification中的单引号,尝试一下输入如下指令:
curl -v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/modify -X POST -d {"Replace":{"PatientName":"hello","PatientID":"world"}} >c:orthanc-modify.dcm
令我们失望的是依然看到了HTTP Server返回的404错误。
由于Modification指令中单引号内部还存在着更多的双引号。根据上次的经验,有可能还是Json格式输入输入错误。让我们只保留json_reader.cpp中的readToken断点,直接查看一下解析结果是否是tokenError?重新输入指令,进入到readToken内部,发现函数依然返回tokenError,结果如下图所示:
利用VS自带的查看工具,我们可以看到readToken函数解析的字符串current_,其真实内容是:{Replace:{PatientName:hello,PatientID:world}},其中并未看到我们输入的双引号,将{Replace:{PatientName:hello,PatientID:world}}输入到http://www.bejson.com/中,也得到了错误的结果提示:
这说明我们在命令行中输入的Json指令中的双引号,在被VS读入过程中被忽略了。那么我们利用转义字符将双引号传递进入,输入指令:
curl -v http://localhost:8042/instances/c77324ec-f5e76fc5-c96846bf-2ed4097d-86f9e79c/modify -X POST -d {"Replace":{"PatientName":"hello","PatientID":"world"}} >c:orthanc-modify.dcm
查看结果:
最终运行输出为,
在DCM看图软件中打开orthanc-modify.dcm,可以看到PatientID和PatientName已经修改。
至此,关于Orthanc Modification和Anonymization的调试已经顺利完成,可以松一口气啦,还是调试源代码给力啊^_^。
知识点补充:
Orthanc作为开源项目,获取其源码自然是轻而易举。虽然号称轻型Server系统,但是代码量还是足以吓退大多数人的。初次面对海量的代码无从下手进行调试,下面以上述调试Orthanc官方Cookbook中的Anonymization和Modification示例过程为例,讲解一下如何开始调试Orthanc。
1)如何调试Orthanc中的Http Server
之所以上一节最开始将断点设置在了RestXXX.cpp文件中,是因为本博文中的介绍的Modification和Anonymization使用的是Orthanc提供的REST API服务,因此首先推测错误应该出现在REST API的相关实现函数中。这种方法可以顺利的解决我们上面遇到的问题,并且如你所见我们已经实现了问题的排查。
但是既然调试进入了Orthanc的源码内,就顺道儿搞清楚Orthanc中Http Server的整体流程,也顺便找找究竟是在哪一个地方将curl指令中的双引号忽略的。 再一次输入指令,进入调试状态。断点第一次停在RestApi.cpp中的Visit函数处,在前一节中我们利用“单步调试”顺利的逐步进入到了JSON的解析函数readToken内,“单步调试”是向前追溯的最佳手段,也是VS提供给我们的最直观最有利的错误排查工具。但是为了要了解整个流程,我们此处需要“回溯”,那么VS提供给我们逆流而上的工具是什么呢?那就是“查找所有引用”,如下图所示:
我们可以由RestApi.cpp 转到上一级,即RestApiHierarchy.cpp中的LookupResource函数,如此迭代下去相信可以回溯到整个流程的最顶端。这种方法虽然笨拙了一些,但是比较实用。想必在Windows编程环境下大家还是更喜欢“可视化”的直观操作,即所见即所得。那么单击菜单栏中的“调试”,选择“窗口”中的“调用堆栈”,可以直观的看到在RestApi.cpp中的Visit函数之前的各种函数调用顺序,如下图所示:
至此我们可以看到整个Http Server服务的起始端是mongoose.cpp中的worker_thread函数(在“调用堆栈”中相关函数右键选择“转到源码”可以查看函数的具体位置)。由上图中“调用堆栈”的顺序我们已经可以清晰的看到Orthanc Http Server的整个调用流程。那么我们在worker_thread函数中插入断点,重新输入curl指令,查看一下在整个流程的起始状态下,Orthanc Http Server接收到的数据体是什么格式?如下图所示:
在process_new_connection函数内部读取curl发出的POST请求指令时,连接缓存buf的内容中已经不存在双引号了,因此可以确定curl的-d参数在发送JSON格式数据到Orthanc的Http Server时忽略了双引号。后续可以研究一下curl在windows环境下的使用,尤其是-d参数的设置。
2)如何调试Orthanc中的DICOM Server
由于DICOM Server比较熟悉,先验知识比较多,因此调试起来比HTTP Server容易。在Orthanc的main函数中我们可以看到httpServer.Start()和dicomServer.Start(),dicomServer.Start()就是DICOM Server的入口点,由于Orthanc是基于DCMTK开发的,因此后续的流程应该是交由DCMTK来完成的,更详细的细节可参考本专栏前面的DCMTK网络系列文章。预告一下下篇博文会详细介绍利用fo-dicom开源库打造一个简单的DICOM Server服务端,那时再详细的对比分析Orthanc中的DICOM Server,本篇暂且到此为止。
参考资料:
https://code.google.com/p/orthanc/wiki/OrthancCookbook,Cookbook官网链接
https://code.google.com/p/orthanc/wiki/Anonymization,Orthanc的Modification和Anonymization介绍
http://curl.haxx.se/docs/manpage.html,curl中参数介绍
后续专栏博文预告:
fo-dicom搭建简单的DICOM Server
时间:2014-11-29