在此之前,此系统是结合DICOM的WADO标准,在浏览器里通过javascript操作返回的JPG图片。这种服务器端解析,客户端展现的方式,对实现图像的移动、缩放、旋转、测量等图像操作能够实现实时的交互。但这种方式存在着几个弊端:

1.获取图像上的CT值(钙化值)信息的时候,要频繁的和服务器进行交互。

2.调整图像的窗宽窗位或者对图像进行反色,也要和服务器进行频繁的交互。

3.对图像进行测量(长方形测量,椭圆测量等)只能获取到面值和周长的简单的信息,这对于医生的诊断没多大的用处,实际运用中需要知道所测量的区域的最大值、最小值、方差值、均值等测量信息。

以上的缺点归结为一点:即本地没有处理像素信息的操作。但是HTML5对于像素级处理的能力已经支持得很好,完成可以实现客户端对像素信息的操作。所以为了解决以上问题最近对系统做了一次比较大的升级。即客户端端直接操作DICOM的像素数据进行JS端图像的生成以及JS端实现窗宽窗位的调整。

获取dicom中的像素数据,可考虑以下两种方式:

A:服务器端直接以字节流的方式返回DICOM文件,客户端用JS来接收字节流,并负责解析DICOM中的图像数据,这种方式不仅要根据DICOM 的传输语法(0002,0010)Transfer Syntax UID,还要根据  (0028,0002)Samples per pixel、(0028,0004)Photometric Interpretation,(0028,0010)Rows,(0028,0011)Columns,(0028,0100)Bits Allocated,(0028,0103)Pixel Representation等标签来确定像素数据的结构,复杂点的可能还会用到查找表来查找((0028,0004)Photometric Interpretation的值等于==PALETTE COLOR)。对于非压缩的显示VR或者是隐形VR, (0028,0004)Photometric Interpretation等于MONOCHROME1或者MONOCHROME2来说JS解析出像素数据确实很方便,但是DICOM文件各式各样,要 写出包罗给种传输语法以及各种像素结构的JS文件确实很费劲。还要考虑到多帧动态图像,如果多针图像很大整个文件下载下来解析估计浏览器会彻底奔溃。所以 觉得这种方式不太可行。(虽然这过程中实现了显示VR的DICOM文件的JS解析,但是中途考虑到复杂性和难度还是放弃了)。

B:从服务器端获取DICOM文件的像素数组,既然目前基于C/S模式的PACS已经相当成熟,各式各样的第三方开源的dicom解析工具如 DCMTK,DCM4CHE,MDCM,OPENDICOM等也相当的多,用开源的DICOM解析工具获取到像素数据也相当的方便。所以在服务器获取到像 素数据返回给JS端,让JS端直接操作像素数据来生成要显示的图像。对于多帧图像也可以按需按帧的从服务器下载像素数据。

言归正传,目前此系统是基于第二种方式来实现。需要特别注意的是:做窗宽窗位调整的时候要先做Hounsfield 值的转换。

HU[i] = pixel_val[i]*rescaleSlope+ rescaleIntercept。窗宽窗位的调整使用了线性的window-leveling 算法针对CT/MR等图像,或者是非线性的gamma算法针对DX图像(即当windowWidth比较大的时候要考虑非线性的gamma算法,因为线性 算法中每windowWidth/255个原始密度会压缩成一个显示灰度,windowWidth很大的时候损失可能会很大)

//线性的window-leveling算法
min = (2*windowCenter - windowWidth)/2.0 - 0.5; 
max = (2*windowCenter + windowWidth)/2.0 - 0.5;
for (var i = 0; i != nNumPixels; i++){
    showPixelValue = (pixelHuValue[i] - min)*255.0/(double)(max - min);
}
//非线性的gamma算法
min = (2*windowCenter - windowWidth)/2.0 - 0.5; 
max = (2*windowCenter + windowWidth)/2.0 - 0.5;
for (var i = 0; i != nNumPixels; i++){
    showPixelValue = 255.0 * Math.pow(pixelHuValue/(max-min), 1.0/gamma);
}

