suv融合
continuous-integration/drone/push Build is passing Details

uat
wangxiaoshuang 2025-04-29 17:51:54 +08:00
parent e80d439377
commit 3551a521c8
2 changed files with 246 additions and 37 deletions

View File

@ -754,7 +754,7 @@ export default {
},
voiChange(v) {
this.cellType.forEach((item, index) => {
if (item === 'FUSION') {
if (['FUSION', 'MIP', 'pt', 'PT'].includes(item)) {
this.$refs[`viewport-${index}`][0].voiChange(v)
}
})
@ -923,7 +923,7 @@ export default {
this.$refs[`viewport-${2}`][0].setSeriesInfo({ ct: this.ctSeriesInfo, pet: this.petSeriesInfo }, false, { isFusion: true })
this.$refs[`viewport-${2}`][0].setPreset(this.rgbPresetName)
this.$refs[`viewport-${3}`][0].setSeriesInfo(this.petSeriesInfo, false, { isMip: true })
this.cellType = ['CT', 'PET', 'FUSION', 'MIP']
this.cellType = ['CT', 'PT', 'FUSION', 'MIP']
},
handleStudyIdChange(v) {
var i = this.studyList.findIndex(i => i.StudyId === v)
@ -972,6 +972,7 @@ export default {
cache.setMaxCacheSize(6 * 1024 * 1024 * 1024);
let renderingEngine = getRenderingEngine(renderingEngineId)
if (!renderingEngine) {
console.log(11111111111111)
renderingEngine = new RenderingEngine(renderingEngineId)
}
const element1 = this.$refs['viewport-0'][0].$el
@ -1995,6 +1996,7 @@ export default {
},
setMoreToolActive(toolName) {
if (this.readingTaskState === 2) return
this.setToolsPassive()
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
const toolGroupId = `viewport-${this.activeViewportIndex}`
@ -2744,6 +2746,50 @@ export default {
}
</script>
<style lang="scss" scoped>
.activeLi {
background-color: #919295;
}
.series-table {
::v-deep.el-table {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
::v-deep.el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #dfdfdf;
}
.el-table--border::after,
.el-table--group::after,
.el-table::before {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
}
::v-deep.el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
tr:hover>td {
background-color: #1e1e1e !important;
}
}
::v-deep.el-table__empty-block {
background-color: #1e1e1e !important;
}
}
.tool-wrapper {
display: flex;
flex-direction: column;

View File

@ -2,18 +2,19 @@
<div ref="viewport" class="viewport-wrapper" @mouseup="sliderMouseup" @mousemove="sliderMousemove"
@mouseleave="sliderMouseleave">
<div v-if="series && taskInfo" class="left-top-text">
<div v-if="taskInfo.IsExistsClinicalData" class="cd-info" :title="$t('trials:reading:button:clinicalData')">
<div v-if="taskInfo.IsExistsClinicalData && !isMip && !isFusion" class="cd-info"
:title="$t('trials:reading:button:clinicalData')">
<svg-icon style="cursor: pointer;" icon-class="documentation" class="svg-icon"
@click.stop="viewCD(series.TaskInfo.VisitTaskId)" />
</div>
<h2 v-if="taskInfo.IsReadingShowSubjectInfo && series.TaskInfo" class="subject-info">
{{ `${series.TaskInfo.SubjectCode} ${series.TaskInfo.TaskBlindName} ` }}
</h2>
<div>Series: #{{ series.SeriesNumber }}</div>
<div v-if="series.Stack">Image: #{{ `${series.SliceIndex + 1}/${series.Stack.length}` }}</div>
<div>{{ series.Modality }}</div>
<div v-if="!isMip && !isFusion">Series: #{{ series.SeriesNumber }}</div>
<div v-if="series.Stack && !isMip">Image: #{{ `${series.SliceIndex + 1}/${series.Stack.length}` }}</div>
<div v-if="!isMip && !isFusion">{{ series.Modality }}</div>
</div>
<div v-if="series && taskInfo && taskInfo.IsReadingTaskViewInOrder === 1 && series.TaskInfo"
<div v-if="series && taskInfo && taskInfo.IsReadingTaskViewInOrder === 1 && series.TaskInfo && !isMip && !isFusion"
class="top-center-tool">
<div class="toggle-visit-container">
<div class="arrw_icon"
@ -33,7 +34,7 @@
</div>
</div>
</div>
<div v-if="series" class="right-top-text">
<div v-if="series && !isMip && !isFusion" class="right-top-text">
<div>{{ series.Description }}</div>
</div>
<div v-if="series" class="left-bottom-text">
@ -41,44 +42,50 @@
Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}, {{ mousePosition.index[2] }}
</div>
<div
v-if="(series.Modality === 'CT' || series.Modality === 'DR' || series.Modality === 'CR') && mousePosition.value">
v-if="(series.Modality === 'CT' || series.Modality === 'DR' || series.Modality === 'CR') && mousePosition.value && !isMip && !isFusion">
HU: {{ mousePosition.value }}
</div>
<div v-else-if="(series.Modality === 'PT' && mousePosition.value)">
<div v-else-if="(series.Modality === 'PT' && mousePosition.value) && !isMip && !isFusion">
SUVbw(g/ml): {{ digitPlaces === -1 ? mousePosition.value.toFixed(3) : mousePosition.value.toFixed(digitPlaces)
}}
</div>
<div v-else-if="mousePosition.value">
<div v-else-if="mousePosition.value && !isMip && !isFusion">
Density: {{ mousePosition.value }}
</div>
<div v-show="imageInfo.size">
<div v-show="imageInfo.size && !isMip && !isFusion">
W*H: {{ imageInfo.size }}
</div>
</div>
<div v-if="series" class="right-bottom-text">
<div v-show="imageInfo.location">Location: {{ `${Number(imageInfo.location).toFixed(digitPlaces)} mm` }}</div>
<div v-show="series.SliceThickness">Slice Thickness: {{ `${Number(series.SliceThickness).toFixed(digitPlaces)} mm`
<div v-if="series && !isMip" class="right-bottom-text">
<div v-show="imageInfo.location && !isFusion">Location: {{ `${Number(imageInfo.location).toFixed(digitPlaces)} mm`
}}</div>
<div v-show="series.SliceThickness && !isFusion">Slice Thickness: {{
`${Number(series.SliceThickness).toFixed(digitPlaces)} mm`
}}</div>
<div v-show="imageInfo.wwwc">WW/WL: {{ imageInfo.wwwc }}</div>
</div>
<div class="orientation-top">
<div class="orientation-top" v-if="!isMip">
{{ markers.top }}
</div>
<div class="orientation-right">
<div class="orientation-right" v-if="!isMip">
{{ markers.right }}
</div>
<div class="orientation-bottom">
<div class="orientation-bottom" v-if="!isMip">
{{ markers.bottom }}
</div>
<div class="orientation-left">
<div class="orientation-left" v-if="!isMip">
{{ markers.left }}
</div>
<div v-if="isFusion" class="color_bar">
<canvas :id="`colorBar_Canvas_${viewportId}`" />
<!-- <div id="colorBar_Canvas" /> -->
</div>
<div ref="sliderBox" class="right-slider-box" @click.stop="clickSlider($event)">
<div v-if="isMip" id="rotateBar" ref="rotateBar" class="rotate_slider_box" @click.stop="clickRotate($event)">
<div id="rotateSlider" :style="{ left: rotateBarLeft + 'px' }" class="box" @click.stop.prevent="() => { return }"
@mousedown.stop="rotateBarMousedown($event)" />
</div>
<div ref="sliderBox" class="right-slider-box" @click.stop="clickSlider($event)" v-show="!isMip">
<div :style="{ top: sliderInfo.height + '%' }" class="slider" @click.stop.prevent="() => { return }"
@mousedown.stop="sliderMousedown($event)" />
</div>
@ -94,12 +101,16 @@ import {
getRenderingEngine,
volumeLoader,
// eventTarget,
utilities as csUtils
utilities as csUtils,
} from '@cornerstonejs/core'
import * as cornerstoneTools from '@cornerstonejs/tools'
import { utilities as cstUtils } from '@cornerstonejs/tools';
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
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 setPetTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetTransferFunctionForVolumeActor'
import { setPetColorMapTransferFunctionForVolumeActor } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetColorMapTransferFunctionForVolumeActor'
const {
ToolGroupManager,
MIPJumpToClickTool,
@ -111,7 +122,7 @@ const { MouseBindings } = csToolsEnums
const { getColormap, getColormapNames } = csUtils.colormap;
const { ViewportColorbar } = cstUtils.voi.colorbar;
const { ColorbarRangeTextPosition } = cstUtils.voi.colorbar.Enums;
import { vec3 } from 'gl-matrix'
import { vec3, mat4 } from 'gl-matrix'
export default {
name: 'ImageViewport',
props: {
@ -165,7 +176,14 @@ export default {
presetName: '',
volumeId: null,
defaultWindowLevel: {},
Colorbar: null
Colorbar: null,
rotateAngle: 0,
rotateBarLeft: 0,
rotateBarInfo: {
initX: null,
initLeft: null,
isMove: false
},
}
},
mounted() {
@ -191,8 +209,22 @@ export default {
this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)
this.element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.cornerstoneToolsMouseMove)
this.element.addEventListener(cornerstoneTools.Enums.Events.MOUSE_WHEEL, this.handletoolsMouseWheel)
this.element.addEventListener('mouseleave', () => {
this.mousePosition.index = []
this.mousePosition.value = null
})
document.addEventListener('mouseup', () => {
this.sliderMouseup()
if (this.isMip) {
this.rotateBarMouseup()
}
})
document.addEventListener('mousemove', (e) => {
this.sliderMousemove(e)
if (this.isMip) {
this.rotateBarMousemove(e)
}
})
// console.log(cornerstoneTools)
// element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage)
@ -428,6 +460,12 @@ export default {
}
}
},
async createImageIdsAndCacheMetaData(obj) {
await createImageIdsAndCacheMetaData({
modality: obj.Modality,
imageIds: obj.ImageIds
})
},
async setSeriesInfo(obj, isLocate = false, option = {}) {
try {
if (this.series && obj.Id === this.series.Id && obj.Description === this.series.Description && !isLocate) {
@ -440,6 +478,7 @@ export default {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
viewport.setOrientation(OrientationAxis.AXIAL)
viewport.setProperties({ background: [0, 0, 0] })
const toolGroup = ToolGroupManager.getToolGroup(this.viewportId)
toolGroup.setToolEnabled(MIPJumpToClickTool.toolName)
toolGroup.setToolEnabled(VolumeRotateTool.toolName)
@ -448,31 +487,40 @@ export default {
this.renderColorBar(this.presetName)
})
let { ct, pet } = obj
this.series = { ...ct }
this.series = { ...pet }
this.ctSeries = { ...ct }
this.petSeries = { ...pet }
await this.prefetchMetadataInformation(ct.ImageIds)
await this.prefetchMetadataInformation(pet.ImageIds)
const maxCount = Math.max(this.ctSeries.ImageIds.length, this.petSeries.ImageIds.length)
this.$set(this.ctSeries, 'imageMaxLength', maxCount)
this.$set(this.petSeries, 'imageMaxLength', maxCount)
await this.createImageIdsAndCacheMetaData(this.ctSeries)
await this.createImageIdsAndCacheMetaData(this.petSeries)
let ctVolumeId =
'fusion_ct' + ':' + csUtils.uuidv4()
let ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, { imageIds: ct.ImageIds })
let ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, { imageIds: this.ctSeries.ImageIds })
this.volumeId =
'fusion_ct' + ':' + csUtils.uuidv4()
let petVolume = await volumeLoader.createAndCacheVolume(this.volumeId, { imageIds: pet.ImageIds })
let petVolume = await volumeLoader.createAndCacheVolume(this.volumeId, { imageIds: this.petSeries.ImageIds })
ctVolume.load();
petVolume.load();
viewport
.setVolumes([{ volumeId: ctVolumeId, callback: () => { console.log("融合ct渲染成功") } }, {
volumeId: this.volumeId, callback: () => {
await viewport
.setVolumes([{
volumeId: ctVolumeId, callback: (r) => {
setCtTransferFunctionForVolumeActor(r)
console.log("融合ct渲染成功")
}
}, {
volumeId: this.volumeId, callback: (r) => {
setPetColorMapTransferFunctionForVolumeActor(r)
console.log("融合pet渲染成功");
}
}]).then(() => {
this.setColorMap(this.presetName)
})
return renderingEngine.render()
} else {
this.series = { ...obj }
await this.prefetchMetadataInformation(obj.ImageIds)
await this.createImageIdsAndCacheMetaData(obj)
this.volumeId =
'volume' + ':' + csUtils.uuidv4()
let volume = await volumeLoader.createAndCacheVolume(this.volumeId, { imageIds: obj.ImageIds })
@ -480,6 +528,7 @@ export default {
volume.load();
if (this.isMip) {
viewport.setOrientation(OrientationAxis.CORONAL);
viewport.setProperties({ background: [0, 0, 0] })
toolGroup.setToolActive(VolumeRotateTool.toolName, {
bindings: [
{
@ -503,7 +552,9 @@ export default {
viewport
.setVolumes([{
volumeId: this.volumeId,
callback: () => { console.log("mip渲染成功") },
callback: (r) => {
console.log("mip渲染成功")
},
slabThickness,
blendMode: BlendModes.MAXIMUM_INTENSITY_BLEND,
defaultOptions: {
@ -512,11 +563,14 @@ export default {
}])
} else {
viewport
.setVolumes([{ volumeId: this.volumeId, callback: () => { console.log("渲染成功") } }])
.setVolumes([{
volumeId: this.volumeId, callback: (r) => {
console.log("渲染成功")
}
}])
}
}
viewport.render()
if (!this.series.cellType) {
if (this.isFusion) return this.series.cellType = 'FUSION'
@ -605,6 +659,94 @@ export default {
if (!this.sliderInfo.isMove) return
this.sliderInfo.isMove = false
},
handletoolsMouseWheel(e) {
const { viewportId, wheel } = e.detail
if (this.series.cellType === 'MIP') {
const container = document.getElementById('rotateBar')
const slider = document.getElementById('rotateSlider')
const containerWidth = container.offsetWidth
const sliderWidth = slider.offsetWidth
const maxX = containerWidth - sliderWidth
const { direction } = wheel
var x = Math.trunc(30 * ((containerWidth - sliderWidth) / 360))
if (direction > 0 && (this.rotateBarLeft + x) > maxX) {
this.rotateBarLeft = x - (containerWidth - sliderWidth - this.rotateBarLeft)
} else if (direction < 0 && (this.rotateBarLeft < x)) {
this.rotateBarLeft = containerWidth - (x - this.rotateBarLeft + sliderWidth)
} else {
this.rotateBarLeft = x * direction + this.rotateBarLeft
}
}
},
rotateBarMouseup(e) {
this.rotateBarInfo.isMove = false
},
rotateBarMousemove(e) {
//
if (!this.rotateBarInfo.isMove) return
const container = document.getElementById('rotateBar')
const slider = document.getElementById('rotateSlider')
const containerWidth = container.offsetWidth
const sliderWidth = slider.offsetWidth
let x = Math.trunc(e.clientX - this.rotateBarInfo.initX + this.rotateBarInfo.initLeft)
if (x < 0) x = 0
if (x > containerWidth - sliderWidth) x = containerWidth - sliderWidth
const deltaX = x - this.rotateBarLeft
const angle = Math.sin((deltaX * (360 / (containerWidth - sliderWidth))) * Math.PI / 180)
this.rotate(angle)
this.rotateBarLeft = x
},
rotateBarMousedown(e) {
this.rotateBarInfo.initLeft = e.srcElement.offsetLeft
this.rotateBarInfo.initX = e.clientX
this.rotateBarInfo.isMove = true
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
},
rotate(angle) {
let renderingEngine = getRenderingEngine(this.renderingEngineId)
let viewport = renderingEngine.getViewport(this.viewportId)
const camera = viewport.getCamera()
const { viewUp, position, focalPoint } = camera
const [cx, cy, cz] = focalPoint
const [ax, ay, az] = [0, 0, 1]
const newPosition = [0, 0, 0]
const newFocalPoint = [0, 0, 0]
const newViewUp = [0, 0, 0]
const transform = mat4.identity(new Float32Array(16))
mat4.translate(transform, transform, [cx, cy, cz])
mat4.rotate(transform, transform, angle, [ax, ay, az])
mat4.translate(transform, transform, [-cx, -cy, -cz])
vec3.transformMat4(newPosition, position, transform)
vec3.transformMat4(newFocalPoint, focalPoint, transform)
mat4.identity(transform)
mat4.rotate(transform, transform, angle, [ax, ay, az])
vec3.transformMat4(newViewUp, viewUp, transform)
viewport.setCamera({
position: newPosition,
viewUp: newViewUp,
focalPoint: newFocalPoint
})
viewport.render()
},
clickRotate(e) {
// console.log('clickRotate')
const container = document.getElementById('rotateBar')
const containerWidth = container.offsetWidth
const slider = document.getElementById('rotateSlider')
const sliderWidth = slider.offsetWidth
const x = Math.trunc(e.offsetX)
const deltaX = x - this.rotateBarLeft
const angle = Math.sin((deltaX * (360 / (containerWidth - sliderWidth))) * Math.PI / 180)
this.rotate(angle)
this.rotateBarLeft = x
},
preventDefault(e) {
e.stopImmediatePropagation()
e.stopPropagation()
@ -766,7 +908,6 @@ export default {
}
.color_bar {
position: absolute;
transform: rotate(-90deg);
transform-origin: right;
@ -774,4 +915,26 @@ export default {
top: 30%;
z-index: 1;
}
.rotate_slider_box {
position: absolute;
width: 380px;
height: 10px;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
background: #fff;
z-index: 10;
cursor: pointer;
.box {
z-index: 10;
background: #9e9e9e;
height: 100%;
width: 20px;
position: absolute;
top: 0;
cursor: move
}
}
</style>