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

基于cornerstone3D的dicom影像浏览器 第二十四章 显示方位、坐标系、vr轮廓线

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 一、工具栏修改
  • 二、切片窗口显示方位文字
    • 1. 修改mprvr.js,添加函数getOrientationMarkers
    • 2. 修改DisplayerArea3D.vue
  • 三、vr窗口显示坐标系
    • 1. 修改mprvr.js 添加OrientationMarkerTool
    • 2. view3d.vue中响应工具栏事件
    • 3. 修改DisplayerArea3D.vue
  • 四、vr窗口显示轮廓线
    • 1. 修改mprvr.js 添加addOutline,showOutline函数
    • 2. view3d.vue中响应工具栏操作
    • 3. 修改DisplayerArea3D.vue
  • 总结


前言

vr = volume rendering 体绘制,体渲染
本章实现三个功能:

  1. mpr窗口中显示方位文字
  2. vr窗口右下角显示坐标系
  3. vr窗口显示轮廓线
    效果如下:
    在这里插入图片描述

一、工具栏修改

  • 在工具栏上添加“VR坐标系”、“VR轮廓”、“方位文字” 三个checkbox,用来控制各自的显示与隐藏。添加select选择器用来切换VR坐标系的显示外观(CUBE, AXES, CUSTOM)。 其中CUSTOM的vtp文件点此处下载
<template><div class="toolbar">...<div class="toolbar-row"><el-checkbox v-model="showAxes" label="VR坐标系" size="large" style="margin: 0 10px" /><el-select v-model="currentAxes" placeholder="Select Axes Type" style="width: 200px"><el-option v-for="item in axes" :key="item.name" :label="item.name" :value="item.value" /></el-select></div><div class="toolbar-row"><el-checkbox v-model="showOutline" label="VR轮廓" size="large" style="margin-left: 10px" /><el-checkbox v-model="showOrientText" label="方位文字" size="large" style="margin-left: 10px" /></div></div>
</template>
  • 监听以上各元素绑定的变量,发送事件到view3d
const currentAxes = ref(1);
const showOutline = ref(true);
const showAxes = ref(true);
const showOrientText = ref(true);watch(showAxes, (newValue) => {emit("action", { name: "showAxes", value: newValue });
});watch(showOutline, (newValue) => {emit("action", { name: "showOutline", value: newValue });
});watch(showOrientText, (newValue) => {emit("action", { name: "toggleOrientText" });
});watch(currentAxes, (newValue) => {emit("action", { name: "changeAxesType", value: newValue });
});

二、切片窗口显示方位文字

计算切片方位文字的算法参考第八章 在Displayer中显示图像方位

1. 修改mprvr.js,添加函数getOrientationMarkers

export default class MPR {constructor(params) {this.toolGroup = null;this.vrToolGroup = null;this.renderingEngine = null;this.registered = false;...this.init(params);}init(config = {}) {...}...getOrientationMarkers({ camera, rotation }) {let flipVertical = camera.flipVertical || false;let flipHorizontal = camera.flipHorizontal || false;let newRotation = rotation || 0;let rowCosines, columnCosines;const { viewUp, viewPlaneNormal } = camera;const viewRight = vec3.create();vec3.cross(viewRight, viewUp, viewPlaneNormal);columnCosines = [-viewUp[0], -viewUp[1], -viewUp[2]];rowCosines = viewRight;const rowString = getOrientationStringLPS(rowCosines);const columnString = getOrientationStringLPS(columnCosines);const oppositeRowString = invertOrientationStringLPS(rowString);const oppositeColumnString = invertOrientationStringLPS(columnString);const markers = {top: oppositeColumnString,left: oppositeRowString,right: rowString,bottom: columnString};// If any vertical or horizontal flips are applied, change the orientation strings ahead of// the rotation applicationsif (flipVertical) {markers.top = invertOrientationStringLPS(markers.top);markers.bottom = invertOrientationStringLPS(markers.bottom);}if (flipHorizontal) {markers.left = invertOrientationStringLPS(markers.left);markers.right = invertOrientationStringLPS(markers.right);}// Swap the labels accordingly if the viewport has been rotated// This could be done in a more complex way for intermediate rotation values (e.g. 45 degrees)if (newRotation === 90 || newRotation === -270) {return {top: markers.left,left: invertOrientationStringLPS(markers.top),right: invertOrientationStringLPS(markers.bottom),bottom: markers.right // left};} else if (newRotation === -90 || newRotation === 270) {return {top: invertOrientationStringLPS(markers.left),left: markers.top,bottom: markers.left,right: markers.bottom};} else if (newRotation === 180 || newRotation === -180) {return {top: invertOrientationStringLPS(markers.top),left: invertOrientationStringLPS(markers.left),bottom: invertOrientationStringLPS(markers.bottom),right: invertOrientationStringLPS(markers.right)};}return markers;}
}

2. 修改DisplayerArea3D.vue