如下代码展示JS端如何用后台获取到的像素数据生成图像。其中用到了查找表的概念。

/**
 * @author http://www.cnblogs.com/poxiao
 * pixelBuffer代表是从后台获取到的像素信息数组,代码只列出了单色灰度图像的情况,
 * 如果是三色的RGB图像自己稍微改动下代码即可。篇幅有限不在叙述。
 **/
var pixelBuffer;
//width 代表图像的宽度,即DICOM中的标签(0028,0011)Columns
var width;
//height 代表图像的高度,即DICOM中的标签(0028,0010)Rows
var height;
/**
 * @windowCenter 代表当前要显示的窗位
 * @windowWidth 代表当前要显示的窗宽
 * @bitsStored (0028,0101) 根据每个像素的存储位数生成查找表大小
 * @rescaleSlope (0028,1053)用于计算HU值
 * @rescaleIntercept  (0028,1052)用于计算HU值
 * **/
function createImageCanvas(windowCenter,windowWidth,bitsStored,rescaleSlope,rescaleIntercept){
    var lookupObject=new LookupTable();
    lookupObject.setData(windowCenter,windowWidth,bitsStored,rescaleSlope,rescaleIntercept);
    lookupObject.calculateHULookup();
    lookupObject.calculateLookup();
   
    var imageCanvas=document.createElement("canvas");
    imageCanvas.width = width;
    imageCanvas.height =height;
    imageCanvas.style.width = width;
    imageCanvas.style.height = height;
    var tmpCxt = imageCanvas.getContext("2d");
    var imageData = tmpCxt.getImageData(0,0,width,height);
    var n=0;
    for(var yPix=0; yPix<height; yPix++)
    {
        for(var xPix=0; xPix<width;xPix++)
        {
            var offset = (yPix * width + xPix) * 4;
            var pixelValue=lookupObject.lookup[pixelBuffer[n]];
            imageData.data[offset]=    pixelValue;
            imageData.data[offset+1]=pixelValue;
            imageData.data[offset+2]=pixelValue;
            imageData.data[offset+3]=255;
            n++;
        }
    }
    tmpCxt.putImageData(imageData, 0,0);
   
    return imageCanvas;
};
/**
 * 像素查找表,主要要先根据rescaleSlope和rescaleIntercept进行Hounsfield值的转换
 * HU[i] = pixel_val[i]*rescaleSlope+ rescaleIntercept
 */
function LookupTable()
{
    this.bitsStored;
    this.rescaleSlope;
    this.rescaleIntercept;
    this.windowCenter;
    this.windowWidth;
   
    this.huLookup;
    this.lookup;
} LookupTable.prototype.setData=function(wc,ww,bs,rs,ri)
{   
    this.windowCenter=wc;
    this.windowWidth=ww;
    this.bitsStored=bs;
    this.rescaleSlope=rs;
    this.rescaleIntercept=ri;
}; LookupTable.prototype.setWindowingdata=function(wc,ww)
{
    this.windowCenter=wc;
    this.windowWidth=ww;
}; LookupTable.prototype.calculateHULookup=function()
{
    var size=1<<this.bitsStored;
    this.huLookup = new Array(size);
    for(var inputValue=0;inputValue<size;inputValue++)
    {
        if(this.rescaleSlope == undefined && this.rescaleIntercept == undefined) {
            this.huLookup[inputValue] = inputValue;
        } else {      
            this.huLookup[inputValue] = inputValue * this.rescaleSlope + this.rescaleIntercept;
        }
    }
};
/**
 * 窗宽窗位的调整线性的Window-leveling算法
 * 非线性的gamma算法,稍微修改下:
 *  var y=255.0 * Math.pow(this.huLookup[inputValue]/this.windowWidth, 1.0/gamma);
 * **/
