分割标记渲染结构变更

uat_us
wangxiaoshuang 2026-04-14 16:19:36 +08:00
parent 5a7fa1fb77
commit 312ab754e1
6 changed files with 1221 additions and 500 deletions

View File

@ -16,6 +16,7 @@
"dependencies": {
"@aws-sdk/client-s3": "3.726.1",
"@cornerstonejs/adapters": "^4.19.2",
"@cornerstonejs/polymorphic-segmentation": "4.19.2",
"@cornerstonejs/calculate-suv": "^1.1.0",
"@cornerstonejs/core": "^4.19.2",
"@cornerstonejs/dicom-image-loader": "^4.19.2",
@ -124,4 +125,4 @@
"not dead",
"not op_mini all"
]
}
}

View File

@ -81,6 +81,19 @@ import {
} from './helpers/index.js'
import { vec3, mat4 } from 'gl-matrix'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import {
renderSegmentation,
readingSegmentByConfig,
selectSegmentation,
selectSegment,
createSegmentationRepresentation,
viewSegmentation,
viewSegment,
jumpBidirectional,
viewBidirectional,
changeColor,
resetViewport
} from "./helpers/segmentations"
export default {
name: 'MPRViewport',
props: {
@ -105,6 +118,32 @@ export default {
histogramVisible: {
type: Boolean,
default: false
},
actionConfiguration: {
type: Object,
default: () => {
return {}
}
},
SegmentConfig: {
type: Object,
default: () => {
return {}
}
},
curSegSeries: {
type: Object,
default: () => {
return {}
}
},
segmentIndex: {
type: Number,
default: 0
},
segmentationId: {
type: String,
default: ''
}
},
data() {
@ -155,6 +194,39 @@ export default {
this.$nextTick(() => {
this.initViewport()
})
DicomEvent.$on('createSegmentationRepresentation', (segmentationId) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
createSegmentationRepresentation(this.viewportId, segmentationId)
})
DicomEvent.$on('viewSegmentation', (obj) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
viewSegmentation(obj, this.viewportId)
})
DicomEvent.$on('viewSegment', (obj) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
viewSegment(obj, this.viewportId)
})
DicomEvent.$on('jumpBidirectional', (obj) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
jumpBidirectional(obj, this.viewportId, this.volumeId)
})
DicomEvent.$on('viewBidirectional', (obj) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
viewBidirectional(obj, this.viewportId)
})
DicomEvent.$on('changeColor', (obj) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
changeColor(obj, this.viewportId)
})
DicomEvent.$on('resetViewport', () => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
resetViewport(this.viewportId)
})
DicomEvent.$on('renderSegmentation', async (viewportId) => {
// if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
if (this.viewportId !== viewportId) return false
await renderSegmentation(this.series, this.series.TaskInfo, this.viewportId, this.SegmentConfig, this.segmentationId, this.segmentIndex, this.renderingEngineId, null, this.actionConfiguration)
})
},
watch: {
MPRInfo: {
@ -173,6 +245,25 @@ export default {
}
},
deep: true
},
SegmentConfig: {
handler() {
if (!this.segmentationId) return false
if (!this.series.TaskInfo) return false
readingSegmentByConfig(this.series, this.series.TaskInfo, this.viewportId, this.segmentationId, this.SegmentConfig)
},
deep: true
},
segmentIndex() {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
if (this.segmentIndex <= 0) return false
selectSegment(this.viewportId, this.segmentationId, this.segmentIndex)
},
segmentationId() {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
if (!this.segmentationId) return false
selectSegmentation(this.viewportId, this.segmentationId)
readingSegmentByConfig(this.series, this.series.TaskInfo, this.viewportId, this.segmentationId, this.SegmentConfig)
}
},
methods: {
@ -505,7 +596,6 @@ export default {
setCtTransferFunctionForVolumeActor(r)
}
console.log("渲染成功")
DicomEvent.$emit("isloaded", { isChange: false })
}
}]).then(r => {
if (data.isLocation || !this.imageInfo.zoom) {
@ -522,6 +612,7 @@ export default {
renderingEngine.render()
}, 100)
}
await renderSegmentation(this.series, this.series.TaskInfo, this.viewportId, this.SegmentConfig, this.segmentationId, this.segmentIndex, this.renderingEngineId, null, this.actionConfiguration)
} catch (e) {
console.log(e)
}

View File