  • 在mpr 三个div上中、下中、左中、右中添加用于显示方位文字的元素。都与变量showOrientText绑定,用来控制显示/隐藏
<template><divclass="container3d"ref="elContainer"v-loading="loading"element-loading-text="正在处理..."element-loading-background="rgba(0, 0, 0, 0.8)"@mousedown.prevent="OnSelectView"><div class="axialparent" :style="axialStyle" v-show="showAxial" @dblclick="OnDbClick"><div ref="elAxial" class="sliceview" @contextmenu.prevent>...<!--显示方位文字--><span class="orient_top" v-show="showOrientText">{{ axialText.orient.top }}</span><span class="orient_bottom" v-show="showOrientText">{{ axialText.orient.bottom }}</span><span class="orient_left" v-show="showOrientText">{{ axialText.orient.left }}</span><span class="orient_right" v-show="showOrientText">{{ axialText.orient.right }}</span></div></div><div class="vrcprparent" v-show="showVR" @dblclick="OnDbClick">...</div><div class="sagittalparent" v-show="showSagittal" @dblclick="OnDbClick"><div ref="elSagittal" class="sliceview" @contextmenu.prevent>...<!--显示方位文字--><span class="orient_top" v-show="showOrientText">{{ sagittalText.orient.top }}</span><span class="orient_bottom" v-show="showOrientText">{{ sagittalText.orient.bottom }}</span><span class="orient_left" v-show="showOrientText">{{ sagittalText.orient.left }}</span><span class="orient_right" v-show="showOrientText">{{ sagittalText.orient.right }}</span></div></div><div class="coronalparent" v-show="showCoronal" @dblclick="OnDbClick"><div ref="elCoronal" class="sliceview" @contextmenu.prevent>...<!--显示方位文字--><span class="orient_top" v-show="showOrientText">{{ coronalText.orient.top }}</span><span class="orient_bottom" v-show="showOrientText">{{ coronalText.orient.bottom }}</span><span class="orient_left" v-show="showOrientText">{{ coronalText.orient.left }}</span><span class="orient_right" v-show="showOrientText">{{ coronalText.orient.right }}</span></div></div></div>
</template>
  • 定义三个切片方位文字的保存变量axialText,sagittalText, coronalText
  • 在onMounted中调用MPR bindCameraEvent绑定CAMERA_MODIFIED事件,调用UdpateOrientation函数
  • 添加cameraHandler,UdpateOrientation函数更新方位文字
import { ViewportId, getDicomInfo } from "../cornerstone3D/mprvr.js";const cornerText = reactive({[ViewportId.AXIAL]: {wwwc: "",slice: "",orient: {top: "",bottom: "",left: "",right: ""}},[ViewportId.SAGITTAL]: {wwwc: "",slice: "",orient: {top: "",bottom: "",left: "",right: ""}},[ViewportId.CORONAL]: {wwwc: "",slice: "",orient: {top: "",bottom: "",left: "",right: ""}}
});const cameraHandler = e => {UdpateOrientation(e);
};const UdpateOrientation = e => {const { viewportId, camera, rotation} = e.detail;const markers = theMPR.getOrientationMarkers({ camera, rotation });if (markers && showOrientText.value) {cornerText[viewportId].orient = markers;}
};onMounted(() => {theMPR = new MPR({elAxial: elAxial.value,elSagittal: elSagittal.value,elCoronal: elCoronal.value,elVR: elVR.value});load();theMPR.bindRenderEvent(renderHandler);theMPR.bindCameraEvent(cameraHandler);});

三、vr窗口显示坐标系

1. 修改mprvr.js 添加OrientationMarkerTool

