基于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.pswp.cn/pingmian/82518.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82518.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/82518.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【C/C++】線程局部存儲:原理與應用詳解

文章目錄 1 基礎概念1.1 定義1.2 初始化規則1.3 全局TLS vs 局部靜態TLS 2 內存布局2.1 實現機制2.2 典型內存結構2.3 性能特點 3 使用場景/用途3.1 場景3.2 用途 4 注意事項5 對比其他技術6 示例代碼7 建議7.1 調試7.2 優化 8 學習資料9 總結 在 C 多線程編程中&#xff0c;線…

【圖像大模型】IP-Adapter:圖像提示適配器的技術解析與實踐指南

IP-Adapter&#xff1a;圖像提示適配器的技術解析與實踐指南 一、項目背景與技術價值1.1 圖像生成中的個性化控制需求1.2 IP-Adapter的核心貢獻 二、技術原理深度解析2.1 整體架構設計2.2 圖像特征編碼器2.3 訓練策略 三、項目部署與實戰指南3.1 環境配置3.2 模型下載3.3 基礎生…

MySQL-5.7 修改密碼和連接訪問權限

一、MySQL-5.7 修改密碼和連接權限設置 修改密碼語法 注意&#xff1a;rootlocalhost 和 root192.168.56.% 是兩個不同的用戶。在修改密碼時&#xff0c;兩個用戶的密碼是各自分別保存&#xff0c;如果兩個用戶密碼設置不一樣則登陸時注意登陸密碼 GRANT ALL PRIVILEGES ON …

Linux基本指令篇 —— touch指令

touch是Linux和Unix系統中一個非常基礎但實用的命令&#xff0c;主要用于操作文件的時間戳和創建空文件。下面我將詳細介紹這個命令的用法和功能。 目錄 一、基本功能 1. 創建空文件 2. 同時創建多個文件 3. 創建帶有空格的文件名&#xff08;需要使用引號&#xff09; 二、…

mysql explain使用

文章目錄 type 訪問類型性能高到低多注意type: index 出現的場景 key 實際使用的索引Extra 額外信息其他字段 通過 EXPLAIN 你可以知道&#xff1a;如是否使用索引、掃描多少行、是否需要排序或臨時表 EXPLAIN 三板斧&#xff08;type、key、Extra&#xff09; 例子&#xff1…

JMeter-SSE響應數據自動化

結構圖 背景&#xff1a; 需要寫一個JMeter腳本來進行自動化測試&#xff0c;主要是通過接口調用一些東西&#xff0c;同時要對響應的數據進行處理&#xff0c;包括不限于錯誤信息的輸出。 1.SSE(摘錄) SSE&#xff08;Server-Sent Events&#xff09;是一種基于HTTP協議、允許…

<<運算符重載 和 c_str() 的區別和聯系

例題 文章開始之前我們看下以下代碼&#xff0c;你能精準的說出正確的輸出結果并知道其原理嗎&#xff1f; void test() {string s1("hello world");cout << s1 << endl;//cout << s1.c_str() << endl;//const char* p1 "xxxx"…

python web flask專題-Flask入門指南:從安裝到核心功能詳解

Flask入門指南&#xff1a;從安裝到核心功能詳解 Flask作為Python最流行的輕量級Web框架之一&#xff0c;以其簡潔靈活的特性廣受開發者喜愛。本文將帶你從零開始學習Flask&#xff0c;涵蓋安裝配置、項目結構、應用實例、路由系統以及請求響應處理等核心知識點。 1. Flask安…

一種C# 的SM4 的 加解密的實現,一般用于醫療或者支付

一種C# 的SM4 的 加解密的實現 一般用于醫療或者支付 加密 string cipherText SM4Helper.Encrypt_test(data, key); public static string Encrypt_test(string plainText, string key) { byte[] keyBytes Encoding.ASCII.GetBytes(key); byte[] input…

“軒轅杯“云盾礪劍CTF挑戰賽 Web wp

