diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/PetCtViewport.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/PetCtViewport.vue index 94d69fa0..a2262c30 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/PetCtViewport.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/PetCtViewport.vue @@ -2,9 +2,9 @@
-
-
{{ Math.round(nmOpacity * 100) }}%
- +
{{ Math.round(fusionOpacity * 100) }}%
+
@@ -22,29 +22,13 @@
{{ ctSeries.Modality }} / {{ series.Modality }}
MIP
-
{{ series.Description }}
+
+ {{ fusionCtOnTop ? `${ctSeries.Modality}/${series.Modality}` : `${series.Modality}/${ctSeries.Modality}` }} +
Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}, {{ mousePosition.index[2] }} @@ -111,7 +95,7 @@ import { import * as cornerstoneTools from '@cornerstonejs/tools' import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'; 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 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' @@ -146,6 +130,7 @@ export default { petSeries: {}, isFusion: false, isMip: false, + fusionCtOnTop: false, taskInfo: null, sliderInfo: { oldB: null, @@ -185,9 +170,10 @@ export default { }, ptVolumeId: null, loading: false, -Colorbar: null, - nmOpacity: 0.6, - nmFusionVolumeActor: null + Colorbar: null, + fusionOpacity: 0.5, + topFusionVolumeActor: null, + currentVoiUpper: null } }, mounted() { @@ -288,6 +274,14 @@ Colorbar: null, } if (properties && properties.voiRange) { var { lower, upper } = properties.voiRange + if ((!upper || upper === 0) && this.currentVoiUpper > 0) { + upper = this.currentVoiUpper + } else if (upper) { + this.currentVoiUpper = upper + } + + if (!upper) return + const { windowWidth, windowCenter } = csUtils.windowLevel.toWindowLevel( lower, upper @@ -296,6 +290,9 @@ Colorbar: null, if (this.series.Modality === 'PT' || this.series.Modality === 'NM' || this.isFusion) { this.$emit('upperRangeChange', Math.round(upper)) } + if (this.isFusion && !this.fusionCtOnTop && this.topFusionVolumeActor) { + this.applyFusionOpacity() + } } }, getOrientationMarker() { @@ -416,6 +413,11 @@ Colorbar: null, voiChange(v) { const renderingEngine = getRenderingEngine(this.renderingEngineId) const voiRange = { lower: 0, upper: v } + this.currentVoiUpper = v + if (this.isFusion) { + console.log('voiChange', v) + } + const viewport = renderingEngine.getViewport(this.viewportId) if (!viewport) return let volumeId = this.isFusion ? this.ptVolumeId : this.volumeId @@ -425,6 +427,11 @@ Colorbar: null, ) viewport.setProperties({ voiRange }, volumeId) + + // if (this.isFusion && !this.fusionCtOnTop && this.topFusionVolumeActor) { + // this.applyFusionOpacity() + // } + viewportsContainingVolumeUID.forEach((vp) => { vp.render() // this.$refs[vp.id].voiModified() @@ -464,17 +471,21 @@ Colorbar: null, ctx.fillStyle = gradient 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); + opacityChange(opacity) { + this.fusionOpacity = opacity const renderingEngine = getRenderingEngine(this.renderingEngineId) - renderingEngine?.render?.(); + const viewport = renderingEngine.getViewport(this.viewportId) + if (!viewport) return + let volumeId = this.isFusion ? this.ptVolumeId : this.volumeId + + viewport.setProperties( + { colormap: { opacity: Number(opacity) } }, + volumeId + ) + viewport.render() + }, + applyFusionOpacity() { + this.opacityChange(this.fusionOpacity) }, setPreset(presetName) { this.presetName = presetName @@ -507,6 +518,85 @@ Colorbar: null, }) this.loading = false }, + getFusionVolumes() { + const ctVolumeId = this.ctSeries?.SeriesInstanceUid + const ptFusionVolumeId = this.ptVolumeId + if (!ctVolumeId || !ptFusionVolumeId) { + return [] + } + + const ctEntry = { + volumeId: ctVolumeId, + callback: (r) => { + setCtTransferFunctionForVolumeActor({ ...r, volumeId: ctVolumeId }) + if (this.fusionCtOnTop) { + this.topFusionVolumeActor = r.volumeActor + this.applyFusionOpacity() + } + console.log("融合ct渲染成功") + } + } + + const ptFusionEntry = { + volumeId: ptFusionVolumeId, + callback: (r) => { + setPetColorMapTransferFunctionForVolumeActor({ ...r, volumeId: ptFusionVolumeId }) + if (!this.fusionCtOnTop) { + this.topFusionVolumeActor = r.volumeActor + this.applyFusionOpacity() + } + console.log("融合pet渲染成功") + } + } + + const volumes = [] + if (this.series.Modality !== 'NM') { + volumes.push({ + volumeId: this.volumeId, + callback: (r) => { + console.log("融合pet渲染成功"); + } + }) + } + + if (this.fusionCtOnTop) { + volumes.push(ptFusionEntry, ctEntry) + } else { + volumes.push(ctEntry, ptFusionEntry) + } + + return volumes + }, + async applyFusionRenderOrder() { + if (!this.isFusion) return + const renderingEngine = getRenderingEngine(this.renderingEngineId) + const viewport = renderingEngine?.getViewport?.(this.viewportId) + if (!viewport) return + + const volumes = this.getFusionVolumes() + if (!volumes.length) return + + const camera = viewport.getCamera?.() + + const savedVoiUpper = this.currentVoiUpper + + await viewport.setVolumes(volumes) + if (camera) { + viewport.setCamera(camera) + } + + if (savedVoiUpper) { + viewport.setProperties({ voiRange: { lower: 0, upper: savedVoiUpper } }, this.ptVolumeId) + } + + viewport.render() + }, + toggleFusionRenderOrder() { + if (!this.isFusion) return + this.fusionCtOnTop = !this.fusionCtOnTop + this.applyFusionRenderOrder() + this.$emit('upperRangeChange', Math.round(this.currentVoiUpper)) + }, async setSeriesInfo(obj, isLocate = false, option = {}) { try { let { data } = obj @@ -519,11 +609,12 @@ Colorbar: null, this.volumeId = data.SeriesInstanceUid this.ptVolumeId = null this.series = {} - this.nmFusionVolumeActor = null + this.topFusionVolumeActor = null let { isFusion, isMip, colorMap } = option this.isFusion = isFusion; this.isMip = isMip; if (this.isFusion) { + this.fusionCtOnTop = false this.$nextTick(() => { this.renderColorBar(this.presetName) }) @@ -531,34 +622,10 @@ Colorbar: null, let { ct, data } = obj this.series = { ...data } this.ctSeries = { ...ct } - let volumes = [ - { - volumeId: ct.SeriesInstanceUid, callback: (r) => { - setCtTransferFunctionForVolumeActor(r) - console.log("融合ct渲染成功") - } - }, - { - volumeId: this.ptVolumeId, callback: (r) => { - setPetColorMapTransferFunctionForVolumeActor(r) - if (this.series.Modality === 'NM') { - this.nmFusionVolumeActor = r.volumeActor - this.applyNmOpacity() - } - console.log("融合pet渲染成功") - } - }, - ] - if (this.series.Modality !== 'NM') { - volumes.unshift({ - volumeId: this.volumeId, callback: (r) => { - // setPetColorMapTransferFunctionForVolumeActor(r) - console.log("融合pet渲染成功"); - } - }) - } - await viewport.setVolumes(volumes) + + + await viewport.setVolumes(this.getFusionVolumes()) } else { this.series = { ...data } if (this.isMip) { @@ -593,9 +660,9 @@ Colorbar: null, volumeId: this.volumeId, callback: (r) => { if (this.series.Modality === 'PT' || this.series.Modality === 'NM') { // setPetColorMapTransferFunctionForVolumeActor(r, true) - setPetTransferFunctionForVolumeActor(r) + setPetTransferFunctionForVolumeActor({ ...r, volumeId: this.volumeId }) } else { - setCtTransferFunctionForVolumeActor(r) + setCtTransferFunctionForVolumeActor({ ...r, volumeId: this.volumeId }) } } }]) @@ -603,6 +670,7 @@ Colorbar: null, } viewport.render() + this.voiChange(this.currentVoiUpper) } catch (e) { console.log(e) } @@ -782,7 +850,7 @@ Colorbar: null, }, beforeDestroy() { this.series = null - this.nmFusionVolumeActor = null + this.topFusionVolumeActor = null }, computed: { NSTip() { @@ -892,6 +960,20 @@ Colorbar: null, font-size: 12px; } + .fusion-order-toggle { + position: absolute; + right: 5px; + top: 5px; + z-index: 2; + font-size: 12px; + padding: 2px 8px; + background: rgba(0, 0, 0, 0.35); + border: 1px solid rgba(255, 255, 255, 0.25); + border-radius: 2px; + cursor: pointer; + user-select: none; + } + .left-bottom-text { position: absolute; left: 5px; diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue index 4ff9f2cd..005c2349 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue @@ -273,8 +273,12 @@
diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/colorMap.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/colorMap.vue index 703d6b50..ee58b425 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/colorMap.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/colorMap.vue @@ -34,7 +34,7 @@ - +
@@ -55,10 +55,6 @@ const { registerColormap, getColormapNames, getColormap } = csUtils.colormap export default { name: "colorMap", props: { - unit: { - type: String, - default: 'g/ml' - }, modality: { type: String, default: '' @@ -163,6 +159,7 @@ export default { position = parseInt((position / maxLeft) * upper) if (position > upper) position = upper if (position < 0) position = 0 + if (this.modality === 'NM') { positionValue.textContent = upper > 0 ? Math.round((position / upper) * 100) + '%' : '0%' } else {