当前位置: 首页 > news >正文

基于cornerstone3D的dicom影像浏览器 第四章 鼠标实现翻页、放大、移动、窗宽窗位调节

先看效果

dicom鼠标调节

1.定义utils/initTools.js工具函数

import { Enums, Settings } from "@cornerstonejs/core";
import * as cornerstoneTools from "@cornerstonejs/tools";
import MyProbeTool from "./MyProbeTool.js";
const {annotation
} = cornerstoneTools;
cornerstoneTools.init()
const {ToolGroupManager,Enums: csToolsEnums,StackScrollTool,WindowLevelTool,PanTool,ZoomTool,
} = cornerstoneTools;
cornerstoneTools.addTool(MyProbeTool);cornerstoneTools.addTool(StackScrollTool);
cornerstoneTools.addTool(WindowLevelTool);
cornerstoneTools.addTool(PanTool);
cornerstoneTools.addTool(ZoomTool);const { MouseBindings } = csToolsEnums;
const toolGroupId = "tpid_2d";
const toolGroup = ToolGroupManager.createToolGroup(toolGroupId);function initTools() {// Add tools to the tool grouptoolGroup.addTool(WindowLevelTool.toolName);toolGroup.addTool(PanTool.toolName);toolGroup.addTool(MyProbeTool.toolName);toolGroup.addTool(ZoomTool.toolName, {zoomToCenter: true,invert: true,minZoomScale: 0.1,maxZoomScale: 20,preventDefault: true});toolGroup.addTool(StackScrollTool.toolName);toolGroup.setToolActive(WindowLevelTool.toolName, {bindings: [{mouseButton: MouseBindings.Primary // Left Click}]});toolGroup.setToolActive(PanTool.toolName, {bindings: [{mouseButton: MouseBindings.Auxiliary // Middle Click}]});toolGroup.setToolActive(ZoomTool.toolName, {bindings: [{mouseButton: MouseBindings.Secondary, // Right Click}]});toolGroup.setToolActive(StackScrollTool.toolName, {bindings: [{mouseButton: MouseBindings.Wheel // Wheel Mouse}]})
}export { initTools, toolGroup };

2.MyProbeTool.js

