vue 使用cornerstone展示 dicom 影像数据

cornerstone 库地址

可能访问失败,建议翻墙使用

需要用到的依赖库,可 npm 可自行加入项目public 文件夹中,这里提供文件下载

点此下载 密码: tcg3

注意(坑)

cornerstoneWADOImageLoader.js 文件中需要自行修改 path 路径(注意:是绝对路径哦),大概在 1750 行。当时卡了好久。

var defaultConfig = {
 maxWebWorkers: navigator.hardwareConcurrency || 1,
 startWebWorkersOnDemand: true,
 webWorkerPath: '/models/js/cornerstoneWADOImageLoaderWebWorker.js',//此路径需要自行修改
 webWorkerTaskPaths: [],
 taskConfiguration: {
 decodeTask: {
 loadCodecsOnStartup: true,
 initializeCodecsOnStartup: false,
 codecsPath: '/models/js/cornerstoneWADOImageLoaderCodecs.js',//此路径需要自行修改
 usePDFJS: false,
 strict: options.strict
 }
 }
};

写在前面

我只介绍了 cornerstone 的一些基本用法,具体如何在项目中应用,还是要灵活使用,例如我在项目中使用是先通过递归来缓存了所有的图像再逐个展示,节省了加载的时间;通过算法分析的结节坐标信息在图像上绘制结节等。如果需要帮助可留言。码字不易,研究不易,如需转载请注明出处。

准备工作完毕,开始引入组件中

 *  需要安装的插件
 *  npm install --save dicom-parser //这个需要安装
*/
// import Hammer from 'hammerjs';//移动端手势插件 npm install hammerjs --save //选用,不需要移动端可不用
import * as cornerstone from '../../public/models/js/cornerstone';
import * as cornerstoneTools from '../../public/models/js/cornerstoneTools.js';
import * as cornerstoneWADOImageLoader from '../../public/models/js/cornerstoneWADOImageLoader';
/*
 * cornerstoneMath.min.js文件 import 始终报错,无奈,最终在 index.html中加入解决,原因未知,知道的大佬可以告知一下
 * <script src="./models/js/cornerstoneMath.min.js"></script>
*/
​
cornerstoneTools.external.cornerstone = cornerstone;
// cornerstoneTools.external.Hammer = Hammer;
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
​
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.cornerstoneMath = cornerstoneMath;

初始化

<template>
 <div id="DICOM" ref='dicom_canvas'></div>
</template>
......
init(){
 let element=this.$refs.dicom_canvas;//获取 dicom 元素节点
 cornerstone.enable(element);//启用节点
 window.addEventListener('resize',function(e){//自适应窗口
 cornerstone.resize(element,true)
 })
}

载入影像数据

start(){
 cornerstone.loadAndCacheImage('http:XXXXXXXX.dcm').then((image)=>{//加载并缓存数据
 cornerstone.displayImage(element,image)//展示 dicom,参数 1:element 元素,参数 2:image
 this.getImageInfo(image);//获取图片信息
 }).catch((err)=>console.log(err))
}

cornerstoneimagerendered事件(重要!)

每当图像被cornerstone重新绘制时,将发出CornerstoneImageRendered事件调用。这个事件包括可以使用HTML5 canvas上下文(例如几何图形或文本)在图像上绘制的信息。你也可以更新HTML覆盖与各种视口属性,如窗口宽度,窗口级别和缩放。

......
 element.addEventListener('cornerstoneimagerendered',function(e){
 let eventData=e.detail;
 this.getViewPort(eventData);//获取当前图片视口信息
 })
