仅提供参考和学习,代码仅为了指明个思路,转载请注明出处。

要查看此系统更多的图像处理功能请参考:

区域医疗移动医疗影像解决方案--基于HTML5的PACS--HTML5图像处理

在此之前,此系统是结合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很大的时候损失可能会很大)

1 //线性的window-leveling算法

2 min = (2*windowCenter - windowWidth)/2.0 - 0.5;

3 max = (2*windowCenter + windowWidth)/2.0 - 0.5;

4 for (var i = 0; i != nNumPixels; i++){

5 showPixelValue = (pixelHuValue[i] - min)*255.0/(double)(max - min);

6 }

7 //非线性的gamma算法

8 min = (2*windowCenter - windowWidth)/2.0 - 0.5;

9 max = (2*windowCenter + windowWidth)/2.0 - 0.5;

10 for (var i = 0; i != nNumPixels; i++){

11 showPixelValue = 255.0 * Math.pow(pixelHuValue/(max-min), 1.0/gamma);

12 }

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

1 /**

2 * @author http://www.cnblogs.com/poxiao

3 * pixelBuffer代表是从后台获取到的像素信息数组,代码只列出了单色灰度图像的情况,

4 * 如果是三色的RGB图像自己稍微改动下代码即可。篇幅有限不在叙述。

5 **/

6 var pixelBuffer;

7 //width 代表图像的宽度,即DICOM中的标签(0028,0011)Columns

8 var width;

9 //height 代表图像的高度,即DICOM中的标签(0028,0010)Rows

10 var height;

11 /**

12 * @windowCenter 代表当前要显示的窗位

13 * @windowWidth 代表当前要显示的窗宽

14 * @bitsStored (0028,0101) 根据每个像素的存储位数生成查找表大小

15 * @rescaleSlope (0028,1053)用于计算HU值

16 * @rescaleIntercept (0028,1052)用于计算HU值

17 * **/