文章目錄 ezflaskezjsezrceezssrf1.0簽到ezsql1.0ez_web1非預期預期解 ezflask ssti, 過濾了一些關鍵詞, 繞一下就行 name{{url_for["__globals__"]["__builtins__"]["eval"]("__tropmi__"[::-1])(os)["po""pen"…

Matlab快速上手五十六:詳解符號運算里假設的用法,通過假設可以設置符號變量的取值范圍,也可以通過假設設置變量屬于集合:整數、正數和實數等

1.符號變量中假設的概念 在符號數學工具箱中&#xff0c;符號變量默認范圍是全體復數&#xff0c;也就是說&#xff0c;符號運算是在全體復數域進行的&#xff0c;若需要運算中&#xff0c;不使用全體復數域&#xff0c;可以為變量設定取值范圍&#xff0c;這就用到了假設&…

【python實用小腳本-79】[HR轉型]Excel難民到數據工程師|用Python實現CSV秒轉JSON(附HRIS系統對接方案)

場景故事&#xff1a;從手動復制粘貼到自動化數據流轉 "Kelly&#xff0c;我們需要把3000名員工的考勤數據導入新HR系統&#xff0c;今天能完成嗎&#xff1f;"去年這個時候&#xff0c;作為HRIS項目負責人的我&#xff0c;面對這個需求時第一反應是打開Excel開始手…

數據透視:水安 B 證如何影響水利企業的生存指數?

某大數據公司提取了 3000 家水利企業的經營數據&#xff0c;一組關聯分析令人震驚&#xff1a;B 證配備率與企業利潤率的相關系數達 0.67—— 這意味著持證率每提升 10%&#xff0c;企業利潤率平均提高 4.2 個百分點。當我們用數據解剖這本紅本本&#xff0c;會發現它像一根無形…

從零搭建上門做飯平臺:高并發訂單系統設計

你知道為什么聰明人都在搶著做上門做飯平臺嗎&#xff1f;因為這可能是餐飲行業最后一片藍海&#xff01;傳統餐飲還在為房租人工發愁時&#xff0c;上門私廚已經輕裝上陣殺出重圍。不需要門店租金&#xff0c;不用養服務員&#xff0c;廚師直接上門服務&#xff0c;成本直降60…

openpi π? 項目部署運行邏輯(四)——機器人主控程序 main.py — aloha_real

π? 機器人主控腳本都在 examples 中&#xff1a; 可以看到包含了多種類機器人適配 此筆記首先記錄了 aloha_real 部分 aloha_real 中&#xff0c;main.py 是 openpi ALOHA 平臺上“主控執行入口”&#xff0c;負責&#xff1a; 建立與推理服務器&#xff08;serve_policy.…

利用 Python 爬蟲獲取唯品會 VIP 商品詳情:實戰指南

在當今電商競爭激烈的環境中&#xff0c;VIP 商品往往是商家的核心競爭力所在。這些商品不僅代表著品牌的高端形象&#xff0c;更是吸引高價值客戶的關鍵。因此&#xff0c;獲取 VIP 商品的詳細信息對于市場分析、競品研究以及優化自身產品策略至關重要。Python 作為一種強大的…

鴻蒙桌面快捷方式開發

桌面快捷方式開發實戰 [參考文檔] (https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-desktop-shortcuts) 在module.json5配置文件中的abilities標簽下的metadata中設置resource屬性值為$profile:shortcuts_config&#xff0c;指定應用的快捷方式配置文件&…

3分鐘學會跨瀏覽器富文本編輯器開發:精準光標定位+內容插入(附完整代碼)

一、痛點直擊&#xff1a;傳統編輯器的三大坑 作為前端開發&#xff0c;你是否遇到過以下靈魂拷問&#xff1f; ? 為什么Firefox光標能精準定位&#xff0c;IE卻永遠跳轉到開頭&#xff1f;? 圖片上傳后如何保證插入位置不偏移&#xff1f;? 跨瀏覽器兼容測試時&#xff0…

RK3562 Linux-5.10 內核HUSB311 Type-C 控制器芯片調試記錄

硬件原理&#xff1a; 1. type C 接口&#xff1a; 1.1 HUSB311芯片&#xff0c; CC1和CC2 邏輯接到HUSB311 上面&#xff0c; 接I2C0組和USBCC_INT_L USBCC_INT_L 接到GPIO0_A6 做為CC的邏輯中斷 1.2 TYPEC_DP/TYPEC_DM 接到ARM 端的USB3.0 OTG上面 1.2 TYPEC_RX1P/TYPEC…

深入理解Java中的BigDecimal:高精度計算的核心工具

精心整理了最新的面試資料和簡歷模板&#xff0c;有需要的可以自行獲取 點擊前往百度網盤獲取 點擊前往夸克網盤獲取 引言 在Java編程中&#xff0c;處理浮點數運算時可能會遇到精度丟失的問題。例如&#xff1a; System.out.println(0.1 0.2); // 輸出&#xff1a;0.30000…