diff --git a/package.json b/package.json index 4ab1c6d9..8641f081 100644 --- a/package.json +++ b/package.json @@ -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" ] -} +} \ No newline at end of file diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/MPRViewport.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/MPRViewport.vue index f715b4ee..44cb1d08 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/MPRViewport.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/MPRViewport.vue @@ -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) } diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue index c8b803b5..b58b06af 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue @@ -313,7 +313,9 @@ @dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)"> @@ -362,9 +367,11 @@ + :SegmentConfig="SegmentConfig" :segId.sync="segId" :segIndex.sync="segIndex" + :curSegSeries.sync="curSegSeries" :activeViewportIndex="activeViewportIndex" + :activeTool.sync="activeTool" :actionConfiguration="actionConfiguration" + :histogramVisible="histogramVisible" @setToolsPassive="setToolsPassive" + @resetQuestion="resetQuestion" />
+ + @@ -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) diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue index 3024c415..5f67806e 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue @@ -80,7 +80,8 @@ @click.stop="changeShowSegmentConfig" />
-
+
{{ $t('trials:reading:Segmentations:button:addSegmention') }} @@ -124,16 +125,11 @@ $t('trials:reading:Segmentations:title:InactiveSegmentationsShow') }}
-
-
+
{{ $t("trials:reading:Segmentations:button:saveAll") }} @@ -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 diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/VolumeViewport.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/VolumeViewport.vue index 935e6697..59b3d097 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/VolumeViewport.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/VolumeViewport.vue @@ -14,6 +14,25 @@
Image: #{{ `${series.SliceIndex + 1}/${imageInfo.total}` }}
{{ series.Modality }}
+
+
+
+ +
+
+ {{ series.TaskInfo ? series.TaskInfo.TaskBlindName : '' }} +
+
+ +
+
+
{{ series.Description }}
@@ -40,10 +59,10 @@
Location: {{ `${Number(imageInfo.location).toFixed(digitPlaces)} mm` - }}
+ }}
Slice Thickness: {{ `${Number(imageInfo.sliceThickness).toFixed(digitPlaces)} mm` - }}
+ }}
WW/WL: {{ imageInfo.wwwc }}
@@ -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 diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/helpers/segmentations.js b/src/views/trials/trials-panel/reading/dicoms3D/components/helpers/segmentations.js new file mode 100644 index 00000000..055c0c59 --- /dev/null +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/helpers/segmentations.js @@ -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 +} \ No newline at end of file