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转换过来的BMPJPEG文件,因此某些依赖于DICOM文件其他信息的操作(例如窗宽窗位调整)需要重新请求服务端,要求更新数据,因此对服务器的处理能力消耗较大,对带宽也有一定要求。

两种方式的示意图如下:


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

1WADO

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

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


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

2CGI

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

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

实际环境搭建:

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

1WampServer+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版本不是由电脑的操作系统类型(32or64位)来决定,而是应该根据WampServer安装包的类型来选择。安装完Visual C++ Redistributable for VisualStudio 2012 Update 4后需要重新安装WampServer

配置FastCGI环境:

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

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

2C语言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 PACSWeb服务端就已经搭建的差不多了,利用CGIFastCGI我们可以使用服务端的其他语言开发的程序来实现我们想要的功能,那么后续的工作就跟开发C/S模式的PACS一样了,用C/C++C#JAVA等高级语言开发PACS服务相关的程序供Web服务器调用即可。本博文中演示的是调用exe可执行文件,这种方式有风险,后续我会介绍在FastCGI模式下的利用PHP调用C++C#JAVA动态库的更安全的实现方式。


后续博文介绍

利用DCMTK搭建WML服务器

利用oracle直接操作DICOM数据

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

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




作者:[email protected]

时间:2014-10-19

 

转载于:https://my.oschina.net/zssure/blog/354775

版权声明:本文为weixin_33854644原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33854644/article/details/91874538

智能推荐

初谈指针(1)

初谈指针(1) 初谈指针(1) 前言 什么是指针 指针传递 多级指针 指针函数 一个不一样的收尾 前言 每一门语言都有其特性,说到C,就一定绕不过指针。 指针“随意”“奔放”,穿梭在内存地址之间,用得好就恣意潇洒。然而相伴的危害也大,使许多程序员“成也指针,败也指针”。要想熟练掌握指针,其难度系数不可谓之小。所以高校老师不爱讲...

初谈栈帧

一、栈帧 每一次函数调用都是一个过程,这个过程称之为函数的调用过程。,这个过程要为函数开辟空间,用于用于本次函数的调用中临时变量的保存、现场保护,这块栈空间我们称之为函数栈帧。 在函数调用的过程中这ebp(栈底寄存器)和esp(栈顶寄存器)两个存器存放了维护这个栈的栈底和栈顶指针。 二、调用过程 以下面代码(修改变量)为例 其中对于main函数而言 要将a与b压入栈中,变量a和b都在main函数的...

初谈设计模式

一. 单例模式 (Singleton) 概念:一个对象在程序运行的过程当中只允许被实例化一次。 实现方式: 1. 饿汉式:将一个对象直接实例化成一个静态变量 优点:使用简单 缺点:当类加载的时候,静态变量会被自动实例化。 以下为代码示例: 懒汉式: a. 做if判断并加锁,在实例化的时候对当前的代码块进行加锁(一层判断) b. 减少被锁的代码块,使用双重的if判断(二层判断) 以下为代码示例: 静...

Linux信号及工作原理

  什么是信号     信号可以理解为软件中断,是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是差不多的。信号是异步的,一个进程不必通过任何操作来等待信号的到达。信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件。   谁来产生信号 信号事件的发生有两个...

手机端图片放大,双指放大,元素的双指缩放

在做webapp时候,遇到一个需要做双指放大的功能,需求是:一张带有坐标的图片上有固定的点,需要点击这些坐标上的点进入相应的商品,并且需要对这一块进行双指可以缩放,双击缩放; 一开始是自己写监听touch事件进行处理,但是再缩放的时候,偶尔出现卡顿闪烁,用户体验不很好,后来采用插件 pinch-zoom GitHub地址: https://github.com/manuelstofer/...

猜你喜欢

leetcode 剑指offer 54 二叉搜索树的第k大节点

第 K 大节点即反中序遍历大第 K 个数。...

redis 初步了解

1.连接redis 通过java操作 1.首先 导入redis驱动 2.连接redis通过jedis 2.创建redis连接池 连接redis通过 jedis 相当于HTTPclient 1.创建单例模式的方法 在调用的时候被创建 2.创建私有静态 jedisPool 3. 创建私有类 创建静态代码块 放入连接池的基本配置 4.有 最大连接数 最大空闲书 最小空闲连接数 5. 创建连接redis对...

第6章数据类型-基本数据类型-Boolean类型-main

防采集标记:亢少军老师的课程和资料 Dart交流群:1046954554 Flutter开源项目请关注: https://github.com/kangshaojun @作者: 亢少军 '...

使用 Infura 和 web3.js 呼叫合约

如果你希望马上开始学习以太坊DApp开发,可以访问汇智网提供的出色的在线互动教程: 以太坊DApp实战入门教程 以太坊去中心化电商应用开发实战 如果你希望了解如何部署合约,可以查看另一篇文章:在truffle中使用infura部署以太坊智能合约。 Infura 提供公开的 Ethereum 主网和测试网络节点。到 Infura 官网申请,只要输入一点基本资料和 Email,就可以收到 API-ke...

手写RPC通信框架

RPC基本介绍 服务器端构建 API包下主体编写 首先初始采用Socket进行通信利用IO进行数据交互,对客户端要提供API调用接口,使得客户端可以利用API中提供的接口,获得服务器端的数据。服务器端模块目录如下: 先编写一个接口,该接口放置在api模块下,实现类放置在provider模块下,这样做目的是让客户端仅加载API包的条件下,可以调用服务器端的实现类完成具体业务逻辑。 服务器端需要客户端...