与医学、医院相关的项目搞起来真的是要命。。。完全生疏的方向 。。。其中合作伙伴说,让我用AI直接生成代码就可以了。。我就在想,区域链、人工智能、AI等等这些喙头啥时候是个头儿。。。资本用来圈钱的各种概念总是那么不务实!
DICOM是医学图像和相关信息的国际标准,它定义了满足临床需要的可用于数据交换的医学图像格式,被广泛用于放射、成像的诊疗诊断设备。也就是说,在医院放射科使用的设备上读取的影像基本都是DICOM格式,包括CT、MRI、超声等的讲解及技术规范。咱就不展开讨论了,网上一堆,大家去查阅即可。
咱的项目基本都是趋于实战,很少讲原理,因为有些原理讲起来可能我就没那么多心血来写作了,那么大篇幅的进行整理及输出。我还有我的事情要做。。。
下来就讲讲如何在web端进行dicom图像的展示有相关操作。本次分享实现两点功能:
1、影像加载
2、影像标注
其他功能后续再一一补充实现 。
项目安装以下依赖:
"cornerstone-core": "^2.6.1", "cornerstone-math": "^0.1.10", "cornerstone-tools": "^6.0.10", "cornerstone-wado-image-loader": "^4.13.2", "dicom-parser": "^1.8.21", "hammerjs": "^2.0.8",
在你项目中创建一个Vue组件页面粘贴以下代码实现一个预览组件:
<template><div v-loading="loading" class="dicom" ref="dicomImage"><span v-if="loadFailed">加载失败</span></div> </template> <script> import cornerstone from 'cornerstone-core'; import dicomParser from 'dicom-parser'; import wadoImageLoader from 'cornerstone-wado-image-loader'; import cornerstoneMath from "cornerstone-math" import cornerstoneTools from "cornerstone-tools" import Hammer from "hammerjs"// 配置 Web Worker var config = {webWorkerPath: "cornerstone-wado-image-loader/cornerstoneWADOImageLoaderNoWebWorkers.bundle.min.js",taskConfiguration: {decodeTask: {codecsPath: "cornerstone-wado-image-loader/cornerstoneWADOImageLoaderCodecs.js"}} }; wadoImageLoader.webWorkerManager.initialize(config);cornerstoneTools.external.cornerstone = cornerstone; cornerstoneTools.external.cornerstoneMath = cornerstoneMath; cornerstoneTools.external.Hammer = Hammer;// 初始化cornerstoneTools工具 cornerstoneTools.init({mouseEnabled: true,// 当元素被启用时,是否监听鼠标事件touchEnabled: true, // 当元素被启用时,是否监听触摸事件globalToolSyncEnabled: true,// 全局工具同步showSVGCursors: true, // 显示svg光标autoResizeViewports: true,// 自动调整视口大小lineDash: [4, 4] // 虚线样式 });wadoImageLoader.external.cornerstone = cornerstone; wadoImageLoader.external.dicomParser = dicomParser;export default {name: 'DicomPreview',props: {imageId: {type: String,required: true}},data() {return {loading: true,loadFailed: false,}},// watch:{// imageId(newValue,oldValue){// var url = `wadouri:${this.imageId}`;// this.loadAndViewImage(url);// } // },mounted() {var url = `wadouri:${this.imageId}`;this.loadAndViewImage(url);},methods: {loadAndViewImage(imageId) {let element = this.$refs.dicomImageconst lengthProps = {configuration: {handleRadius: 1,drawHandlesOnHover: true,hideHandlesIfMoving: true,digits: 2}};const LengthTool = cornerstoneTools.LengthTool;//添加获取到的窗宽,窗高工具cornerstoneTools.addTool(LengthTool, lengthProps);cornerstoneTools.addToolForElement(element, LengthTool, lengthProps);// 绑定工具操作功能到鼠标左键cornerstoneTools.setToolActive('Length', {mouseButtonMask: 1})//所有工具在此行前添加cornerstone.enable(element)cornerstone.loadAndCacheImage(imageId).then((image) => {var viewport = cornerstone.getDefaultViewportForImage(element, image);cornerstone.displayImage(element, image, viewport);this.loading = falsethis.loadFailed = falsethis.$nextTick(this.enableLengthTools())}, (err) => {console.log(err)this.loading = falsethis.loadFailed = true});},enableLengthTools() {},clearTools() {cornerstoneTools.clearToolState(element, 'Length');cornerstone.updateImage(element); // 刷新图像}} } </script> <style lang="scss" scoped> .dicom {height: 100%;width: 500px;height: 500px; } </style>
在使用的父页面中引入此子组件使用,并传参数即可:
import DicomPreview from './dicom/index.vue'components: {DicomPreview },data() {return {lineChartData: lineChartData.newVisitis,imageIds: [{dicIds: 'http://47.107.255.46:1000/wado/SERVERAE?requestType=WADO&studyUID=1.2.392.200036.9116.2.5.1.3268.2047634718.1719992618.411948&seriesUid=840.113747.1719992443.59096.130764.1965210712314854.1337&objectUid=4.59096.130764.1965210712314854.1341&contentType=application%2Fdicom'}, {dicIds: "https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/1.dcm"}]} },<el-row :gutter="20"><el-col :span="12"><div class="w-100 flex-row"><dicom-preview :imageId="imageIds[1].dicIds" style="width: 500px;height:500px;"></dicom-preview></div></el-col><el-col :span="12"><dicom-preview :imageId="imageIds[0].dicIds" style="width: 500px;height:500px;"></dicom-preview></el-col> </el-row>
如你所见,为啥两张影像显示的单位不同呢?来我们分析一下源码显示单位的逻辑:
第一个影像数据为:
第二个影像数据为:
可以看到,虽然都为影像图片,但是有些输出设备输出dicom时未携带横纵像素间距导致无法将pixel转为mm单位所致!
再贴两个页面,你自己创建两个html文件,将代码分别粘贴进去,使用浏览器打开查看体验即可:
web.html页面:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="https://unpkg.com/hammerjs@2.0.8/hammer.js"></script><script src="https://unpkg.com/cornerstone-core@2.6.1/dist/cornerstone.js"></script><script src="https://unpkg.com/cornerstone-math@0.1.10/dist/cornerstoneMath.min.js"></script><script src="https://unpkg.com/cornerstone-wado-image-loader@4.1.3/dist/cornerstoneWADOImageLoader.bundle.min.js"></script><script src="https://unpkg.com/cornerstone-web-image-loader@2.1.1/dist/cornerstoneWebImageLoader.min.js"></script><script src="https://unpkg.com/cornerstone-tools@6.0.7/dist/cornerstoneTools.js"></script><script src="https://unpkg.com/dicom-parser@1.8.13/dist/dicomParser.min.js"></script> </head> <body><!-- 用于加载图片的div区域 --><div id="dicomImage" style="width: 512px;height: 512px;"></div> </body> <script>// 注册并挂载cornerstone及其cornerstoneTools,固定操作cornerstoneTools.external.cornerstone = cornerstone;cornerstoneTools.external.cornerstoneMath = cornerstoneMath;cornerstoneTools.external.Hammer = Hammer;cornerstoneWADOImageLoader.external.dicomParser = dicomParser;cornerstoneWADOImageLoader.external.cornerstone = cornerstone;// imageId就是cornerstone要求的.dcm图片地址,例如:var imageId = "wadouri:http://127.0.0.1:6699/ctdcm1.dcm";var imageId = "wadouri:https://tools.cornerstonejs.org/examples/assets/dicom/bellona/chest_lung/1.dcm";// 初始化cornerstoneTools工具cornerstoneTools.init();// 获取要用于加载图片的div区域var element = document.getElementById('dicomImage');//激活获取到的用于图片加载的区域cornerstone.enable(element);// 从cornerstoneTools库中获取窗宽,窗高工具const WwwcTool = cornerstoneTools.WwwcTool;//添加获取到的窗宽,窗高工具cornerstoneTools.addTool(WwwcTool);// 绑定工具操作功能到鼠标左键cornerstoneTools.setToolActive('Wwwc', {mouseButtonMask: 1})//使用loadAndCacheImage()方法加载并缓存图片,然后使用displayImage()方法显示图片。cornerstone.loadAndCacheImage(imageId).then(function (image) {cornerstone.displayImage(element, image);}) </script> </html>
web2.html页面:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>CornerstoneTools Demo</title><script src="https://unpkg.com/cornerstone-core@2.3.0/dist/cornerstone.js"></script><script src="https://unpkg.com/cornerstone-wado-image-loader@3.3.1/dist/cornerstoneWADOImageLoader.min.js"></script><script src="https://unpkg.com/dicom-parser@1.8.7/dist/dicomParser.min.js"></script><script src="https://unpkg.com/cornerstone-tools@6.0.7/dist/cornerstoneTools.js"></script><script src="https://unpkg.com/cornerstone-math@0.1.10/dist/cornerstoneMath.min.js"></script> </head> <body> <div id="dicomImage" style="width: 512px;height: 512px;"></div> <button onclick="EllipticalRoiToolFun()">椭圆测量</button> <button onclick="removeTool()">清除工具</button> <button onclick="hideLabel()">隐藏标注</button> <button onclick="showLabel()">显示标注</button> <button onclick="getToolInfo()">获取指定工具的信息</button> <button onclick="clearMarker()">清除图像上的所有标注</button> <script>let imageId = "wadouri:" + "http://47.107.255.46:1000/wado/SERVERAE?requestType=WADO&studyUID=1.2.392.200036.9116.2.5.1.3268.2047634718.1719992618.411948&seriesUid=840.113747.1719992443.59096.130764.1965210712314854.1337&objectUid=4.59096.130764.1965210712314854.1341&contentType=application%2Fdicom";let element = document.querySelector("#dicomImage");/**** @description cornerstone初始化* @param imageId*/function cornerstoneInit(imageId) {cornerstoneTools.external.cornerstone = cornerstone;cornerstoneTools.external.cornerstoneMath = cornerstoneMath;cornerstoneWADOImageLoader.external.cornerstone = cornerstone;// 初始化cornerstoneToolscornerstoneTools.init({mouseEnabled: true,// 当元素被启用时,是否监听鼠标事件touchEnabled: false, // 当元素被启用时,是否监听触摸事件globalToolSyncEnabled: true,// 全局工具同步showSVGCursors: true, // 显示svg光标autoResizeViewports: true,// 自动调整视口大小lineDash: [4, 4] // 虚线样式});cornerstone.enable(element); // 初始并启用绘制的元素const ApiTool = cornerstoneTools[`EllipticalRoiTool`];cornerstoneTools.addToolForElement(element, ApiTool); // 给指定启用元素添加指定工具// cornerstoneTools.addTool(ApiTool);或者,直接给全部启用元素添加指定工具loadImage(element, imageId); // 加载并且绘制影像到指定元素上}/**** @description 加载并图像* @param element 承载影像的容器div元素* @param imageId 影像Id*/function loadImage(element, imageId) {cornerstone.loadAndCacheImage(imageId).then(image => {cornerstone.displayImage(element, image); // 渲染影像到指定元素})}/**** @description 椭圆测量* @constructor*/function EllipticalRoiToolFun(){const options = {mouseButtonMask: 1};setToolActive('EllipticalRoi',options);}/****@description 获取已添加的指定工具 , 可以查看工具当前的mode状态* mode: "disabled" , "active"*/function getTool(toolName){const tools = cornerstoneTools.getToolForElement(element, toolName);console.log("已添加的工具 " + toolName +" 信息")return tools}/**** @description 给指定启用元素激活指定工具* @param toolName 工具名* @param element 影像渲染元素* @param options 公户配置项*/function setToolActive(toolName,options){cornerstoneTools.setToolActiveForElement(element, toolName, options);// cornerstoneTools.setToolActive(toolName, options); 或者,直接激活全部启用元素的指定工具}/****@description 清除工具*/function removeTool() {cornerstoneTools.removeToolForElement(element,'EllipticalRoi'); // 给指定启用元素清除指定工具// cornerstoneTools.removeTool(toolName); 或者,直接清除全部启用元素的指定工具}/**** @description 隐藏标注* @desc 特此说明,当需要重新隐藏标注时,直接将工具状态禁用即可* @param toolName 工具名*/function hideLabel(){cornerstoneTools.setToolDisabledForElement(element,'EllipticalRoi');// cornerstoneTools.setToolDisabled(toolItem.toolName); 或者,直接将全部的启用元素隐藏标注cornerstone.updateImage(element); // 刷新图像,}/**** @description 显示标注* @desc 特此说明,当需要重新显示标注时,需要重新将所有工具激活即可* @param toolName 工具集*/function showLabel(toolName){EllipticalRoiToolFun();cornerstone.updateImage(element); // 刷新图像 , 需要重新刷新}/**** @description 获取指定工具的信息* @constructor*/function getToolInfo() {console.log(getTool('EllipticalRoi'));}/**** @description 清除图像上的所有标注,永久清除* @desc 当需要清除图像上显示的测量标注时,需要先清空工具的 state 记录*/function clearMarker(){cornerstoneTools.clearToolState(element, 'EllipticalRoi');cornerstone.updateImage(element); // 刷新图像}cornerstoneInit(imageId); </script> </body> </html>
右键使用浏览器打开预览即可。细节的代码注释则不进行讲解了,自己研究查看。
下面给一些官方API文档连接:打开自行研读哦。
Cornerstone Tools: Examples
Overview | Cornerstone.js