import * as cornerstoneTools from "@cornerstonejs/tools";
import { VolumeViewport, utilities as csUtils } from "@cornerstonejs/core";
import drawHandlesSvg from "./drawHandlesSvg.js";const {Enums: csToolsEnums,ProbeTool,annotation,drawing
} = cornerstoneTools;const { ChangeTypes } = csToolsEnums;
const { getAnnotations } = annotation.state;
const { drawTextBox: drawTextBoxSvg } = drawing;class MyProbeTool extends ProbeTool {static toolName = "MyProbe";constructor(options = {}) {super(options);}/*** it is used to draw the probe annotation in each* request animation frame. It calculates the updated cached statistics if* data is invalidated and cache it.** @param enabledElement - The Cornerstone's enabledElement.* @param svgDrawingHelper - The svgDrawingHelper providing the context for drawing.*/renderAnnotation = (enabledElement, svgDrawingHelper) => {let renderStatus = false;const { viewport } = enabledElement;const { element } = viewport;let annotations = getAnnotations(this.getToolName(), element);if (!annotations?.length) {return renderStatus;}annotations = this.filterInteractableAnnotationsForElement(element,annotations);if (!annotations?.length) {return renderStatus;}const targetId = this.getTargetId(viewport);const renderingEngine = viewport.getRenderingEngine();const styleSpecifier = {toolGroupId: this.toolGroupId,toolName: this.getToolName(),viewportId: enabledElement.viewport.id};for (let i = 0; i < annotations.length; i++) {const annotation = annotations[i];const annotationUID = annotation.annotationUID;const data = annotation.data;const point = data.handles.points[0];const canvasCoordinates = viewport.worldToCanvas(point);styleSpecifier.annotationUID = annotationUID;const { color, lineWidth } = this.getAnnotationStyle({annotation,styleSpecifier});if (!data.cachedStats) {data.cachedStats = {};}if (!data.cachedStats[targetId] ||data.cachedStats[targetId].value === null) {data.cachedStats[targetId] = {Modality: null,index: null,value: null};this._calculateCachedStats(annotation,renderingEngine,enabledElement,ChangeTypes.StatsUpdated);} else if (annotation.invalidated) {this._calculateCachedStats(annotation,renderingEngine,enabledElement);// If the invalidated data is as a result of volumeViewport manipulation// of the tools, we need to invalidate the related stackViewports data if// they are not at the referencedImageId, so that// when scrolling to the related slice in which the tool were manipulated// we re-render the correct tool position. This is due to stackViewport// which doesn't have the full volume at each time, and we are only working// on one slice at a time.if (viewport instanceof VolumeViewport) {const { referencedImageId } = annotation.metadata;// invalidate all the relevant stackViewports if they are not// at the referencedImageIdfor (const targetId in data.cachedStats) {if (targetId.startsWith("imageId")) {const viewports =renderingEngine.getStackViewports();const invalidatedStack = viewports.find(vp => {// The stack viewport that contains the imageId but is not// showing it currentlyconst referencedImageURI =csUtils.imageIdToURI(referencedImageId);const hasImageURI =vp.hasImageURI(referencedImageURI);const currentImageURI = csUtils.imageIdToURI(vp.getCurrentImageId());return (hasImageURI &&currentImageURI !== referencedImageURI);});if (invalidatedStack) {delete data.cachedStats[targetId];}}}}}// If rendering engine has been destroyed while renderingif (!viewport.getRenderingEngine()) {console.warn("Rendering Engine has been destroyed");return renderStatus;}const handleGroupUID = "0";// 重写此函数drawHandlesSvg(svgDrawingHelper,annotationUID,handleGroupUID,[canvasCoordinates],{color,lineWidth,handleRadius: this.configuration.handleRadius,type: "path"});renderStatus = true;const options = this.getLinkedTextBoxStyle(styleSpecifier,annotation);if (!options.visibility) {continue;}const textLines = this.configuration.getTextLines(data, targetId);if (textLines) {const textCanvasCoordinates = [canvasCoordinates[0] + 6,canvasCoordinates[1] - 6];const textUID = "0";drawTextBoxSvg(svgDrawingHelper,annotationUID,textUID,textLines,[textCanvasCoordinates[0], textCanvasCoordinates[1]],options);}}return renderStatus;};
}export default MyProbeTool;

3.drawHandlesSvg.js

// import _getHash from "./_getHash";
// import setNewAttributesIfValid from './setNewAttributesIfValid';
// import setAttributesIfNecessary from "./setAttributesIfNecessary";function _getHash(annotationUID, drawingElementType, nodeUID) {return `${annotationUID}::${drawingElementType}::${nodeUID}`;
}function setNewAttributesIfValid(attributes, svgNode) {Object.keys(attributes).forEach(key => {const newValue = attributes[key];if (newValue !== undefined && newValue !== "") {svgNode.setAttribute(key, newValue);}});
}function setAttributesIfNecessary(attributes, svgNode) {Object.keys(attributes).forEach(key => {const currentValue = svgNode.getAttribute(key);const newValue = attributes[key];if (newValue === undefined || newValue === "") {svgNode.removeAttribute(key);} else if (currentValue !== newValue) {svgNode.setAttribute(key, newValue);}});
}function drawHandlesSvg(svgDrawingHelper,annotationUID,handleGroupUID,handlePoints,options = {}
) {handlePoints.forEach((handle, i) => {drawHandle(svgDrawingHelper,annotationUID,handleGroupUID,handle,options,i);});
}function drawHandle(svgDrawingHelper,annotationUID,handleGroupUID,handle,options = {},uniqueIndex
) {const { color, handleRadius, width, lineWidth, fill, type, opacity } =Object.assign({color: "rgb(0, 255, 0)",handleRadius: "6",width: "2",lineWidth: undefined,fill: "transparent",type: "circle", //type: 'circle|rect|path',opacity: 1},options);// for supporting both lineWidth and width optionsconst strokeWidth = lineWidth || width;// variable for the namespaceconst svgns = "http://www.w3.org/2000/svg";const svgNodeHash = _getHash(annotationUID,"handle",`hg-${handleGroupUID}-index-${uniqueIndex}`);let attributes;if (type === "circle") {attributes = {cx: `${handle[0]}`,cy: `${handle[1]}`,r: handleRadius,stroke: color,fill,"stroke-width": strokeWidth,opacity: opacity};} else if (type === "rect") {const handleRadiusFloat = parseFloat(handleRadius);const side = handleRadiusFloat * 1.5;const x = handle[0] - side * 0.5;const y = handle[1] - side * 0.5;attributes = {x: `${x}`,y: `${y}`,width: `${side}`,height: `${side}`,stroke: color,fill,"stroke-width": strokeWidth,rx: `${side * 0.1}`,opacity: opacity};} else if (type === "path") {const handleRadiusFloat = parseFloat(handleRadius);const side = handleRadiusFloat * 1.5;const x = handle[0] - side * 0.5;const y = handle[1] - side * 0.5;const d = `M ${x} ${handle[1]} L ${x + side} ${handle[1]} M ${handle[0]} ${y} L ${handle[0]} ${y + side}`;attributes = {d,stroke: color,fill,"stroke-width": strokeWidth,opacity: opacity};} else {throw new Error(`Unsupported handle type: ${type}`);}const existingHandleElement = svgDrawingHelper.getSvgNode(svgNodeHash);if (existingHandleElement) {setAttributesIfNecessary(attributes, existingHandleElement);svgDrawingHelper.setNodeTouched(svgNodeHash);} else {const newHandleElement = document.createElementNS(svgns, type);setNewAttributesIfValid(attributes, newHandleElement);svgDrawingHelper.appendNode(newHandleElement, svgNodeHash);}
}export default drawHandlesSvg;

4.在displayerArea.vue调用

import { initTools } from "@/utils/initTools";onMounted(() => {initTools()  
});


文章转载自:

http://MkR9WvEB.xzqzd.cn
http://2kd8xtrp.xzqzd.cn
http://sXaqvVlB.xzqzd.cn
http://rEaD2Qkc.xzqzd.cn
http://5ncnfdng.xzqzd.cn
http://2rBuoFtc.xzqzd.cn
http://EGTloPiN.xzqzd.cn
http://LctRTbO0.xzqzd.cn
http://adanIzim.xzqzd.cn
http://PW8NPuvn.xzqzd.cn
http://DG9OoESV.xzqzd.cn
http://QISTfmtq.xzqzd.cn
http://RXq3UfDK.xzqzd.cn
http://mIXPtNaa.xzqzd.cn
http://39Ntx7Pv.xzqzd.cn
http://RYC8g8ZD.xzqzd.cn
http://YVPmDvkp.xzqzd.cn
http://PFj3kPb4.xzqzd.cn
http://Qxe7UaXf.xzqzd.cn
http://Htu2m71J.xzqzd.cn
http://zSULCLbz.xzqzd.cn
http://EXZrd4PS.xzqzd.cn
http://JaEfd2W6.xzqzd.cn
http://NRzlnhL5.xzqzd.cn
http://hTCtclSM.xzqzd.cn
http://WqwGo5Im.xzqzd.cn
http://e4BjlP3m.xzqzd.cn
http://m8UbpVll.xzqzd.cn
http://okDzp0pG.xzqzd.cn
http://7HyoUXJI.xzqzd.cn
http://www.dtcms.com/a/369479.html

相关文章:

  • Java高级编程–网络编程
  • linux ubi文件系统
  • 2025年统计与数据分析领域专业认证发展指南
  • android 四大组件—Service
  • 告别线缆束缚!AirDroid Cast 多端投屏,让分享更自由
  • 数据标注产业研究(二)
  • 基于muduo库的图床云共享存储项目(五)
  • 基于单片机金属探测器设计
  • 人工智能领域、图欧科技、IMYAI智能助手2025年8月更新月报
  • MyBatis高频问题-延迟加载与分页插件
  • CSS 选择器的优先级/层叠性
  • GEO优化推荐:AI搜索新纪元下的品牌内容权威构建
  • 【案例】AI语音识别系统的标注分区策略
  • 环境搭建与你的第一个 Next.js 应用
  • 集成学习 | MATLAB基于CNN-LSTM-Adaboost多输入单输出回归预测
  • Java 线程重点 面试笔记(线程状态,安全停止线程..)
  • 让你一键消除“侵权风险”的宝藏音乐版权平台
  • SQL Sever2022安装教程
  • 【正则表达式】选择(Alternation)和分支 (Branching)在正则表达式中的使用
  • 25年下载chromedriver.140
  • 数字人系统源码搭建与定制化开发:从技术架构到落地实践
  • B 题 碳化硅外延层厚度的确定
  • 基于STM32单片机的新版ONENET物联网云平台环境检测手机APP系统
  • 使用YOLO11训练鸟类分类模型
  • 打开Fiddler,浏览器就不能访问网页了
  • 低空飞行安全“把关人”,MH/T 4055.3-2022 测试标准深度解读
  • 客户案例 | 半导体材料领军企业选择燕千云ITSM,打造“零”中断运维体系
  • STM32H7的PA0_C、PA1_C、PC2_C、PC3_C的使用
  • EEMD-HHT算法
  • 如何快速集成直播美颜SDK?人脸美型功能开发全流程详解