DICOM医学图像处理:WEB PACS初谈

背景:

周末看到了一篇原公司同事的文章,讲的是关于新的互联网形势下的PACS系统。正好上一篇专栏文章也提到了有想搭建一个worklist服务器的冲动,所以就翻箱倒柜将原本学生时代做课题时搭建的简易Web
PACS找了出来,借着再次搭建的机会学习一下Web PACS相关的技术,例如WADO标准、CGI或者FastCGI等技术。

WEB PACS技术浅谈:

WEB PACS是一种利用互联网技术,跨越了医院和地域限制的,可随意查询和获取DICOM对象的PACS系统。目前常见的方式有两种:第一种是通过Web服务器提供查询定位,将对应DICOM影像存储服务器(通常为FTP)的地址与路径返回,客户端再向DICOM影像存储服务器请求对象;第二种是通过Web服务器统一提供查询及返回请求对象。两者各有利弊,第一种通过分别部署Web服务器和FTP文件服务器,减轻了Web服务器的负担,加快了响应时间,但是该方案也存在着诸多缺点,例如获取图像需要发送两次请求,FTP服务器安全性维护代价高,要求在浏览器下载能够解析DICOM图像的插件等等;第二种Web服务器整合了查询与返回,去除了FTP服务器,部署方便,它的缺点是由于Web服务器返回的数据是真实DCM转换过来的BMP或JPEG文件,因此某些依赖于DICOM文件其他信息的操作(例如窗宽窗位调整)需要重新请求服务端,要求更新数据,因此对服务器的处理能力消耗较大,对带宽也有一定要求。

两种方式的示意图如下:

(摘自文献《DICOM WADO原理及应用研究》)

1)WADO

WADO(Web Access to DICOM Persistent Object),是DICOM标准中的一部分,提供了一种通过HTTP或HTTPS协议并利用DICOM的标识符从HTML页或XML文档中存取与重现DICOM对象的机制,用于解决在互联网环境下访问DICOM对象——这也可以认为是Web
PACS的终极目标。与标准的基于HTTP或HTTPS的网络访问方式相同,用户在浏览器地址栏中输入URL,向服务器发送WEB请求,服务器接收到请求后根据URL中提供的参数,在服务端定位要求的图像或报告回送给浏览器,示意图如下(摘自DICOM标准第18章),

其实WADO标准就是定义了客户端和服务端之间交互的规则,可以简单的理解为双向交互时的参数约定,即服务端可以根据浏览器端发送参数的不同来实现常见的单机版PACS的C-FIND、C-MOVE、C-STORE等功能。DICOM标准中关于WADO部分的介绍也主要是各种参数规则的说明,以及部分URL实例,这里截取一个来简单的说明一下:

如上图所示,URL采用常见的GET方式,将传统的PACS系统客户端发送查询时的参数发送给服务端,例如studyUID、seriesUID和objectUID(其实就是DICOM图像中的SOP
Instance UID)。

2)CGI

CGI(Common Gateway Interface),是WWW技术中最重要的技术之一,有着不可取代的重要地位。CGI定义了外部应用程序(CGI程序)与Web服务器之间的接口标准,独立于开发语言,给用户提供了一种从网页浏览器向执行在服务器上的程序请求数据的方式——为Web
PACS的实现提供了一种途径。

为了理解CGI的含义,必须要搞清楚WEB开发中常见的前端和后端。前端就是Web应用中用户可以看得见碰的着的东西,服务端接收到请求后大多直接将数据传输到浏览器;后端更多的是用户看不到的(这里指的看不到不是操作后的结果看不到,而是操作的流程看不到),接收到请求后需要服务端进一步操作的,例如查询数据库、算法运算等等。而CGI就是实现这种由浏览器的输入触发在WEB服务器上运行的程序的标准。

实际环境搭建:

正如博文第一部分所述,由于第一类Web PACS需要浏览器安装第三方插件,需要单独部署FTP服务器,因此在课题起初没有采用该方案。第二种Web
PACS服务端在接受请求后会再向影响服务器发送请求,这正是上文中提到的CGI技术的一种很好的应用场景。下面就具体介绍一下如何搭建CGI应用环境:

