当前位置: 首页 > 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://www.xdnf.cn/news/1461385.html

相关文章:

  • 进阶向:Python生成艺术图案(分形、数学曲线)
  • 深度相机详解
  • Spring Boot启动失败从循环依赖到懒加载配置的深度排查指南
  • 《Keil 开发避坑指南:STM32 头文件加载异常与 RTE 配置问题全解决》
  • 【译】GitHub Copilot for Azure(预览版)已经在 Visual Studio 2022 中推出
  • 动物专家?单词测试!基于 TensorFlow+Tkinter 的动物识别系统与动物识别小游戏
  • claude-sonnet4和GLM-4-5-HTML版本迷宫小游戏
  • honmony 中集成 tuanjie/unity
  • 自由学习记录(95)
  • Bug 排查日记:从问题浮现到解决的技术之旅
  • C++ opencv RTSP小工具 RTSP流播放、每一帧保存
  • 爆改YOLOv8 | 即插即用的AKConv让目标检测既轻量又提点
  • 光伏运维迎来云端革命!AcrelCloud-1200如何破解分布式光伏四大痛点?
  • Elasticsearch面试精讲 Day 9:复合查询与过滤器优化
  • PPT中如何将设置的文本框边距设为默认
  • 【Javascript】Capacitor 文件存储在 Windows 上的位置
  • Git 同步最新代码:用 stash -> pull -> pop 安全同步更新
  • Docker 容器核心指令与数据库容器化实践
  • 安全运维-云计算系统安全
  • 【1】策略模式 + 模板方法模式的联合应用
  • 具身智能的工程落地:视频-控制闭环的实践路径
  • 手写React状态hook
  • AI测试:自动化测试框架、智能缺陷检测、A/B测试优化
  • 分片上传-
  • Boost搜索引擎 网络库与前端(4)
  • 力扣hot100:搜索二维矩阵 II(常见误区与高效解法详解)(240)
  • OpenBMC之编译加速篇
  • 三、神经网络
  • VisionPro联合编程相机拍照 九点标定实战
  • pinia状态管理的作用和意义