非dicom阅片更改

uat
caiyiling 2025-03-18 15:26:51 +08:00
parent bfe2f1dbfb
commit 121bf497bb
3 changed files with 232 additions and 26 deletions

View File

@ -63,6 +63,14 @@
>
<svg-icon icon-class="polygon" class="svg-icon" />
</div>
<!-- <div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'SplineROITool' ? 'tool-item-active' : '']"
:title="$t('trials:reading:button:SplineROITool')"
@click.prevent="setAnnotateToolActive('SplineROITool')"
>
<svg-icon icon-class="polygon" class="svg-icon" />
</div> -->
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'Eraser' ? 'tool-item-active' : '']"
:title="$t('trials:dicom-show:Eraser')"
@ -70,13 +78,13 @@
>
<svg-icon icon-class="clear" class="svg-icon" />
</div>
<!-- <div
<div
:class="['tool-item', readingTaskState === 2 ? 'tool-disabled' : '', activeTool === 'Length' ? 'tool-item-active' : '']"
:title="$t('trials:dicom-show:length')"
@click.prevent="setAnnotateToolActive('Length')"
>
<svg-icon icon-class="length" class="svg-icon" />
</div> -->
</div>
<!-- 截图 -->
<!-- <div
class="tool-item"
@ -242,6 +250,7 @@ const {
ArrowAnnotateTool,
RectangleROITool,
PlanarFreehandROITool,
SplineROITool,
EraserTool,
LengthTool
// cursors
@ -255,6 +264,12 @@ export default {
default() {
return {}
}
},
psArr: {
type: Array,
default() {
return []
}
}
},
data() {
@ -272,7 +287,8 @@ export default {
activeTool: '',
readingTaskState: 2,
renderHistoryAnnotationTaskIds: [],
imageType: ['image/jpeg', 'image/jpg', 'image/bmp', 'image/png']
imageType: ['image/jpeg', 'image/jpg', 'image/bmp', 'image/png'],
digitPlaces: 2
}
},
computed: {
@ -344,6 +360,8 @@ export default {
isMove: false,
height: 0
}))
let digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.initLoader()
window.addEventListener('message', this.handleIframeMessage)
},
@ -380,6 +398,7 @@ export default {
element.oncontextmenu = (e) => e.preventDefault()
resizeObserver.observe(element)
element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage)
})
const viewportInputArray = [
{
@ -413,6 +432,7 @@ export default {
cornerstoneTools.addTool(ArrowAnnotateTool)
cornerstoneTools.addTool(RectangleROITool)
cornerstoneTools.addTool(PlanarFreehandROITool)
cornerstoneTools.addTool(SplineROITool)
cornerstoneTools.addTool(EraserTool)
cornerstoneTools.addTool(LengthTool)
@ -434,14 +454,25 @@ export default {
return doneChangingTextCallback(await this.customPrompt())
}
})
toolGroup.addTool(RectangleROITool.toolName)
toolGroup.addTool(RectangleROITool.toolName, {
cachedStats: false,
getTextLines: this.getRectangleROIToolTextLines
})
toolGroup.addTool(PlanarFreehandROITool.toolName, {
allowOpenContours: false,
cachedStats: false
cachedStats: false,
getTextLines: this.getPlanarFreehandROIToolTextLines
})
// const splineConfig = toolGroup.getToolConfiguration(
// splineToolName,
// 'spline'
// )
toolGroup.addTool(SplineROITool.toolName)
toolGroup.addTool(EraserTool.toolName)
toolGroup.addTool(LengthTool.toolName, {
getTextLines: this.getLengthToolTextLines
getTextLines: this.getLengthToolTextLines,
cachedStats: false
})
toolGroup.setToolActive(StackScrollTool.toolName, {
@ -454,11 +485,13 @@ export default {
toolGroup.setToolPassive(ArrowAnnotateTool.toolName)
toolGroup.setToolPassive(RectangleROITool.toolName)
toolGroup.setToolPassive(PlanarFreehandROITool.toolName)
toolGroup.setToolPassive(SplineROITool.toolName)
toolGroup.setToolPassive(LengthTool.toolName)
} else {
toolGroup.setToolEnabled(ArrowAnnotateTool.toolName)
toolGroup.setToolEnabled(RectangleROITool.toolName)
toolGroup.setToolEnabled(PlanarFreehandROITool.toolName)
toolGroup.setToolEnabled(SplineROITool.toolName)
toolGroup.setToolEnabled(LengthTool.toolName)
}
toolGroup.setToolPassive(EraserTool.toolName)
@ -482,18 +515,38 @@ export default {
renderHistoryAnnotations(obj) {
if (this.renderHistoryAnnotationTaskIds.includes(obj.VisitTaskId)) return
this.renderHistoryAnnotationTaskIds.push(obj.VisitTaskId)
const arr = []
// let arr = []
obj.Annotations.map(i => {
if (typeof i.MeasureData === 'string') {
const annotation = JSON.parse(i.MeasureData)
// if (typeof i.MeasureData === 'string') {
// const annotation = JSON.parse(i.MeasureData)
const annotation = i.MeasureData
annotation.annotationId = i.Id
arr.push(annotation)
// if (annotation.metadata.toolName === 'Length') {
// let annotations = obj.Annotations.filter(item=>{
// const annotation = JSON.parse(item.MeasureData)
// item.metadata.referencedImageId === annotation.metadata.referencedImageId
// })
// annotations.map(i=>{
// return {
// i,
// 'i.data.ps': annotation.data.handles.ps
// }
// })
// console.log(annotations)
// }
cornerstoneTools.annotation.state.addAnnotation(annotation)
if (obj.ReadingTaskState === 2) {
cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
}
}
})
// arr.map(i=>{
// cornerstoneTools.annotation.state.addAnnotation(i)
// if (obj.ReadingTaskState === 2) {
// cornerstoneTools.annotation.locking.setAnnotationLocked(i.annotationUID)
// }
// })
},
//
async renderImage(imageIds, canvasIndex, sliceIndex) {
@ -726,6 +779,18 @@ export default {
if (this.activeTool) {
toolGroup.setToolPassive(this.activeTool)
}
if (toolName === 'Length') {
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${this.activeCanvasIndex}`)
let imageId = viewport.csImage.imageId
let annotations = cornerstoneTools.annotation.state.getAllAnnotations()
let idx = annotations.findIndex(i=>i.metadata.referencedImageId === imageId && i.metadata.toolName === 'Length')
if (idx > -1) {
this.activeTool = ''
this.$message.warning('当前图像已标注比例尺!')
return
}
}
toolGroup.setToolActive(toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
})
@ -772,11 +837,20 @@ export default {
if (!annotation) return
if (annotation.annotationId) {
await deleteTrialFileType(annotation.annotationId)
} else {
cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID)
}
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${this.activeCanvasIndex}`)
viewport.render()
//
const i = this.viewportInfos.findIndex(i => i.index === this.activeCanvasIndex)
const imageId = annotation.metadata.referencedImageId
const path = imageId.split(`web:${this.OSSclientConfig.basePath}`)[1]
const fileList = this.viewportInfos[i].fileList
const fileIndex = fileList.findIndex(f => f.Path === path)
if (annotation.metadata.toolName === 'Length') {
this.$emit('setPS', {NoneDicomFileId: fileList[fileIndex].Id,Path: fileList[fileIndex].Path, PS: null})
}
},
async annotationModifiedListener(e) {
console.log('Modified')
@ -793,6 +867,15 @@ export default {
const fileList = this.viewportInfos[i].fileList
const fileIndex = fileList.findIndex(f => f.Path === path)
if (fileIndex === -1) return
if (annotation.metadata.toolName === 'Length') {
const value = annotation.data.l
if (value) {
let cachedStats = Object.keys(annotation.data.cachedStats)
let ps = value / annotation.data.cachedStats[cachedStats[0]].length
annotation.data.ps = ps
this.$emit('setPS', {NoneDicomFileId: fileList[fileIndex].Id,Path: fileList[fileIndex].Path, PS: ps})
}
}
const params = {
id: annotation.annotationId,
visitTaskId: this.viewportInfos[i].taskInfo.VisitTaskId,
@ -820,16 +903,29 @@ export default {
const fileIndex = fileList.findIndex(f => f.Path === path)
if (fileIndex === -1) return
if (annotation.metadata.toolName === 'Length') {
const { value } = await this.$prompt('请录入录入物理长度mm')
annotation.data.handles.scale = value
if (!value) {
//
cornerstoneTools.annotation.state.addAnnotation(annotation.annotationUID)
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${this.activeCanvasIndex}`)
viewport.render()
const value = await this.$prompt('请录入物理长度mm', '', {
showClose: false,
showCancelButton: false,
closeOnClickModal: false,
inputPattern: /^\d+$/,
inputErrorMessage: '请输入数值'
}).then(({ value }) => {
return value
}).catch(() => {
return null
})
if (value) {
annotation.data.l = parseFloat(value)
let cachedStats = Object.keys(annotation.data.cachedStats)
let ps = parseFloat(value) / annotation.data.cachedStats[cachedStats[0]].length
annotation.data.ps = ps
this.$emit('setPS', {NoneDicomFileId: fileList[fileIndex].Id,Path: fileList[fileIndex].Path, PS: ps})
} else {
cornerstoneTools.annotation.state.removeAnnotation(annotation.annotationUID)
return
}
}
const params = {
id: '',
visitTaskId: this.viewportInfos[i].taskInfo.VisitTaskId,
@ -841,8 +937,14 @@ export default {
const res = await addNoneDicomMark(params)
annotation.annotationId = res.Result
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(`canvas-${this.activeCanvasIndex}`)
viewport.render()
//
// this.$message.success(this.$t('common:message:savedSuccessfully'))
},
calculateCachedStats(imageId) {
},
getLengthToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId]
@ -850,12 +952,96 @@ export default {
if (length === undefined || length === null || isNaN(length)) {
return
}
if (data.handles.scale === undefined || data.handles.scale === null || isNaN(data.handles.scale)) {
if (data.l === undefined || data.l === null || isNaN(data.l)) {
return
}
const textLines = []
textLines.push(`${csUtils.roundNumber(length)} ${unit}`)
textLines.push(`${csUtils.roundNumber(data.handles.scale)} mm`)
textLines.push(`P: ${parseFloat(length).toFixed(this.digitPlaces)} ${unit}`)
if (data.l) {
textLines.push(`L: ${parseFloat(data.l).toFixed(this.digitPlaces)} mm`)
textLines.push(`PS: ${parseFloat(data.l / length).toFixed(this.digitPlaces)} mm/px`)
}
return textLines;
},
getPlanarFreehandROIToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId];
const {
area,
mean,
stdDev,
length,
perimeter,
max,
isEmptyArea,
unit,
areaUnit,
modalityUnit,
} = cachedVolumeStats || {};
const textLines = [];
let ps = null
let path = targetId.split(`web:${this.OSSclientConfig.basePath}`)[1]
let i = this.psArr.findIndex(i=>i.Path === path)
if (i > -1 && this.psArr[i].PS) {
ps = parseFloat(this.psArr[i].PS).toFixed(3)
}
if (area) {
const areaLine = isEmptyArea
? `Area: Oblique not supported`
: `Area: ${ps ? parseFloat(area * ps).toFixed(this.digitPlaces) : parseFloat(area).toFixed(this.digitPlaces)} ${ps ? 'mm' + '\xb2' : areaUnit}`;
textLines.push(areaLine);
}
if (mean) {
textLines.push(`Mean: ${parseFloat(mean).toFixed(this.digitPlaces)} ${modalityUnit}`);
}
if (Number.isFinite(max)) {
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
}
if (stdDev) {
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
}
if (perimeter) {
if (ps) {
textLines.push(`Perimeter: ${ parseFloat(perimeter * ps).toFixed(this.digitPlaces) } mm`);
} else {
textLines.push(`Perimeter: ${ parseFloat(perimeter).toFixed(this.digitPlaces) } ${unit}`);
}
}
// if (length) {
// // No need to show length prefix as there is just the single value
// textLines.push(`${csUtils.roundNumber(length)} ${unit}`);
// }
return textLines;
},
getRectangleROIToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId];
const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats;
if (mean === undefined) {
return;
}
const textLines = [];
let ps = null
let path = targetId.split(`web:${this.OSSclientConfig.basePath}`)[1]
let i = this.psArr.findIndex(i=>i.Path === path)
if (i > -1 && this.psArr[i].PS) {
ps = parseFloat(this.psArr[i].PS).toFixed(3)
}
if (ps) {
textLines.push(`Area: ${parseFloat(area * ps).toFixed(this.digitPlaces)} ${'mm' + '\xb2'}`);
} else {
textLines.push(`Area: ${parseFloat(area).toFixed(this.digitPlaces)} ${areaUnit}`);
}
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`);
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`);
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`);
return textLines;
},

View File

@ -305,7 +305,7 @@ export default {
imgVisible: false,
imageUrl: '',
urls: [],
digitPlaces: null
digitPlaces: 2
}
},
watch: {

View File

@ -51,10 +51,12 @@
<file-viewer
ref="fileViewer"
:related-study-info="relatedStudyInfo"
:psArr="psArr"
@toggleTaskByViewer="toggleTaskByViewer"
@toggleTask="toggleTask"
@toggleImage="toggleImage"
@previewCD="previewCD"
@setPS="setPS"
/>
</div>
<!-- 表单 -->
@ -152,7 +154,8 @@ export default {
clinicalDataVisible: false,
isClinicalDataFullscreen: false,
trialId: '',
cdVisitTaskId: ''
cdVisitTaskId: '',
psArr: []
}
},
computed: {
@ -249,7 +252,16 @@ export default {
visitTaskId: visitTaskId
}
const res = await getNoneDicomMarkListOutDto(params)
this.$set(this.visitTaskList[visitTaskIdx], 'Annotations', res.Result.NoneDicomMarkList)
let arr = res.Result.NoneDicomMarkList.map(i=>{
if (typeof i.MeasureData === 'string') {
i.MeasureData = JSON.parse(i.MeasureData)
}
if (i.MeasureData.metadata.toolName === 'Length' && this.psArr.findIndex(p=>p.NoneDicomFileId === i.NoneDicomFileId) === -1) {
this.psArr.push({NoneDicomFileId: i.NoneDicomFileId, Path: i.Path, PS: i.MeasureData.data.ps})
}
return i
})
this.$set(this.visitTaskList[visitTaskIdx], 'Annotations', arr)
this.loading = false
resolve()
} catch (e) {
@ -259,6 +271,14 @@ export default {
}
})
},
setPS(obj) {
let i = this.psArr.findIndex(p=>p.NoneDicomFileId === obj.NoneDicomFileId)
if (i > -1) {
this.psArr[i].PS = obj.PS
} else {
this.psArr.push(obj)
}
},
//
toggleTask(taskInfo) {
this.setActiveTaskVisitId(taskInfo.VisitTaskId)