第一章 下载源码 运行cornerstone3D example
第二章 修改示例crosshairs的图像源
第三章 vite+vue3+cornerstonejs项目创建
第四章 加载本地文件夹中的dicom文件并归档
第五章 dicom文件生成png,显示检查栏,序列栏
第六章 stack viewport 显示dicom序列
第七章 在Displayer四个角落显示文字
第八章 在Displayer中显示图像方位
第九章 自动加载、清空显示、修改布局
第十章 显示标尺
第十一章 测量工具
第十二章 镜像、负像、旋转、伪彩、复位
第十三章 自定义垂直滚动条
第十四章 参考线、同步调窗、同步缩放、同步移动
第十五章 预设窗值
第十六章 工具栏svg按钮
第十七章 同步滚动
第十八章 自定义序列自动播放条
第十九章 显示overlay
第二十章 显示多帧图
第二十一章 显示DICOM TAGS
实现一个简单的mpr + vr 功能。
cornerstone3D已经全都实现了,修改为自己的界面。
查看官方示例-cursor3d
cornerstonejs源码位置packages\tools\examples\cursor3D\index.ts
效果如下:
<script setup name="View3d">
import { ref, onMounted, computed, watch } from "vue";
import { useRoute } from "vue-router";
import DisplayerArea3D from "../components/DisplayerArea3D.vue";
import Toolbar3D from "../components/Toolbar3D.vue";
import { useArchiveStore } from "../stores/archive";
import { useAppStore } from "../stores/appStore";
import { storeToRefs } from "pinia";
const archiveStore = useArchiveStore();
const appStore = useAppStore();
const route = useRoute();
const view3d = ref(null);
const displayArea = ref(null);
const toolbar = ref(null);
const toolbarHW = ref(300);
const { toolbarPos, navbarPos } = storeToRefs(appStore);
const mainClass = computed(() => {
const vorh = toolbarPos.value === "top" ? "vertical" : "horizontal-reverse";
return "flex-" + vorh;
});
const toolbarClass = computed(() => {
return "toolbar-" + toolbarPos.value;
});
const toolbarStyle = computed(() => {
if (toolbarPos.value === "top") {
return {
height: toolbarHW.value + "px"
};
} else {
return {
width: toolbarHW.value + "px"
};
}
});
function resizeToolbar(e) {
e.stopPropagation();
view3d.value.style.cursor = "ew-resize";
view3d.value.onmousemove = function (e) {
let delta, minVal, maxVal;
if (toolbarPos.value === "top") {
delta = e.movementY;
minVal = 146;
maxVal = 220;
} else {
delta = -e.movementX;
minVal = 110;
maxVal = 520;
}
const value = toolbarHW.value + delta;
if (value < minVal || value > maxVal) return;
toolbarHW.value = value;
};
document.onmouseup = function () {
view3d.value.onmousemove = null;
document.onmouseup = null;
view3d.value.style.cursor = "";
};
}
async function OnToolbarAction(action) {
switch (action.name) {
case "layoutMpr":
displayArea.value.setLayout("layoutmpr");
break;
case "layoutMprVr":
displayArea.value.setLayout("layoutmprvr");
break;
default:
break;
}
}
</script>
<template>
<div class="container flex-vertical" ref="view3d" @contextmenu.prevent>
<div class="main" :class="mainClass">
<div :class="toolbarClass" :style="toolbarStyle">
<Toolbar3D ref="toolbar" @action="OnToolbarAction" />
</div>
<div class="resizer-vertical" @mousedown="resizeToolbar"></div>
<div class="display-area">
<DisplayerArea3D ref="displayArea" />
</div>
</div>
<div class="footer">Statusbar</div>
</div>
</template>
<style scoped lang="scss">
$bg-color: #333;
$main-color: lightblue;
$header-color: #45b7ec;
$footer-color: #45b7ec;
$resizer-width: 2px;
$header-height: 100px;
$footer-height: 34px;
.container {
background-color: $bg-color;
height: 100vh;
width: 100vw;
max-width: 100%;
max-height: 100%;
justify-content: center;
color: black;
user-select: none;
}
.flex-vertical {
display: flex;
flex-direction: column;
}
.flex-vertical-reverse {
display: flex;
flex-direction: column-reverse;
}
.flex-horizontal {
display: flex;
flex-direction: row;
}
.flex-horizontal-reverse {
display: flex;
flex-direction: row-reverse;
}
.resizer-vertical {
cursor: ew-resize;
width: $resizer-width;
background-color: gray;
flex-shrink: 0;
z-index: 10;
}
.resizer-horizontal {
cursor: ns-resize;
height: $resizer-width;
background-color: gray;
width: 100%;
flex-shrink: 0;
z-index: 10;
}
.main {
width: 100%;
height: 100%;
background-color: $main-color;
.toolbar-top {
height: 200px;
width: 100%;
}
.toolbar-right {
width: 200px;
height: 100%;
flex-shrink: 0;
}
.display-area {
flex: 1;
flex-shrink: 0;
}
}
.footer {
background-color: $footer-color;
height: $footer-height;
border-top: gray solid 1px;
}
</style>
<template>
<div
class="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">
<div ref="elAxial" class="sliceview" @contextmenu.prevent>
<h4 class="desc">Axial</h4>
<span class="text_studyinfo">{{ studyInfo }}</span>
<span class="text_wwwc">{{ axialText.wwwc }}</span>
<span class="text_slice">{{ axialText.slice }}</span>
<span class="orient_top">{{ axialText.orient.top }}</span>
<span class="orient_bottom">{{ axialText.orient.bottom }}</span>
<span class="orient_left">{{ axialText.orient.left }}</span>
<span class="orient_right">{{ axialText.orient.right }}</span>
</div>
</div>
<div class="vrcprparent" v-show="showVR" @dblclick="OnDbClick">
<div ref="elVR" class="sliceview" @contextmenu.prevent>
<h4 class="desc">VR</h4>
<span class="text_studyinfo">{{ studyInfo }}</span>
</div>
</div>
<div class="sagittalparent" v-show="showSagittal">
<div ref="elSagittal" class="sliceview" @contextmenu.prevent>
<h4 class="desc">Sagittal</h4>
<span class="text_studyinfo">{{ studyInfo }}</span>
<span class="text_wwwc">{{ sagittalText.wwwc }}</span>
<span class="text_slice">{{ sagittalText.slice }}</span>
<span class="orient_top">{{ sagittalText.orient.top }}</span>
<span class="orient_bottom">{{ sagittalText.orient.bottom }}</span>
<span class="orient_left">{{ sagittalText.orient.left }}</span>
<span class="orient_right">{{ sagittalText.orient.right }}</span>
</div>
</div>
<div class="coronalparent" v-show="showCoronal">
<div ref="elCoronal" class="sliceview" @contextmenu.prevent>
<h4 class="desc">Coronal</h4>
<span class="text_studyinfo">{{ studyInfo }}</span>
<span class="text_wwwc">{{ coronalText.wwwc }}</span>
<span class="text_slice">{{ coronalText.slice }}</span>
<span class="orient_top">{{ coronalText.orient.top }}</span>
<span class="orient_bottom">{{ coronalText.orient.bottom }}</span>
<span class="orient_left">{{ coronalText.orient.left }}</span>
<span class="orient_right">{{ coronalText.orient.right }}</span>
</div>
</div>
</div>
</template>
<script setup name="DisplayerArea3D">
import { ref, onMounted, onBeforeUnmount, computed, reactive, watch, onUnmounted } from "vue";
import MPR from "../cornerstone3D/mprvr.js";
import { ViewportId, getDicomInfo } from "../cornerstone3D/mprvr.js";
import { useAppStore } from "../stores/appStore";
import { storeToRefs } from "pinia";
const appStore = useAppStore();
const { currentDisplayer } = storeToRefs(appStore);
let theMPR = null;
const loading = ref(false);
const layout = reactive({
type: "layoutmprvr",
viewIds: [],
maxViewId: null
});
let resizeObserver = null;
const studyInfo = ref("");
const defWL = ref(40);
const defWW = ref(400);
const showAxial = ref(true);
const showSagittal = ref(true);
const showCoronal = ref(true);
const showVR = ref(true);
const currentViewportId = ref("");
const elContainer = ref(null);
const elAxial = ref(null);
const elSagittal = ref(null);
const elCoronal = ref(null);
const elVR = ref(null);
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 axialText = computed(() => {
return cornerText[ViewportId.AXIAL];
});
const sagittalText = computed(() => {
return cornerText[ViewportId.SAGITTAL];
});
const coronalText = computed(() => {
return cornerText[ViewportId.CORONAL];
});
const axialStyle = computed(() => {
if (layout.type == "layoutmpr") {
return {
gridRowStart: 1,
gridColumnStart: 2,
gridRowEnd: 3,
gridColumnEnd: 3
};
} else if (layout.type == "layoutmprvr") {
return {
gridRowStart: 1,
gridColumnStart: 1,
gridRowEnd: 2,
gridColumnEnd: 2
};
}
});
watch(
() => layout.type,
(newVal, oldVal) => {
layout.viewIds.length = 0;
if (newVal === "layoutmpr") {
showAxial.value = true;
showSagittal.value = true;
showCoronal.value = true;
showVR.value = false;
} else if (newVal === "layoutmprvr") {
showAxial.value = true;
showSagittal.value = true;
showCoronal.value = true;
showVR.value = true;
}
if (showAxial.value) {
layout.viewIds.push(ViewportId.AXIAL);
}
if (showSagittal.value) {
layout.viewIds.push(ViewportId.SAGITTAL);
}
if (showCoronal.value) {
layout.viewIds.push(ViewportId.CORONAL);
}
if (showVR.value) {
layout.viewIds.push(ViewportId.VOLUME);
}
resizeViews();
}
);
const setLayout = type => {
layout.type = type;
};
const renderHandler = e => {
UpdateText(e);
};
const UpdateText = e => {
const { viewportId } = e.detail;
const wwwl = theMPR.getWindow(viewportId);
const text = `WL: ${wwwl.windowCenter} WW: ${wwwl.windowWidth}`;
const sliceCount = theMPR.getSliceCount(viewportId);
const sliceIdx = theMPR.getSliceIndex(viewportId);
const sliceText = `Im: ${sliceIdx + 1} / ${sliceCount}`;
cornerText[viewportId].wwwc = text;
cornerText[viewportId].slice = sliceText;
};
const onSelectView = e => {
const elem = e.target.closest(".sliceview");
if (!elem) {
return;
}
currentViewportId = elem.getAttribute("data-viewport-uid");
};
const load = async () => {
const imageIds = currentDisplayer.value.getImageIds();
loading.value = true;
const info = await getDicomInfo(imageIds[0]);
studyInfo.value = info.studyInfo;
defWL.value = info.defWL;
defWW.value = info.defWW;
theMPR.loadImages(imageIds).then(() => {
loading.value = false;
});
};
const resizeViews = () => {
const resizeTimer = setTimeout(() => {
theMPR.resize(layout.viewIds, layout.maxViewId);
clearTimeout(resizeTimer);
}, 100);
};
defineExpose({
setLayout,
});
onMounted(() => {
theMPR = new MPR({
elAxial: elAxial.value,
elSagittal: elSagittal.value,
elCoronal: elCoronal.value,
elVR: elVR.value
});
load();
theMPR.bindRenderEvent(renderHandler);
});
onBeforeUnmount(() => {
theMPR.destroy();
theMPR = null;
});
</script>
<style lang="scss" scoped>
$font-size: 14px;
@mixin text() {
position: absolute;
color: white;
font-size: $font-size;
font-family: Arial, Helvetica, sans-serif;
text-align: left;
z-index: 10;
}
@mixin orient($text-align: left) {
position: absolute;
color: white;
font-size: $font-size;
text-align: $text-align;
z-index: 10;
}
.container3d {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
grid-gap: 1px 1px;
height: 100%;
background-color: black;
color: #fff;
}
:deep(.el-loading-spinner) {
font-size: 80px;
font-weight: bold;
}
:deep(.el-loading-mask .el-loading-spinner .el-loading-text) {
font-size: 20px;
}
.desc {
@include text();
top: 2px;
left: 2px;
}
.text_studyinfo {
@include text();
top: 24px;
left: 2px;
}
.text_wwwc {
@include text();
top: 2px;
right: 2px;
}
.text_slice {
@include text();
top: 24px;
right: 2px;
}
.orient_top {
@include orient(center);
top: 2px;
left: calc(50% - 30px);
width: 60px;
}
.orient_bottom {
@include orient(center);
bottom: 2px;
left: calc(50% - 30px);
width: 60px;
}
.orient_left {
@include orient();
top: calc(50% - 20px);
left: 2px;
}
.orient_right {
@include orient();
top: calc(50% - 20px);
right: 2px;
}
.axialparent {
padding: 1px;
border: 1px solid rgb(135, 206, 250);
background-color: black;
}
.sagittalparent {
padding: 1px;
border: 1px solid red;
background-color: black;
}
.coronalparent {
padding: 1px;
border: 1px solid green;
background-color: black;
}
.vrcprparent {
padding: 1px;
border: 1px solid white;
background-color: black;
}
.sliceview {
position: relative;
width: 100%;
height: 100%;
}
</style>
把官方示例中的代码整理成一个类, MPR
删除绑定元素,清理缓存
import {
RenderingEngine,
Enums,
setVolumesForViewports,
volumeLoader,
getRenderingEngine,
eventTarget,
getEnabledElement,
utilities as csUtils,
cache,
imageLoader
} from "@cornerstonejs/core";
import { Enums as toolsEnums, utilities } from "@cornerstonejs/tools";
import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader";
import * as cornerstoneTools from "@cornerstonejs/tools";
import { readTagAsString, decodeChinese, desensitizeSubstring } from "@/utils";
import { renderingEngineId } from "../cornerstone3D";
const {
ToolGroupManager,
Enums: csToolsEnums,
TrackballRotateTool,
StackScrollTool,
WindowLevelTool,
PanTool,
ZoomTool,
} = cornerstoneTools;
const { MouseBindings, KeyboardBindings } = csToolsEnums;
const { ViewportType } = Enums;
const { IMAGE_RENDERED, CAMERA_MODIFIED, VOI_MODIFIED } = Enums.Events;
const ViewportId = Object.freeze({
AXIAL: "DICOM3D_AXIAL",
SAGITTAL: "DICOM3D_SAGITTAL",
CORONAL: "DICOM3D_CORONAL",
VOLUME: "DICOM3D_VOLUME"
});
const idAxial = ViewportId.AXIAL;
const idSagittal = ViewportId.SAGITTAL;
const idCoronal = ViewportId.CORONAL;
const idVolume = ViewportId.VOLUME;
const volumeId = "DICOM3D_VOLUME_ID";
const viewportIds = [idAxial, idSagittal, idCoronal, idVolume];
const mprViewportIds = [idAxial, idSagittal, idCoronal];
const cameraSynchronizerId = "CAMERA_SYNCHRONIZER_ID";
const voiSynchronizerId = "VOI_SYNCHRONIZER_ID";
const toolGroupId = "MPR_TOOLGROUP_ID";
const vrToolGroupId = "VR_TOOL_GROUP_ID";
const viewportColors = {
[idAxial]: "rgb(135, 206, 250)",
[idSagittal]: "rgb(255, 0, 0)",
[idCoronal]: "rgb(0, 255, 0)",
[idVolume]: "rgb(128, 128, 128)"
};
const viewportReferenceLineControllable = [idAxial, idSagittal, idCoronal, idVolume];
const viewportReferenceLineDraggableRotatable = [idAxial, idSagittal, idCoronal, idVolume];
const viewportReferenceLineSlabThicknessControlsOn = [idAxial, idSagittal, idCoronal, idVolume];
function getReferenceLineColor(viewportId) {
return viewportColors[viewportId];
}
function getReferenceLineControllable(viewportId) {
const index = viewportReferenceLineControllable.indexOf(viewportId);
return index !== -1;
}
function getReferenceLineDraggableRotatable(viewportId) {
const index = viewportReferenceLineDraggableRotatable.indexOf(viewportId);
return index !== -1;
}
function getReferenceLineSlabThicknessControlsOn(viewportId) {
const index = viewportReferenceLineSlabThicknessControlsOn.indexOf(viewportId);
return index !== -1;
}
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(TrackballRotateTool);
cornerstoneTools.addTool(ZoomTool);
cornerstoneTools.addTool(PanTool);
cornerstoneTools.addTool(WindowLevelTool);
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(PanTool.toolName);
this.vrToolGroup.setToolActive(TrackballRotateTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary // Left Click
}
]
});
this.vrToolGroup.setToolActive(ZoomTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Secondary
}
]
});
this.vrToolGroup.setToolActive(PanTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Auxiliary
}
]
});
}
// Instantiate a rendering engine
this.renderingEngine = new RenderingEngine(renderingEngineId);
this.viewportInputArray = [
{
viewportId: idAxial,
type: ViewportType.ORTHOGRAPHIC,
element: elAxial,
defaultOptions: {
orientation: Enums.OrientationAxis.AXIAL,
background: [0, 0, 0]
}
},
{
viewportId: idSagittal,
type: ViewportType.ORTHOGRAPHIC,
element: elSagittal,
defaultOptions: {
orientation: Enums.OrientationAxis.SAGITTAL,
background: [0, 0, 0]
}
},
{
viewportId: idCoronal,
type: ViewportType.ORTHOGRAPHIC,
element: elCoronal,
defaultOptions: {
orientation: Enums.OrientationAxis.CORONAL,
background: [0, 0, 0]
}
},
{
viewportId: idVolume,
type: ViewportType.VOLUME_3D,
element: elVR,
defaultOptions: {
background: [0, 0, 0],
orientation: Enums.OrientationAxis.CORONAL
}
}
];
// Define tool groups to add the segmentation display tool to
this.toolGroup = ToolGroupManager.getToolGroup(toolGroupId);
if (!this.toolGroup) {
this.toolGroup = ToolGroupManager.createToolGroup(toolGroupId);
this.toolGroup.addTool(WindowLevelTool.toolName);
const isMobile = window.matchMedia("(any-pointer:coarse)").matches;
this.toolGroup.addTool(CrosshairsTool.toolName, {
getReferenceLineColor,
getReferenceLineControllable,
getReferenceLineDraggableRotatable,
getReferenceLineSlabThicknessControlsOn,
referenceLinesCenterGapRadius: 14,
mobile: {
enabled: isMobile,
opacity: 0.8,
handleRadius: 9
}
});
this.addManipulationBindings(this.toolGroup);
}
// For the crosshairs to operate, the viewports must currently be
// added ahead of setting the tool active. This will be improved in the future.
this.toolGroup.addViewport(idAxial, renderingEngineId);
this.toolGroup.addViewport(idSagittal, renderingEngineId);
this.toolGroup.addViewport(idCoronal, renderingEngineId);
this.vrToolGroup.addViewport(idVolume, renderingEngineId);
}
destroy() {
this.renderingEngine.disableElement(idVolume);
this.renderingEngine.disableElement(idAxial);
this.renderingEngine.disableElement(idSagittal);
this.renderingEngine.disableElement(idCoronal);
this.toolGroup.removeViewports(renderingEngineId);
this.vrToolGroup.removeViewports(renderingEngineId);
cache.purgeCache();
}
addManipulationBindings(toolGroup, options = {}) {
const zoomBindings = [
{
mouseButton: MouseBindings.Secondary
}
];
const { is3DViewport = false, enableShiftClickZoom = false, toolMap = new Map() } = options;
if (enableShiftClickZoom === true) {
zoomBindings.push({
mouseButton: MouseBindings.Primary, // Shift Left Click
modifierKey: KeyboardBindings.Shift
});
}
if (!this.registered) {
for (const [, config] of toolMap) {
if (config.tool) {
cornerstoneTools.addTool(config.tool);
}
}
}
this.registered = true;
toolGroup.addTool(PanTool.toolName);
toolGroup.addTool(ZoomTool.toolName, {
zoomToCenter: true,
invert: true,
minZoomScale: 0.1,
maxZoomScale: 20
});
if (is3DViewport) {
toolGroup.addTool(TrackballRotateTool.toolName);
} else {
toolGroup.addTool(StackScrollTool.toolName);
}
toolGroup.addTool(StackScrollTool.toolName);
toolGroup.setToolActive(PanTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Auxiliary
},
{
numTouchPoints: 1,
modifierKey: KeyboardBindings.Ctrl
}
]
});
toolGroup.setToolActive(ZoomTool.toolName, {
bindings: zoomBindings
});
// Need a binding to navigate without a wheel mouse
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary,
modifierKey: KeyboardBindings.Alt
},
{
numTouchPoints: 1,
modifierKey: KeyboardBindings.Alt
},
{
mouseButton: MouseBindings.Wheel
}
]
});
if (is3DViewport) {
toolGroup.setToolActive(TrackballRotateTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary
}
]
});
} else {
toolGroup.setToolActive(StackScrollTool.toolName);
}
// Add extra tools from the toolMap
for (const [toolName, config] of toolMap) {
if (config.baseTool) {
if (!toolGroup.hasTool(config.baseTool)) {
toolGroup.addTool(config.baseTool, toolMap.get(config.baseTool)?.configuration);
}
toolGroup.addToolInstance(toolName, config.baseTool, config.configuration);
} else if (!toolGroup.hasTool(toolName)) {
toolGroup.addTool(toolName, config.configuration);
}
if (config.passive) {
// This can be applied during add/remove contours
toolGroup.setToolPassive(toolName);
}
if (config.bindings || config.selected) {
toolGroup.setToolActive(
toolName,
(config.bindings && config) || {
bindings: [{ mouseButton: MouseBindings.Primary }]
}
);
}
}
}
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 memory
this.volume = await volumeLoader.createAndCacheVolume(volumeId, {
imageIds: newImageIds
});
this.renderingEngine.setViewports(this.viewportInputArray);
this.volume.load();
// Set volumes on the viewports
await setVolumesForViewports(
this.renderingEngine,
[
{
volumeId,
callback: null
}
],
viewportIds
);
this.toolGroup.setToolActive(CrosshairsTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
});
this.renderingEngine.renderViewports(viewportIds);
const viewport = this.renderingEngine.getViewport(idVolume);
await setVolumesForViewports(this.renderingEngine, [{ volumeId }], [idVolume]).then(() => {
viewport.setProperties({
preset: "CT-Coronary-Arteries-2"
});
});
this.loaded = true;
}
getSliceCount(viewportId) {
if (!this.loaded) {
return;
}
const viewport = this.renderingEngine.getViewport(viewportId);
return viewport.getNumberOfSlices();
}
getSliceIndex(viewportId) {
if (!this.loaded) {
return;
}
const viewport = this.renderingEngine.getViewport(viewportId);
return viewport.getSliceIndex();
}
resize(viewIds, maxViewId) {
if (!this.loaded) return;
this.renderingEngine.resize(true, true);
viewIds.forEach(viewportId => {
const viewport = this.renderingEngine.getViewport(viewportId);
if (viewport) {
const presentation = viewport.getViewPresentation();
viewport.setViewPresentation(presentation);
}
});
if (maxViewId) {
this.toolGroup.setToolDisabled(CrosshairsTool.toolName);
this.toolGroup.setToolEnabled(WindowLevelTool.toolName);
this.toolGroup.setToolActive(WindowLevelTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary
}
]
});
} else {
this.toolGroup.setToolEnabled(CrosshairsTool.toolName);
this.toolGroup.setToolDisabled(WindowLevelTool.toolName);
this.toolGroup.setToolActive(CrosshairsTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
});
}
}
bindRenderEvent(callback) {
for (let i = 0; i < this.viewportInputArray.length; i++) {
if (this.viewportInputArray[i].viewportId !== idVolume) {
const elem = this.viewportInputArray[i].element;
elem.addEventListener(IMAGE_RENDERED, callback);
}
}
}
bindCameraEvent(callback) {
for (let i = 0; i < this.viewportInputArray.length; i++) {
if (this.viewportInputArray[i].viewportId !== idVolume) {
const elem = this.viewportInputArray[i].element;
elem.addEventListener(CAMERA_MODIFIED, callback);
}
}
}
}
async function getDicomInfo(imageId, lang = "zh_CN") {
const image = await imageLoader.loadAndCacheImage(imageId);
const defWL = Math.round(image.windowCenter);
const defWW = Math.round(image.windowWidth);
const charset = image.data.string("x00080005") || "ISO_IR 100";
const isUtf8 = charset.indexOf("ISO_IR 192") != -1;
let studyInfo = "";
let val = "";
let tag = image.data.elements.x00100010; // 姓名
if (tag) {
val = readTagAsString(image.data.byteArray, tag.dataOffset, tag.length);
}
if (val) {
val = decodeChinese(val, isUtf8);
if (config.desensitize) {
val = desensitizeSubstring(val, 1, -1);
}
studyInfo += val + " ";
}
val = image.data.string("x00101010");
if (!val || val.length <= 0) {
val = image.data.string("x00100030");
if (val === undefined) val = "";
} else {
if (lang == "zh_CN") {
val = val.replace(/Y|y/, "岁");
val = val.replace(/M|m/, "月");
val = val.replace(/D|d/, "天");
}
}
val = val.replace(/^0+/g, "");
if (val) {
studyInfo += val + " ";
}
val = image.data.string("x00100040");
if (lang == "zh_CN") {
if (!val || val.length <= 0) tmp = "未知";
if (val === "M" || val === "m") val = "男";
else if (val === "F" || val === "f") val = "女";
else val = "未知";
}
if (val) {
studyInfo += val;
}
return {
defWL,
defWW,
studyInfo
};
}
export { presetColors, ViewportId, getDicomInfo, loadDicom };
整理cornerstone3D中的官方示例 cursor3d的代码,实现mpr功能