@ -313,7 +313,9 @@
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
<VolumeViewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
:histogramVisible="histogramVisible" @activeViewport="activeViewport"
:histogramVisible="histogramVisible" :actionConfiguration="actionConfiguration"
:SegmentConfig="SegmentConfig" :segmentationId.sync="segId" :segmentIndex.sync="segIndex"
:curSegSeries.sync="curSegSeries" @activeViewport="activeViewport"
@toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup"
@resetViewport="resetViewport" v-if="readingTool === 3"
@ -333,7 +335,10 @@
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
<MPRViewport :ref="`viewport-MPR-${index}`" :data-viewport-uid="`viewport-MPR-${index}`"
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-MPR-${index}`"
:viewport-index="index" :MPRInfo="MPRInfo" @activeViewport="activeViewport" @setMPRInfo="setMPRInfo"
:viewport-index="index" :histogramVisible="histogramVisible"
:actionConfiguration="actionConfiguration" :SegmentConfig="SegmentConfig"
:segmentationId.sync="segId" :segmentIndex.sync="segIndex" :curSegSeries.sync="curSegSeries"
:MPRInfo="MPRInfo" @activeViewport="activeViewport" @setMPRInfo="setMPRInfo"
@toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD"
@renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup"
v-resize="(e) => handleSizeChange(e, `viewport-MPR-${index}`)" />
@ -362,9 +367,11 @@
<Segmentations ref="Segmentations" :visitInfo="taskInfo" :isMPR="isMPR"
:volumeToolGroupId="volumeToolGroupId" :viewportKey="viewportKey" :global-loading.sync="loading"
:loadingText.sync="loadingText" :rendering-engine-id="renderingEngineId"
:activeViewportIndex="activeViewportIndex" :activeTool.sync="activeTool"
:actionConfiguration="actionConfiguration" :histogramVisible="histogramVisible"
@setToolsPassive="setToolsPassive" @resetQuestion="resetQuestion" />
:SegmentConfig="SegmentConfig" :segId.sync="segId" :segIndex.sync="segIndex"
:curSegSeries.sync="curSegSeries" :activeViewportIndex="activeViewportIndex"
:activeTool.sync="activeTool" :actionConfiguration="actionConfiguration"
:histogramVisible="histogramVisible" @setToolsPassive="setToolsPassive"
@resetQuestion="resetQuestion" />
</el-tab-pane>
<el-tab-pane :label="$t('trials:reading:dicom3D:tabs:ecrf')" name="ecrf">
<div v-for="s in visitTaskList" v-show="lastViewportTaskId === s.VisitTaskId" :key="s.VisitTaskId"
@ -498,6 +505,11 @@
<!--直方图-->
<histogram ref="histogram" v-if="readingTool === 3" :visible.sync="histogramVisible" :activeTool.sync="activeTool"
:viewportKey="viewportKey" :rendering-engine-id="renderingEngineId" :activeViewportIndex="activeViewportIndex" />
<!--分割可视化窗口-->
<!-- <SurfaceViewport ref="surfaceViewport" viewportId="surfaceViewport" v-if="readingTool === 3"
:visible.sync="surfaceVisible" :renderingEngineId="renderingEngineId" :visitInfo="taskInfo" />
<ContourViewport ref="contourViewport" viewportId="contourViewport" v-if="readingTool === 3"
:renderingEngineId="renderingEngineId" :visitInfo="taskInfo" /> -->
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible"
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" />
@ -516,6 +528,7 @@ import {
RenderingEngine,
Enums,
// imageLoader,
// CONSTANTS,
metaData,
volumeLoader,
getRenderingEngine,
@ -537,6 +550,8 @@ import MPRViewport from './MPRViewport'
import VolumeViewport from './VolumeViewport'
import Segmentations from './Segmentations'
import histogram from "./histogram"
// import SurfaceViewport from "./SurfaceViewport"
// import ContourViewport from "./ContourViewport"
import mRecisit from './mRecist/QuestionList'
import recisit from './Recist/QuestionList'
import customizeQuestionList from './customize/QuestionList'
@ -567,6 +582,9 @@ const {
ToolGroupManager,
Enums: csToolsEnums,
StackScrollTool,
TrackballRotateTool,
PlanarFreehandContourSegmentationTool,
SplineContourSegmentationTool,
// ScaleOverlayTool,
PanTool,
ZoomTool,
@ -629,6 +647,8 @@ export default {
VolumeViewport,
Segmentations,
histogram,
// SurfaceViewport,
// ContourViewport,
mRecisit,
recisit,
customizeQuestionList,
@ -662,7 +682,7 @@ export default {
activeTaskIndex: -1,
activeStudyIndex: -1,
activeSeriesIndex: -1,
currentVisitInfo: null,
currentVisitInfo: {},
layout: 1,
cellsMax: 4,
rows: 1,
@ -772,14 +792,28 @@ export default {
},
},
},
SegmentConfig: {
renderOutline: true,
renderFill: true,
fillAlpha: 0.5,
outlineWidth: 1,
InactiveSegmentations: {
show: true,
fillAlpha: 0.3,
}
},
segId: null,
segIndex: null,
curSegSeries: {},
fusionOverlayModality: null,
lastUpper: null,
hasFusionUpperInitialized: false,
timer: null,
timer: {},
FullTimerOut: null,
isDelay: false,
histogramVisible: false
histogramVisible: false,
// surfaceVisible: false
}
},
computed: {
@ -951,6 +985,11 @@ export default {
this.getSystemInfoReading();
},
methods: {
showSurface(obj) {
// this.surfaceVisible = true
// this.$refs.contourViewport.setSeriesInfo(obj)
// this.$refs.surfaceViewport.setSeriesInfo(obj)
},
async openHistogram() {
this.histogramVisible = true
this.setToolsPassive()
@ -958,8 +997,8 @@ export default {
},
handleSizeChange(e, viewportId) {
// console.log('handleSizeChange', e)
this.resetRenderingEngine(viewportId)
let index = this.$refs[viewportId][0].series.SliceIndex
this.resetRenderingEngine(viewportId, index)
},
resetQuestion() {
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].getQuestions(false)
@ -1297,6 +1336,27 @@ export default {
}
}
]
// let element5 = this.$refs.surfaceViewport.$el
// let element6 = this.$refs.contourViewport.$el
// viewportInputArray.push({
// viewportId: 'surfaceViewport',
// type: ViewportType.VOLUME_3D,
// element: element5,
// defaultOptions: {
// orientation: Enums.OrientationAxis.CORONAL,
// background: [1, 1, 1]
// }
// })
// viewportInputArray.push({
// viewportId: 'contourViewport',
// type: ViewportType.ORTHOGRAPHIC,
// element: element6,
// defaultOptions: {
// orientation: Enums.OrientationAxis.AXIAL
// }
// })
// viewportIds.push('surfaceViewport')
// viewportIds.push('contourViewport')
}
if ((this.criterionType === 0 && this.readingTool === 0) || this.readingTool === 3) {
const volumeElement1 = this.$refs['viewport-MPR-0'][0].$el
@ -1377,6 +1437,7 @@ export default {
}
renderingEngine.setViewports(viewportInputArray)
this.addAnnotationListeners()
// cornerstoneTools.addTool(TrackballRotateTool)
cornerstoneTools.addTool(StackScrollTool)
cornerstoneTools.addTool(PanTool)
cornerstoneTools.addTool(ZoomTool)
@ -1401,6 +1462,8 @@ export default {
cornerstoneTools.addTool(LabelMapEditWithContourTool)
cornerstoneTools.addTool(BrushTool)
cornerstoneTools.addTool(SegmentBidirectionalTool)
// cornerstoneTools.addTool(PlanarFreehandContourSegmentationTool);
// cornerstoneTools.addTool(SplineContourSegmentationTool);
viewportIds.forEach((viewportId, i) => {
// const toolGroupId = `viewport-${i}`
let toolGroupId = viewportId
@ -1410,196 +1473,222 @@ export default {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) ? ToolGroupManager.getToolGroup(toolGroupId) : ToolGroupManager.createToolGroup(toolGroupId)
toolGroup.addViewport(viewportId, renderingEngineId)
toolGroup.addTool(StackScrollTool.toolName, {
loop: true, //
})
toolGroup.addTool(ScaleOverlayTool.toolName)
toolGroup.addTool(PanTool.toolName)
toolGroup.addTool(ZoomTool.toolName)
toolGroup.addTool(BrushTool.toolName)
if (this.readingTool === 3 || toolGroupId === this.volumeToolGroupId) {
toolGroup.addToolInstance(
'histogram_RectangleROI',
RectangleROITool.toolName,
);
toolGroup.addToolInstance(
'histogram_CircleROI',
CircleROITool.toolName,
);
toolGroup.addToolInstance(
'histogram_PlanarFreehandROI',
PlanarFreehandROITool.toolName,
);
toolGroup.addToolInstance(
'CircularBrush',
BrushTool.toolName,
{
activeStrategy: 'FILL_INSIDE_CIRCLE',
preview: {
previewColors: {}
}
}
);
toolGroup.addToolInstance(
'CircularEraser',
BrushTool.toolName,
{
activeStrategy: 'ERASE_INSIDE_CIRCLE',
preview: {
previewColors: {}
}
}
)
toolGroup.addToolInstance(
'ThresholdCircle',
BrushTool.toolName,
{
activeStrategy: 'THRESHOLD_INSIDE_CIRCLE',
}
)
toolGroup.addToolInstance(
'ThresholdSphere',
BrushTool.toolName,
{
activeStrategy: 'THRESHOLD_INSIDE_SPHERE',
}
)
toolGroup.addTool(LabelMapEditWithContourTool.toolName);
toolGroup.addTool(SegmentBidirectionalTool.toolName, {
getTextLines: this.getBidirectionalToolTextLines
});
}
if (volumeViewportIds.includes(viewportId)) {
toolGroup.addTool(WindowLevelTool.toolName, {
targetViewportIds: volumeViewportIds
})
toolGroup.addTool(CrosshairsTool.toolName, {
getReferenceLineColor: this.setCrosshairsToolLineColor
});
if (toolGroupId.includes('surface')) {
// toolGroup.addTool(TrackballRotateTool.toolName, {
// rotateSampleDistanceFactor: 0,
// });
// toolGroup.setToolActive(TrackballRotateTool.toolName, {
// bindings: [
// {
// mouseButton: MouseBindings.Primary,
// },
// ],
// });
} else if (toolGroupId.includes('contour')) {
// toolGroup.addTool(PlanarFreehandContourSegmentationTool.toolName);
// toolGroup.addTool(SplineContourSegmentationTool.toolName);
// toolGroup.setToolActive(PlanarFreehandContourSegmentationTool.toolName, {
// bindings: [
// {
// mouseButton: MouseBindings.Primary, // Middle Click
// },
// ],
// });
} else {
toolGroup.addTool(StackScrollTool.toolName, {
loop: true, //
})
toolGroup.addTool(ScaleOverlayTool.toolName)
toolGroup.addTool(PanTool.toolName)
toolGroup.addTool(ZoomTool.toolName)
toolGroup.addTool(BrushTool.toolName)
if (this.readingTool === 3 || toolGroupId === this.volumeToolGroupId) {
toolGroup.addToolInstance(
'histogram_RectangleROI',
RectangleROITool.toolName,
);
toolGroup.addToolInstance(
'histogram_CircleROI',
CircleROITool.toolName,
);
toolGroup.addToolInstance(
'histogram_PlanarFreehandROI',
PlanarFreehandROITool.toolName,
);
toolGroup.addToolInstance(
'CircularBrush',
BrushTool.toolName,
{
activeStrategy: 'FILL_INSIDE_CIRCLE',
preview: {
previewColors: {}
}
}
);
toolGroup.addToolInstance(
'CircularEraser',
BrushTool.toolName,
{
activeStrategy: 'ERASE_INSIDE_CIRCLE',
preview: {
previewColors: {}
}
}
)
toolGroup.addToolInstance(
'ThresholdCircle',
BrushTool.toolName,
{
activeStrategy: 'THRESHOLD_INSIDE_CIRCLE',
}
)
toolGroup.addToolInstance(
'ThresholdSphere',
BrushTool.toolName,
{
activeStrategy: 'THRESHOLD_INSIDE_SPHERE',
}
)
toolGroup.addTool(LabelMapEditWithContourTool.toolName);
toolGroup.addTool(SegmentBidirectionalTool.toolName, {
getTextLines: this.getBidirectionalToolTextLines
});
}
if (volumeViewportIds.includes(viewportId)) {
toolGroup.addTool(WindowLevelTool.toolName, {
targetViewportIds: volumeViewportIds
})
toolGroup.addTool(CrosshairsTool.toolName, {
getReferenceLineColor: this.setCrosshairsToolLineColor
});
} else {
toolGroup.addTool(WindowLevelTool.toolName)
}
toolGroup.addTool(WindowLevelTool.toolName)
}
toolGroup.addTool(WindowLevelTool.toolName)
toolGroup.addTool(WindowLevelRegionTool.toolName)
toolGroup.addTool(PlanarRotateTool.toolName)
toolGroup.addTool(ArrowAnnotateTool.toolName, {
arrowHeadStyle: 'standard',
changeTextCallback: async (data, eventData, doneChangingTextCallback) => {
return doneChangingTextCallback(data.text)
},
getTextCallback: async (doneChangingTextCallback) => {
return doneChangingTextCallback('Annotation')
toolGroup.addTool(WindowLevelRegionTool.toolName)
toolGroup.addTool(PlanarRotateTool.toolName)
toolGroup.addTool(ArrowAnnotateTool.toolName, {
arrowHeadStyle: 'standard',
changeTextCallback: async (data, eventData, doneChangingTextCallback) => {
return doneChangingTextCallback(data.text)
},
getTextCallback: async (doneChangingTextCallback) => {
return doneChangingTextCallback('Annotation')
}
})
toolGroup.addTool(RectangleROITool.toolName, {
cachedStats: false,
getTextLines: this.criterionType === 0 ? this.getCustomRectangleROIToolTextLines : this.getRectangleROIToolTextLines
})
toolGroup.addTool(PlanarFreehandROITool.toolName, {
allowOpenContours: false,
cachedStats: false,
getTextLines: this.getPlanarFreehandROIToolTextLines
})
toolGroup.addTool(EraserTool.toolName)
toolGroup.addTool(LengthTool.toolName, {
getTextLines: this.getLengthToolTextLines
// cachedStats: false
})
toolGroup.addTool(BidirectionalTool.toolName, {
// cachedStats: true,
actions: this.actionConfiguration,
getTextLines: this.getBidirectionalToolTextLines
})
toolGroup.addTool(CircleROITool.toolName, {
getTextLines: this.getCircleROIToolTextLines
})
toolGroup.addTool(EllipticalROITool.toolName, {
getTextLines: this.getEllipticalROIToolTextLines
})
toolGroup.addTool(FixedRadiusCircleROITool.toolName), {
getTextLines: this.getCircleROIToolTextLines
}
})
toolGroup.addTool(RectangleROITool.toolName, {
cachedStats: false,
getTextLines: this.criterionType === 0 ? this.getCustomRectangleROIToolTextLines : this.getRectangleROIToolTextLines
})
toolGroup.addTool(PlanarFreehandROITool.toolName, {
allowOpenContours: false,
cachedStats: false,
getTextLines: this.getPlanarFreehandROIToolTextLines
})
toolGroup.addTool(EraserTool.toolName)
toolGroup.addTool(LengthTool.toolName, {
getTextLines: this.getLengthToolTextLines
// cachedStats: false
})
toolGroup.addTool(BidirectionalTool.toolName, {
// cachedStats: true,
actions: this.actionConfiguration,
getTextLines: this.getBidirectionalToolTextLines
})
toolGroup.addTool(CircleROITool.toolName, {
getTextLines: this.getCircleROIToolTextLines
})
toolGroup.addTool(EllipticalROITool.toolName, {
getTextLines: this.getEllipticalROIToolTextLines
})
toolGroup.addTool(FixedRadiusCircleROITool.toolName), {
getTextLines: this.getCircleROIToolTextLines
}
toolGroup.addTool(AngleTool.toolName, {
getTextLines: this.getAngleToolTextLines
})
toolGroup.addTool(CobbAngleTool.toolName, {
getTextLines: this.getCobbAngleToolTextLines
})
if (toolGroupId === 'viewport-fusion-3') {
toolGroup.addTool(VolumeRotateTool.toolName)
toolGroup.setToolActive(VolumeRotateTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Wheel // mouse wheel
}
]
toolGroup.addTool(AngleTool.toolName, {
getTextLines: this.getAngleToolTextLines
})
toolGroup.addTool(MIPJumpToClickTool.toolName, {
targetViewportIds: fusionViewportIds.filter((id) => id !== toolGroupId)
toolGroup.addTool(CobbAngleTool.toolName, {
getTextLines: this.getCobbAngleToolTextLines
})
if (toolGroupId === 'viewport-fusion-3') {
toolGroup.addTool(VolumeRotateTool.toolName)
toolGroup.setToolActive(VolumeRotateTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Wheel // mouse wheel
}
]
})
toolGroup.addTool(MIPJumpToClickTool.toolName, {
targetViewportIds: fusionViewportIds.filter((id) => id !== toolGroupId)
})
// Set the initial state of the tools, here we set one tool active on left click.
// This means left click will draw that tool.
toolGroup.setToolActive(MIPJumpToClickTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary // Left Click
}
]
})
}
toolGroup.setToolConfiguration(
ScaleOverlayTool.toolName,
{
scaleLocation: 'bottom'
},
true // overwrite
)
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Wheel }]
})
toolGroup.setToolActive(ZoomTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Secondary }]
})
toolGroup.setToolActive(PanTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Auxiliary }]
})
toolGroup.setToolPassive(WindowLevelTool.toolName)
toolGroup.setToolPassive(WindowLevelRegionTool.toolName)
toolGroup.setToolPassive(PlanarRotateTool.toolName)
if (this.readingTaskState < 2) {
toolGroup.setToolPassive(ArrowAnnotateTool.toolName)
toolGroup.setToolPassive(RectangleROITool.toolName)
toolGroup.setToolPassive(PlanarFreehandROITool.toolName)
toolGroup.setToolPassive(LengthTool.toolName)
toolGroup.setToolPassive(BidirectionalTool.toolName)
toolGroup.setToolPassive(CircleROITool.toolName)
toolGroup.setToolPassive(EllipticalROITool.toolName)
toolGroup.setToolPassive(FixedRadiusCircleROITool.toolName)
toolGroup.setToolPassive(AngleTool.toolName)
toolGroup.setToolPassive(CobbAngleTool.toolName)
if (this.readingTool === 3) {
toolGroup.setToolPassive(LabelMapEditWithContourTool.toolName)
toolGroup.setToolPassive(SegmentBidirectionalTool.toolName, {});
// Set the initial state of the tools, here we set one tool active on left click.
// This means left click will draw that tool.
toolGroup.setToolActive(MIPJumpToClickTool.toolName, {
bindings: [
{
mouseButton: MouseBindings.Primary // Left Click
}
]
})
}
} else {
toolGroup.setToolEnabled(ArrowAnnotateTool.toolName)
toolGroup.setToolEnabled(RectangleROITool.toolName)
toolGroup.setToolEnabled(PlanarFreehandROITool.toolName)
toolGroup.setToolEnabled(LengthTool.toolName)
toolGroup.setToolEnabled(BidirectionalTool.toolName)
toolGroup.setToolEnabled(CircleROITool.toolName)
toolGroup.setToolEnabled(EllipticalROITool.toolName)
toolGroup.setToolEnabled(FixedRadiusCircleROITool.toolName)
toolGroup.setToolEnabled(AngleTool.toolName)
toolGroup.setToolEnabled(CobbAngleTool.toolName)
if (this.readingTool === 3) toolGroup.setToolEnabled(LabelMapEditWithContourTool.toolName)
toolGroup.setToolConfiguration(
ScaleOverlayTool.toolName,
{
scaleLocation: 'bottom'
},
true // overwrite
)
toolGroup.setToolActive(StackScrollTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Wheel }]
})
toolGroup.setToolActive(ZoomTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Secondary }]
})
toolGroup.setToolActive(PanTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Auxiliary }]
})
toolGroup.setToolPassive(WindowLevelTool.toolName)
toolGroup.setToolPassive(WindowLevelRegionTool.toolName)
toolGroup.setToolPassive(PlanarRotateTool.toolName)
if (this.readingTaskState < 2) {
toolGroup.setToolPassive(ArrowAnnotateTool.toolName)
toolGroup.setToolPassive(RectangleROITool.toolName)
toolGroup.setToolPassive(PlanarFreehandROITool.toolName)
toolGroup.setToolPassive(LengthTool.toolName)
toolGroup.setToolPassive(BidirectionalTool.toolName)
toolGroup.setToolPassive(CircleROITool.toolName)
toolGroup.setToolPassive(EllipticalROITool.toolName)
toolGroup.setToolPassive(FixedRadiusCircleROITool.toolName)
toolGroup.setToolPassive(AngleTool.toolName)
toolGroup.setToolPassive(CobbAngleTool.toolName)
if (this.readingTool === 3) {
toolGroup.setToolPassive(LabelMapEditWithContourTool.toolName)
toolGroup.setToolPassive(SegmentBidirectionalTool.toolName, {});
}
} else {
toolGroup.setToolEnabled(ArrowAnnotateTool.toolName)
toolGroup.setToolEnabled(RectangleROITool.toolName)
toolGroup.setToolEnabled(PlanarFreehandROITool.toolName)
toolGroup.setToolEnabled(LengthTool.toolName)
toolGroup.setToolEnabled(BidirectionalTool.toolName)
toolGroup.setToolEnabled(CircleROITool.toolName)
toolGroup.setToolEnabled(EllipticalROITool.toolName)
toolGroup.setToolEnabled(FixedRadiusCircleROITool.toolName)
toolGroup.setToolEnabled(AngleTool.toolName)
toolGroup.setToolEnabled(CobbAngleTool.toolName)
if (this.readingTool === 3) toolGroup.setToolEnabled(LabelMapEditWithContourTool.toolName)
}
toolGroup.setToolPassive(EraserTool.toolName)
}
toolGroup.setToolPassive(EraserTool.toolName)
})
eventTarget.addEventListener('cornerstoneimageloadprogress', this.imageLoadProgress)
// console.log(Events, toolsEvents)
@ -2627,7 +2716,7 @@ export default {
viewport.render()
renderingEngine.render()
if (this.readingTool === 3) {
DicomEvent.$emit('isloaded', { isChange: false })
DicomEvent.$emit('isloaded', { isChange: false, viewportId })
}
},
//
@ -2907,25 +2996,25 @@ export default {
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resize(forceFitToWindow)
},
//
resetRenderingEngine(viewportId = null) {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
resetRenderingEngine(viewportId = null, i) {
if (this.timer[viewportId]) {
clearInterval(this.timer[viewportId])
this.timer[viewportId] = null
}
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(viewportId)
if (!viewport) return false
if (viewport.volumeIds.size <= 0) return false
let index = null
this.timer = setTimeout(() => {
index = index || index === 0 ? index : this.$refs[viewportId ? viewportId : `${this.viewportKey}-${this.activeViewportIndex}`][0].series.SliceIndex
this.timer[viewportId] = setTimeout(() => {
index = i || i === 0 ? i : this.$refs[viewportId ? viewportId : `${this.viewportKey}-${this.activeViewportIndex}`][0].series.SliceIndex
renderingEngine.resize(true, false)
renderingEngine.render()
this.$refs[viewportId ? viewportId : `${this.viewportKey}-${this.activeViewportIndex}`][0].setFullScreen(index)
clearTimeout(this.timer)
this.timer = null
clearTimeout(this.timer[viewportId])
this.timer[viewportId] = null
if (this.readingTool === 3) {
DicomEvent.$emit('isloaded', { isChange: false })
DicomEvent.$emit('isloaded', { isChange: false, viewportId })
}
}, 100)
},
@ -2946,7 +3035,7 @@ export default {
if (this.readingTool === 3 || this.isMPR) {
// this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series)
this.$nextTick(() => {
this.resetRenderingEngine()
// this.resetRenderingEngine(`${this.viewportKey}-${index}`)
this.isDelay = true
this.setDelay(2000)
// if (this.readingTool === 3) {
@ -4048,8 +4137,13 @@ export default {
this.saveCustomAnnotationTimer = null
}
if (this.timer) {
clearInterval(this.timer)
this.timer = null
Object.keys(this.timer).forEach(key => {
if (this.timer[key]) {
clearInterval(this.timer[key])
this.timer[key] = null
}
})
this.timer = {}
}
if (this.FullTimerOut) {
clearTimeout(this.FullTimerOut)

View File

@ -80,7 +80,8 @@
@click.stop="changeShowSegmentConfig" />
</div>
</template>
<div class="addSegmentBox viewHover" @click.stop="addSegment" v-if="segmentList.length <= 0">
<div class="addSegmentBox viewHover" @click.stop="addSegment"
v-if="segmentList.length <= 0 && readingTaskState < 2">
<span><i class="el-icon-plus"></i>
{{ $t('trials:reading:Segmentations:button:addSegmention') }}
</span>
@ -124,16 +125,11 @@
$t('trials:reading:Segmentations:title:InactiveSegmentationsShow')
}}</span>
</div>
<!-- <div class="SegmentConfig" v-if="SegmentConfig.InactiveSegmentations.show">
<span>{{ $t('trials:reading:Segmentations:title:Opacity') }}</span>
<el-slider v-model="SegmentConfig.InactiveSegmentations.fillAlpha" show-input :step="0.1"
:max="1" input-size="mini" :show-input-controls="false" />
</div> -->
</div>
<template v-if="segmentList.length > 0">
<div class="SegmentGroupBox">
<div style="display: flex;align-items: center;">
<el-popover placement="left" width="40" trigger="click">
<el-popover placement="left" width="40" trigger="click" v-if="readingTaskState < 2">
<div class="SegmentGroupBtnBox">
<div class="SegmentGroupBtn" @click.stop="addSegmentGroup">
{{ $t('trials:reading:Segmentations:button:addSegmentGroup') }}
@ -156,7 +152,7 @@
</el-option>
</el-select>
</div>
<div style="display: flex;align-items: center;">
<div style="display: flex;align-items: center;" v-if="readingTaskState < 2">
<i class="el-icon-warning-outline" style="color:red;margin-right: 5px;"
:title="$t('trials:reading:Segmentations:tip:segmentationIsNotSave')"
v-if="!curSegmentGroup.isSaved"></i>
@ -167,7 +163,8 @@
</div>
</div>
<div class="addSegmentBox" @click.stop="addSegment"
style="display: flex;align-items: center;justify-content: space-between;">
style="display: flex;align-items: center;justify-content: space-between;"
v-if="readingTaskState < 2">
<span><i class="el-icon-plus"></i>
{{ $t('trials:reading:Segmentations:button:addSegment') }}
</span>
@ -220,7 +217,7 @@
<i class="el-icon-lock" v-if="item.lock" @click.stop="lockSegment(item, false)"></i>
<el-popover placement="bottom" width="40" trigger="click" class="docShow"
:value="popoverId === `popover-${item.segmentationId}_${item.segmentIndex}`"
@show="handleClickPopover(item)">
@show="handleClickPopover(item)" v-if="readingTaskState < 2">
<div class="SegmentGroupBtnBox">
<div class="SegmentGroupBtn" @click.stop="rename('segment', item)">
{{ $t('trials:reading:Segmentations:button:renameSegmentGroup') }}
@ -245,7 +242,7 @@
</template>
</el-collapse-item>
</el-collapse>
<div class="saveBtnBox">
<div class="saveBtnBox" v-if="readingTaskState < 2">
<el-button type="success" size="small" :disabled="saveLoading" @click="saveSegmentGroup()">
{{ $t("trials:reading:Segmentations:button:saveAll") }}
</el-button>
@ -261,6 +258,8 @@ import * as cornerstoneAdapters from "@cornerstonejs/adapters";
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import { getCustomizeStandardsSegmentDicomTools } from './toolConfig'
import * as polySeg from '@cornerstonejs/polymorphic-segmentation'
cornerstoneTools.init({ addons: { polySeg } })
const {
ToolGroupManager,
Enums: csToolsEnums,
@ -271,6 +270,7 @@ const {
CrosshairsTool,
utilities: CStUtils,
} = cornerstoneTools;
const { MouseBindings, Events: toolsEvents } = csToolsEnums
const { segmentation: segmentationUtils } = CStUtils;
const { cache, getRenderingEngine, imageLoader, eventTarget, metaData, utilities: csUtils, volumeLoader } = cornerstone;
@ -305,12 +305,32 @@ export default {
return {}
}
},
SegmentConfig: {
type: Object,
default: () => {
return {}
}
},
actionConfiguration: {
type: Object,
default: () => {
return {}
}
},
curSegSeries: {
type: Object,
default: () => {
return {}
}
},
segId: {
type: String,
default: ''
},
segIndex: {
type: Number,
default: 0
},
renderingEngineId: {
type: String,
required: true
@ -343,16 +363,6 @@ export default {
ThresholdTools: ['ThresholdCircle', 'ThresholdSphere'],
thresholdType: null,
showSegmentConfig: false,
SegmentConfig: {
renderOutline: true,
renderFill: true,
fillAlpha: 0.5,
outlineWidth: 1,
InactiveSegmentations: {
show: true,
fillAlpha: 0.3,
}
},
segmentList: [],
segmentationId: "",
segmentIndex: null,
@ -368,7 +378,7 @@ export default {
'#ff994d',
'#fb628b',
],
viewprotIds: ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3', 'viewport-MPR-0', 'viewport-MPR-1', 'viewport-MPR-2'], //
viewportIds: [], //
statsKey: [],
drawing: false, //
// isDel: false,
@ -388,14 +398,24 @@ export default {
this.segmentationModifiedCallback
);
DicomEvent.$on('activeSeries', (series) => {
console.log(series, 'series')
let { TaskInfo = {}, Id } = series
if (Id === this.series.Id && TaskInfo.VisitTaskId === this.visitInfo.VisitTaskId) return false
this.series = series
this.$emit("update:curSegSeries", Object.assign(series, {}))
this.getSegmentationList()
})
DicomEvent.$on('isloaded', (data) => {
if (this.isloaded) return false
this.isloaded = true
let { segment, isChange = true } = data
this.delAllSegment(isChange)
this.getSegmentationList(segment)
let { segment, isChange = true, viewportId, series } = data
DicomEvent.$emit('renderSegmentation', viewportId)
// if (!series.TaskInfo || series.TaskInfo.VisitTaskId !== this.visitInfo.VisitTaskId) return false
// if (this.isloaded) return false
// this.isloaded = true
// this.series = series
// this.viewportIds = []
// this.viewportIds.push(viewportId)
// // this.delAllSegment(isChange)
// this.readingSegmentToViewport(segment)
})
const digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
@ -414,12 +434,15 @@ export default {
s = this.curSegmentGroup.segments.find(item => item.segmentIndex === this.segmentIndex)
}
return s
},
readingTaskState() {
return this.series.TaskInfo ? this.series.TaskInfo.ReadingTaskState : 0
}
},
watch: {
SegmentConfig: {
handler() {
this.readingSegmentByConfig()
// this.readingSegmentByConfig()
},
deep: true
},
@ -434,9 +457,23 @@ export default {
this.setBrushThreshold()
},
deep: true
},
segmentIndex() {
this.$emit('update:segIndex', this.segmentIndex)
},
segmentationId() {
this.$emit('update:segId', this.segmentationId)
}
},
methods: {
showSurface(item) {
this.$emit("showSurface", {
segmentationId: item.segmentationId,
segmentIndex: item.segmentIndex,
volumeId: this.series.SeriesInstanceUid,
segmentations: this.curSegmentGroup
})
},
handleClickPopover(item) {
this.popoverId = `popover-${item.segmentationId}_${item.segmentIndex}`
},
@ -504,7 +541,6 @@ export default {
segmentationId: list[0].segmentationId,
segmentIndices: list.map(item => item.segmentIndex),
});
console.log(bidirectionalData, list[0].segmentationId, 'bidirectionalData')
if (bidirectionalData.length <= 0) {
list.forEach(item => {
let annotations = annotation.state.getAllAnnotations().filter(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex);
@ -563,6 +599,7 @@ export default {
},
setToolActive(toolName) {
// if (!this.series.TaskInfo || this.series.TaskInfo.VisitTaskId !== this.visitInfo.VisitTaskId) return false
if (this.segmentList.length <= 0) return false
if (this.curSegment.lock) return false
if (this.histogramVisible && !this.ThresholdTools.includes(toolName)) return false
@ -602,71 +639,33 @@ export default {
viewBidirectional(arr, view) {
for (let j = 0; j < arr.length; j++) {
let item = arr[j]
let bidirectional = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
item.bidirectionalView = view
if (!bidirectional) continue
annotation.visibility.setAnnotationVisibility(bidirectional.annotationUID, view)
// let bidirectional = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
// item.bidirectionalView = view
// if (!bidirectional) continue
// annotation.visibility.setAnnotationVisibility(bidirectional.annotationUID, view)
}
this.resetViewport()
DicomEvent.$emit('viewBidirectional', arr)
// this.resetViewport()
},
async jumpBidirectional(item) {
if (item.bidirectional) {
let an = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
console.log(an, 'an')
if (!an) return false
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId)
let key = Object.keys(an.data.cachedStats)[0]; // referencedImageId
if (key) {
let sliceIndex = key.split("?")[1].split("&")[0].split("=")[1]
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIndex });
} else {
const points = an.data.handles.points;
const worldPoint = points[0]; //
let volume = cache.getVolume(this.series.SeriesInstanceUid)
let { imageData, numFrames } = volume
const ijk = imageData.worldToIndex(worldPoint);
const sliceIndex = Math.abs(Math.round(ijk[2]));
// console.log(sliceIndex, 'sliceIndex')
csUtils.jumpToSlice(viewport.element, { imageIndex: numFrames - sliceIndex - 1 });
}
}
DicomEvent.$emit('jumpBidirectional', item)
},
viewSegmentGroup(item) {
let view = !item.view
this.viewprotIds.forEach(id => {
segmentation.config.visibility.setSegmentationRepresentationVisibility(
id,
{
segmentationId: item.segmentationId,
type: csToolsEnums.SegmentationRepresentations.Labelmap,
},
view
);
})
item.view = view
item.view = !item.view
item.segments.forEach(i => {
i.view = view
// this.viewBidirectional(i, view)
i.view = item.view
})
this.viewBidirectional(item.segments, view)
DicomEvent.$emit('viewSegmentation', item)
// this.viewBidirectional(item.segments, view)
},
viewSegment(item, view) {
this.viewprotIds.forEach(id => {
segmentation.config.visibility.setSegmentIndexVisibility(id, {
segmentationId: item.segmentationId,
type: csToolsEnums.SegmentationRepresentations.Labelmap,
}, item.segmentIndex, view)
})
item.view = view
this.viewBidirectional([item], view)
this.$emit('setToolsPassive')
DicomEvent.$emit('viewSegment', item)
},
lockSegment(item, lock) {
if (this.readingTaskState >= 2) return false
segmentation.segmentLocking.setSegmentIndexLocked(item.segmentationId, item.segmentIndex, lock)
item.lock = lock
if (!lock) this.changeSegmentationSavedStatus(item.segmentationId, lock)
@ -676,7 +675,7 @@ export default {
this.segmentationId = item.segmentationId;
this.segmentIndex = item.segmentIndex;
}
segmentation.segmentIndex.setActiveSegmentIndex(item.segmentationId, item.segmentIndex);
// segmentation.segmentIndex.setActiveSegmentIndex(item.segmentationId, item.segmentIndex);
if (isChange) { this.jumpBidirectional(item) }
if (item.lock) {
@ -685,15 +684,14 @@ export default {
// this.resetViewport()
},
selectSegmentGroup(s) {
this.viewprotIds.forEach(id => {
segmentation.activeSegmentation.setActiveSegmentation(id, this.segmentationId)
})
// segmentation.activeSegmentation.setActiveSegmentation(`${this.viewportKey}-${this.activeViewportIndex}`, this.segmentationId)
let segment = s ? s : this.segmentList.find(item => item.segmentationId === this.segmentationId).segments[0]
this.selectSegment(segment, s ? false : true)
this.readingSegmentByConfig()
this.segmentIndex = segment.segmentIndex
// this.selectSegment(segment, s ? false : true)
// this.readingSegmentByConfig()
},
async addSegmentGroup() {
let viewprotIds = this.viewprotIds
let viewportIds = this.viewportIds
// let segmentationId = this.$guid();
let obj = {
name: this.$t('trials:reading:Segmentations:name:SegmentGroup') + (this.segmentList.length + 1),
@ -723,13 +721,14 @@ export default {
await this.createSegmentation(obj.segmentationId)
this.createSegmentationRepresentation(obj.segmentationId)
this.segmentIndex = 1
viewprotIds.forEach(id => {
segmentation.config.color.setSegmentIndexColor(id, obj.segmentationId, 1, this.hex2Rgb(this.colors[0]))
})
this.changeColor(this.colors[0], { segmentationId: obj.segmentationId, segmentIndex: 1, color: this.colors[0] })
// viewportIds.forEach(id => {
// segmentation.config.color.setSegmentIndexColor(id, obj.segmentationId, 1, this.hex2Rgb(this.colors[0]))
// })
this.selectSegmentGroup()
},
async addSegment() {
let viewprotIds = this.viewprotIds;
let viewportIds = this.viewportIds;
if (this.segmentList.length <= 0) {
let obj = {
name: this.$t('trials:reading:Segmentations:name:SegmentGroup') + 1,
@ -759,10 +758,11 @@ export default {
this.createSegmentationRepresentation(this.segmentationId)
this.segmentIndex = 1
segmentation.segmentIndex.setActiveSegmentIndex(this.segmentList[0].segmentationId, 1);
viewprotIds.forEach(id => {
segmentation.config.color.setSegmentIndexColor(id, this.segmentList[0].segmentationId, 1, this.hex2Rgb(this.colors[0]))
})
this.readingSegmentByConfig()
this.changeColor(this.colors[0], { segmentationId: this.segmentList[0].segmentationId, segmentIndex: 1, color: this.colors[0] })
// viewportIds.forEach(id => {
// segmentation.config.color.setSegmentIndexColor(id, this.segmentList[0].segmentationId, 1, this.hex2Rgb(this.colors[0]))
// })
// this.readingSegmentByConfig()
} else {
let item = this.segmentList.find(i => i.segmentationId === this.segmentationId)
@ -787,19 +787,22 @@ export default {
let id = await this.addOrUpdateSegment({ name: obj.SegmentLabel, color: obj.color, segmentIndex: obj.segmentIndex, segmentationId: obj.segmentationId })
obj.id = id
item.segments.push(obj)
segmentation.segmentIndex.setActiveSegmentIndex(obj.segmentationId, obj.segmentIndex);
viewprotIds.forEach(id => {
segmentation.config.color.setSegmentIndexColor(id, obj.segmentationId, obj.segmentIndex, this.hex2Rgb(obj.color))
})
this.segmentIndex = obj.segmentIndex
segmentation.segmentIndex.setActiveSegmentIndex(obj.segmentationId, obj.segmentIndex);
this.changeColor(obj.color, { segmentationId: obj.segmentationId, segmentIndex: obj.segmentIndex, color: obj.color })
// viewportIds.forEach(id => {
// segmentation.config.color.setSegmentIndexColor(id, obj.segmentationId, obj.segmentIndex, this.hex2Rgb(obj.color))
// })
}
},
changeColor(e, item) {
this.viewprotIds.forEach(id => {
segmentation.config.color.setSegmentIndexColor(id, item.segmentationId, item.segmentIndex, this.hex2Rgb(e))
})
DicomEvent.$emit('changeColor', item)
// this.viewportIds.forEach(id => {
// segmentation.config.color.setSegmentIndexColor(id, item.segmentationId, item.segmentIndex, this.hex2Rgb(e))
// })
},
//
delAllSegment(isChange) {
@ -836,7 +839,7 @@ export default {
} else {
this.segmentationId = ''
}
this.readingSegmentByConfig()
// this.readingSegmentByConfig()
this.resetViewport()
this.$emit('resetQuestion')
},
@ -868,11 +871,7 @@ export default {
},
resetViewport(passive = true) {
let renderingEngine = getRenderingEngine(this.renderingEngineId)
this.viewprotIds.forEach(id => {
const viewport = renderingEngine.getViewport(id)
viewport.render()
})
DicomEvent.$emit('resetViewport')
if (passive) this.$emit('setToolsPassive')
},
async rename(key, item) {
@ -925,7 +924,7 @@ export default {
//
changeInactiveSegmentShow() {
let segmentList = this.segmentList.filter(item => item.segmentationId !== this.segmentationId)
this.viewprotIds.forEach(id => {
this.viewportIds.forEach(id => {
segmentation.config.visibility.setSegmentationRepresentationVisibility(
id,
{
@ -967,113 +966,118 @@ export default {
this.exportSegmentation(this.segmentationId, group, true)
},
exportSegmentation(segmentationId, group, isFile = false) {
const segmentationIds = segmentation.state
.getSegmentations()
.map(x => x.segmentationId);
if (!segmentationIds.length) {
return;
}
const segmentGroup =
segmentation.state.getSegmentation(segmentationId);
let { imageIds } = segmentGroup.representationData.Labelmap;
let segImages = imageIds.map(imageId => cache.getImage(imageId));
segImages = segImages.reverse()
let referencedImages = segImages.map(image =>
cache.getImage(image.referencedImageId)
);
const labelmaps2D = [];
let z = 0;
for (const segImage of segImages) {
const segmentsOnLabelmap = new Set();
const pixelData = segImage.getPixelData();
const { rows, columns } = segImage;
for (let i = 0; i < pixelData.length; i++) {
const segment = pixelData[i];
if (segment !== 0) {
segmentsOnLabelmap.add(segment);
}
try {
const segmentationIds = segmentation.state
.getSegmentations()
.map(x => x.segmentationId);
if (!segmentationIds.length) {
return;
}
const segmentGroup =
segmentation.state.getSegmentation(segmentationId);
labelmaps2D[z++] = {
segmentsOnLabelmap: Array.from(segmentsOnLabelmap),
pixelData,
rows,
columns
};
}
let { imageIds } = segmentGroup.representationData.Labelmap;
const allSegmentsOnLabelmap = labelmaps2D.map(
labelmap => labelmap.segmentsOnLabelmap
);
const labelmap3D = {
segmentsOnLabelmap: Array.from(new Set(allSegmentsOnLabelmap.flat())),
metadata: [],
labelmaps2D
};
let segmentIndexs = []
labelmap3D.segmentsOnLabelmap.forEach(segmentIndex => {
const color = segmentation.config.color.getSegmentIndexColor(
`${this.viewportKey}-${this.activeViewportIndex}`,
segmentationId,
segmentIndex
let segImages = imageIds.map(imageId => cache.getImage(imageId));
segImages = segImages.reverse()
let referencedImages = segImages.map(image =>
cache.getImage(image.referencedImageId)
);
const RecommendedDisplayCIELabValue = dcmjs.data.Colors.rgb2DICOMLAB(
color.slice(0, 3).map(value => value / 255)
).map(value => Math.round(value));
let segment = group.segments.find(item => item.segmentIndex === segmentIndex)
if (segment) {
segmentIndexs.push(segmentIndex)
let SegmentLabel = segment.SegmentLabel
const segmentMetadata = {
SegmentNumber: segmentIndex.toString(),
SegmentLabel: SegmentLabel,
SegmentAlgorithmType: "MANUAL",
SegmentAlgorithmName: "OHIF Brush",
RecommendedDisplayCIELabValue,
SegmentedPropertyCategoryCodeSequence: {
CodeValue: "T-D0050",
CodingSchemeDesignator: "SRT",
CodeMeaning: "Tissue"
},
SegmentedPropertyTypeCodeSequence: {
CodeValue: "T-D0050",
CodingSchemeDesignator: "SRT",
CodeMeaning: "Tissue"
const labelmaps2D = [];
let z = 0;
for (const segImage of segImages) {
const segmentsOnLabelmap = new Set();
const pixelData = segImage.getPixelData();
const { rows, columns } = segImage;
for (let i = 0; i < pixelData.length; i++) {
const segment = pixelData[i];
if (segment !== 0) {
segmentsOnLabelmap.add(segment);
}
}
labelmaps2D[z++] = {
segmentsOnLabelmap: Array.from(segmentsOnLabelmap),
pixelData,
rows,
columns
};
if (segment.stats) labelmap3D.metadata[segmentIndex] = segmentMetadata;
}
});
if (labelmap3D.metadata.length <= 0) {
return false
}
const generatedSegmentation =
Cornerstone3D.Segmentation.generateSegmentation(
referencedImages,
labelmap3D,
metaData
const allSegmentsOnLabelmap = labelmaps2D.map(
labelmap => labelmap.segmentsOnLabelmap
);
segmentIndexs.forEach((item, index) => {
if (generatedSegmentation.dataset.SegmentSequence[index]) {
generatedSegmentation.dataset.SegmentSequence[index].SegmentNumber = item.toString()
}
})
generatedSegmentation.dataset.SegmentSequence.sort((a, b) => a.SegmentNumber - b.SegmentNumber)
if (!isFile) {
const buffer = Buffer.from(dcmjs.data.datasetToDict(generatedSegmentation.dataset).write());
let blob = new Blob([buffer], { type: "application/dicom" });
return blob
} else {
this.downloadDICOMData(generatedSegmentation.dataset, `${group.name}.dcm`);
const labelmap3D = {
segmentsOnLabelmap: Array.from(new Set(allSegmentsOnLabelmap.flat())),
metadata: [],
labelmaps2D
};
let segmentIndexs = []
labelmap3D.segmentsOnLabelmap.forEach(segmentIndex => {
const color = segmentation.config.color.getSegmentIndexColor(
`${this.viewportKey}-${this.activeViewportIndex}`,
segmentationId,
segmentIndex
);
const RecommendedDisplayCIELabValue = dcmjs.data.Colors.rgb2DICOMLAB(
color.slice(0, 3).map(value => value / 255)
).map(value => Math.round(value));
let segment = group.segments.find(item => item.segmentIndex === segmentIndex)
if (segment) {
segmentIndexs.push(segmentIndex)
let SegmentLabel = segment.SegmentLabel
const segmentMetadata = {
SegmentNumber: segmentIndex.toString(),
SegmentLabel: SegmentLabel,
SegmentAlgorithmType: "MANUAL",
SegmentAlgorithmName: "OHIF Brush",
RecommendedDisplayCIELabValue,
SegmentedPropertyCategoryCodeSequence: {
CodeValue: "T-D0050",
CodingSchemeDesignator: "SRT",
CodeMeaning: "Tissue"
},
SegmentedPropertyTypeCodeSequence: {
CodeValue: "T-D0050",
CodingSchemeDesignator: "SRT",
CodeMeaning: "Tissue"
}
};
if (segment.stats) labelmap3D.metadata[segmentIndex] = segmentMetadata;
}
});
if (labelmap3D.metadata.length <= 0) {
return false
}
const generatedSegmentation =
Cornerstone3D.Segmentation.generateSegmentation(
referencedImages,
labelmap3D,
metaData
);
segmentIndexs.forEach((item, index) => {
if (generatedSegmentation.dataset.SegmentSequence[index]) {
generatedSegmentation.dataset.SegmentSequence[index].SegmentNumber = item.toString()
}
})
generatedSegmentation.dataset.SegmentSequence.sort((a, b) => a.SegmentNumber - b.SegmentNumber)
if (!isFile) {
const buffer = Buffer.from(dcmjs.data.datasetToDict(generatedSegmentation.dataset).write());
let blob = new Blob([buffer], { type: "application/dicom" });
return blob
} else {
this.downloadDICOMData(generatedSegmentation.dataset, `${group.name}.dcm`);
}
} catch (err) {
console.log(err)
}
},
downloadDICOMData(bufferOrDataset, filename) {
let blob;
@ -1123,7 +1127,7 @@ export default {
const arrayBuffer = image.data.byteArray.buffer;
await this.loadSegmentation(arrayBuffer, obj.segmentationId);
this.createSegmentationRepresentation(obj.segmentationId);
// this.createSegmentationRepresentation(obj.segmentationId);
},
async loadSegmentation(arrayBuffer, segmentationId) {
const generateToolState =
@ -1306,51 +1310,49 @@ export default {
}
},
createSegmentationRepresentation(segmentationId) {
this.viewprotIds.forEach(id => {
segmentation.addSegmentationRepresentations(id, [
{
segmentationId,
type: csToolsEnums.SegmentationRepresentations.Labelmap,
},
])
})
DicomEvent.$emit('createSegmentationRepresentation', segmentationId)
this.$emit('setToolsPassive')
},
contentMouseup() {
// console.log("segment contentMouseup")
if (!this.drawing) return false
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
this.timeoutId = setTimeout(() => {
this.timeoutId = null;
this.drawing = false;
let segmentGroup = this.segmentList.find(item => item.segmentationId === this.segmentationId)
if (segmentGroup && segmentGroup.segments && segmentGroup.segments.length > 0) {
let segmentIndexs = []
segmentGroup.segments.forEach(item => {
segmentIndexs.push(item.segmentIndex)
item.bidirectional = null
item.stats = null
})
annotation.state.getAllAnnotations().forEach(i => {
if (i.metadata.segmentationId === this.segmentationId && i.metadata.toolName === "SegmentBidirectional") {
annotation.state.removeAnnotation(i.annotationUID)
}
})
this.calculateStatistics(segmentIndexs, this.segmentationId, 'individual');
this.resetViewport(false)
// this.getBidirectional(segmentGroup.segments)
try {
// console.log("segment contentMouseup")
if (!this.drawing) return false
if (this.timeoutId) {
clearTimeout(this.timeoutId);
this.timeoutId = null;
}
}, 500);
this.timeoutId = setTimeout(() => {
this.timeoutId = null;
this.drawing = false;
let segmentGroup = this.segmentList.find(item => item.segmentationId === this.segmentationId)
if (segmentGroup && segmentGroup.segments && segmentGroup.segments.length > 0) {
let segmentIndexs = []
segmentGroup.segments.forEach(item => {
segmentIndexs.push(item.segmentIndex)
item.bidirectional = null
item.stats = null
})
annotation.state.getAllAnnotations().forEach(i => {
if (i.metadata.segmentationId === this.segmentationId && i.metadata.toolName === "SegmentBidirectional") {
annotation.state.removeAnnotation(i.annotationUID)
}
})
this.calculateStatistics(segmentIndexs, this.segmentationId, 'individual');
this.resetViewport(false)
// this.getBidirectional(segmentGroup.segments)
}
}, 500);
} catch (err) {
console.log(err)
}
},
//
async getSegmentBindingList(param = {}) {
try {
let data = {
VisitTaskId: this.visitInfo.VisitTaskId,
VisitTaskId: this.series.TaskInfo.VisitTaskId,
PageSize: 9999,
PageIndex: 1,
}
@ -1367,7 +1369,7 @@ export default {
async saveSegmentBindingAndAnswer(list) {
try {
let data = {
VisitTaskId: this.visitInfo.VisitTaskId,
VisitTaskId: this.series.TaskInfo.VisitTaskId,
BindingList: list
}
let res = await saveSegmentBindingAndAnswer(data)
@ -1482,7 +1484,7 @@ export default {
let bidirectional = bidirectionalData[0]
const { segmentIndex } = bidirectional;
const { majorAxis, minorAxis, maxMajor, maxMinor } = bidirectional;
const { majorAxis, minorAxis } = bidirectional;
let item = list.find(i => i.segmentIndex === segmentIndex)
SegmentBidirectionalTool.hydrate(viewportId, [majorAxis, minorAxis], {
segmentIndex,
@ -1491,7 +1493,7 @@ export default {
let an = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === bidirectional.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
if (an) {
annotation.locking.setAnnotationLocked(an.annotationUID, true)
annotation.visibility.setAnnotationVisibility(an.annotationUID, item.bidirectionalView)
annotation.visibility.setAnnotationVisibility(an.annotationUID, true)
}
item.bidirectional = bidirectional
reslove(true)
@ -1536,7 +1538,7 @@ export default {
SegmentId: item.SegmentId,
SegmentationId: item.SegmentationId,
TableQuestionId: item.TableQuestionId,
VisitTaskId: this.visitInfo.VisitTaskId,
VisitTaskId: this.series.TaskInfo.VisitTaskId,
}
bindingList.push(o)
}
@ -1554,7 +1556,7 @@ export default {
SubjectId: this.visitInfo.SubjectId,
SubjectVisitId: this.visitInfo.VisistId,
TrialId: this.$route.query.trialId,
VisitTaskId: this.visitInfo.VisitTaskId,
VisitTaskId: this.series.TaskInfo.VisitTaskId,
}
if (url) data.SegUrl = url;
if (id) data.Id = id;
@ -1574,7 +1576,7 @@ export default {
try {
let data = {
SeriesId: this.series.Id,
VisitTaskId: this.visitInfo.VisitTaskId,
VisitTaskId: this.series.TaskInfo.VisitTaskId,
PageSize: 9999,
PageIndex: 1,
}
@ -1583,6 +1585,8 @@ export default {
this.loading = false;
if (res.IsSuccess) {
this.segmentList = []
this.segmentationId = null;
this.segmentIndex = null;
let list = res.Result.CurrentPageData;
for (let i = 0; i < list.length; i++) {
let item = list[i]
@ -1597,12 +1601,12 @@ export default {
segments: []
}
this.segmentList.push(obj)
if (item.SEGUrl) {
await this.readSegmentation(obj)
} else {
await this.createSegmentation(obj.segmentationId)
this.createSegmentationRepresentation(obj.segmentationId)
}
// if (item.SEGUrl) {
// await this.readSegmentation(obj)
// } else {
// await this.createSegmentation(obj.segmentationId)
// // this.createSegmentationRepresentation(obj.segmentationId)
// }
}
if (!this.segmentationId) {
this.segmentationId = obj.segmentationId
@ -1621,45 +1625,41 @@ export default {
bidirectional: SegmentJson.bidirectional,
bidirectionalView: true,
view: true,
lock: item.locked,
lock: true,
id: s.Id
}
obj.segments.push(o)
this.selectSegment(o, false)
this.changeColor(s.ColorRgb, o)
this.lockSegment(o, true)
// this.selectSegment(o, false)
// this.changeColor(s.ColorRgb, o)
// this.lockSegment(o, true)
}
if (!this.segmentIndex) {
this.segmentIndex = s.SegmentNumber
}
})
this.$nextTick(() => {
if (SEGMENT) {
// console.log(SEGMENT, 'SEGMENT')
return this.getBidirectional(obj.segments, SEGMENT)
}
this.getBidirectional(obj.segments, null, false)
})
}
if (this.segmentationId && this.segmentIndex && this.segmentList && this.segmentList.length > 0) {
let o = this.segmentList.find(item => item.segmentationId === this.segmentationId)
if (o) {
let s = o.segments.find(item => item.segmentIndex === this.segmentIndex)
this.selectSegmentGroup(s)
} else {
this.segmentationId = this.segmentList[0].segmentationId
this.segmentIndex = this.segmentationId ? this.segmentList[0].segments[0].segmentIndex : null
if (this.segmentationId && this.segmentIndex) {
this.selectSegmentGroup(this.segmentList[0].segments[0])
}
}
// console.log(segment, 'segment')
// this.selectSegment(segment)
// this.$nextTick(() => {
// if (SEGMENT) {
// // console.log(SEGMENT, 'SEGMENT')
// return this.getBidirectional(obj.segments, SEGMENT)
// }
// this.getBidirectional(obj.segments, null, false)
// })
}
// if (this.segmentationId && this.segmentIndex && this.segmentList && this.segmentList.length > 0) {
// let o = this.segmentList.find(item => item.segmentationId === this.segmentationId)
// if (o) {
// let s = o.segments.find(item => item.segmentIndex === this.segmentIndex)
// this.selectSegmentGroup(s)
// } else {
// this.segmentationId = this.segmentList[0].segmentationId
// this.segmentIndex = this.segmentationId ? this.segmentList[0].segments[0].segmentIndex : null
// if (this.segmentationId && this.segmentIndex) {
// this.selectSegmentGroup(this.segmentList[0].segments[0])
// }
// }
// }
this.isloaded = false
this.readingSegmentByConfig()
// this.readingSegmentByConfig()
}
} catch (err) {
this.loading = false
@ -1688,7 +1688,7 @@ export default {
SegmentName: name,
SegmentNumber: segmentIndex,
SegmentationId: segmentationId,
VisitTaskId: this.visitInfo.VisitTaskId,
VisitTaskId: this.series.TaskInfo.VisitTaskId,
SegmentJson: segmentJson
}
if (id) data.Id = id

View File

@ -14,6 +14,25 @@
<div v-if="imageInfo.total">Image: #{{ `${series.SliceIndex + 1}/${imageInfo.total}` }}</div>
<div>{{ series.Modality }}</div>
</div>
<div v-if="series && taskInfo && taskInfo.IsReadingTaskViewInOrder === 1" class="top-center-tool">
<div class="toggle-visit-container">
<div class="arrw_icon"
:style="{ cursor: series.TaskInfo && series.TaskInfo.VisitTaskNum !== 0 ? 'pointer' : 'not-allowed', color: series.TaskInfo && series.TaskInfo.VisitTaskNum !== 0 ? '#fff' : '#6b6b6b' }"
@click.stop.prevent="toggleTask($event, series.TaskInfo.VisitTaskNum, -1)"
@dblclick.stop="preventDefault($event)">
<i class="el-icon-caret-left" />
</div>
<div class="arrow_text">
{{ series.TaskInfo ? series.TaskInfo.TaskBlindName : '' }}
</div>
<div class="arrw_icon"
:style="{ cursor: series.TaskInfo && series.TaskInfo.VisitTaskNum < taskInfo.VisitNum ? 'pointer' : 'not-allowed', color: series.TaskInfo && series.TaskInfo.VisitTaskNum < taskInfo.VisitNum ? '#fff' : '#6b6b6b' }"
@click.stop.prevent="toggleTask($event, series.TaskInfo.VisitTaskNum, 1)"
@dblclick.stop="preventDefault($event)">
<i class="el-icon-caret-right" />
</div>
</div>
</div>
<div v-if="series" class="right-top-text">
<div>{{ series.Description }}</div>
</div>
@ -40,10 +59,10 @@
<div v-if="series" class="right-bottom-text">
<div v-show="imageInfo.location">Location: {{
`${Number(imageInfo.location).toFixed(digitPlaces)} mm`
}}</div>
}}</div>
<div v-show="imageInfo.sliceThickness">Slice Thickness: {{
`${Number(imageInfo.sliceThickness).toFixed(digitPlaces)} mm`
}}</div>
}}</div>
<div v-show="imageInfo.wwwc">WW/WL: {{ imageInfo.wwwc }}</div>
</div>
<div class="orientation-top">
@ -83,6 +102,19 @@ import {
setPetTransferFunctionForVolumeActor
} from './helpers/index.js'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import {
renderSegmentation,
readingSegmentByConfig,
selectSegmentation,
selectSegment,
createSegmentationRepresentation,
viewSegmentation,
viewSegment,
jumpBidirectional,
viewBidirectional,
changeColor,
resetViewport
} from "./helpers/segmentations"
export default {
name: 'MPRViewport',
props: {
@ -101,6 +133,32 @@ export default {
histogramVisible: {
type: Boolean,
default: false
},
actionConfiguration: {
type: Object,
default: () => {
return {}
}
},
SegmentConfig: {
type: Object,
default: () => {
return {}
}
},
curSegSeries: {
type: Object,
default: () => {
return {}
}
},
segmentIndex: {
type: Number,
default: 0
},
segmentationId: {
type: String,
default: ''
}
},
data() {
@ -154,6 +212,39 @@ export default {
this.$nextTick(() => {
this.initViewport()
})
DicomEvent.$on('createSegmentationRepresentation', (segmentationId) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
createSegmentationRepresentation(this.viewportId, segmentationId)
})
DicomEvent.$on('viewSegmentation', (obj) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
viewSegmentation(obj, this.viewportId)
})
DicomEvent.$on('viewSegment', (obj) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
viewSegment(obj, this.viewportId)
})
DicomEvent.$on('jumpBidirectional', (obj) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
jumpBidirectional(obj, this.viewportId, this.volumeId)
})
DicomEvent.$on('viewBidirectional', (obj) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
viewBidirectional(obj, this.viewportId)
})
DicomEvent.$on('changeColor', (obj) => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
changeColor(obj, this.viewportId)
})
DicomEvent.$on('resetViewport', () => {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
resetViewport(this.viewportId)
})
DicomEvent.$on('renderSegmentation', async (viewportId) => {
// if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
if (this.viewportId !== viewportId) return false
await renderSegmentation(this.series, this.series.TaskInfo, this.viewportId, this.SegmentConfig, this.segmentationId, this.segmentIndex, this.renderingEngineId, null, this.actionConfiguration)
})
},
watch: {
MPRInfo: {
@ -172,6 +263,25 @@ export default {
}
},
deep: true
},
SegmentConfig: {
handler() {
if (!this.segmentationId) return false
if (!this.series.TaskInfo) return false
readingSegmentByConfig(this.series, this.series.TaskInfo, this.viewportId, this.segmentationId, this.SegmentConfig)
},
deep: true
},
segmentIndex() {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
if (this.segmentIndex <= 0) return false
selectSegment(this.viewportId, this.segmentationId, this.segmentIndex)
},
segmentationId() {
if (this.curSegSeries.Id !== this.series.Id || this.curSegSeries.VisitTaskId !== this.series.VisitTaskId) return false
if (!this.segmentationId) return false
selectSegmentation(this.viewportId, this.segmentationId)
readingSegmentByConfig(this.series, this.series.TaskInfo, this.viewportId, this.segmentationId, this.SegmentConfig)
}
},
methods: {
@ -549,13 +659,9 @@ export default {
console.log("渲染成功")
}
}]).then(r => {
if (data.segment) {
return DicomEvent.$emit("isloaded", { segment: data.segment, isChange: data.isChange })
}
if (data.isLocation) {
setTimeout(() => { csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex }); })
}
DicomEvent.$emit("isloaded", { isChange: data.isChange })
})
viewport.render()
if (this.series.Modality === 'PT' || this.series.Modality === 'NM') {
@ -567,11 +673,10 @@ export default {
renderingEngine.render()
}, 100)
}
await renderSegmentation(this.series, this.series.TaskInfo, this.viewportId, this.SegmentConfig, this.segmentationId, this.segmentIndex, this.renderingEngineId, data.segment, this.actionConfiguration)
} catch (e) {
console.log(e)
}
},
cornerstoneToolsMouseMove(e) {
const { currentPoints } = e.detail

View File

@ -0,0 +1,430 @@
import * as cornerstoneTools from '@cornerstonejs/tools';
import * as cornerstone from "@cornerstonejs/core";
import dcmjs from '@/utils/dcmUpload/dcmjs'
import * as cornerstoneAdapters from "@cornerstonejs/adapters";
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
import * as polySeg from '@cornerstonejs/polymorphic-segmentation'
cornerstoneTools.init({ addons: { polySeg } })
import { getSegmentationList, getSegmentList } from '@/api/reading'
import Vue from 'vue'
const {
ToolGroupManager,
Enums: csToolsEnums,
segmentation,
annotation,
LabelMapEditWithContourTool,
SegmentBidirectionalTool,
CrosshairsTool,
utilities: CStUtils,
} = cornerstoneTools;
const { MouseBindings, Events: toolsEvents } = csToolsEnums
const { segmentation: segmentationUtils } = CStUtils;
const { cache, getRenderingEngine, imageLoader, eventTarget, metaData, utilities: csUtils, volumeLoader } = cornerstone;
const { Cornerstone3D } = cornerstoneAdapters.adaptersSEG;
let viewportInfo = {}
let renderingEngineId = null
async function createSegmentation(toolGroupId, volumeId, segmentationId) {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) || ToolGroupManager.getToolGroup('share-viewport-volume')
toolGroup.setToolActive(
LabelMapEditWithContourTool.toolName,
);
if (!cache.getVolume(segmentationId)) {
await volumeLoader.createAndCacheDerivedLabelmapVolume(
volumeId,
{
volumeId: segmentationId
}
)
}
if (!segmentation.state.getSegmentation(segmentationId)) {
segmentation.addSegmentations([
{
segmentationId,
representation: {
type: cornerstoneTools.Enums.SegmentationRepresentations
.Labelmap,
data: {
volumeId: segmentationId
}
}
}
]);
}
}
async function createSegmentationRepresentation(viewportId, segmentationId) {
segmentation.addSegmentationRepresentations(viewportId, [
{
segmentationId,
type: csToolsEnums.SegmentationRepresentations.Labelmap,
},
])
}
async function readSegmentation(obj, series, segmentationId, isFile = false) {
let imageId = null
if (isFile) {
imageId = cornerstoneDICOMImageLoader.wadouri.fileManager.add(obj);
} else {
const imageIdObj = await cornerstoneDICOMImageLoader.wadouri.loadImage(`wadouri:${Vue.prototype.OSSclientConfig.basePath}${obj}`).promise
imageId = imageIdObj.imageId
}
const image = await imageLoader.loadAndCacheImage(imageId);
if (!image) {
return;
}
const instance = metaData.get("instance", imageId);
if (instance.Modality !== "SEG") {
console.error("This is not segmentation: " + file.name);
return;
}
const arrayBuffer = image.data.byteArray.buffer;
await loadSegmentation(arrayBuffer, series, segmentationId);
}
async function loadSegmentation(arrayBuffer, series, segmentationId) {
const generateToolState =
await Cornerstone3D.Segmentation.generateToolState(
series.ImageIds,
arrayBuffer,
metaData,
);
if (generateToolState.labelmapBufferArray.length !== 1) {
alert(
"Overlapping segments in your segmentation are not supported yet. You can turn on the skipOverlapping option but it will override the overlapping segments."
);
return;
}
// await createSegmentation(segmentationId);
let arr = []
generateToolState.segMetadata.data.forEach(item => {
if (item) {
let Target = JSON.parse(JSON.stringify(item))
arr.push(Target)
}
})
let mapping = {}
arr.forEach((item, index) => {
mapping[index + 1] = Number(item.SegmentNumber)
})
const megmentGroup =
segmentation.state.getSegmentation(segmentationId);
const { imageIds } = megmentGroup.representationData.Labelmap;
const derivedSegmentationImages = imageIds.map(imageId =>
cache.getImage(imageId)
);
const volumeScalarData = new Uint8Array(
generateToolState.labelmapBufferArray[0]
);
const remappedData = new Uint8Array(volumeScalarData.length);
for (let i = 0; i < volumeScalarData.length; i++) {
const value = volumeScalarData[i];
remappedData[i] = value === 0 ? 0 : (mapping[value] ? mapping[value] : value);
}
for (let i = 0; i < derivedSegmentationImages.length; i++) {
const voxelManager = derivedSegmentationImages[i].voxelManager;
const scalarData = voxelManager.getScalarData();
scalarData.set(
remappedData.slice(
i * scalarData.length,
(i + 1) * scalarData.length
)
);
voxelManager.setScalarData(scalarData);
}
}
function hex2Rgb(hexValue, alpha = 1) {
const rgx = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
const hex = hexValue.replace(rgx, (m, r, g, b) => r + r + g + g + b + b);
const rgb = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
if (!rgb) {
return hexValue;
}
const r = parseInt(rgb[1], 16),
g = parseInt(rgb[2], 16),
b = parseInt(rgb[3], 16);
return [r, g, b, alpha * 255];
}
function removeSegmentFromViewport(viewportId) {
return new Promise(resolve => {
if (viewportInfo[viewportId] && viewportInfo[viewportId].length > 0) {
viewportInfo[viewportId].forEach(async segmentationId => {
segmentation.removeSegmentation(segmentationId)
segmentation.state.removeSegmentation(segmentationId)
let annotations = annotation.state.getAllAnnotations().filter(item => item.metadata.segmentationId && segmentationId === item.metadata.segmentationId && item.metadata.segmentIndex && item.metadata.toolName === "SegmentBidirectional");
annotations.forEach(item => {
annotation.state.removeAnnotation(item.annotationUID)
})
})
}
viewportInfo[viewportId] = []
resolve(true)
})
}
function createSegmentConfiguration(segmentIndex, segmentationId, viewportId, actionConfiguration, otherSegments) {
const containedSegmentIndices = otherSegments
? { has: (segmentIndex) => otherSegments.indexOf(segmentIndex) !== -1 }
: undefined;
const colorConfig = segmentation.config.color.getSegmentIndexColor(
viewportId,
segmentationId,
segmentIndex
);
// Allow null style to skip style set
let color, activeColor;
if (colorConfig?.length) {
color = `rgb(${colorConfig.join(',')})`;
activeColor = color;
}
const style = {
color,
colorHighlightedActive: activeColor,
colorActive: activeColor,
textBoxColor: color,
textBoxColorActive: activeColor,
textBoxColorHighlightedActive: activeColor,
};
const label = otherSegments
? `Combined ${segmentIndex} with ${otherSegments.join(', ')}`
: `Segment ${segmentIndex}`;
actionConfiguration.contourBidirectional.data.segmentData.set(segmentIndex, {
containedSegmentIndices,
label,
style,
});
actionConfiguration.contourBidirectional.data.segmentationId = segmentationId
actionConfiguration.contourBidirectional.data.segmentIndex = segmentIndex
}
async function readingSegmentByConfig(series, visitInfo, viewportId, segmentationId, SegmentConfig) {
let data = {
SeriesId: series.Id,
VisitTaskId: visitInfo.VisitTaskId,
PageSize: 9999,
PageIndex: 1,
}
let res = await getSegmentationList(data);
if (res.IsSuccess) {
let list = res.Result.CurrentPageData;
changeInactiveSegmentShow(list, viewportId, segmentationId, SegmentConfig)
}
segmentation.config.style.setStyle(
{
type: csToolsEnums.SegmentationRepresentations.Labelmap,
},
{
renderFill: SegmentConfig.renderFill,
renderOutline: SegmentConfig.renderOutline,
outlineWidth: Number(SegmentConfig.outlineWidth),
fillAlpha: Number(SegmentConfig.fillAlpha),
}
)
}
function selectSegmentation(viewportId, segmentationId) {
segmentation.activeSegmentation.setActiveSegmentation(viewportId, segmentationId)
}
function selectSegment(viewportId, segmentationId, segmentIndex) {
selectSegmentation(viewportId, segmentationId)
segmentation.segmentIndex.setActiveSegmentIndex(segmentationId, segmentIndex);
}
async function changeInactiveSegmentShow(list, viewportId, segmentationId, SegmentConfig) {
let segmentList = list
segmentList.forEach(segment => {
segmentation.config.visibility.setSegmentationRepresentationVisibility(
viewportId,
{
segmentationId: segment.Id,
type: csToolsEnums.SegmentationRepresentations.Labelmap,
},
SegmentConfig.InactiveSegmentations.show
);
})
segmentation.config.visibility.setSegmentationRepresentationVisibility(
viewportId,
{
segmentationId: segmentationId,
type: csToolsEnums.SegmentationRepresentations.Labelmap,
},
true
);
let arr = []
for (let i = 0; i < segmentList.length; i++) {
let item = segmentList[i]
let params = {
SegmentationId: item.Id,
PageSize: 9999,
PageIndex: 1,
}
let r = await getSegmentList(params)
if (r.IsSuccess) {
let segments = r.Result.CurrentPageData
segments.forEach(s => {
let obj = {
segmentationId: item.Id,
segmentIndex: s.SegmentNumber,
view: SegmentConfig.InactiveSegmentations.show
}
if (item.Id === segmentationId) {
obj.view = true
}
arr.push(obj)
})
}
}
viewBidirectional(arr, viewportId)
}
function viewSegmentation(item, viewportId) {
segmentation.config.visibility.setSegmentationRepresentationVisibility(
viewportId,
{
segmentationId: item.segmentationId,
type: csToolsEnums.SegmentationRepresentations.Labelmap,
},
item.view
);
viewBidirectional(item.segments, viewportId)
}
async function jumpBidirectional(item, viewportId, volumeId) {
// DicomEvent.$emit('jumpBidirectional', item)
if (item.bidirectional) {
let an = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
console.log(an, 'an')
if (!an) return false
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(viewportId)
let key = Object.keys(an.data.cachedStats)[0]; // referencedImageId
if (key) {
let sliceIndex = key.split("?")[1].split("&")[0].split("=")[1]
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIndex });
} else {
const points = an.data.handles.points;
const worldPoint = points[0]; // 取一个点
let volume = cache.getVolume(volumeId)
let { imageData, numFrames } = volume
const ijk = imageData.worldToIndex(worldPoint);
const sliceIndex = Math.abs(Math.round(ijk[2]));
// console.log(sliceIndex, 'sliceIndex')
csUtils.jumpToSlice(viewport.element, { imageIndex: numFrames - sliceIndex - 1 });
}
}
}
function viewSegment(item, viewportId) {
segmentation.config.visibility.setSegmentIndexVisibility(viewportId, {
segmentationId: item.segmentationId,
type: csToolsEnums.SegmentationRepresentations.Labelmap,
}, item.segmentIndex, item.view)
viewBidirectional([item], viewportId)
}
function viewBidirectional(arr, viewportId) {
for (let j = 0; j < arr.length; j++) {
let item = arr[j]
let bidirectional = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
// item.bidirectionalView = view
if (!bidirectional) continue
annotation.visibility.setAnnotationVisibility(bidirectional.annotationUID, item.view)
}
resetViewport(viewportId)
}
function resetViewport(viewportId) {
let renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine.getViewport(viewportId)
viewport.render()
}
function changeColor(item, viewportId) {
segmentation.config.color.setSegmentIndexColor(viewportId, item.segmentationId, item.segmentIndex, hex2Rgb(item.color))
}
async function renderSegmentation(series, visitInfo, viewportId, SegmentConfig, segmentationId, segmentIndex, RenderingEngineId, Segment = null, actionConfiguration) {
try {
// console.log(segmentation, 'segmentation')
renderingEngineId = RenderingEngineId
await removeSegmentFromViewport(viewportId)
let data = {
SeriesId: series.Id,
VisitTaskId: visitInfo.VisitTaskId,
PageSize: 9999,
PageIndex: 1,
}
let res = await getSegmentationList(data);
if (res.IsSuccess) {
let list = res.Result.CurrentPageData;
for (let i = 0; i < list.length; i++) {
let item = list[i]
await createSegmentation(viewportId, series.SeriesInstanceUid, item.Id)
if (item.SEGUrl) await readSegmentation(item.SEGUrl, series, item.Id)
createSegmentationRepresentation(viewportId, item.Id)
if (!viewportInfo[viewportId]) {
viewportInfo[viewportId] = [item.Id]
} else {
viewportInfo[viewportId].push(item.Id)
}
let params = {
SegmentationId: item.Id,
PageSize: 9999,
PageIndex: 1,
}
let r = await getSegmentList(params)
if (r.IsSuccess) {
let segments = r.Result.CurrentPageData
segments.forEach(s => {
let SegmentJson = s.SegmentJson ? JSON.parse(s.SegmentJson) : {};
segmentation.segmentIndex.setActiveSegmentIndex(s.SegmentationId, s.SegmentNumber);
segmentation.config.color.setSegmentIndexColor(viewportId, s.SegmentationId, s.SegmentNumber, hex2Rgb(s.ColorRgb))
segmentation.segmentLocking.setSegmentIndexLocked(s.SegmentationId, s.SegmentNumber, true)
if (SegmentJson.bidirectional) {
let { majorAxis, minorAxis } = SegmentJson.bidirectional
// createSegmentConfiguration(s.SegmentNumber, s.SegmentationId, viewportId, actionConfiguration)
SegmentBidirectionalTool.hydrate(viewportId, [majorAxis, minorAxis], {
segmentIndex: s.SegmentNumber,
segmentationId: s.SegmentationId,
})
let an = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === s.SegmentationId && i.metadata.segmentIndex === SegmentJson.bidirectional.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
if (an) {
annotation.locking.setAnnotationLocked(an.annotationUID, true)
annotation.visibility.setAnnotationVisibility(an.annotationUID, item.bidirectionalView)
}
}
})
}
// this.$nextTick(() => {
// if (SEGMENT) {
// // console.log(SEGMENT, 'SEGMENT')
// return this.getBidirectional(obj.segments, SEGMENT)
// }
// this.getBidirectional(obj.segments, null, false)
// })
selectSegment(viewportId, segmentationId, segmentIndex)
if (Segment) {
jumpBidirectional(Segment, viewportId, series.SeriesInstanceUid)
}
}
}
readingSegmentByConfig(series, visitInfo, viewportId, segmentationId, SegmentConfig)
} catch (err) {
console.log(err)
}
}
export {
createSegmentation,
createSegmentationRepresentation,
readSegmentation,
renderSegmentation,
readingSegmentByConfig,
selectSegment,
selectSegmentation,
viewSegmentation,
viewSegment,
jumpBidirectional,
viewBidirectional,
changeColor,
resetViewport
}