vue 使用cornerstone展示 dicom 影像數(shù)據(jù)

cornerstone 庫地址

可能訪問失敗怠惶,建議翻墻使用

需要用到的依賴庫愧捕,可 npm 可自行加入項(xiàng)目public 文件夾中谜悟,這里提供文件下載

點(diǎn)此下載 密碼: tcg3

注意(坑)

cornerstoneWADOImageLoader.js 文件中需要自行修改 path 路徑(注意:是絕對路徑哦)饥瓷,大概在 1750 行舷丹。當(dāng)時(shí)卡了好久氓奈。

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 的一些基本用法翘魄,具體如何在項(xiàng)目中應(yīng)用,還是要靈活使用舀奶,例如我在項(xiàng)目中使用是先通過遞歸來緩存了所有的圖像再逐個(gè)展示暑竟,節(jié)省了加載的時(shí)間;通過算法分析的結(jié)節(jié)坐標(biāo)信息在圖像上繪制結(jié)節(jié)等育勺。如果需要幫助可留言但荤。碼字不易,研究不易涧至,如需轉(zhuǎn)載請注明出處腹躁。

準(zhǔn)備工作完畢,開始引入組件中

 *  需要安裝的插件
 *  npm install --save dicom-parser //這個(gè)需要安裝
*/
// 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 始終報(bào)錯(cuò)纺非,無奈哑了,最終在 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 元素節(jié)點(diǎn)
 cornerstone.enable(element);//啟用節(jié)點(diǎn)
 window.addEventListener('resize',function(e){//自適應(yīng)窗口
 cornerstone.resize(element,true)
 })
}

載入影像數(shù)據(jù)

start(){
 cornerstone.loadAndCacheImage('http:XXXXXXXX.dcm').then((image)=>{//加載并緩存數(shù)據(jù)
 cornerstone.displayImage(element,image)//展示 dicom弱左,參數(shù) 1:element 元素,參數(shù) 2:image
 this.getImageInfo(image);//獲取圖片信息
 }).catch((err)=>console.log(err))
}

cornerstoneimagerendered事件(重要?换础)

每當(dāng)圖像被cornerstone重新繪制時(shí)拆火,將發(fā)出CornerstoneImageRendered事件調(diào)用。這個(gè)事件包括可以使用HTML5 canvas上下文(例如幾何圖形或文本)在圖像上繪制的信息涂圆。你也可以更新HTML覆蓋與各種視口屬性们镜,如窗口寬度,窗口級別和縮放乘综。

......
 element.addEventListener('cornerstoneimagerendered',function(e){
 let eventData=e.detail;
 this.getViewPort(eventData);//獲取當(dāng)前圖片視口信息
 })
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;
 // 放射放大系數(shù)
 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 (工具)

// 啟用工具
/*
 數(shù)字:1 代表鼠標(biāo)左鍵憎账;2 代表鼠標(biāo)中鍵;4 代表鼠標(biāo)右鍵
 可逐一試驗(yàn)功能
*/
cornerstoneTools.mouseInput.enable(this.element);//啟動鼠標(biāo)按下事件
 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);//獲取當(dāng)前圖像默認(rèn)的窗寬窗位
 // 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);//調(diào)節(jié)窗寬窗位
 },

鼠標(biāo)滾輪切換圖像

 /*鼠標(biāo)滾輪事件 */
 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);//根據(jù) ID 索引切換圖像
?
 // 防止頁面滾動
 return false;
 },

其他功能

// set the canvas context to the image coordinate system(將畫布上下文設(shè)置為圖像坐標(biāo)系統(tǒng))
//繪制前調(diào)用
 cornerstone.setToPixelCoordinateSystem(eventData.enabledElement, eventData.canvasContext);
?
//坐標(biāo)轉(zhuǎn)換
cornerstone.pageToPixel(element, X, Y)//將像素坐標(biāo)轉(zhuǎn)為圖像坐標(biāo)卡辰,返回x,y對象
?
//更新圖像
cornerstone.updateImage(element);
?
//清除緩存
cornerstone.imageCache.purgeCache();
cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.purge();
cornerstoneWADOImageLoader.wadouri.fileManager.purge();
?
//查看緩存
const cacheInfo = cornerstone.imageCache.getCacheInfo();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市邪意,隨后出現(xiàn)的幾起案子九妈,更是在濱河造成了極大的恐慌,老刑警劉巖雾鬼,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萌朱,死亡現(xiàn)場離奇詭異,居然都是意外死亡策菜,警方通過查閱死者的電腦和手機(jī)晶疼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來又憨,“玉大人翠霍,你說我怎么就攤上這事〈垒海” “怎么了寒匙?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長躏将。 經(jīng)常有香客問我锄弱,道長,這世上最難降的妖魔是什么祸憋? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任会宪,我火速辦了婚禮,結(jié)果婚禮上蚯窥,老公的妹妹穿的比我還像新娘掸鹅。我一直安慰自己喜命,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布河劝。 她就那樣靜靜地躺著壁榕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赎瞎。 梳的紋絲不亂的頭發(fā)上牌里,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音务甥,去河邊找鬼牡辽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛敞临,可吹牛的內(nèi)容都是我干的态辛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼挺尿,長吁一口氣:“原來是場噩夢啊……” “哼奏黑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起编矾,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤熟史,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后窄俏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹂匹,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年凹蜈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了限寞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡仰坦,死狀恐怖履植,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缎岗,我是刑警寧澤静尼,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站传泊,受9級特大地震影響鼠渺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜眷细,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一拦盹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧溪椎,春花似錦普舆、人聲如沸恬口。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祖能。三九已至,卻和暖如春蛾洛,著一層夾襖步出監(jiān)牢的瞬間养铸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工轧膘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钞螟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓谎碍,卻偏偏與公主長得像鳞滨,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子蟆淀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容