1)WampServer+FastCGI

Web服务器搭建:

WampServer安装包下载http://www.wampserver.com/en/#wampserver-64-bits-php-5-5

安装过程中有可能会遇到“缺少msvcr110.dll,程序无法启动”错误,可以参照http://jingyan.baidu.com/article/ed2a5d1f3303d709f7be1776.html中给出的方法解决,需要提醒的是下载的Visual
C++ Redistributable for VisualStudio 2012 Update 4版本不是由电脑的操作系统类型(32位or64位)来决定,而是应该根据WampServer安装包的类型来选择。安装完Visual
C++ Redistributable for VisualStudio 2012 Update 4后需要重新安装WampServer。

配置FastCGI环境:

参考http://www.admin10000.com/Document/53.htmlhttp://my.oschina.net/Twitter/blog/210044对Apache服务器进行配置。在配置完成后重启WampServer竟然失败,出现如下错误:

更悲剧的是查看Apache的ErrorLog竟然没有提示,所以只能对修改的httpd.conf配置文件逐行排查,通过逐行注释的本方法最后找到了问题所在,由于修改DirectoryIndex引发的错误,恢复到原本的顺序后,重启WampServer竟然奇迹般的成功了,小有成就感啊,至于具体的原因后续在慢慢查找,确定了再补充上来。

2)C语言CGI实例

配置完开发环境后,给出一个简单的测试,由于电脑中没有安装PHP,所以这里就讨巧一下,直接利用Apache自带的cgi来调用一下C语言开发的程序,关于C语言CGI的配置比较简单,在Apache目录下的modules中已经包含了cgi模块,只需要在httpd.conf配置文件中指定c-cgi运行的目录即可,添加如下代码:

ScriptAlias /cgi-bin/"C:/wamp/www/c-cgi/"
AddHandler cgi-script .exe .pl .cgi
<Directory"C:/wamp/www/c-cgi/">
Options Indexes FollowSymLinks ExecCGI
AllowOverride all
Order allow,deny
Allow from all
Require local
</Directory>

具体的配置可参考http://blog.sina.com.cn/s/blog_66ec4d660100rd2h.html,实例的话就不要用该博文中的了,用我下面给出的完整示例。

GET方法实例源码

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
        char *data;
        char a[10],b[10];
        printf("Content-Type:text/html\n\n");
        printf("<HTML>\n");
        printf("<HEAD>\n<TITLE >Get Method</TITLE>\n</HEAD>\n");
        printf("<BODY>\n");
        printf("<div style=\"font-size:12px\">\n");
        data = getenv("QUERY_STRING");
		if(data==NULL)
			return 1;
        if(sscanf(data,"a=%[^&]&b=%s",a,b)!=2){
                printf("<DIV STYLE=\"COLOR:RED\">Error parameters should be entered!</DIV>\n");
        }
        else{
               printf("<DIV STYLE=\"COLOR:GREEN; font-size:15px;font-weight:bold\">a + b = %d</DIV>\n",atoi(a)+atoi(b));
        }
        printf("<HR COLOR=\"blue\" align=\"left\" width=\"100\">");
        printf("<input type=\"button\" value=\"Back CGI\" onclick=\"javascript:window.location='../cgitest-c.html'\">");
        printf("</div>\n");
        printf("</BODY>\n");
        printf("</HTML>\n");
        return 0;
}

利用VS编译后的可执行文件为gettest.exe,放到/www/cgitest-c目录下。

POST方法实例源码