18 function createImageCanvas(windowCenter,windowWidth,bitsStored,rescaleSlope,rescaleIntercept){

19 var lookupObject=new LookupTable();

20 lookupObject.setData(windowCenter,windowWidth,bitsStored,rescaleSlope,rescaleIntercept);

21 lookupObject.calculateHULookup();

22 lookupObject.calculateLookup();

23

24 var imageCanvas=document.createElement('canvas');

25 imageCanvas.width = width;

26 imageCanvas.height =height;

27 imageCanvas.style.width = width;

28 imageCanvas.style.height = height;

29 var tmpCxt = imageCanvas.getContext('2d');

30 var imageData = tmpCxt.getImageData(0,0,width,height);

31 var n=0;

32 for(var yPix=0; yPix

33 {

34 for(var xPix=0; xPix

35 {

36 var offset = (yPix * width + xPix) * 4;

37 var pixelValue=lookupObject.lookup[pixelBuffer[n]];

38 imageData.data[offset]= pixelValue;

39 imageData.data[offset+1]=pixelValue;

40 imageData.data[offset+2]=pixelValue;

41 imageData.data[offset+3]=255;

42 n++;

43 }

44 }

45 tmpCxt.putImageData(imageData, 0,0);

46

47 return imageCanvas;

48 };

49 /**

50 * 像素查找表,主要要先根据rescaleSlope和rescaleIntercept进行Hounsfield值的转换

51 * HU[i] = pixel_val[i]*rescaleSlope+ rescaleIntercept

52 */

53 function LookupTable()

54 {

55 this.bitsStored;

56 this.rescaleSlope;

57 this.rescaleIntercept;

58 this.windowCenter;

59 this.windowWidth;

60

61 this.huLookup;

62 this.lookup;

63 }

64

65 LookupTable.prototype.setData=function(wc,ww,bs,rs,ri)

66 {

67 this.windowCenter=wc;

68 this.windowWidth=ww;

69 this.bitsStored=bs;

70 this.rescaleSlope=rs;

71 this.rescaleIntercept=ri;

72 };

73

74 LookupTable.prototype.setWindowingdata=function(wc,ww)

75 {

76 this.windowCenter=wc;

77 this.windowWidth=ww;

78 };

79

80 LookupTable.prototype.calculateHULookup=function()

81 {

82 var size=1<

83 this.huLookup = new Array(size);

84 for(var inputValue=0;inputValue

85 {

86 if(this.rescaleSlope == undefined && this.rescaleIntercept == undefined) {

87 this.huLookup[inputValue] = inputValue;

88 } else {

89 this.huLookup[inputValue] = inputValue * this.rescaleSlope + this.rescaleIntercept;

90 }

91 }

92 };

93 /**

94 * 窗宽窗位的调整线性的Window-leveling算法

95 * 非线性的gamma算法,稍微修改下:

96 * var y=255.0 * Math.pow(this.huLookup[inputValue]/this.windowWidth, 1.0/gamma);

97 * **/

98 LookupTable.prototype.calculateLookup=function()

99 {

100 var size=1<

101 var min=this.windowCenter-0.5-(this.windowWidth-1)/2;

102 var max=this.windowCenter-0.5+(this.windowWidth-1)/2;

103 this.lookup=new Array(size);

104 for(var inputValue=0;inputValue

105 {

106 if(this.huLookup[inputValue]<=min){

107 this.lookup[inputValue]=0 ;

108 }else if (this.huLookup[inputValue]>max){

109 this.lookup[inputValue]=255;

110 }else{

111 var y=((this.huLookup[inputValue]-(this.windowCenter-0.5))/(this.windowWidth-1)+0.5)*255;

112 this.lookup[inputValue]= parseInt(y);

113 }

114 }

115 };

鼠标调整窗宽窗位的时候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调窗宽窗位的意见,是不是还要用到别的算法啥的?。先谢谢了。

用matlab调节窗宽窗位的代码,基于HTML5的PACS HTML5图像处理(7)实现客户端JS调整窗宽窗位...相关推荐

  1. 在sweetalert弹出窗插件中加入html代码

    sweetalert介绍: SweetAlert是一个JavaScript原生插件,不需要jquery支持的,能够完美替代JavaScript自带的alert弹出框,并且功能强大.设计优美.最重要的一 ...

  2. JS实现复制页面文字弹出消息提醒/在sweetalert(swal)弹出窗插件中加入html代码

    简洁版: <script type="text/javascript">document.body.oncopy=function(){alert("复制成功 ...

  3. 基于python编码实现多智能体进化算法求解带硬时间窗约束的VRP问题(适配版)

    作者:Logintern09 发布时间:2022年10月23日16时 出处:CSDN博客 专栏:<智能优化算法> 书接上回:多智能体进化算法求解带硬时间窗约束的VRP问题(附完整pytho ...

  4. 如何将MATLAB程序发布为独立的不依赖MATLAB环境可执行的程序包(基于Matlab R2015b版 )

    如何将MATLAB程序发布为独立的不依赖MATLAB环境可执行的程序包(基于MatlabR2015b版) 关键字:MatlabR2015b,可独立运行,可脱离Matlab环境,m文件,Matlab程序 ...

  5. matlab复杂周期信号类建立,实验五 基于Matlab的信号频谱分析(复杂)

    本次实验注意:<实验五MALTAB基础知识(简单)> <实验五 基于Matlab的信号频谱分析(复杂)> 选作一个即可 实验五 基于Matlab的信号频谱分析 (一) 实验目的 ...

  6. 某计算机地址总线宽度为32位,这台计算机能够寻址的内存单元是多少?,某计算机地址总线宽度为32位,这台计算机能够寻址的内存单元是多少?...

    2的32次方,是4G. 地址总线宽度为32位,一次可以发送的一个数据是32位的,则寻址的单元最大就是32位数据的最大值,就是2的32次方. 地址总线宽度 地址总线宽度决定了CPU可以访问的物理地址空间 ...

  7. STM32F10x_模拟I2C读写EEPROM(2)(切换SDA方向 + 读ACK位 + 完整代码)

    文章目录 前言 一.宏定义 二.I2C延时函数 1. 注意 三.起始 / 停止信号 1. 时序图 2. 起始信号 3. 停止信号 四.切换SDA方向 1. SDA配置为输入模式 2. SDA配置为输出 ...

  8. 对角阵在matlab,MATLABSimulink实现对角阵解耦(模型和代码).pdf

    MATLABSimulink实现对角阵解耦(模型和代码) MATLAB/Simulink 实现对角阵解耦 (模型和代码) 1.耦合与解耦: 在控制系统中,不同被控量之间往往存在相互影响,比如某封闭罐体 ...

  9. 在64位的环境中使用VS建立Web项目进行Oracle连接需要注意WebDev是32位的

    我们平时使用的都是32位的机器进行开发,装的都是32位的软件,但是我们的服务器一般都是64位的,所以有时也需要在64位的环境下装一个VS调试程序.最近遇到的一个问题就是一个同事换了一个电脑,用的是X6 ...

  10. oracle客户端免安装配置、64位机器PL/SQL和VS自带的IIS连接问题

    一.oracle客户端免安装配置 1.到oracle官网下载Oracle InstantClient, 把它解压缩到单独目录,例如C:\OracleClient, 2. 添加环境变量 ORACLE_H ...

最新文章

  1. Python:KNN
  2. 20步打造最安全的Nginx Web服务器
  3. Apache Spark探秘:三种分布式部署方式比较
  4. java数据分析平台源码_DataGear数据可视化分析平台 v2.0.0
  5. html5 测试用例,Web 测试通用测试用例
  6. linux加密框架 crypto 算法管理 - 算法查找接口 crypto_alg_mod_lookup
  7. 从java读取Excel继续说大道至简 .
  8. 榆落微时光社区小程序源码V1.0.35
  9. 《JavaSE基础教程》电子版书正式发布,欢迎大家下载
  10. 最全的Windows进程详解!
  11. CAD 开发 渐变填充
  12. linux根文件系统树制作
  13. 成为研究生后你都明白了什么?
  14. android 方向键 按钮,这不科学!安卓虚拟按键、实体键谁更好?
  15. 无限渗透实战(2)--绕过认证上校园网
  16. 天地伟业客户端服务器维护,天地伟业监控维保常见问题总结
  17. SMS、call中的MO、MT
  18. web前端开发新技术,CSS介绍
  19. 2021年「博客之星」参赛博主:dnbug Blog
  20. android studio 工作区间背景颜色设置眼睛保护色

热门文章

  1. java中valueof_JAVA中intValue()和ValueOf()什么意思,还有Value什么意思
  2. Kjava林林工具箱源代码(jbuilder工程)
  3. 快来喝杯Java(初级第一章)
  4. 数字图像处理基础——图像空间操作的3种形式
  5. no input file specified 三种解决方法
  6. 认知LTE簇优化和全网优化
  7. python图片裁剪
  8. IT行业的6大热门岗位,薪酬都有多高?
  9. java怎么编写木马,实现简单木马免杀(示例代码)
  10. 数据中心网络架构 — 网络带宽的收敛比