getViewPort(eventData){
 this.imageInfo.windowWidth=Math.round(eventData.viewport.voi.windowWidth);//窗宽
 this.imageInfo.windowCenter=Math.round(eventData.viewport.voi.windowCenter);//窗位
 this.imageInfo.scale=parseInt(10 * eventData.viewport.scale);//缩放等级
}
getImageInfo(image){
 this.imageInfo.seriesNumber=image.data.string('x00200011');//图像序列号
 this.imageInfo.imageNum=image.data.string('x00200013');//图像位置
 this.imageInfo.imageDate=image.data.string("x00080021");//拍摄日期
 this.imageInfo.sliceThickness=image.data.string('x00180050');//层厚
 this.imageInfo.patientId=image.data.string('x00100020');//病理号
 // 判断窗宽窗位是否合法
 this.pixelR = image.data.uint16('x00280103');
 this.heightBit = image.data.uint16('x00280102') || '';
 // 病人基本信息
 this.patientName = image.data.string('x00100010');
 this.patientBirthDate = image.data.string('x00100030');
 this.patientID = image.data.string('x00100020');
 this.patientGender = image.data.string('x00100040');
 this.sID = image.data.string('x00200011');
 // 像素间距
 this.pixelSpacing = image.data.string('x00280030');
 this.imagePixelSpacing = image.data.string('x00181164') || '';
 this.rowPixelSpacing = image.rowPixelSpacing;
 // 放射放大系数
 this.magnification = Number(image.data.string('x00181114'));
 // 放射源到面板的距离
 this.sourceTOdetector = image.data.string('x00181110');
 // 放射源到病人的距离
 this.sourceTOpatient = image.data.string('x00181111');
 //this.modalityLUT = cornerstone.metaData.get('modalityLutModule', image.imageId).modalityLUTSequence;
 this.voiContent = cornerstone.metaData.get('voiLutModule', image.imageId);
 // 斜率截距
 this.rescaleIntercept = Number(image.data.string('x00281052'));
 this.rescaleSlope = Number(image.data.string('x00281053'));
}

cornerstoneTools (工具)

// 启用工具
/*
 数字:1 代表鼠标左键;2 代表鼠标中键;4 代表鼠标右键
 可逐一试验功能
*/
cornerstoneTools.mouseInput.enable(this.element);//启动鼠标按下事件
 enableAllTools() {
 cornerstoneTools.wwwc.activate(element);
 cornerstoneTools.pan.activate(element, 2); 
 cornerstoneTools.zoom.activate(element, 1); 
 cornerstoneTools.probe.activate(element, 1);
 cornerstoneTools.length.activate(element, 1);
 cornerstoneTools.ellipticalRoi.activate(element, 1);
 cornerstoneTools.rectangleRoi.activate(element, 1);
 cornerstoneTools.angle.activate(element, 1);
 cornerstoneTools.highlight.activate(element, 1);
 cornerstoneTools.freehand.activate(element, 1);
 cornerstoneTools.stackScroll.activate(element);
 cornerstoneTools.arrowAnnotate.activate(element, 1);
 cornerstoneTools.freehand.activate(element, 1);
 // touch
 cornerstoneTools.wwwcTouchDrag.deactivate(element);
 cornerstoneTools.probeTouch.deactivate(element);
 cornerstoneTools.panTouchDrag.deactivate(element);
 cornerstoneTools.zoomTouchDrag.deactivate(element);
 cornerstoneTools.lengthTouch.deactivate(element);
 cornerstoneTools.ellipticalRoiTouch.deactivate(element);
 cornerstoneTools.rectangleRoiTouch.deactivate(element);
 cornerstoneTools.angleTouch.deactivate(element);
 cornerstoneTools.stackScrollTouchDrag.deactivate(element);
 cornerstoneTools.arrowAnnotateTouch.deactivate(element);
 cornerstoneTools.rotateTouchDrag.deactivate(element);
 cornerstoneTools.rotateTouch.disable(element);
 }