#include <stdio.h>
#include <stdlib.h>
int main(void){
        int len;
        char *lenstr,poststr[20];
        char m[10],n[10];
        printf("Content-Type:text/html\n\n");
        printf("<HTML>\n");
        printf("<HEAD>\n<TITLE >post Method</TITLE>\n</HEAD>\n");
        printf("<BODY>\n");
        printf("<div style=\"font-size:12px\">\n");
        lenstr=getenv("CONTENT_LENGTH");
        if(lenstr == NULL)
                printf("<DIV STYLE=\"COLOR:RED\">Error parameters should be entered!</DIV>\n");
        else{
                len=atoi(lenstr);
                fgets(poststr,len+1,stdin);
                if(sscanf(poststr,"m=%[^&]&n=%s",m,n)!=2){
                        printf("<DIV STYLE=\"COLOR:RED\">Error: Parameters are not right!</DIV>\n");
                }
                else{
                       printf("<DIV STYLE=\"COLOR:GREEN; font-size:15px;font-weight:bold\">m * n = %d</DIV>\n",atoi(m)*atoi(n));
                }
        }
        printf("<HR COLOR=\"blue\" align=\"left\" width=\"100\">");
        printf("<input type=\"button\" value=\"Back CGI\" onclick=\"javascript:window.location='../cgitest-c.html'\">");
        printf("</div>\n");
        printf("</BODY>\n");
        printf("</HTML>\n");
        fflush(stdout);
        return 0;
}

利用VS编译后的可执行文件为posttest.exe,同样放到/www/cgitest-c目录下。

测试网页源码

<html>
<head>
<title>CGI Testing</title>
</head>
<body>
<table width="200" height="180" border="0" style="font-size:12px">
<tr><td>
<div style="font-weight:bold; font-size:15px">Method: GET</div>
<div>please input two number:<div>
<form method="get" action="./c-cgi/gettest.exe">
<input type="txt" size="3" name="a">+
<input type="txt" size="3" name="b">=
<input type="submit" value="sum">
</form>
</td></tr>
<tr><td>
<div style="font-weight:bold; font-size:15px">Method: POST</div>
<div>please input two number:<div>
<form method="post" action="./c-cgi/posttest.exe">
<input type="txt" size="3" name="m">*
<input type="txt" size="3" name="n">=
<input type="submit" value="resu">
</form>
</td></tr>
<tr><td><input type="button" value="Back Home" onclick='javascript:window.location="./index.php"'></td></tr>
</table>
</body>
</html>

放到Apache服务器根目录下,在浏览器中通过localhost/cgitest-c.html可以访问到。

实际运行结果

上述通过一个简单的计算来演示了CGI技术的实现和开发流程,当然对于简单的数字计算WEB前端自己就搞定了,此处只是为了说明CGI流程。至此整个Web
PACS的Web服务端就已经搭建的差不多了,利用CGI或FastCGI我们可以使用服务端的其他语言开发的程序来实现我们想要的功能,那么后续的工作就跟开发C/S模式的PACS一样了,用C/C++、C#或JAVA等高级语言开发PACS服务相关的程序供Web服务器调用即可。本博文中演示的是调用exe可执行文件,这种方式有风险,后续我会介绍在FastCGI模式下的利用PHP调用C++或C#或JAVA动态库的更安全的实现方式。

后续博文介绍

利用DCMTK搭建WML服务器

C#的异步编程模式在fo-dicom中的应用

VMWare三种网络连接模式的实际测试

作者:[email protected]

时间:2014-10-19

时间: 2024-10-15 13:38:20

DICOM医学图像处理:WEB PACS初谈的相关文章

DICOM医学图像处理:WEB PACS初谈二,图像的传输

背景: 如前一篇专栏博文所述,借助于CGI或FastCGI技术转发浏览器发送过来的用户请求,启动本地的DCMTK和CxImage库响应.然后将处理结果转换成常规图像返回到浏览器来实现Web PACS.本博文通过实际的代码測试来验证这一模式的可行性,同一时候对C语言编写CGI脚本提出了一些问题. 难题: 计划參照DCMTK自带工具dcm2pnm.exe的源代码.通过DicomImage将DCM文件转换成BMP文件,然后利用CGI技术返回到浏览器.实现一次简单的WEB PACS的影像传输模拟.详细的

DICOM医学图像处理:WEB PACS初谈三,PHP扩展骨架

背景: 最近两篇专栏博文讲解的都是有关WEB PACS环境的搭建,如果搭建的平台后端不进行DICOM的相关操作,其实跟PACS压根就一点关系也没有,所以最近几篇看似有些跑题,不过大家不要着急,开发环境的搭建本身就是一项巨大而且艰难的工程,等调试好环境后续的PACS相关开发就会如单机版一样得心应手,再忍耐一会,近期马上会开始介绍在平台上进行WEB PACS的研发. C/C++编写PHP扩展的环境搭建: 上两篇博文只是对该环境的一个取巧的尝试,第一篇博文直接利用APACHE服务自带的CGI,直接调用

