CT/NM融合更改
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
d07adc4948
commit
7870c3d74b
|
|
@ -1,7 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div ref="viewport-fusion" class="viewport-wrapper" v-loading="loading" :element-loading-text="NSTip"
|
<div ref="viewport-fusion" class="viewport-wrapper" v-loading="loading" :element-loading-text="NSTip"
|
||||||
element-loading-background="rgba(0, 0, 0, 0.8)" @mouseup="sliderMouseup" @mousemove="sliderMousemove"
|
element-loading-background="rgba(0, 0, 0, 0.8)" @mouseup="sliderMouseup" @mousemove="sliderMousemove"
|
||||||
@mouseleave="sliderMouseleave" :style="{ color: series.Modality === 'PT' || isMip ? '#666' : '#ddd' }">
|
@mouseleave="sliderMouseleave" :style="{ color: series.Modality === 'PT' || series.Modality === 'NM' || isMip ? '#666' : '#ddd' }">
|
||||||
|
<div v-if="isFusion && series.Modality === 'NM'" class="opacity-slider-wrapper" @mousedown.stop @mousemove.stop @mouseup.stop @wheel.stop>
|
||||||
|
<div class="slider-title">{{ Math.round(nmOpacity * 100) }}%</div>
|
||||||
|
<input type="range" min="0" max="1" step="0.05" v-model.number="nmOpacity" @input="applyNmOpacity"
|
||||||
|
class="opacity-slider" />
|
||||||
|
</div>
|
||||||
<div v-if="series && taskInfo" class="left-top-text">
|
<div v-if="series && taskInfo" class="left-top-text">
|
||||||
<div v-if="taskInfo.IsExistsClinicalData && !isMip && !isFusion" class="cd-info"
|
<div v-if="taskInfo.IsExistsClinicalData && !isMip && !isFusion" class="cd-info"
|
||||||
:title="$t('trials:reading:button:clinicalData')">
|
:title="$t('trials:reading:button:clinicalData')">
|
||||||
|
|
@ -104,9 +109,14 @@ import {
|
||||||
import * as cornerstoneTools from '@cornerstonejs/tools'
|
import * as cornerstoneTools from '@cornerstonejs/tools'
|
||||||
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
|
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
|
||||||
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
|
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
|
||||||
|
import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunction'
|
||||||
import { createImageIdsAndCacheMetaData } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/createImageIdsAndCacheMetaData'
|
import { createImageIdsAndCacheMetaData } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/createImageIdsAndCacheMetaData'
|
||||||
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
|
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
|
||||||
import { setPetColorMapTransferFunctionForVolumeActor } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetColorMapTransferFunctionForVolumeActor'
|
import { setPetColorMapTransferFunctionForVolumeActor } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetColorMapTransferFunctionForVolumeActor'
|
||||||
|
import {
|
||||||
|
setMipTransferFunctionForVolumeActor,
|
||||||
|
setPetTransferFunctionForVolumeActor
|
||||||
|
} from './helpers/index.js'
|
||||||
const { BlendModes, OrientationAxis } = Enums;
|
const { BlendModes, OrientationAxis } = Enums;
|
||||||
const { getColormap } = csUtils.colormap;
|
const { getColormap } = csUtils.colormap;
|
||||||
import { vec3, mat4 } from 'gl-matrix'
|
import { vec3, mat4 } from 'gl-matrix'
|
||||||
|
|
@ -172,7 +182,10 @@ export default {
|
||||||
isMove: false
|
isMove: false
|
||||||
},
|
},
|
||||||
ptVolumeId: null,
|
ptVolumeId: null,
|
||||||
loading: false
|
loading: false,
|
||||||
|
Colorbar: null,
|
||||||
|
nmOpacity: 0.6,
|
||||||
|
nmFusionVolumeActor: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -220,7 +233,9 @@ export default {
|
||||||
},
|
},
|
||||||
stackNewImage(e) {
|
stackNewImage(e) {
|
||||||
const { detail } = e
|
const { detail } = e
|
||||||
this.series.SliceIndex = detail.imageIndex
|
if (this.series) {
|
||||||
|
this.series.SliceIndex = detail.imageIndex
|
||||||
|
}
|
||||||
this.sliderInfo.height = detail.imageIndex * 100 / detail.numberOfSlices
|
this.sliderInfo.height = detail.imageIndex * 100 / detail.numberOfSlices
|
||||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||||
|
|
@ -267,7 +282,7 @@ export default {
|
||||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||||
let properties = viewport.getProperties()
|
let properties = viewport.getProperties()
|
||||||
if (this.isFusion) {
|
if (this.isFusion) {
|
||||||
properties = viewport.getProperties(this.volumeId)
|
properties = viewport.getProperties(this.ptVolumeId || this.volumeId)
|
||||||
}
|
}
|
||||||
if (properties && properties.voiRange) {
|
if (properties && properties.voiRange) {
|
||||||
var { lower, upper } = properties.voiRange
|
var { lower, upper } = properties.voiRange
|
||||||
|
|
@ -445,6 +460,18 @@ export default {
|
||||||
ctx.fillStyle = gradient
|
ctx.fillStyle = gradient
|
||||||
ctx.fillRect(0, 0, rectWidth, rectHeight)
|
ctx.fillRect(0, 0, rectWidth, rectHeight)
|
||||||
},
|
},
|
||||||
|
applyNmOpacity() {
|
||||||
|
if (!this.nmFusionVolumeActor?.getProperty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ofun = vtkPiecewiseFunction.newInstance();
|
||||||
|
ofun.addPoint(0, 0.0);
|
||||||
|
ofun.addPoint(0.1, 0.9 * this.nmOpacity);
|
||||||
|
ofun.addPoint(5, 1.0 * this.nmOpacity);
|
||||||
|
this.nmFusionVolumeActor.getProperty().setScalarOpacity(0, ofun);
|
||||||
|
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||||
|
renderingEngine?.render?.();
|
||||||
|
},
|
||||||
setPreset(presetName) {
|
setPreset(presetName) {
|
||||||
this.presetName = presetName
|
this.presetName = presetName
|
||||||
},
|
},
|
||||||
|
|
@ -482,13 +509,13 @@ export default {
|
||||||
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description && !isLocate) {
|
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description && !isLocate) {
|
||||||
data.SliceIndex = this.series.SliceIndex
|
data.SliceIndex = this.series.SliceIndex
|
||||||
}
|
}
|
||||||
// this.series = { ...data }
|
|
||||||
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
const renderingEngine = getRenderingEngine(this.renderingEngineId)
|
||||||
const viewport = renderingEngine.getViewport(this.viewportId)
|
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||||
if (isLocate) return csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex });
|
if (isLocate) return csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex });
|
||||||
this.volumeId = data.SeriesInstanceUid
|
this.volumeId = data.SeriesInstanceUid
|
||||||
this.ptVolumeId = null
|
this.ptVolumeId = null
|
||||||
this.series = {}
|
this.series = {}
|
||||||
|
this.nmFusionVolumeActor = null
|
||||||
let { isFusion, isMip, colorMap } = option
|
let { isFusion, isMip, colorMap } = option
|
||||||
this.isFusion = isFusion;
|
this.isFusion = isFusion;
|
||||||
this.isMip = isMip;
|
this.isMip = isMip;
|
||||||
|
|
@ -499,33 +526,34 @@ export default {
|
||||||
this.ptVolumeId = `fusion_${this.volumeId}`
|
this.ptVolumeId = `fusion_${this.volumeId}`
|
||||||
let { ct, data } = obj
|
let { ct, data } = obj
|
||||||
this.series = { ...data }
|
this.series = { ...data }
|
||||||
this.ctSeries = { ...ct }
|
let volumes = [
|
||||||
this.petSeries = { ...data }
|
{
|
||||||
await viewport
|
volumeId: ct.SeriesInstanceUid, callback: (r) => {
|
||||||
.setVolumes([
|
setCtTransferFunctionForVolumeActor(r)
|
||||||
{
|
console.log("融合ct渲染成功")
|
||||||
volumeId: this.volumeId, callback: (r) => {
|
}
|
||||||
setPetColorMapTransferFunctionForVolumeActor(r)
|
},
|
||||||
console.log("融合pet渲染成功");
|
{
|
||||||
|
volumeId: data.SeriesInstanceUid, callback: (r) => {
|
||||||
|
setPetColorMapTransferFunctionForVolumeActor(r)
|
||||||
|
if (this.series.Modality === 'NM') {
|
||||||
|
this.nmFusionVolumeActor = r.volumeActor
|
||||||
|
this.applyNmOpacity()
|
||||||
}
|
}
|
||||||
},
|
console.log("融合pet渲染成功")
|
||||||
{
|
}
|
||||||
volumeId: ct.SeriesInstanceUid, callback: (r) => {
|
},
|
||||||
setCtTransferFunctionForVolumeActor(r)
|
]
|
||||||
console.log("融合ct渲染成功")
|
if (this.series.Modality !== 'NM') {
|
||||||
}
|
volumes.unshift({
|
||||||
},
|
volumeId: this.volumeId, callback: (r) => {
|
||||||
{
|
// setPetColorMapTransferFunctionForVolumeActor(r)
|
||||||
volumeId: this.ptVolumeId, callback: (r) => {
|
console.log("融合pet渲染成功");
|
||||||
setPetColorMapTransferFunctionForVolumeActor(r)
|
|
||||||
console.log("融合pet渲染成功");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]).then(res => {
|
|
||||||
if (colorMap) {
|
|
||||||
this.setColorMap(this.presetName)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await viewport.setVolumes(volumes)
|
||||||
} else {
|
} else {
|
||||||
this.series = { ...data }
|
this.series = { ...data }
|
||||||
if (this.isMip) {
|
if (this.isMip) {
|
||||||
|
|
@ -540,7 +568,12 @@ export default {
|
||||||
.setVolumes([{
|
.setVolumes([{
|
||||||
volumeId: this.volumeId,
|
volumeId: this.volumeId,
|
||||||
callback: (r) => {
|
callback: (r) => {
|
||||||
setPetColorMapTransferFunctionForVolumeActor(r)
|
if (this.series.Modality === 'NM') {
|
||||||
|
setMipTransferFunctionForVolumeActor({ ...r, volumeId: this.volumeId })
|
||||||
|
} else {
|
||||||
|
setPetTransferFunctionForVolumeActor(r)
|
||||||
|
}
|
||||||
|
// setPetColorMapTransferFunctionForVolumeActor(r)
|
||||||
console.log("mip渲染成功")
|
console.log("mip渲染成功")
|
||||||
},
|
},
|
||||||
slabThickness,
|
slabThickness,
|
||||||
|
|
@ -548,30 +581,19 @@ export default {
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
orientation: OrientationAxis.CORONAL
|
orientation: OrientationAxis.CORONAL
|
||||||
}
|
}
|
||||||
}]).then(res => {
|
}])
|
||||||
if (colorMap) {
|
|
||||||
this.setColorMap(this.presetName)
|
|
||||||
}
|
|
||||||
if (isLocate) {
|
|
||||||
setTimeout(() => { csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex }); })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
viewport
|
viewport
|
||||||
.setVolumes([{
|
.setVolumes([{
|
||||||
volumeId: this.volumeId, callback: (r) => {
|
volumeId: this.volumeId, callback: (r) => {
|
||||||
if (this.series.Modality === 'PT') {
|
if (this.series.Modality === 'PT' || this.series.Modality === 'NM') {
|
||||||
setPetColorMapTransferFunctionForVolumeActor(r, true)
|
// setPetColorMapTransferFunctionForVolumeActor(r, true)
|
||||||
|
setPetTransferFunctionForVolumeActor(r)
|
||||||
} else {
|
} else {
|
||||||
setCtTransferFunctionForVolumeActor(r)
|
setCtTransferFunctionForVolumeActor(r)
|
||||||
}
|
}
|
||||||
console.log("渲染成功")
|
|
||||||
}
|
}
|
||||||
}]).then(res => {
|
}])
|
||||||
if (colorMap) {
|
|
||||||
this.setColorMap(this.presetName)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -751,7 +773,11 @@ export default {
|
||||||
e.stopImmediatePropagation()
|
e.stopImmediatePropagation()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
}
|
},
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.series = null
|
||||||
|
this.nmFusionVolumeActor = null
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
NSTip() {
|
NSTip() {
|
||||||
|
|
@ -767,6 +793,41 @@ export default {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: default !important;
|
cursor: default !important;
|
||||||
|
|
||||||
|
.opacity-slider-wrapper {
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 10;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 40px;
|
||||||
|
|
||||||
|
.slider-title {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #ddd;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
width: 30px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opacity-slider {
|
||||||
|
-webkit-appearance: slider-vertical;
|
||||||
|
writing-mode: bt-lr;
|
||||||
|
width: 10px;
|
||||||
|
height: 150px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.left-top-text {
|
.left-top-text {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 5px;
|
left: 5px;
|
||||||
|
|
@ -943,4 +1004,4 @@ export default {
|
||||||
cursor: move
|
cursor: move
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -273,7 +273,9 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- 伪彩 -->
|
<!-- 伪彩 -->
|
||||||
<template v-if="readingTool === 2">
|
<template v-if="readingTool === 2">
|
||||||
<colorMap v-show="isFusion" ref="colorMap" @setColorMap="setColorMap" @voiChange="voiChange" />
|
<colorMap v-show="isFusion" ref="colorMap" :unit="fusionOverlayModality === 'NM' ? 'counts' : 'g/ml'"
|
||||||
|
:modality="fusionOverlayModality"
|
||||||
|
@setColorMap="setColorMap" @voiChange="voiChange" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -299,11 +301,9 @@
|
||||||
<!-- viewports -->
|
<!-- viewports -->
|
||||||
<div class="viewports-wrapper">
|
<div class="viewports-wrapper">
|
||||||
<div ref="container" class="grid-container">
|
<div ref="container" class="grid-container">
|
||||||
<!-- isMPR && index === 2 ? 'grid-cell-3' : '', -->
|
<div :class="['viewports-box', isFusion || isMPR ? 'viewports-box-down' : '', fullScreenIndex !== null ? 'viewports-box-full-screen' : '']" :style="gridStyle">
|
||||||
<div :class="['viewports-box', isFusion || isMPR ? 'viewports-box-down' : '']" :style="gridStyle">
|
|
||||||
<div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-${index}`"
|
<div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-${index}`"
|
||||||
:style="cellStyle"
|
:class="['grid-cell', isMPR && index === 2 ? 'grid-cell-3' : '', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||||
:class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
|
||||||
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
||||||
<VolumeViewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
|
<VolumeViewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
|
||||||
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
|
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
|
||||||
|
|
@ -315,10 +315,12 @@
|
||||||
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" v-else />
|
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" v-else />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="(criterionType === 0 && readingTool === 0) || readingTool === 3"
|
<div v-if="criterionType === 0 && readingTool === 0 || readingTool === 3"
|
||||||
:class="['viewports-box', !isMPR ? 'viewports-box-down' : '']" :style="gridStyleMPR">
|
:class="['viewports-box', !isMPR ? 'viewports-box-down' : '', fullScreenIndex !== null ? 'viewports-box-full-screen' : '']" :style="gridStyleMPR">
|
||||||
<div v-for="(v, index) in 3" :key="`viewport-MPR-${index}`" :style="cellStyle"
|
<div v-for="(v, index) in 3" :key="`viewport-MPR-${index}`"
|
||||||
:class="['grid-cell', index === 0 ? 'grid-cell-3' : '', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
v-show="index < cells.length"
|
||||||
|
:style="cellStyle"
|
||||||
|
:class="['grid-cell', isMPR && index === 0 ? 'grid-cell-3' : '', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||||
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
||||||
<MPRViewport :ref="`viewport-MPR-${index}`" :data-viewport-uid="`viewport-MPR-${index}`"
|
<MPRViewport :ref="`viewport-MPR-${index}`" :data-viewport-uid="`viewport-MPR-${index}`"
|
||||||
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-MPR-${index}`"
|
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-MPR-${index}`"
|
||||||
|
|
@ -327,8 +329,7 @@
|
||||||
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" />
|
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="readingTool === 2"
|
<div v-if="readingTool === 2" :class="['viewports-box', !isFusion ? 'viewports-box-down' : '', fullScreenIndex !== null ? 'viewports-box-full-screen' : '']"
|
||||||
:class="['viewports-box', !isFusion ? 'viewports-box-down' : '', fullScreenIndex !== null ? 'viewports-box-full-screen' : '']"
|
|
||||||
:style="gridStyle">
|
:style="gridStyle">
|
||||||
<div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-fusion-${index}`"
|
<div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-fusion-${index}`"
|
||||||
:class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
:class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||||
|
|
@ -500,7 +501,7 @@ import {
|
||||||
RenderingEngine,
|
RenderingEngine,
|
||||||
Enums,
|
Enums,
|
||||||
// imageLoader,
|
// imageLoader,
|
||||||
// metaData,
|
metaData,
|
||||||
volumeLoader,
|
volumeLoader,
|
||||||
getRenderingEngine,
|
getRenderingEngine,
|
||||||
eventTarget,
|
eventTarget,
|
||||||
|
|
@ -862,7 +863,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.tools = getTools(this.criterionType)
|
this.tools = getTools(this.criterionType)
|
||||||
}
|
}
|
||||||
console.log(toolsEvents, 'toolsEvents')
|
|
||||||
this.trialId = this.$route.query.trialId
|
this.trialId = this.$route.query.trialId
|
||||||
this.readingTaskState = this.taskInfo.ReadingTaskState
|
this.readingTaskState = this.taskInfo.ReadingTaskState
|
||||||
if (!this.taskInfo.IsBaseLine && this.taskInfo.IsReadingTaskViewInOrder !== 0) {
|
if (!this.taskInfo.IsBaseLine && this.taskInfo.IsReadingTaskViewInOrder !== 0) {
|
||||||
|
|
@ -1332,6 +1333,7 @@ export default {
|
||||||
if (volumeViewportIds.includes(viewportId)) {
|
if (volumeViewportIds.includes(viewportId)) {
|
||||||
toolGroupId = this.volumeToolGroupId
|
toolGroupId = this.volumeToolGroupId
|
||||||
}
|
}
|
||||||
|
|
||||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) ? ToolGroupManager.getToolGroup(toolGroupId) : ToolGroupManager.createToolGroup(toolGroupId)
|
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) ? ToolGroupManager.getToolGroup(toolGroupId) : ToolGroupManager.createToolGroup(toolGroupId)
|
||||||
toolGroup.addViewport(viewportId, renderingEngineId)
|
toolGroup.addViewport(viewportId, renderingEngineId)
|
||||||
toolGroup.addTool(StackScrollTool.toolName, {
|
toolGroup.addTool(StackScrollTool.toolName, {
|
||||||
|
|
@ -1668,6 +1670,7 @@ export default {
|
||||||
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].setAnnotation({ annotation, toolName: annotation.metadata.toolName })
|
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].setAnnotation({ annotation, toolName: annotation.metadata.toolName })
|
||||||
this.markedSeriesIds.push(series.Id)
|
this.markedSeriesIds.push(series.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setToolsPassive()
|
this.setToolsPassive()
|
||||||
},
|
},
|
||||||
annotationModifiedListener(e) {
|
annotationModifiedListener(e) {
|
||||||
|
|
@ -1762,6 +1765,7 @@ export default {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setToolsPassive()
|
this.setToolsPassive()
|
||||||
},
|
},
|
||||||
validMarkName(markName) {
|
validMarkName(markName) {
|
||||||
|
|
@ -3080,7 +3084,7 @@ export default {
|
||||||
}
|
}
|
||||||
if (this.isFusion) {
|
if (this.isFusion) {
|
||||||
this.activeViewportIndex = 0
|
this.activeViewportIndex = 0
|
||||||
if (series.Modality === 'PT') {
|
if (series.Modality === 'PT' || series.Modality === 'NM') {
|
||||||
this.activeViewportIndex = 2
|
this.activeViewportIndex = 2
|
||||||
}
|
}
|
||||||
series = {
|
series = {
|
||||||
|
|
@ -3137,7 +3141,7 @@ export default {
|
||||||
}
|
}
|
||||||
if (this.isFusion) {
|
if (this.isFusion) {
|
||||||
this.activeViewportIndex = 0
|
this.activeViewportIndex = 0
|
||||||
if (series.Modality === 'PT') {
|
if (series.Modality === 'PT' || series.Modality === 'NM') {
|
||||||
this.activeViewportIndex = 2
|
this.activeViewportIndex = 2
|
||||||
}
|
}
|
||||||
series = {
|
series = {
|
||||||
|
|
@ -3502,9 +3506,9 @@ export default {
|
||||||
syncColormap: false
|
syncColormap: false
|
||||||
})
|
})
|
||||||
let viewportIds = [
|
let viewportIds = [
|
||||||
`viewport-MPR-0`,
|
`viewport-volume-0`,
|
||||||
`viewport-MPR-1`,
|
`viewport-volume-1`,
|
||||||
`viewport-MPR-2`
|
`viewport-volume-2`
|
||||||
]
|
]
|
||||||
viewportIds.forEach((viewportId) => {
|
viewportIds.forEach((viewportId) => {
|
||||||
MPRVoiSynchronizer.add({
|
MPRVoiSynchronizer.add({
|
||||||
|
|
@ -3528,7 +3532,8 @@ export default {
|
||||||
synchronizer.setEnabled(false);
|
synchronizer.setEnabled(false);
|
||||||
},
|
},
|
||||||
setColorMap(rgbPresetName) {
|
setColorMap(rgbPresetName) {
|
||||||
const fusionViewportIds = [`viewport-fusion-1`, `viewport-fusion-2`, `viewport-fusion-3`]
|
const fusionViewportIds = [`viewport-fusion-2`]
|
||||||
|
// const fusionViewportIds = [`viewport-fusion-1`, `viewport-fusion-2`, `viewport-fusion-3`]
|
||||||
fusionViewportIds.forEach(id => {
|
fusionViewportIds.forEach(id => {
|
||||||
this.$refs[id][0].setPreset(rgbPresetName)
|
this.$refs[id][0].setPreset(rgbPresetName)
|
||||||
this.$refs[id][0].renderColorBar(rgbPresetName)
|
this.$refs[id][0].renderColorBar(rgbPresetName)
|
||||||
|
|
@ -3590,6 +3595,7 @@ export default {
|
||||||
this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].removeAllNoSaveAnnotation()
|
this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].removeAllNoSaveAnnotation()
|
||||||
}
|
}
|
||||||
const { ct, pt } = data
|
const { ct, pt } = data
|
||||||
|
this.fusionOverlayModality = pt?.Modality || null
|
||||||
if (ct.ImageIds.length > 400) {
|
if (ct.ImageIds.length > 400) {
|
||||||
let res = await this.getSystemInfo()
|
let res = await this.getSystemInfo()
|
||||||
if (!res) return false
|
if (!res) return false
|
||||||
|
|
@ -3638,12 +3644,26 @@ export default {
|
||||||
this.$refs[`viewport-3`][0].setSeriesInfo(pt)
|
this.$refs[`viewport-3`][0].setSeriesInfo(pt)
|
||||||
|
|
||||||
this.$refs[`viewport-fusion-0`][0].setSeriesInfo(ctData)
|
this.$refs[`viewport-fusion-0`][0].setSeriesInfo(ctData)
|
||||||
this.$refs[`viewport-fusion-1`][0].setSeriesInfo(ptData, false, { colorMap: true })
|
this.$refs[`viewport-fusion-1`][0].setSeriesInfo(ptData, false, { colorMap: false })
|
||||||
this.$refs[`viewport-fusion-2`][0].setSeriesInfo(fusionData, false, { isFusion: true, colorMap: true })
|
this.$refs[`viewport-fusion-2`][0].setSeriesInfo(fusionData, false, { isFusion: true, colorMap: true })
|
||||||
this.$refs[`viewport-fusion-3`][0].setSeriesInfo(ptData, false, { isMip: true, colorMap: true })
|
this.$refs[`viewport-fusion-3`][0].setSeriesInfo(ptData, false, { isMip: true, colorMap: false })
|
||||||
// this.resetAnnotation = false
|
// this.resetAnnotation = false
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs[`colorMap`].init()
|
this.$refs[`colorMap`].init()
|
||||||
|
if (this.fusionOverlayModality === 'NM') {
|
||||||
|
const imageIds = this.sortImageIdsByImagePositionPatient(pt.ImageIds)
|
||||||
|
const imageId = imageIds?.[0]
|
||||||
|
const voiLutModule = imageId ? metaData.get('voiLutModule', imageId) : null
|
||||||
|
const rawWidth = Array.isArray(voiLutModule?.windowWidth) ? voiLutModule.windowWidth[0] : voiLutModule?.windowWidth
|
||||||
|
const nmMax = Number(rawWidth)
|
||||||
|
if (Number.isFinite(nmMax) && nmMax > 0) {
|
||||||
|
const halfMax = Math.round(nmMax * 0.5)
|
||||||
|
this.$refs.colorMap.range = Math.round(nmMax)
|
||||||
|
this.$refs.colorMap.upper = halfMax
|
||||||
|
this.$refs.colorMap.upperRangeChange(Math.round(nmMax))
|
||||||
|
this.voiChange(halfMax)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
|
@ -3665,12 +3685,56 @@ export default {
|
||||||
volume = cache.getVolume(volumeId)
|
volume = cache.getVolume(volumeId)
|
||||||
} else {
|
} else {
|
||||||
await this.$refs[`${this.viewportKey}-0`][0].createImageIdsAndCacheMetaData(serie)
|
await this.$refs[`${this.viewportKey}-0`][0].createImageIdsAndCacheMetaData(serie)
|
||||||
volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: serie.ImageIds })
|
let imageIds = this.sortImageIdsByImagePositionPatient(serie.ImageIds)
|
||||||
|
volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: imageIds })
|
||||||
volume.load()
|
volume.load()
|
||||||
}
|
}
|
||||||
res({ volumeId, volume })
|
res({ volumeId, volume })
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
sortImageIdsByImagePositionPatient(imageIds) {
|
||||||
|
if (!Array.isArray(imageIds) || imageIds.length < 2) {
|
||||||
|
return imageIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstPlane = metaData.get('imagePlaneModule', imageIds[0]);
|
||||||
|
if (!firstPlane?.imagePositionPatient || !firstPlane?.imageOrientationPatient) {
|
||||||
|
return imageIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reference = firstPlane.imagePositionPatient.map((v) => Number(v));
|
||||||
|
const iop = firstPlane.imageOrientationPatient.map((v) => Number(v));
|
||||||
|
const row = [iop[0], iop[1], iop[2]];
|
||||||
|
const col = [iop[3], iop[4], iop[5]];
|
||||||
|
const normal = [
|
||||||
|
row[1] * col[2] - row[2] * col[1],
|
||||||
|
row[2] * col[0] - row[0] * col[2],
|
||||||
|
row[0] * col[1] - row[1] * col[0],
|
||||||
|
];
|
||||||
|
|
||||||
|
const pairs = [];
|
||||||
|
for (const imageId of imageIds) {
|
||||||
|
const plane = metaData.get('imagePlaneModule', imageId);
|
||||||
|
const ipp = plane?.imagePositionPatient;
|
||||||
|
if (!ipp) {
|
||||||
|
return imageIds;
|
||||||
|
}
|
||||||
|
const pos = ipp.map((v) => Number(v));
|
||||||
|
const positionVector = [
|
||||||
|
reference[0] - pos[0],
|
||||||
|
reference[1] - pos[1],
|
||||||
|
reference[2] - pos[2],
|
||||||
|
];
|
||||||
|
const distance =
|
||||||
|
positionVector[0] * normal[0] +
|
||||||
|
positionVector[1] * normal[1] +
|
||||||
|
positionVector[2] * normal[2];
|
||||||
|
pairs.push({ imageId, distance });
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs.sort((a, b) => b.distance - a.distance);
|
||||||
|
return pairs.map((p) => p.imageId);
|
||||||
|
},
|
||||||
upperRangeChange(upper) {
|
upperRangeChange(upper) {
|
||||||
this.$refs.colorMap.upper = upper
|
this.$refs.colorMap.upper = upper
|
||||||
this.$refs.colorMap.upperRangeChange(upper)
|
this.$refs.colorMap.upperRangeChange(upper)
|
||||||
|
|
@ -3680,6 +3744,7 @@ export default {
|
||||||
},
|
},
|
||||||
closeFusion() {
|
closeFusion() {
|
||||||
this.fusionVisible = false
|
this.fusionVisible = false
|
||||||
|
this.fusionOverlayModality = null
|
||||||
},
|
},
|
||||||
getTrialCriterion() {
|
getTrialCriterion() {
|
||||||
getCriterionReadingInfo({
|
getCriterionReadingInfo({
|
||||||
|
|
@ -4091,17 +4156,17 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewports-box-down {
|
.viewports-box-down {
|
||||||
>.grid-cell {
|
> .grid-cell {
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewports-box-full-screen {
|
.viewports-box-full-screen {
|
||||||
>.grid-cell {
|
> .grid-cell {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
>.grid-cell.cell-full-screen {
|
> .grid-cell.cell-full-screen {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -498,7 +498,7 @@ export default {
|
||||||
viewport
|
viewport
|
||||||
.setVolumes([{
|
.setVolumes([{
|
||||||
volumeId: this.volumeId, callback: (r) => {
|
volumeId: this.volumeId, callback: (r) => {
|
||||||
if (this.series.Modality === 'PT') {
|
if (this.series.Modality === 'PT' || this.series.Modality === 'NM') {
|
||||||
setPetColorMapTransferFunctionForVolumeActor(r, true)
|
setPetColorMapTransferFunctionForVolumeActor(r, true)
|
||||||
} else {
|
} else {
|
||||||
setCtTransferFunctionForVolumeActor(r)
|
setCtTransferFunctionForVolumeActor(r)
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,11 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-left:-1px;border: 1px solid #424242;">
|
<div v-if="modality !== 'NM'" style="margin-left:-1px;border: 1px solid #424242;">
|
||||||
<el-input v-model="range" size="mini" style="width:120px" maxlength="3"
|
<el-input v-model="range" size="mini" style="width:120px" :maxlength="maxLength"
|
||||||
oninput="if(value){value=value.replace(/[^\d]/g,'')} if(value<=0){value=''}"
|
oninput="if(value){value=value.replace(/[^\d]/g,'')} if(value<=0){value=''}"
|
||||||
@change="upperRangeChange">
|
@change="upperRangeChange">
|
||||||
<template slot="append">g/ml</template>
|
<template slot="append">{{ unit }}</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
<div id="slider" style="position: absolute;left: 6px;top:5px;cursor: pointer;">
|
<div id="slider" style="position: absolute;left: 6px;top:5px;cursor: pointer;">
|
||||||
|
|
@ -54,6 +54,20 @@ import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/C
|
||||||
const { registerColormap, getColormapNames, getColormap } = csUtils.colormap
|
const { registerColormap, getColormapNames, getColormap } = csUtils.colormap
|
||||||
export default {
|
export default {
|
||||||
name: "colorMap",
|
name: "colorMap",
|
||||||
|
props: {
|
||||||
|
unit: {
|
||||||
|
type: String,
|
||||||
|
default: 'g/ml'
|
||||||
|
},
|
||||||
|
modality: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 6
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
colorMaps: [],
|
colorMaps: [],
|
||||||
|
|
@ -111,7 +125,11 @@ export default {
|
||||||
var positionValue = document.getElementById('slider-position')
|
var positionValue = document.getElementById('slider-position')
|
||||||
var upper = this.range
|
var upper = this.range
|
||||||
position = parseInt((position / maxLeft) * upper)
|
position = parseInt((position / maxLeft) * upper)
|
||||||
positionValue.textContent = position
|
if (this.modality === 'NM') {
|
||||||
|
positionValue.textContent = Math.round((position / upper) * 100) + '%'
|
||||||
|
} else {
|
||||||
|
positionValue.textContent = position
|
||||||
|
}
|
||||||
this.upper = position
|
this.upper = position
|
||||||
this.voiChange(position)
|
this.voiChange(position)
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +156,11 @@ export default {
|
||||||
var slider = document.getElementById('slider')
|
var slider = document.getElementById('slider')
|
||||||
slider.style.left = left + 'px'
|
slider.style.left = left + 'px'
|
||||||
var positionValue = document.getElementById('slider-position')
|
var positionValue = document.getElementById('slider-position')
|
||||||
positionValue.textContent = this.upper
|
if (this.modality === 'NM') {
|
||||||
|
positionValue.textContent = Math.round((this.upper / this.range) * 100) + '%'
|
||||||
|
} else {
|
||||||
|
positionValue.textContent = this.upper
|
||||||
|
}
|
||||||
},
|
},
|
||||||
createColorBar(rgbPresetName, elId, width, height) {
|
createColorBar(rgbPresetName, elId, width, height) {
|
||||||
var colorMap = null
|
var colorMap = null
|
||||||
|
|
@ -322,4 +344,4 @@ export default {
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import setPetTransferFunctionForVolumeActor from "./setPetTransferFunctionForVolumeActor";
|
||||||
|
import setMipTransferFunctionForVolumeActor from "./setMipTransferFunctionForVolumeActor";
|
||||||
|
|
||||||
|
|
||||||
|
export {
|
||||||
|
setPetTransferFunctionForVolumeActor,
|
||||||
|
setMipTransferFunctionForVolumeActor
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { cache, metaData, utilities } from "@cornerstonejs/core";
|
||||||
|
import vtkPiecewiseFunction from "@kitware/vtk.js/Common/DataModel/PiecewiseFunction";
|
||||||
|
|
||||||
|
export default function setMipTransferFunctionForVolumeActor({
|
||||||
|
volumeActor,
|
||||||
|
volumeId,
|
||||||
|
}) {
|
||||||
|
const mapper = volumeActor.getMapper?.();
|
||||||
|
if (mapper?.setSampleDistance) {
|
||||||
|
mapper.setSampleDistance(1.0);
|
||||||
|
}
|
||||||
|
const rgbTransferFunction = volumeActor
|
||||||
|
.getProperty()
|
||||||
|
.getRGBTransferFunction(0);
|
||||||
|
let range = null;
|
||||||
|
const imageVolume = volumeId ? cache.getVolume(volumeId) : null;
|
||||||
|
const imageId = imageVolume?.imageIds?.[0];
|
||||||
|
if (imageId) {
|
||||||
|
const voiLutModule = metaData.get("voiLutModule", imageId);
|
||||||
|
const rawCenter = Array.isArray(voiLutModule?.windowCenter)
|
||||||
|
? voiLutModule.windowCenter[0]
|
||||||
|
: voiLutModule?.windowCenter;
|
||||||
|
const rawWidth = Array.isArray(voiLutModule?.windowWidth)
|
||||||
|
? voiLutModule.windowWidth[0]
|
||||||
|
: voiLutModule?.windowWidth;
|
||||||
|
const center = Number(rawCenter);
|
||||||
|
const width = Number(rawWidth);
|
||||||
|
if (Number.isFinite(center) && Number.isFinite(width) && width > 0) {
|
||||||
|
const upper = center + width / 2;
|
||||||
|
range = [0, upper];
|
||||||
|
} else if (Number.isFinite(center)) {
|
||||||
|
range = [0, center];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!range) {
|
||||||
|
range = [0, 5];
|
||||||
|
}
|
||||||
|
|
||||||
|
rgbTransferFunction.setRange(range[0], range[1]);
|
||||||
|
utilities.invertRgbTransferFunction(rgbTransferFunction);
|
||||||
|
const upper = Number(range[1]);
|
||||||
|
if (Number.isFinite(upper) && upper > 0) {
|
||||||
|
const ofun = vtkPiecewiseFunction.newInstance();
|
||||||
|
ofun.addPoint(0, 0.0);
|
||||||
|
ofun.addPoint(upper * 0.02, 0.9);
|
||||||
|
ofun.addPoint(upper, 1.0);
|
||||||
|
volumeActor.getProperty().setScalarOpacity(0, ofun);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { utilities } from '@cornerstonejs/core';
|
||||||
|
|
||||||
|
export default function setPetTransferFunction({ volumeActor }) {
|
||||||
|
const rgbTransferFunction = volumeActor
|
||||||
|
.getProperty()
|
||||||
|
.getRGBTransferFunction(0);
|
||||||
|
|
||||||
|
rgbTransferFunction.setRange(0, 5);
|
||||||
|
|
||||||
|
utilities.invertRgbTransferFunction(rgbTransferFunction);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue