非dicom阅片更改
parent
bfe2f1dbfb
commit
121bf497bb
|
@ -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;
|
||||
},
|
||||
|
|
|
@ -305,7 +305,7 @@ export default {
|
|||
imgVisible: false,
|
||||
imageUrl: '',
|
||||
urls: [],
|
||||
digitPlaces: null
|
||||
digitPlaces: 2
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue