mpr阅片
continuous-integration/drone/push Build is passing Details

main
wangxiaoshuang 2025-12-03 16:52:52 +08:00
parent 1b6f48a5a5
commit ae536a7fc4
4 changed files with 107 additions and 55 deletions

View File

@ -161,7 +161,7 @@
<svg-icon v-else icon-class="fitToImage" class="svg-icon" />
</div>
<!-- MPR -->
<div class="tool-item" :title="`${$t('trials:reading:button:mpr')}`" @click.prevent="openMPRViewport"
<div class="tool-item" :title="`${$t('trials:reading:button:mpr')}`" @click.prevent="openMPRViewport()"
v-if="criterionType === 0 && readingTool === 0">
<svg-icon icon-class="mpr" class="svg-icon" />
</div>
@ -308,7 +308,7 @@
<div v-if="criterionType === 0 && readingTool === 0"
:class="['viewports-box', !isMPR ? 'viewports-box-down' : '']" :style="gridStyle">
<div v-for="(v, index) in 3" :key="`viewport-volume-${index}`" :style="cellStyle"
:class="['grid-cell', isMPR && index === 2 ? 'grid-cell-3' : '', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
: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)">
<VolumeViewport :ref="`viewport-volume-${index}`" :data-viewport-uid="`viewport-volume-${index}`"
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-volume-${index}`"
@ -672,8 +672,8 @@ export default {
gridStyle() {
return {
display: 'grid',
gridTemplateRows: `repeat(${this.rows}, 1fr)`,
gridTemplateColumns: `repeat(${this.cols}, 1fr)`,
gridTemplateRows: `repeat(${this.isMPR ? 2 : this.rows}, 1fr)`,
gridTemplateColumns: `repeat(${this.isMPR ? 2 : this.cols}, 1fr)`,
height: '100%',
width: '100%'
}
@ -1510,6 +1510,7 @@ export default {
annotation.visitTaskId = series.TaskInfo.VisitTaskId
annotation.studyId = series.StudyId
annotation.seriesId = series.Id
annotation.from = this.isMPR ? 'MPR' : this.isFusion ? "Fusion" : ''
annotation.instanceId = params.instanceId
annotation.sliceThickness = series.SliceThickness
annotation.numberOfFrames = isNaN(parseInt(params.frame)) ? null : parseInt(params.frame)
@ -2683,7 +2684,7 @@ export default {
}
return obj
},
getMarkedSeries(studyList, annotation) {
getMarkedSeries(studyList, annotation, checkFrom = false) {
let obj = {}
const sIdx = studyList.findIndex(s => s.StudyId === annotation.studyId)
if (sIdx > -1) {
@ -2709,18 +2710,37 @@ export default {
obj.IsMarked = true
obj.MeasureData = annotation
}
if (annotation.from === 'MPR' && checkFrom) {
obj = Object.assign({}, seriesList[seriesIdx])
obj.SliceIndex = annotation?.metadata?.sliceIndex
obj.IsMarked = true
obj.MeasureData = annotation
}
}
}
return obj
},
viewCustomAnnotationSeries(obj) {
async viewCustomAnnotationSeries(obj) {
const i = this.visitTaskList.findIndex(i => i.VisitTaskId === obj.visitTaskId)
if (i === -1) return
const studyList = this.visitTaskList[i].StudyList
const series = this.getMarkedSeries(studyList, obj.annotation)
const series = this.getMarkedSeries(studyList, obj.annotation, true)
if (series) {
this.$refs[`${this.viewportKey}-${this.cells.length - 1}`][0].setSeriesInfo(series, true)
this.activeViewportIndex = i
this.activeViewportIndex = this.cells.length - 1
if (obj.annotation.from === 'MPR') {
let res = await this.openMPRViewport(series)
let viewPlaneNormal = obj.annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')
if (viewPlaneNormal === '0,0,-1') {
this.activeViewportIndex = 0
} else if (viewPlaneNormal === '0,-1,0') {
this.activeViewportIndex = 1
} else if (viewPlaneNormal === '1,0,0') {
this.activeViewportIndex = 2
}
if (!res) return false
}
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, true)
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
}
},
@ -2728,17 +2748,29 @@ export default {
const i = this.visitTaskList.findIndex(i => i.VisitTaskId === obj.visitTaskId)
if (i === -1) return
const studyList = this.visitTaskList[i].StudyList
const series = this.getMarkedSeries(studyList, obj.annotation)
const series = this.getMarkedSeries(studyList, obj.annotation, true)
if (series) {
this.$refs[`${this.viewportKey}-${this.cells.length - 1}`][0].setSeriesInfo(series, true)
this.activeViewportIndex = i
this.activeViewportIndex = this.cells.length - 1
if (obj.annotation.from === 'MPR') {
let viewPlaneNormal = obj.annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')
if (viewPlaneNormal === '0,0,-1') {
this.activeViewportIndex = 0
} else if (viewPlaneNormal === '0,-1,0') {
this.activeViewportIndex = 1
} else if (viewPlaneNormal === '1,0,0') {
this.activeViewportIndex = 2
}
}
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, true)
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
const divForDownloadViewport = document.querySelector(
`div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
)
const canvas = await html2canvas(divForDownloadViewport)
const base64Str = canvas.toDataURL('image/png', 1)
callback(base64Str)
setTimeout(async () => {
const divForDownloadViewport = document.querySelector(
`div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
)
const canvas = await html2canvas(divForDownloadViewport)
const base64Str = canvas.toDataURL('image/png', 1)
callback(base64Str)
}, 500)
}
},
async getScreenshots(measureData, callback) {
@ -3090,24 +3122,29 @@ export default {
this.$refs[id][0].voiChange(v)
})
},
async openMPRViewport() {
this.isMPR = true
this.rows = 2
this.cols = 2
this.loading = true
this.loadingText = this.$t('trials:lugano:message:loadVolumes')
const series = this.$refs[`viewport-${this.activeViewportIndex}`][0].series
this.activeViewportIndex = 0
this.$refs[`viewport-0`][0].setSeriesInfo(series)
this.$refs[`viewport-1`][0].setSeriesInfo(series)
this.$refs[`viewport-2`][0].setSeriesInfo(series)
this.$refs[`viewport-3`][0].setSeriesInfo(series)
await this.getVolume(series)
this.loading = false
this.loadingText = null
this.$refs[`viewport-volume-0`][0].setSeriesInfo(Object.assign({ orientation: 'AXIAL' }, series))
this.$refs[`viewport-volume-1`][0].setSeriesInfo(Object.assign({ orientation: 'CORONAL' }, series))
this.$refs[`viewport-volume-2`][0].setSeriesInfo(Object.assign({ orientation: 'SAGITTAL' }, series))
async openMPRViewport(data = null) {
return new Promise(async (resolve, reject) => {
if (this.isMPR) return resolve(true)
this.isMPR = true
this.rows = 3
this.cols = 1
this.loading = true
this.loadingText = this.$t('trials:lugano:message:loadVolumes')
const series = data ? data : this.$refs[`viewport-${this.activeViewportIndex}`][0].series
this.activeViewportIndex = 0
// this.$refs[`viewport-0`][0].setSeriesInfo(series)
// this.$refs[`viewport-1`][0].setSeriesInfo(series)
// this.$refs[`viewport-2`][0].setSeriesInfo(series)
// this.$refs[`viewport-3`][0].setSeriesInfo(series)
await this.getVolume(series)
this.loading = false
this.loadingText = null
this.$refs[`viewport-volume-0`][0].setSeriesInfo(Object.assign({ orientation: 'AXIAL' }, series))
this.$refs[`viewport-volume-1`][0].setSeriesInfo(Object.assign({ orientation: 'CORONAL' }, series))
this.$refs[`viewport-volume-2`][0].setSeriesInfo(Object.assign({ orientation: 'SAGITTAL' }, series))
resolve(false)
})
},
async handleFusion(data) {
try {
@ -3570,7 +3607,7 @@ export default {
}
.grid-cell-3 {
grid-column: 2 / 3;
grid-column: 0 / 3;
grid-row: 1 / 3;
}

View File

@ -11,7 +11,7 @@
{{ `${series.TaskInfo.SubjectCode} ${series.TaskInfo.TaskBlindName} ` }}
</h2>
<div>Series: #{{ series.SeriesNumber }}</div>
<div v-if="series.Stack">Image: #{{ `${series.SliceIndex + 1}/${imageInfo.total}` }}</div>
<div v-if="imageInfo.total">Image: #{{ `${series.SliceIndex + 1}/${imageInfo.total}` }}</div>
<div>{{ series.Modality }}</div>
</div>
<div v-if="series" class="right-top-text">
@ -40,10 +40,10 @@
<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>
}}</div>
<div v-show="imageInfo.sliceThickness">Slice Thickness: {{
`${Number(imageInfo.sliceThickness).toFixed(digitPlaces)} mm`
}}</div>
<div v-show="imageInfo.wwwc">WW/WL: {{ imageInfo.wwwc }}</div>
</div>
<div class="orientation-top">
@ -117,7 +117,8 @@ export default {
location: null,
sliceThickness: null,
wwwc: null,
total: 0
total: 0,
sliceThickness: 0
},
digitPlaces: 2,
orientationMarkers: [],
@ -194,21 +195,24 @@ export default {
this.imageInfo.zoom = zoom.toFixed(4)
let imageIds = viewport.getImageIds(this.volumeId)
let imageId = imageIds[0]
if (this.series.orientation === 'AXIAL') imageId = viewport.getCurrentImageId()
// if (this.series.orientation === 'AXIAL') imageId = viewport.getCurrentImageId()
if (imageId) {
const imagePlaneModule = metaData.get('imagePlaneModule', imageId)
if (this.series.orientation === 'AXIAL') {
this.imageInfo.size = `${imagePlaneModule.columns}*${imagePlaneModule.rows}`
this.imageInfo.location = imagePlaneModule.sliceLocation
this.imageInfo.total = imageIds.length
this.imageInfo.sliceThickness = imagePlaneModule.sliceThickness
}
if (this.series.orientation === 'CORONAL') {
this.imageInfo.size = `${imagePlaneModule.columns}*${imageIds.length}`
this.imageInfo.total = imagePlaneModule.rows
this.imageInfo.sliceThickness = imagePlaneModule.rowPixelSpacing
}
if (this.series.orientation === 'SAGITTAL') {
this.imageInfo.size = `${imagePlaneModule.rows}*${imageIds.length}`
this.imageInfo.total = imagePlaneModule.columns
this.imageInfo.sliceThickness = imagePlaneModule.columnPixelSpacing
}
this.getOrientationMarker()
@ -395,15 +399,16 @@ export default {
})
this.loading = false
},
async setSeriesInfo(obj) {
async setSeriesInfo(obj, isLocate = false) {
try {
let data = obj
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description) {
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description && !isLocate) {
data.SliceIndex = this.series.SliceIndex
}
this.series = {}
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
if (isLocate) return csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex });
this.series = {}
this.volumeId = data.SeriesInstanceUid
this.series = { ...data }
viewport
@ -456,8 +461,8 @@ export default {
clickSlider(e) {
const height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
this.sliderInfo.height = height
let sliceIdx = Math.trunc(this.series.Stack.length * height / 100)
sliceIdx = sliceIdx >= this.series.Stack.length ? this.series.Stack.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
let sliceIdx = Math.trunc(this.imageInfo.total * height / 100)
sliceIdx = sliceIdx >= this.imageInfo.total ? this.imageInfo.total : sliceIdx < 0 ? 0 : sliceIdx
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(
this.viewportId
@ -486,8 +491,8 @@ export default {
if (delta < 0) return
if (delta > boxHeight) return
const height = delta * 100 / boxHeight
let sliceIdx = Math.trunc(this.series.Stack.length * height / 100)
sliceIdx = sliceIdx >= this.series.Stack.length ? this.series.Stack.length - 1 : sliceIdx < 0 ? 0 : sliceIdx
let sliceIdx = Math.trunc(this.imageInfo.total * height / 100)
sliceIdx = sliceIdx >= this.imageInfo.total ? this.imageInfo.total - 1 : sliceIdx < 0 ? 0 : sliceIdx
this.sliderInfo.height = height
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(

View File

@ -1244,9 +1244,14 @@ export default {
},
getAnnotationProp(annotation, prop) {
if (!annotation) return
const referencedImageId = annotation?.metadata?.referencedImageId
let referencedImageId = null
if (annotation.from) {
referencedImageId = `${annotation?.metadata?.volumeId}?sliceIndex=${annotation?.metadata?.sliceIndex}&viewPlaneNormal=${annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')}`
} else {
referencedImageId = annotation?.metadata?.referencedImageId
}
if (!referencedImageId) return null
const cacheKey = `imageId:${referencedImageId}`
const cacheKey = annotation.from ? `volumeId:${referencedImageId}` : `imageId:${referencedImageId}`
const cachedStats = annotation.data?.cachedStats?.[cacheKey]
const hasProp = cachedStats
&& Object.prototype.hasOwnProperty.call(cachedStats, prop)

View File

@ -943,9 +943,14 @@ export default {
},
getAnnotationProp(annotation, prop) {
if (!annotation || !prop) return
const referencedImageId = annotation?.metadata?.referencedImageId
let referencedImageId = null
if (annotation.from) {
referencedImageId = `${annotation?.metadata?.volumeId}?sliceIndex=${annotation?.metadata?.sliceIndex}&viewPlaneNormal=${annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')}`
} else {
referencedImageId = annotation?.metadata?.referencedImageId
}
if (!referencedImageId) return null
const cacheKey = `imageId:${referencedImageId}`
const cacheKey = annotation.from ? `volumeId:${referencedImageId}` : `imageId:${referencedImageId}`
const cachedStats = annotation.data?.cachedStats?.[cacheKey]
const hasProp = cachedStats
&& Object.prototype.hasOwnProperty.call(cachedStats, prop)