LookupTable.prototype.calculateLookup=function()
{   
    var size=1<<this.bitsStored;
    var min=this.windowCenter-0.5-(this.windowWidth-1)/2;
    var max=this.windowCenter-0.5+(this.windowWidth-1)/2;
    this.lookup=new Array(size);
    for(var inputValue=0;inputValue<size;inputValue++)
    {
        if(this.huLookup[inputValue]<=min){
            this.lookup[inputValue]=0 ;
        }else if (this.huLookup[inputValue]>max){
            this.lookup[inputValue]=255;
        }else{               
            var y=((this.huLookup[inputValue]-(this.windowCenter-0.5))/(this.windowWidth-1)+0.5)*255;
            this.lookup[inputValue]= parseInt(y);
        }
    }
};

鼠标调整窗宽窗位的时候JS端生成图像+绘制图形的速度。

1.512 X 512大小的CT图像调整窗宽窗位速度

2.512 X 512大小的彩色CT图像调整窗宽窗位速度

3.512 x 512大小的MR图像调整窗宽窗位速度

4.2057 X 1347大小的CR图像调整窗宽窗位速度

5.有了像素信息后就可以在客户端实时的获取到CT值了。

6:有了像素信息后测量也可以获取到测量区域的最大值、最小值、方差值、均值等测量信息了

进测试,调整窗宽窗位时HTML5上绘制图形的时间还是很快的,总的绘制时间在10毫秒的数量级,而且发现绘制时间还可以变少,这绘制时间包括了图 像边角上的文字信息,但是HTML5绘制文字的信息效率明显比绘制图像的效率要底,所以不必每次刷新都绘制文本信息,可以加以参数控制在图像切换或者调窗 宽窗位的时候也就是文本信息变化的时候才绘制文字信息。关于图像的生成时间,发现图像的生成时间和图像的宽X高成正比,图像越大所需时间越长,对于 CT/MR等图像时间大概在几十个毫秒级。对于2057X1347的CR图像时间大概在400毫秒级,对于2000X3000多的DX图像生成图像的时间 就有点卡顿了,要1秒-2秒左右。。。这速度还得想办法优化有木有。。。。。还有对于DX图像调整窗宽窗位虽然使用了gamma算法,但是出来的图像,我 总感觉得没有用第三方工具比如RadiAnt上看见的光滑,噪声有点大。所以在没得到更好的解决方案前,目前DX的图像只能特殊化即保留原来的方式在服务 器端直接生成JPG让客户端直接绘制,希望会DICOM图像算法的大神们看到此文章后能给小弟我一点关于DX调窗宽窗位的意见,是不是还要用到别的算法啥的?。先谢谢了