// 停用工具    // before making a new tool active
 disableAllTools() {
 cornerstoneTools.wwwc.disable(element);
 cornerstoneTools.pan.deactivate(element, 2);
 cornerstoneTools.zoom.deactivate(element, 1); 
 cornerstoneTools.probe.deactivate(element, 1);
 cornerstoneTools.length.deactivate(element, 1);
 cornerstoneTools.ellipticalRoi.deactivate(element, 1);
 cornerstoneTools.rectangleRoi.deactivate(element, 1);
 cornerstoneTools.angle.deactivate(element, 1);
 cornerstoneTools.highlight.deactivate(element, 1);
 cornerstoneTools.freehand.deactivate(element, 1);
 cornerstoneTools.stackScroll.deactivate(element);
 cornerstoneTools.arrowAnnotate.deactivate(element, 1);
 cornerstoneTools.freehand.deactivate(element, 1);
 // touch
 cornerstoneTools.wwwcTouchDrag.deactivate(element);
 cornerstoneTools.probeTouch.deactivate(element);
 cornerstoneTools.panTouchDrag.deactivate(element);
 cornerstoneTools.zoomTouchDrag.deactivate(element);
 cornerstoneTools.lengthTouch.deactivate(element);
 cornerstoneTools.ellipticalRoiTouch.deactivate(element);
 cornerstoneTools.rectangleRoiTouch.deactivate(element);
 cornerstoneTools.angleTouch.deactivate(element);
 cornerstoneTools.stackScrollTouchDrag.deactivate(element);
 cornerstoneTools.arrowAnnotateTouch.deactivate(element);
 cornerstoneTools.rotateTouchDrag.deactivate(element);
 cornerstoneTools.rotateTouch.disable(element);
 }

改变窗宽窗位

windowChange(index){
 /*
 * index=1 ww:default,wl:default
 * index=2 ww:1500,wl:-450
 * index=3 ww:250,wl:30
 * index=4 ww:1000,wl:250
 * index=5 ww:300,wl:40
 */
 let viewportDefault = cornerstone.getDefaultViewportForImage(element, image);//获取当前图像默认的窗宽窗位
 // console.log(viewportDefault.voi.windowWidth,viewportDefault.voi.windowCenter)
 let viewport = cornerstone.getViewport(this.element);
 viewport.voiLUT = undefined;
​
​
 if(index==1){
 viewport.voi.windowWidth = viewportDefault.voi.windowWidth;
 viewport.voi.windowCenter = viewportDefault.voi.windowCenter;
 }else if(index==2){
 viewport.voi.windowWidth = 1500;
 viewport.voi.windowCenter = -450;
 }else if(index==3){
 viewport.voi.windowWidth = 250;
 viewport.voi.windowCenter = 30;
 }else if(index==4){
 viewport.voi.windowWidth = 1000;
 viewport.voi.windowCenter = 250;
 }else if(index==5){
 viewport.voi.windowWidth = 300;
 viewport.voi.windowCenter = 40;
 }
 cornerstone.setViewport(this.element, viewport);//调节窗宽窗位
 },

鼠标滚轮切换图像

 /*鼠标滚轮事件 */
 mousewheelChange(){
 let _this=this;
 const wheelEvents = ['mousewheel', 'DOMMouseScroll'];
 wheelEvents.forEach((eventType) => {
 this.element.addEventListener(eventType, this.mousewheelHandle);
 });
 },

 mousewheelHandle(e){
 let _this=this;
 // Firefox e.detail > 0 scroll back, < 0 scroll forward
 // chrome/safari e.wheelDelta < 0 scroll back, > 0 scroll forward
 if (e.wheelDelta < 0  || e.detail > 0) {//向下滚动
 _this.currentImageIndex++;
 if(_this.currentImageIndex>_this.nowSeries.length-1){
 _this.currentImageIndex=0
 }
 } else {//向上滚动
 _this.currentImageIndex--;
 if(_this.currentImageIndex<0){
 _this.currentImageIndex=_this.nowSeries.length-1
 }
 }
 _this.updateTheImage(_this.currentImageIndex);//根据 ID 索引切换图像
​
 // 防止页面滚动
 return false;
 },

其他功能

// set the canvas context to the image coordinate system(将画布上下文设置为图像坐标系统)
//绘制前调用
 cornerstone.setToPixelCoordinateSystem(eventData.enabledElement, eventData.canvasContext);
​
//坐标转换
cornerstone.pageToPixel(element, X, Y)//将像素坐标转为图像坐标,返回x,y对象
​
//更新图像
cornerstone.updateImage(element);
​
//清除缓存
cornerstone.imageCache.purgeCache();
cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.purge();
cornerstoneWADOImageLoader.wadouri.fileManager.purge();
​
//查看缓存
const cacheInfo = cornerstone.imageCache.getCacheInfo();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容