  • 添加axesConfig 为OrientationMarkerTool定义三种类型外观配置项
  • 添加工具 this.vrToolGroup.addTool(OrientationMarkerTool.toolName, axesConfig)
  • 添加showAxes函数,显示/隐藏坐标系
  • 添加setAxesType函数,切换坐标系外观
  • loadImages中调用this.showAxes(true),显示默认坐标系-CUBE
const {ToolGroupManager,Enums: csToolsEnums,...OrientationMarkerTool
} = cornerstoneTools;const axesConfig = {orientationWidget: {viewportSize: 0.08,minPixelSize: 70,maxPixelSize: 200},overlayMarkerType: OrientationMarkerTool.OVERLAY_MARKER_TYPES.ANNOTATED_CUBE,overlayConfiguration: {[OrientationMarkerTool.OVERLAY_MARKER_TYPES.ANNOTATED_CUBE]: {faceProperties: {xPlus: {text: "L",faceColor: viewportColors[idSagittal], //"#ffff00",faceRotation: 90},xMinus: {text: "R",faceColor: viewportColors[idSagittal], //"#ffff00",faceRotation: 270},yPlus: {text: "P",faceColor: viewportColors[idCoronal], //"#00ffff",fontColor: "black",faceRotation: 180},yMinus: {text: "A",faceColor: viewportColors[idCoronal], //"#00ffff",fontColor: "black"},zPlus: {text: "S",faceColor: viewportColors[idAxial] //"#00ffff",// fontColor: "white",},zMinus: {text: "I",faceColor: viewportColors[idAxial] //"#00ffff",// fontColor: "white",}},defaultStyle: {fontStyle: "bold",fontFamily: "Arial",fontColor: "black",fontSizeScale: res => res / 2,faceColor: "#0000ff",edgeThickness: 0.05,edgeColor: "black",resolution: 400}},[OrientationMarkerTool.OVERLAY_MARKER_TYPES.AXES]: {},[OrientationMarkerTool.OVERLAY_MARKER_TYPES.CUSTOM]: {polyDataURL: "/src/assets/Human.vtp"}}
};export default class MPR {constructor(params) {this.toolGroup = null;this.vrToolGroup = null;this.renderingEngine = null;this.registered = false;this.viewportInputArray = null;this.crosshairsToolActive = true;this.loaded = false;this.selecteToolName = "";this.params = params;this.volume = null;this.init(params);}init(config = {}) {const { elAxial, elSagittal, elCoronal, elVR } = config;cornerstoneTools.addTool(CrosshairsTool);...cornerstoneTools.addTool(OrientationMarkerTool);this.vrToolGroup = ToolGroupManager.getToolGroup(vrToolGroupId);if (!this.vrToolGroup) {this.vrToolGroup = ToolGroupManager.createToolGroup(vrToolGroupId);this.vrToolGroup.addTool(TrackballRotateTool.toolName);this.vrToolGroup.addTool(ZoomTool.toolName, {zoomToCenter: true,invert: true,minZoomScale: 0.15,maxZoomScale: 20});...// 添加坐标系工具this.vrToolGroup.addTool(OrientationMarkerTool.toolName, axesConfig);}}async loadImages(imageIds) {let newImageIds = [...new Set(imageIds)];for (let i = 0; i < newImageIds.length; i++) {await cornerstoneDICOMImageLoader.wadouri.loadImage(newImageIds[i]).promise;}// Define a volume in memorythis.volume = await volumeLoader.createAndCacheVolume(volumeId, {imageIds: newImageIds});...// 显示坐标系this.showAxes(true);this.loaded = true;}...setAxesType(type) {  // 坐标系外观axesConfig.overlayMarkerType = type;const options = this.vrToolGroup.getToolOptions(OrientationMarkerTool.toolName);if (options.mode === "Enabled") {this.vrToolGroup.setToolDisabled(OrientationMarkerTool.toolName);this.vrToolGroup.setToolConfiguration(OrientationMarkerTool.toolName, {overlayMarkerType: type});this.vrToolGroup.setToolEnabled(OrientationMarkerTool.toolName);}}showAxes(show) {  // 显示/隐藏坐标系if (show) {this.vrToolGroup.setToolConfiguration(OrientationMarkerTool.toolName, {overlayMarkerType: axesConfig.overlayMarkerType});this.vrToolGroup.setToolEnabled(OrientationMarkerTool.toolName);} else {this.vrToolGroup.setToolDisabled(OrientationMarkerTool.toolName);}}
}

2. view3d.vue中响应工具栏事件

async function OnToolbarAction(action) {switch (action.name) {...case "showOutline":  // 显示/隐藏 轮廓线displayArea.value.showOutline(action.value);break;case "showAxes":  // 显示/隐藏 坐标系displayArea.value.showAxes(action.value);break;case "toggleOrientText":  // 显示/隐藏 切片窗口方位文字displayArea.value.toggleOrientText();break;case "changeAxesType":  // 切换坐标系外观displayArea.value.changeAxesType(action.value);break;default:break;}
}

3. 修改DisplayerArea3D.vue

添加并导出工具栏操作响应函数:showAxes,changeAxesType

const showAxes = show => {theMPR.showAxes(show);
};const changeAxesType = type => {theMPR.setAxesType(type);
};defineExpose({...showAxes,changeAxesType,
});

四、vr窗口显示轮廓线

1. 修改mprvr.js 添加addOutline,showOutline函数

  • 导入vtk.js中的vtkOutlineFilter,vtkMapper,vtkActor
  • 添加addOutline函数,vtkOutlineFilter输入连接this.volume.imageData
  • 添加showOutline函数,显示/隐藏轮廓线
  • loadImages中调用this.showOutline(true),默认显示轮廓线
import vtkOutlineFilter from "@kitware/vtk.js/Filters/General/OutlineFilter";
import vtkMapper from "@kitware/vtk.js/Rendering/Core/Mapper";
import vtkActor from "@kitware/vtk.js/Rendering/Core/Actor";let outlineActor = null;export default class MPR {constructor(params) {this.toolGroup = null;this.vrToolGroup = null;...this.init(params);}init(config = {}) {...}...addOutline() {// Create image outline in 3D viewconst outline = vtkOutlineFilter.newInstance();const mapper = vtkMapper.newInstance();outlineActor = vtkActor.newInstance();outlineActor.setMapper(mapper);outline.setInputData(this.volume.imageData);mapper.setInputData(outline.getOutputData());const viewport = this.renderingEngine.getViewport(idVolume);viewport.addActor({ uid: "VOLUME_OUTLINE", actor: outlineActor });outlineActor.setVisibility(true);viewport.render();}showOutline(show) {if (!outlineActor) return;outlineActor.setVisibility(show);const viewport = this.renderingEngine.getViewport(idVolume);viewport.render();}
}

2. view3d.vue中响应工具栏操作

参考第三节

3. 修改DisplayerArea3D.vue

添加并导出工具栏操作响应函数:showOutline

const showOutline = show => {theMPR.showOutline(show);
};defineExpose({...showOutline
});

总结

mpr切片窗口显示/隐藏 方位文字。
vr窗口显示/隐藏坐标系,切换坐标系外观
vr窗口显示/隐藏轮廓线。

http://www.xdnf.cn/news/672031.html

相关文章:

  • Python requests
  • App Runner和Task Pipeline中的数据库连接管理指南
  • 【数据结构】树形结构--二叉树
  • U-Boot ARMv8 平台异常处理机制解析
  • Android studio 查看aar源码出现/* compiled code */
  • 基于 MindQuantum 记录线路作用下基底态的变化过程
  • 让jupyter notebook显示目录
  • 大模型应用:开发移动端页面个人中心页面提示词
  • 基于大模型预测视神经脊髓炎的技术方案大纲
  • Ubuntu 20.04 下 OpenCV 4.5.4 编译安装与系统默认 OpenCV 4.2 共存切换指南【2025最新版!!!】
  • Elasticsearch创建快照仓库报错处理
  • 嵌入式学习--江协stm32day3
  • 阿里云服务器采用crontab定时任务使acme.sh全自动化申请续签免费SSL证书,并部署在Linux宝塔网站和雷池WAF
  • 基于递归思想的系统架构图自动化生成实践
  • VMware-MySQL主从
  • AI提示工程(Prompt Engineering)高级技巧详解
  • 【大模型实战篇】BGE-Rerank-base重排服务部署教程
  • AI前端开发岗位面试准备指南
  • 什么是数据驱动?以及我们应如何理解数据驱动?
  • 什么是可重组机器人?
  • 33. 自动化测试开发之使用mysql异步连接池实现mysql数据库操作
  • 前端域名、端口、协议一样,本地缓存可以共享吗?
  • 【b站计算机拓荒者】【2025】微信小程序开发教程 - chapter3 项目实践 - 2信息采集
  • Protocol Buffers 复杂嵌套编译指南:生成 C++ 代码
  • JavaScript- 3.2 JavaScript实现不同显示器尺寸的响应式主题和页面
  • 开源酷炫大数据可视化大屏html+eacher 100+套
  • 力扣热题——分类求和并作差
  • Vue-02 (使用不同的 Vue CLI 插件)
  • 从 PyTorch 到 TensorFlow Lite:模型训练与推理
  • 【华为云物联网】iOtDA数据以表格字段转发OBS的设置攻略,便于以后数据上大屏