From 343b9c25e5edb0bb9df536cd15e178e0c3caf783 Mon Sep 17 00:00:00 2001 From: caiyiling <1321909229@qq.com> Date: Thu, 11 Jan 2024 13:26:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=97=85=E7=81=B6=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=98=BE=E7=A4=BA=E4=BF=A1=E6=81=AF=E5=8F=8A=E7=97=85?= =?UTF-8?q?=E7=81=B6=E4=BF=A1=E6=81=AF=E5=BC=B9=E6=A1=86=E5=B8=83=E5=B1=80?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/css/myViewer.css | 3 +- src/assets/css/viewer.css | 3 +- .../reading/dicoms/components/DicomCanvas.vue | 2 + .../dicoms/components/Fusion/PetCt.vue | 32 +- .../components/Fusion/TableQuestionItem.vue | 41 ++- .../components/Fusion/TableQuestions.vue | 6 + .../components/Fusion/tools/CircleROITool.js | 256 ++++++---------- .../dicoms/components/Lugano/QuestionForm.vue | 282 +++++++++--------- .../dicoms/components/Lugano/QuestionList.vue | 4 +- .../reading/dicoms/tools/Length/LengthTool.js | 9 +- vue.config.js | 2 +- 11 files changed, 320 insertions(+), 320 deletions(-) diff --git a/src/assets/css/myViewer.css b/src/assets/css/myViewer.css index fc33e293..4784b08d 100644 --- a/src/assets/css/myViewer.css +++ b/src/assets/css/myViewer.css @@ -377,7 +377,8 @@ } .viewer-backdrop { - background-color: rgba(0, 0, 0, 50%); + /* background-color: rgba(0, 0, 0, 50%); */ + background-color:rgb(255, 255, 255, 50%); } .viewer-invisible { diff --git a/src/assets/css/viewer.css b/src/assets/css/viewer.css index 2716fdbc..74e024a9 100644 --- a/src/assets/css/viewer.css +++ b/src/assets/css/viewer.css @@ -391,7 +391,8 @@ } .viewer-backdrop { - background-color: rgba(0, 0, 0, 50%); + /* background-color: rgba(0, 0, 0, 50%); */ + background-color:rgb(255, 255, 255, 50%); } .viewer-invisible { diff --git a/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue b/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue index da86bb19..a5877ce1 100644 --- a/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue +++ b/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue @@ -159,6 +159,7 @@ cornerstoneTools.external.cornerstoneMath = cornerstoneMath cornerstoneTools.toolStyle.setToolWidth(1) cornerstoneTools.toolColors.setToolColor('rgb(255, 0, 0)') cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)') +cornerstoneTools.store.state.deleteIfHandleOutsideImage = false // cornerstoneTools.init({ globalToolSyncEnabled: true }) cornerstoneTools.init() export default { @@ -331,6 +332,7 @@ export default { }, mounted() { + console.log(cornerstoneTools) this.subjectCode = this.$router.currentRoute.query.subjectCode document.addEventListener('mouseup', () => { this.sliderMouseup() diff --git a/src/views/trials/trials-panel/reading/dicoms/components/Fusion/PetCt.vue b/src/views/trials/trials-panel/reading/dicoms/components/Fusion/PetCt.vue index 6d2b017b..e92faee1 100644 --- a/src/views/trials/trials-panel/reading/dicoms/components/Fusion/PetCt.vue +++ b/src/views/trials/trials-panel/reading/dicoms/components/Fusion/PetCt.vue @@ -371,7 +371,7 @@ import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunc import { mat4, vec3 } from 'gl-matrix' // import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction' // import vtkMath from '@kitware/vtk.js/Common/Core/Math' -// import CircleROITool from './tools/CircleROITool' +import CircleROITool from './tools/CircleROITool' // import ScaleOverlayTool from './tools/ScaleOverlayTool' const { registerColormap, getColormapNames, getColormap } = csUtils.colormap const { @@ -388,7 +388,7 @@ const { // SynchronizerManager, // LengthTool, EllipticalROITool, - CircleROITool, + // CircleROITool, CrosshairsTool, TrackballRotateTool, ProbeTool, @@ -696,19 +696,27 @@ export default { if (isNotValidAnnotationNum === 0) { this.onAnnotationModified(e) } else { - // 移除标记 - this.removeAnnotation({ otherMeasureData: annotation }) + // annotation.isInsideVolume = false + console.log('异常标记:',annotation) const { remark } = annotation.data - // 清除病灶上的标记信息 if (remark === 'Liver' || remark === 'Mediastinum') { - this.$refs['questions'].clearMeasuredData(remark) - // 激活工具 - this.setNonTargetMeasurementStatus({ status: true, toolName: 'CircleROI' }) - } else { - this.$refs['tableQuestions'].clearMeasuredData() - // 激活工具 - this.setBasicToolActive('CircleROI') + this.$refs['questions'].setOutsideMeasuredData(annotation) + }else{ + this.$refs['tableQuestions'].setOutsideMeasuredData(annotation) } + // // 移除标记 + // this.removeAnnotation({ otherMeasureData: annotation }) + // const { remark } = annotation.data + // // 清除病灶上的标记信息 + // if (remark === 'Liver' || remark === 'Mediastinum') { + // this.$refs['questions'].clearMeasuredData(remark) + // // 激活工具 + // this.setNonTargetMeasurementStatus({ status: true, toolName: 'CircleROI' }) + // } else { + // this.$refs['tableQuestions'].clearMeasuredData() + // // 激活工具 + // this.setBasicToolActive('CircleROI') + // } } }, 120) eventTarget.addEventListener(cornerstoneTools.Enums.Events.ANNOTATION_MODIFIED, (e) => { diff --git a/src/views/trials/trials-panel/reading/dicoms/components/Fusion/TableQuestionItem.vue b/src/views/trials/trials-panel/reading/dicoms/components/Fusion/TableQuestionItem.vue index e6810102..105880dd 100644 --- a/src/views/trials/trials-panel/reading/dicoms/components/Fusion/TableQuestionItem.vue +++ b/src/views/trials/trials-panel/reading/dicoms/components/Fusion/TableQuestionItem.vue @@ -256,8 +256,8 @@ export default { deleteInfo: null, pictureBaseStr: '', currentMarkTool: '', - firstRenderAnnotation: true - + firstRenderAnnotation: true, + isInsideVolume:true } }, mounted() { @@ -522,6 +522,7 @@ export default { }, setMeasureData(measureData, isInit = false) { console.log('setMeasureData') + this.isInsideVolume = true return new Promise(resolve => { if (measureData) { // 获取SUVmax(20) @@ -610,6 +611,31 @@ export default { resolve() }) }, + setOutsideMeasuredData(measureData){ + this.isInsideVolume = false + var suvObj = this.questions.find(i => i.QuestionMark === 20) + this.$set(this.questionForm, suvObj.Id, '') + if (this.questionForm.RowId) { + this.$set(this.questionForm, 'saveTypeEnum', 1) + } else { + this.$set(this.questionForm, 'saveTypeEnum', 0) + } + this.$set(this.questionForm, 'OtherMeasureData', measureData) + var isLymph = this.getQuestionVal(2) + isLymph = !isNaN(parseInt(isLymph)) ? parseInt(isLymph) : null + const lesionPart = this.getQuestionVal(8) + const lesionOrgan = this.getQuestionVal(6) + var suvMax = this.getQuestionVal(20) + this.$emit('resetQuestions', { isLymphLesion: isLymph, lesionPart, lesionOrgan, suvMax, saveTypeEnum: this.questionForm.saveTypeEnum, rowIndex: this.rowIndex, questionId: this.parentQsId, anwsers: this.questionForm }) + this.toolType = measureData.type + if (this.questionForm.MeasureData) { + this.organList = [] + this.getOrganInfoList() + } else { + this.organList = [] + this.getOrganInfoList() + } + }, returnFloat(num) { if (num) return var value = Math.round(parseFloat(num) * 100) / 100 @@ -693,6 +719,17 @@ export default { handleSave() { this.$refs.measurementForm.validate(async valid => { if (!valid) return + if(!this.isInsideVolume){ + this.$alert('当前标记在图像外,不允许保存!', '提示', { + callback: action => { + this.$message({ + type: 'info', + message: `action: ${ action }` + }); + } + }) + return + } const loading = this.$loading({ fullscreen: true }) var otherMeasureData = this.questionForm.OtherMeasureData ? Object.assign({}, this.questionForm.OtherMeasureData) : null if (otherMeasureData) { diff --git a/src/views/trials/trials-panel/reading/dicoms/components/Fusion/TableQuestions.vue b/src/views/trials/trials-panel/reading/dicoms/components/Fusion/TableQuestions.vue index 3295b2a8..c90281b6 100644 --- a/src/views/trials/trials-panel/reading/dicoms/components/Fusion/TableQuestions.vue +++ b/src/views/trials/trials-panel/reading/dicoms/components/Fusion/TableQuestions.vue @@ -749,6 +749,12 @@ export default { this.$refs[refName][0].clearMeasurement() } }, + setOutsideMeasuredData(annotation){ + if (this.activeItem.activeCollapseId && this.activeItem.activeRowIndex && this.activeName) { + const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}` + this.$refs[refName][0].setOutsideMeasuredData(annotation) + } + }, // 设置测量数据 setMeasuredData(measureData) { // this.$nextTick(() => { diff --git a/src/views/trials/trials-panel/reading/dicoms/components/Fusion/tools/CircleROITool.js b/src/views/trials/trials-panel/reading/dicoms/components/Fusion/tools/CircleROITool.js index b0d7091f..467bde77 100644 --- a/src/views/trials/trials-panel/reading/dicoms/components/Fusion/tools/CircleROITool.js +++ b/src/views/trials/trials-panel/reading/dicoms/components/Fusion/tools/CircleROITool.js @@ -2,7 +2,8 @@ import { eventTarget, triggerEvent, VolumeViewport, - utilities as csUtils + utilities as csUtils, + getEnabledElement } from '@cornerstonejs/core' const { transformWorldToIndex } = csUtils @@ -11,21 +12,22 @@ const { Enums, utilities, annotation, - drawing + drawing, + cursors } = cornerstoneTools - // const { getWorldWidthAndHeightFromTwoPoints } = utilities.planar // const { roundNumber } = utilities - -const { getAnnotations } = annotation.state +const { hideElementCursor } = cursors.elementCursor +const { getAnnotations,addAnnotation } = annotation.state const { isAnnotationVisible } = annotation.visibility const { isAnnotationLocked } = annotation.locking const drawHandlesSvg = drawing.drawHandles const drawCircleSvg = drawing.drawCircle const drawLinkedTextBoxSvg = drawing.drawLinkedTextBox +const { triggerAnnotationRenderForViewportIds } = utilities const { getTextBoxCoordsCanvas } = utilities.drawing -console.log(utilities) +const { getViewportIdsWithToolToRender } = utilities.viewportFilters // const getCanvasCircleRadius // const getCanvasCircleCorners // const { getCanvasCircleRadius, getCanvasCircleCorners } = utilities.math.circle @@ -40,6 +42,14 @@ import { pointInShapeCallback } from './../js/pointInShapeCallback' import { vec3 } from 'gl-matrix' class CircleROITool extends cornerstoneTools.CircleROITool { static toolName; + // editData= { + // annotation: null, + // viewportIdsToRender: [], + // handleIndex: null, + // movingTextBox: null, + // newAnnotation: null, + // hasMoved: null + // } // touchDragCallback: any; // mouseDragCallback: any; // _throttledCalculateCachedStats: any; @@ -73,6 +83,81 @@ class CircleROITool extends cornerstoneTools.CircleROITool { // ) // this._getTextLines = this.getTextLines } + // addNewAnnotation = (evt) => { + // const eventDetail = evt.detail; + // const { currentPoints, element } = eventDetail; + // const worldPos = currentPoints.world; + // // const canvasPos = currentPoints.canvas; + + // const enabledElement = getEnabledElement(element); + // const { viewport, renderingEngine } = enabledElement; + + // this.isDrawing = true; + + // const camera = viewport.getCamera(); + // const { viewPlaneNormal, viewUp } = camera; + + // const referencedImageId = this.getReferencedImageId( + // viewport, + // worldPos, + // viewPlaneNormal, + // viewUp + // ); + + // const FrameOfReferenceUID = viewport.getFrameOfReferenceUID(); + + // const annotation = { + // highlighted: true, + // invalidated: true, + // metadata: { + // toolName: this.getToolName(), + // viewPlaneNormal: [...viewPlaneNormal], + // viewUp: [...viewUp], + // FrameOfReferenceUID, + // referencedImageId, + // }, + // data: { + // label: '', + // handles: { + // textBox: { + // hasMoved: false, + // worldPosition: [0, 0, 0], + // worldBoundingBox: { + // topLeft: [0, 0, 0], + // topRight: [0, 0, 0], + // bottomLeft: [0, 0, 0], + // bottomRight: [0, 0, 0], + // }, + // }, + // points: [[...worldPos], [...worldPos]], + // activeHandleIndex: 1, + // }, + // cachedStats: {}, + // }, + // }; + // addAnnotation(annotation, element); + + // const viewportIdsToRender = getViewportIdsWithToolToRender( + // element, + // this.getToolName() + // ); + + // this.editData = { + // annotation, + // viewportIdsToRender, + // newAnnotation: true, + // hasMoved: false, + // }; + // this._activateDraw(element); + + // hideElementCursor(element); + + // evt.preventDefault(); + + // triggerAnnotationRenderForViewportIds(renderingEngine, viewportIdsToRender); + + // return annotation; + // }; renderAnnotation = (enabledElement, svgDrawingHelper) => { let renderStatus = false const { viewport } = enabledElement @@ -107,7 +192,12 @@ class CircleROITool extends cornerstoneTools.CircleROITool { const annotation = annotations[i] const { annotationUID, data } = annotation const { handles } = data - const { points, activeHandleIndex } = handles + // const { points, activeHandleIndex } = handles + const { points } = handles + var activeHandleIndex = null + if(annotation.highlighted && !annotation.isLocked && annotation.isVisible){ + activeHandleIndex = 1 + } styleSpecifier.annotationUID = annotationUID @@ -151,7 +241,6 @@ class CircleROITool extends cornerstoneTools.CircleROITool { radiusUnit: null, perimeter: null } - console.log(111111) this._calculateCachedStats( annotation, viewport, @@ -160,7 +249,6 @@ class CircleROITool extends cornerstoneTools.CircleROITool { modalityUnitOptions ) } else if (annotation.invalidated) { - console.log(22222) this._throttledCalculateCachedStats( annotation, @@ -333,156 +421,6 @@ class CircleROITool extends cornerstoneTools.CircleROITool { return renderStatus }; - _calculateCachedStats1 = ( - annotation, - viewport, - renderingEngine, - enabledElement, - modalityUnitOptions - ) => { - const data = annotation.data - const { viewportId, renderingEngineId } = enabledElement - - const { points } = data.handles - - const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p)) - const { viewPlaneNormal, viewUp } = viewport.getCamera() - - const [topLeftCanvas, bottomRightCanvas] = ( - getCanvasCircleCorners(canvasCoordinates) - ) - - const topLeftWorld = viewport.canvasToWorld(topLeftCanvas) - const bottomRightWorld = viewport.canvasToWorld(bottomRightCanvas) - const { cachedStats } = data - - const targetIds = Object.keys(cachedStats) - const worldPos1 = topLeftWorld - const worldPos2 = bottomRightWorld - for (let i = 0; i < targetIds.length; i++) { - const targetId = targetIds[i] - - const image = this.getTargetIdImage(targetId, renderingEngine) - - // If image does not exists for the targetId, skip. This can be due - // to various reasons such as if the target was a volumeViewport, and - // the volumeViewport has been decached in the meantime. - if (!image) { - continue - } - - const { dimensions, imageData, metadata } = image - - const worldPos1Index = transformWorldToIndex(imageData, worldPos1) - - worldPos1Index[0] = Math.floor(worldPos1Index[0]) - worldPos1Index[1] = Math.floor(worldPos1Index[1]) - worldPos1Index[2] = Math.floor(worldPos1Index[2]) - - const worldPos2Index = transformWorldToIndex(imageData, worldPos2) - - worldPos2Index[0] = Math.floor(worldPos2Index[0]) - worldPos2Index[1] = Math.floor(worldPos2Index[1]) - worldPos2Index[2] = Math.floor(worldPos2Index[2]) - - // Check if one of the indexes are inside the volume, this then gives us - // Some area to do stats over. - - if (this._isInsideVolume(worldPos1Index, worldPos2Index, dimensions)) { - const iMin = Math.min(worldPos1Index[0], worldPos2Index[0]) - const iMax = Math.max(worldPos1Index[0], worldPos2Index[0]) - - const jMin = Math.min(worldPos1Index[1], worldPos2Index[1]) - const jMax = Math.max(worldPos1Index[1], worldPos2Index[1]) - - const kMin = Math.min(worldPos1Index[2], worldPos2Index[2]) - const kMax = Math.max(worldPos1Index[2], worldPos2Index[2]) - - const boundsIJK = [ - [iMin, iMax], - [jMin, jMax], - [kMin, kMax] - ] - - const center = [ - (topLeftWorld[0] + bottomRightWorld[0]) / 2, - (topLeftWorld[1] + bottomRightWorld[1]) / 2, - (topLeftWorld[2] + bottomRightWorld[2]) / 2 - ] - - const ellipseObj = { - center, - xRadius: Math.abs(topLeftWorld[0] - bottomRightWorld[0]) / 2, - yRadius: Math.abs(topLeftWorld[1] - bottomRightWorld[1]) / 2, - zRadius: Math.abs(topLeftWorld[2] - bottomRightWorld[2]) / 2 - } - const { worldWidth, worldHeight } = getWorldWidthAndHeightFromTwoPoints( - viewPlaneNormal, - viewUp, - worldPos1, - worldPos2 - ) - const isEmptyArea = worldWidth === 0 && worldHeight === 0 - const scale = getCalibratedScale(image) - const aspect = getCalibratedAspect(image) - const area = Math.abs( - Math.PI * - (worldWidth / scale / 2) * - (worldHeight / aspect / scale / 2) - ) - - const modalityUnit = getModalityUnit( - metadata.Modality, - annotation.metadata.referencedImageId, - modalityUnitOptions - ) - const pointsInShape = pointInShapeCallback( - imageData, - (pointLPS, pointIJK) => pointInEllipse(ellipseObj, pointLPS), - this.configuration.statsCalculator.statsCallback, - boundsIJK - ) - - const stats = this.configuration.statsCalculator.getStatistics() - - cachedStats[targetId] = { - Modality: metadata.Modality, - area, - mean: stats[1] && stats[1].value ? stats[1].value : null, - max: stats[0] && stats[0].value ? stats[0].value : null, - stdDev: stats[2] && stats[2].value ? stats[2].value : null, - statsArray: stats, - pointsInShape: pointsInShape, - isEmptyArea, - areaUnit: getCalibratedAreaUnits(null, image), - radius: worldWidth / 2 / scale, - radiusUnit: getCalibratedLengthUnits(null, image), - perimeter: (2 * Math.PI * (worldWidth / 2)) / scale, - modalityUnit - } - } else { - this.isHandleOutsideImage = true - - cachedStats[targetId] = { - Modality: metadata.Modality - } - } - } - - annotation.invalidated = false - - // Dispatching annotation modified - const eventType = Enums.Events.ANNOTATION_MODIFIED - - const eventDetail = { - annotation, - viewportId, - renderingEngineId - } - triggerEvent(eventTarget, eventType, eventDetail) - - return cachedStats - } } function getCanvasCircleCorners( circleCanvasPoints diff --git a/src/views/trials/trials-panel/reading/dicoms/components/Lugano/QuestionForm.vue b/src/views/trials/trials-panel/reading/dicoms/components/Lugano/QuestionForm.vue index 12960e01..d5245b84 100644 --- a/src/views/trials/trials-panel/reading/dicoms/components/Lugano/QuestionForm.vue +++ b/src/views/trials/trials-panel/reading/dicoms/components/Lugano/QuestionForm.vue @@ -18,116 +18,133 @@ - - - - + - - - - - - - - - - - - - - - - -