基于HTML5的PACS--HTML5图像处理的更多相关文章

  1. 使用Visual Studio Code调试基于ActionScript的LayaAir HTML5游戏

    使用Visual Studio Code(VS Code)调试的优势 使用VS Code我们可以极大地提高LayaAir Html5游戏项目的调试效率,VS Code的优势有以下几点: 在发生Java ...

  2. 在基于TypeScript的LayaAir HTML5游戏开发中使用AMD

    在基于TypeScript的LayaAir HTML5游戏开发中使用AMD AMD AMD是"Asynchronous Module Definition"的缩写,意思就是&quo ...

  3. 借助Visual Studio Code提高基于ActionScript的LayaAir HTML5游戏的调试效率

    借助Visual Studio Code提高基于ActionScript的LayaAir HTML5游戏的调试效率 使用Visual Studio Code(VS Code)调试的优势 借助VS Co ...

  4. HTML5简介及HTML5的发展前景

    WEB技术发展越来越迅速,HTML5的到来更是把WEB技术推向了巅峰,目前HTML5技术已经日趋成熟,不仅在PC段,HTML5更是在移动终端上也有广泛的应用,HTML5的未来十分光明,值得我们去学习. ...

  5. HTML5学习总结——HTML5入门与新增标签

    一.HTML5概要 1.1.为什么需要HTML5 概念: HTML5 是继 HTML4.01, XHTML 1.0 和 DOM 2 HTML 后的又一个重要版本, 旨在消除富 Internet 程序( ...

  6. html5开发学习 html5自学需要怎么学

    记得很多大鳄都说过一句话:只要站在风口上,猪都能飞起来.而对于如今的IT技术领域来说,无疑这只幸运的"猪"非html5莫属.html5开发技术在16年迎来了一个飞跃的发展,这也让很 ...

  7. HTML5 简介、HTML5 浏览器支持

    HTML5是HTML最新的修订版本,2014年10月由万维网联盟(W3C)完成标准制定. HTML5的设计目的是为了在移动设备上支持多媒体. HTML5 简单易学. 什么是 HTML5? HTML5 ...

  8. 【项目1-1】使用HTML5+CSS3绘制HTML5的logo

    作为一个WEB小萌新,自学了有一段时间,总是感觉停滞不前.最近反思中,想到前贤一句话:书读百遍其义自见.说到底,还是项目做的少,如果做多了,想必自然会得心应手. 利用HTML5+CSS3绘制HTML5 ...

  9. windows平台下基于QT和OpenCV搭建图像处理平台

        在之前的博客中,已经分别比较详细地阐述了"windows平台下基于VS和OpenCV"以及"Linux平台下基于QT和OpenCV"搭建图像处理框架,并 ...

  10. 【HTML5】初识HTML5

    HTML5 简介 HTML5是HTML最新的修订版本,2014年10月由万维网联盟(W3C)完成标准制定. HTML5的设计目的是为了在移动设备上支持多媒体. HTML5 简单易学. HTML5 是下 ...

随机推荐

  1. python基础学习笔记——类的约束

    ⾸先, 你要清楚. 约束是对类的约束. 用一个例子说话: 公司让小明给他们的网站完善一个支付功能,小明写了两个类,如下: class QQpay: def pay(self,money): print ...

  2. python基础学习笔记——面向对象初识

    面向对象初识 python中一切皆对象. 类有两种: 新式类:在py3中所有类都是新式类 经典类:在py2中只有类本身继承了object类才叫做新式类,默认是经典类 class Person: cou ...

  3. STL学习笔记1--vector

    C++STL(Standard Template Library)标准模板库是通用类模板和算法的集合.包含一些标准的数据结构的实现如queues(队列),lists(链表),stacks(栈)等.ST ...

  4. Leetcode 466.统计重复个数

    统计重复个数 定义由 n 个连接的字符串 s 组成字符串 S,即 S = [s,n].例如,["abc", 3]="abcabcabc". 另一方面,如果我们可 ...

  5. javascript学习笔记 - 引用类型 Object

    引用类型是一种数据结构,也称作对象定义,类似于类的概念. 对象是引用类型的实例. javascript引用类型有:Object, Array, Date, RegExp, Function 使用new ...

  6. 一句话学Java——Java重载和重写

    概念:重载是指两个不同的函数有相同的名称,可以是在本类之中的函数之间的重载,也可以是子类和父类的函数之间的函数重载. 重写:只能是子类重写父类的函数.这是多态的基础. 重写的规则:     参数:重写 ...

  7. 【转】hibernate映射(单向双向的一对多、多对一以及一对一、多对一)

    多对一关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是多指向一 一对多关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是一指向多 也就是说一对多和多对一的映射策略是一样的,只是站 ...

  8. JAVA接口与抽象类区别

    接口 1.接口可以继承多个接口,extends 接口1,接口2,接口3 2.接口的成员变量默认是public static abstract,必须初始化的: 3.接口只能有抽象方法,继承接口的类必须实 ...

  9. HDU——2602Bone Collector(01背包)

    Bone Collector Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) T ...

  10. BZOJ1297 [SCOI2009]迷路 【矩阵优化dp】

    题目 windy在有向图中迷路了. 该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 T 时刻到达节点 N-1. 现在给出该有向图,你能告诉windy总共有多少种不同的路径吗? 注意: ...