DICOM医学图像处理:WEB PACS初谈四,PHP DICOM Class

背景: 预告了好久的几篇专栏博文一直没有整理好,主要原因是早前希望搭建的WML服务器计划遇到了问题.起初以为参照DCMTK的官方文档wwwapp.txt结合前两天搭建的WAMP服务器可以顺利的实现WML服务,借此就可以同时完成WEB PACS系列以及搭建Dicom WML服务器的两篇博文.可是在实际部署过程中发现了几个严重的问题,一时无法解决.但是在搜索解决方案的时候,偶然间找到了在DCMTK论坛上贴出来的用PHP对DCMTK工具包封装的文章.因此此篇博文在记录搭建WML遇到的问题的同时,主要想

DICOM医学图像处理:DCMTK的wiki资料学习之PACS调试

背景: 前段时间着重从dcmtk和fo-dicom(mDCM)源码角度进行剖析,期望加深对DICOM协议的理解.知其然,知其所以然.如果"所以然"很不好懂,那我们还是先多多"知其然"吧.搞清楚原理的目的不也是为了更好的运用于实践么?所以理论和实践应该彼此交错进行,理论搞不动了就搞搞应用,应用久了就钻研钻研理论. 以前上DCMTK官网仅仅是浏览关于开源库中各个类的设计模式.依赖关系.最近在打开DCMTK官网的wiki时,才发现OFFIS对DCMTK的介绍是如此的详细.

DICOM医学图像处理:DICOM网络传输

背景: 专栏取名为DICOM医学图像处理原因是:博主是从医学图像处理算法研究时开始接触DICOM协议的.当初认识有局限性,认为DICOM只是一个简单的文件格式约定,简而言之,我当时认为DICOM协议就是扩展名为DCM文件的格式说明.其实不然,随着对医疗行业的深入,对DICOM协议也有了更全面的认识.而今才发现DCM文件只是DICOM协议一部分中的一小节,仅仅是整个协议中的一个数据结构,而DICOM协议更多的是关于医疗行业各种服务及相关流程的约定,因此其实DICOM协议中最主要的是信息流,是对医院

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医学图像处理:DICOM存储操作之“多幅BMP图像数据存入DCM文件”

背景: 本专栏"DICOM医学图像处理"受众较窄,起初只想作为自己学习积累和工作经验的简单整理.前几天无聊浏览了一下,发现阅读量两极化严重,主要集中在"关于BMP(JPG)与DCM格式转换"和"DICOM 通讯协议",尤其是许久前的第一篇博文DCMTK开源库的学习笔记1:将DCM文件保存成BMP文件或数据流(即数组).因此在2014年底前打算写几篇关于DCM格式转换的文章,此次主要聚焦"如何将BMP.JPG等常规图像保存成DCM文件&q

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

一.背景: 最近一直在做DCM相关的编程工作,以前项目使用C++居多,所以使用DCMTK开源库,而目前团队使用C#居多,所以需要转向使用fo-dicom库,由于前一篇专栏文章DICOM医学图像处理:利用fo-dicom发送C-Find查询Worklist在调试过程中需要对DIMSE信息进行手动保存,偶然间发现了dcmtk开源库与fo-dicom开源库在保存dcm文件时使用的方式差异很大,因此决定研究一下,期望通过对比分析来看一下孰优孰劣. 二.dcmtk与fo-dicom保存文件函数的源码剖析:

DICOM医学图像处理:DCMTK在VS2012中的配置

背景: 最近由于项目需要,将原本的开发IDE环境由VS2008升级到了VS2012.本以为编译完成后的DCMTK开源库可以直接从VS2008移植到VS2012.但是通过项目属性添加完包含目录和依赖库后,编译会出现大量的链接错误(大多是跟dcmdata.lib.oflog.lib有关). 解决方法: 重新按照原本的博客前辈柳北风儿(大神目前已经博客转移到网易:http://blog.163.com/[email protected]/),利用CMake工具,选择VS2012本地编译器对DCMTK3