分割添加轮廓工具

uat_us
wangxiaoshuang 2026-03-24 15:09:45 +08:00
parent d07adc4948
commit 4e5ecb3938
7 changed files with 194 additions and 99 deletions

View File

@ -0,0 +1 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" class="h-6 w-6"><g id="tool-seg-brush" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><rect id="Rectangle" x="0" y="0" width="24" height="24"></rect><path d="M3.24640621,21.8286833 C3.09173375,21.7551472 2.99513748,21.5971513 3.00018895,21.4259625 C3.00524042,21.2547737 3.11098486,21.1027485 3.26972426,21.0384606 C5.3260304,20.2059201 4.66362518,17.8620247 5.27421252,16.6088957 C6.02197747,15.1026514 7.84114758,14.4766383 9.35746132,15.2037675 C13.9485253,17.4422999 8.48346644,24.3211232 3.24640621,21.8286833 Z" id="Path" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M21.5968442,2.302022 C21.1256527,1.8826075 20.4094461,1.90228142 19.9619901,2.34693083 L9.69255027,12.5887345 C11.0536437,13.0051578 12.1843437,13.9616637 12.8206229,15.2349008 L21.7410706,3.93687606 C22.1347378,3.44008272 22.0714081,2.72222021 21.5968442,2.302022 Z" id="Path" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

1
src/icons/svg/jumpto.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1774331916694" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1627" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M198.4 960A134.4 134.4 0 0 1 64 825.6V198.4A134.4 134.4 0 0 1 198.4 64h268.8a44.8 44.8 0 0 1 44.8 44.8 44.8 44.8 0 0 1-44.8 44.8H198.4a44.8 44.8 0 0 0-44.8 44.8v627.2a44.8 44.8 0 0 0 44.8 44.8h627.2a44.8 44.8 0 0 0 44.8-44.8V556.8a44.8 44.8 0 0 1 44.8-44.8 44.8 44.8 0 0 1 44.8 44.8v268.8A134.4 134.4 0 0 1 825.6 960z m281.984-416.384a44.8 44.8 0 0 1 0-63.296L807.232 153.6H646.464a44.8 44.8 0 0 1-44.8-44.8 44.8 44.8 0 0 1 44.8-44.8h267.264a44.8 44.8 0 0 1 28.288 8.896l1.408 1.152 0.64 0.512 1.024 0.96a41.216 41.216 0 0 1 3.136 3.072l0.896 1.088 0.512 0.64 1.152 1.344a44.8 44.8 0 0 1 8.832 28.288V377.28a44.8 44.8 0 0 1-44.8 44.8 44.8 44.8 0 0 1-44.8-44.8V216.512l-326.4 326.4a44.8 44.8 0 0 1-32 13.248A44.8 44.8 0 0 1 480 543.616z" p-id="1628" fill="#2c2c2c"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -363,9 +363,9 @@
<el-tab-pane :label="$t('trials:reading:dicom3D:tabs:segment')" name="segment"> <el-tab-pane :label="$t('trials:reading:dicom3D:tabs:segment')" name="segment">
<Segmentations ref="Segmentations" :visitInfo="taskInfo" :isMPR="isMPR" <Segmentations ref="Segmentations" :visitInfo="taskInfo" :isMPR="isMPR"
:volumeToolGroupId="volumeToolGroupId" :viewportKey="viewportKey" :volumeToolGroupId="volumeToolGroupId" :viewportKey="viewportKey"
:activeViewportIndex="activeViewportIndex" :activeTool.sync="activeTool" :rendering-engine-id="renderingEngineId" :activeViewportIndex="activeViewportIndex"
:actionConfiguration="actionConfiguration" @setToolsPassive="setToolsPassive" :activeTool.sync="activeTool" :actionConfiguration="actionConfiguration"
@resetQuestion="resetQuestion" /> @setToolsPassive="setToolsPassive" @resetQuestion="resetQuestion" />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<template v-else> <template v-else>
@ -479,7 +479,7 @@
<el-dialog :visible.sync="segmentVisible" :close-on-click-modal="false" :title="$t('trials:segment:title:bind')" <el-dialog :visible.sync="segmentVisible" :close-on-click-modal="false" :title="$t('trials:segment:title:bind')"
width="550px"> width="550px">
<SegmentForm v-if="segmentVisible" :visible.sync="segmentVisible" :visitInfo="segmentVisitInfo" <SegmentForm ref="SegmentForm" v-if="segmentVisible" :visible.sync="segmentVisible" :visitInfo="segmentVisitInfo"
@handleSegmentSave="handleSegmentSave" /> @handleSegmentSave="handleSegmentSave" />
</el-dialog> </el-dialog>
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId" <upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId"
@ -569,7 +569,7 @@ const {
CrosshairsTool, CrosshairsTool,
EllipticalROITool, EllipticalROITool,
synchronizers, synchronizers,
PlanarFreehandContourSegmentationTool, LabelMapEditWithContourTool,
BrushTool, BrushTool,
SegmentBidirectionalTool, SegmentBidirectionalTool,
utilities: CStUtils, utilities: CStUtils,
@ -898,9 +898,13 @@ export default {
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].handleSegmentSave(obj) this.$refs[`ecrf_${this.lastViewportTaskId}`][0].handleSegmentSave(obj)
}, },
openSegmentForm(obj) { openSegmentForm(obj) {
let { visitInfo } = obj // let { visitInfo, operateStateEnum } = obj
this.segmentVisitInfo = visitInfo this.segmentVisitInfo = obj
this.segmentVisible = true this.segmentVisible = true
this.$nextTick(() => {
this.$refs.SegmentForm.setSeries(this.$refs[`viewport-${this.activeViewportIndex}`][0].series)
this.$refs.SegmentForm.init()
})
}, },
handleClick(tab, event) { handleClick(tab, event) {
this.formWrapperActiveName = tab.name this.formWrapperActiveName = tab.name
@ -1323,7 +1327,7 @@ export default {
cornerstoneTools.addTool(MIPJumpToClickTool) cornerstoneTools.addTool(MIPJumpToClickTool)
cornerstoneTools.addTool(VolumeRotateTool) cornerstoneTools.addTool(VolumeRotateTool)
cornerstoneTools.addTool(CrosshairsTool) cornerstoneTools.addTool(CrosshairsTool)
cornerstoneTools.addTool(PlanarFreehandContourSegmentationTool) cornerstoneTools.addTool(LabelMapEditWithContourTool)
cornerstoneTools.addTool(BrushTool) cornerstoneTools.addTool(BrushTool)
cornerstoneTools.addTool(SegmentBidirectionalTool) cornerstoneTools.addTool(SegmentBidirectionalTool)
viewportIds.forEach((viewportId, i) => { viewportIds.forEach((viewportId, i) => {
@ -1364,21 +1368,8 @@ export default {
} }
) )
toolGroup.addTool(PlanarFreehandContourSegmentationTool.toolName, { toolGroup.addTool(LabelMapEditWithContourTool.toolName);
cachedStats: true, toolGroup.addTool(SegmentBidirectionalTool.toolName);
});
toolGroup.addTool(SegmentBidirectionalTool.toolName, {});
// toolGroup.setToolActive(PlanarFreehandContourSegmentationTool.toolName, {
// bindings: [
// {
// mouseButton: MouseBindings.Primary, // Left Click
// },
// {
// mouseButton: MouseBindings.Primary, // Shift + Left Click
// modifierKey: cornerstoneTools.Enums.KeyboardBindings.Shift,
// },
// ],
// });
} }
if (volumeViewportIds.includes(viewportId)) { if (volumeViewportIds.includes(viewportId)) {
toolGroup.addTool(WindowLevelTool.toolName, { toolGroup.addTool(WindowLevelTool.toolName, {
@ -1490,7 +1481,7 @@ export default {
toolGroup.setToolPassive(AngleTool.toolName) toolGroup.setToolPassive(AngleTool.toolName)
toolGroup.setToolPassive(CobbAngleTool.toolName) toolGroup.setToolPassive(CobbAngleTool.toolName)
if (this.readingTool === 3) { if (this.readingTool === 3) {
toolGroup.setToolPassive(PlanarFreehandContourSegmentationTool.toolName) toolGroup.setToolPassive(LabelMapEditWithContourTool.toolName)
toolGroup.setToolPassive(SegmentBidirectionalTool.toolName, {}); toolGroup.setToolPassive(SegmentBidirectionalTool.toolName, {});
} }
} else { } else {
@ -1504,11 +1495,10 @@ export default {
toolGroup.setToolEnabled(FixedRadiusCircleROITool.toolName) toolGroup.setToolEnabled(FixedRadiusCircleROITool.toolName)
toolGroup.setToolEnabled(AngleTool.toolName) toolGroup.setToolEnabled(AngleTool.toolName)
toolGroup.setToolEnabled(CobbAngleTool.toolName) toolGroup.setToolEnabled(CobbAngleTool.toolName)
if (this.readingTool === 3) toolGroup.setToolEnabled(PlanarFreehandContourSegmentationTool.toolName) if (this.readingTool === 3) toolGroup.setToolEnabled(LabelMapEditWithContourTool.toolName)
} }
toolGroup.setToolPassive(EraserTool.toolName) toolGroup.setToolPassive(EraserTool.toolName)
}) })
eventTarget.addEventListener('cornerstoneimageloadprogress', this.imageLoadProgress) eventTarget.addEventListener('cornerstoneimageloadprogress', this.imageLoadProgress)
// console.log(Events, toolsEvents) // console.log(Events, toolsEvents)
if (this.readingTool === 2) { if (this.readingTool === 2) {
@ -1517,6 +1507,7 @@ export default {
if ((this.criterionType === 0 && this.readingTool === 0) || this.readingTool === 3) { if ((this.criterionType === 0 && this.readingTool === 0) || this.readingTool === 3) {
this.setUpSynchronizersMPR() this.setUpSynchronizersMPR()
} }
renderingEngine.render();
}, },
// //
imageLoadProgress(e) { imageLoadProgress(e) {
@ -1720,7 +1711,7 @@ export default {
if (!annotation) return if (!annotation) return
const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName) const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName)
if (i === -1) { if (i === -1) {
if (annotation.metadata.toolName !== PlanarFreehandContourSegmentationTool.toolName) this.setToolsPassive() if (annotation.metadata.toolName !== LabelMapEditWithContourTool.toolName) this.setToolsPassive()
return return
} }
if (annotation.data.segmentation || annotation.data.segment) return if (annotation.data.segmentation || annotation.data.segment) return
@ -1807,7 +1798,7 @@ export default {
if (!annotation) return if (!annotation) return
const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName) const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName)
if (i === -1) { if (i === -1) {
if (annotation.metadata.toolName !== PlanarFreehandContourSegmentationTool.toolName) this.setToolsPassive() if (annotation.metadata.toolName !== LabelMapEditWithContourTool.toolName) this.setToolsPassive()
return return
} }
if (annotation.data.segmentation || annotation.data.segment) return if (annotation.data.segmentation || annotation.data.segment) return
@ -3026,7 +3017,7 @@ export default {
obj.IsMarked = true obj.IsMarked = true
obj.MeasureData = annotation obj.MeasureData = annotation
} }
if (annotation.from === 'MPR' && checkFrom) { if ((annotation.from === 'MPR' || annotation.metadata.volumeId) && checkFrom) {
obj = Object.assign({}, seriesList[seriesIdx]) obj = Object.assign({}, seriesList[seriesIdx])
obj.SliceIndex = annotation?.metadata?.sliceIndex obj.SliceIndex = annotation?.metadata?.sliceIndex
obj.IsMarked = true obj.IsMarked = true
@ -3792,6 +3783,15 @@ export default {
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.form-wrapper {
::v-deep .el-tabs__nav-scroll {
width: 100%;
display: flex;
justify-content: center;
}
}
.read-page-container { .read-page-container {
box-sizing: border-box; box-sizing: border-box;
height: 100%; height: 100%;

View File

@ -37,7 +37,7 @@
</el-form> </el-form>
</template> </template>
<script> <script>
import { getSegmentationList, getSegmentList } from '@/api/reading' import { getSegmentationList, getSegmentList, getSegmentBindingList } from '@/api/reading'
export default { export default {
name: 'FusionForm', name: 'FusionForm',
props: { props: {
@ -64,13 +64,17 @@ export default {
seriesList: [], seriesList: [],
segmentGroupList: [], segmentGroupList: [],
segmentList: [], segmentList: [],
segmentionList: [] segmentionList: [],
series: {}
} }
}, },
mounted() { // mounted() {
this.init() // this.init()
}, // },
methods: { methods: {
setSeries(series) {
this.series = series
},
async init() { async init() {
let studyList = this.visitInfo.StudyList let studyList = this.visitInfo.StudyList
let s = await this.getSegmentationList() let s = await this.getSegmentationList()
@ -82,6 +86,25 @@ export default {
study.SeriesList = study.SeriesList.filter(item => SeriesIds.includes(item.Id)) study.SeriesList = study.SeriesList.filter(item => SeriesIds.includes(item.Id))
}) })
this.studyList = studyList this.studyList = studyList
if (this.visitInfo.operateStateEnum === 21) {
this.form.studyId = this.series.StudyId
this.form.seriesId = this.series.Id
}
if (this.visitInfo.operateStateEnum === 22) {
let o = {}
if (this.isTableQuestion) {
o.TableQuestionId = this.visitInfo.operateQuestionId
o.RowId = this.visitInfo.operateRowId
} else {
o.QuestionId = this.visitInfo.operateQuestionId
}
let list = await this.getSegmentBindingList(o)
this.form.segmentGroupId = list[0].SegmentationId
this.form.segmentId = list[0].SegmentId
let segmentGroup = this.segmentionList.find(item => item.Id === this.form.segmentGroupId)
this.form.studyId = segmentGroup.StudyId
this.form.seriesId = segmentGroup.SeriesId
}
}, },
async handleChange(e, key) { async handleChange(e, key) {
if (key === 'study') { if (key === 'study') {
@ -147,6 +170,23 @@ export default {
console.log(err) console.log(err)
} }
}, },
//
async getSegmentBindingList(param = {}) {
try {
let data = {
VisitTaskId: this.visitInfo.VisitTaskId,
PageSize: 9999,
PageIndex: 1,
}
data = Object.assign(data, param)
let res = await getSegmentBindingList(data)
if (res.IsSuccess) {
return res.Result.CurrentPageData
}
} catch (err) {
console.log(err)
}
},
} }
} }
</script> </script>

View File

@ -7,10 +7,16 @@
</template> </template>
<div class="tool-frame"> <div class="tool-frame">
<div :title="$t('trials:Segmentations:tools:contour')" <div :title="$t('trials:Segmentations:tools:contour')"
:class="['tool-item', activeTool === 'LabelMapEditWithContour' && segmentList.length > 0 ? 'tool-item-active' : '']"
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }"
@click.prevent="setToolActive('LabelMapEditWithContour')">
<svg-icon icon-class="contour" class="svg-icon" />
</div>
<div :title="$t('trials:Segmentations:tools:circularbrush')"
:class="['tool-item', activeTool === 'CircularBrush' && segmentList.length > 0 ? 'tool-item-active' : '']" :class="['tool-item', activeTool === 'CircularBrush' && segmentList.length > 0 ? 'tool-item-active' : '']"
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }" :style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }"
@click.prevent="setToolActive('CircularBrush')"> @click.prevent="setToolActive('CircularBrush')">
<svg-icon icon-class="contour" class="svg-icon" /> <svg-icon icon-class="circularbrush" class="svg-icon" />
</div> </div>
<div :class="['tool-item', activeTool === 'CircularEraser' && segmentList.length > 0 ? 'tool-item-active' : '']" <div :class="['tool-item', activeTool === 'CircularEraser' && segmentList.length > 0 ? 'tool-item-active' : '']"
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }" :style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }"
@ -38,7 +44,7 @@
</template> </template>
<div class="addSegmentBox viewHover" @click.stop="addSegment" v-if="segmentList.length <= 0"> <div class="addSegmentBox viewHover" @click.stop="addSegment" v-if="segmentList.length <= 0">
<span><i class="el-icon-plus"></i> <span><i class="el-icon-plus"></i>
{{ $t('trials:reading:Segmentations:button:addSegment') }} {{ $t('trials:reading:Segmentations:button:addSegmention') }}
</span> </span>
</div> </div>
<div class="SegmentConfigBox" v-if="showSegmentConfig && segmentList.length > 0"> <div class="SegmentConfigBox" v-if="showSegmentConfig && segmentList.length > 0">
@ -89,7 +95,7 @@
</div> </div>
<template v-if="segmentList.length > 0"> <template v-if="segmentList.length > 0">
<div class="SegmentGroupBox"> <div class="SegmentGroupBox">
<el-popover placement="bottom" width="40" trigger="click"> <el-popover placement="left" width="40" trigger="click">
<div class="SegmentGroupBtnBox"> <div class="SegmentGroupBtnBox">
<div class="SegmentGroupBtn" @click.stop="addSegmentGroup"> <div class="SegmentGroupBtn" @click.stop="addSegmentGroup">
{{ $t('trials:reading:Segmentations:button:addSegmentGroup') }} {{ $t('trials:reading:Segmentations:button:addSegmentGroup') }}
@ -126,8 +132,7 @@
v-for="(item) in curSegmentGroup.segments" v-for="(item) in curSegmentGroup.segments"
:key="`${item.segmentationId}_${item.segmentIndex}`" @click.stop="selectSegment(item)"> :key="`${item.segmentationId}_${item.segmentIndex}`" @click.stop="selectSegment(item)">
<div class="messageBox"> <div class="messageBox">
<el-popover placement="top-start" :title="item.SegmentLabel" width="200" <el-popover placement="left" :title="item.SegmentLabel" width="200" trigger="hover">
trigger="hover">
<div class="Bidirectionalbox"> <div class="Bidirectionalbox">
<div class="BidirectionalBtn" @click.stop="getBidirectional(item)" <div class="BidirectionalBtn" @click.stop="getBidirectional(item)"
v-if="!item.bidirectional"> v-if="!item.bidirectional">
@ -140,8 +145,10 @@
</div> </div>
<div class="btnBox"> <div class="btnBox">
<svg-icon :icon-class="!item.bidirectionalView ? 'eye' : 'eye-open'" <svg-icon :icon-class="!item.bidirectionalView ? 'eye' : 'eye-open'"
style="color:#000" style="color:#000;margin-right: 5px;cursor: pointer;"
@click.stop="viewBidirectional(item, !item.bidirectionalView)" /> @click.stop="viewBidirectional(item, !item.bidirectionalView)" />
<svg-icon icon-class="jumpto" style="color:#000;cursor: pointer;"
@click.stop="jumpBidirectional(item)" />
</div> </div>
</template> </template>
</div> </div>
@ -161,13 +168,11 @@
</div> </div>
<div class="btnBox"> <div class="btnBox">
<svg-icon :icon-class="item && !item.view ? 'eye' : 'eye-open'" <svg-icon :icon-class="item && !item.view ? 'eye' : 'eye-open'"
@click.stop="viewSegment(item, !item.view)" @click.stop="viewSegment(item, !item.view)" class="docShow" />
:class="{ segmentBtn: true, docShow: item.segmentIndex === segmentIndex || !item.view }" /> <i class="el-icon-lock" v-if="item.lock" @click.stop="lockSegment(item, false)"></i>
<i class="el-icon-lock" v-if="item.lock"></i> <el-popover placement="bottom" width="40" trigger="click" class="docShow">
<el-popover placement="bottom" width="40" trigger="click"
:class="{ segmentBtn: true, docShow: item.segmentIndex === segmentIndex }">
<div class="SegmentGroupBtnBox"> <div class="SegmentGroupBtnBox">
<div class="SegmentGroupBtn" @click.stop="rename('segment', item.segmentIndex)"> <div class="SegmentGroupBtn" @click.stop="rename('segment', item)">
{{ $t('trials:reading:Segmentations:button:renameSegmentGroup') }} {{ $t('trials:reading:Segmentations:button:renameSegmentGroup') }}
</div> </div>
<div class="SegmentGroupBtn" @click.stop="delSegment(item.segmentIndex)"> <div class="SegmentGroupBtn" @click.stop="delSegment(item.segmentIndex)">
@ -203,18 +208,21 @@ import dcmjs from '@/utils/dcmUpload/dcmjs'
import * as cornerstoneAdapters from "@cornerstonejs/adapters"; import * as cornerstoneAdapters from "@cornerstonejs/adapters";
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader' import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent' import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import { getCustomizeStandardsSegmentDicomTools } from './toolConfig'
const { const {
ToolGroupManager, ToolGroupManager,
Enums: csToolsEnums, Enums: csToolsEnums,
segmentation, segmentation,
annotation, annotation,
LabelMapEditWithContourTool,
SegmentBidirectionalTool, SegmentBidirectionalTool,
CrosshairsTool,
utilities: CStUtils, utilities: CStUtils,
} = cornerstoneTools; } = cornerstoneTools;
const { MouseBindings, Events: toolsEvents } = csToolsEnums const { MouseBindings, Events: toolsEvents } = csToolsEnums
const { segmentation: segmentationUtils, roundNumber } = CStUtils; const { segmentation: segmentationUtils } = CStUtils;
const { cache, getRenderingEngine, imageLoader, eventTarget, metaData, utilities: csUtils, volumeLoader } = cornerstone; const { cache, getRenderingEngine, imageLoader, eventTarget, metaData, utilities: csUtils, volumeLoader } = cornerstone;
const { downloadDICOMData } = cornerstoneAdapters.helpers; // const { downloadDICOMData } = cornerstoneAdapters.helpers;
const { Cornerstone3D } = cornerstoneAdapters.adaptersSEG; const { Cornerstone3D } = cornerstoneAdapters.adaptersSEG;
export default { export default {
name: "Segmentations", name: "Segmentations",
@ -250,7 +258,11 @@ export default {
default: () => { default: () => {
return {} return {}
} }
} },
renderingEngineId: {
type: String,
required: true
},
}, },
data() { data() {
return { return {
@ -285,8 +297,7 @@ export default {
'#fb628b', '#fb628b',
], ],
viewprotIds: ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3', 'viewport-MPR-0', 'viewport-MPR-1', 'viewport-MPR-2'], // viewprotIds: ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3', 'viewport-MPR-0', 'viewport-MPR-1', 'viewport-MPR-2'], //
renderingEngineId: 'myRenderingEngine', statsKey: [],
statsKey: ['max', 'min', 'volume', 'count', 'mean', 'stdDev'],
drawing: false, // drawing: false, //
isDel: false, isDel: false,
digitPlaces: 2 digitPlaces: 2
@ -294,8 +305,9 @@ export default {
}, },
mounted() { mounted() {
this.SegmentHight = window.innerHeight * 0.4; this.SegmentHight = window.innerHeight * 0.4;
console.log(segmentation, 'segmentation') this.statsKey = getCustomizeStandardsSegmentDicomTools('Labelmap')[0].props.filter(item => item !== 'width' && item !== 'length')
console.log(annotation, 'annotation') // console.log(segmentation, 'segmentation')
// console.log(annotation, 'annotation')
eventTarget.addEventListener( eventTarget.addEventListener(
'CORNERSTONE_TOOLS_SEGMENTATION_DATA_MODIFIED', 'CORNERSTONE_TOOLS_SEGMENTATION_DATA_MODIFIED',
this.segmentationModifiedCallback this.segmentationModifiedCallback
@ -386,6 +398,15 @@ export default {
segmentationId: item.segmentationId, segmentationId: item.segmentationId,
segmentIndices: [item.segmentIndex], segmentIndices: [item.segmentIndex],
}); });
// console.log(bidirectionalData, 'bidirectionalData')
if (bidirectionalData.length <= 0) {
let annotations = annotation.state.getAllAnnotations().filter(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex);
annotations.forEach(i => {
annotation.state.removeAnnotation(i.annotationUID)
})
this.resetViewport(false)
item.bidirectional = null
}
bidirectionalData.forEach((bidirectional) => { bidirectionalData.forEach((bidirectional) => {
const { segmentIndex } = bidirectional; const { segmentIndex } = bidirectional;
const { majorAxis, minorAxis, maxMajor, maxMinor } = bidirectional; const { majorAxis, minorAxis, maxMajor, maxMinor } = bidirectional;
@ -418,12 +439,20 @@ export default {
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}` const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (this.activeTool === toolName) { if (this.activeTool === toolName) {
if (toolName === CrosshairsTool.toolName) {
toolGroup.setToolDisabled(this.activeTool)
} else {
toolGroup.setToolPassive(this.activeTool) toolGroup.setToolPassive(this.activeTool)
}
this.$emit('update:activeTool', '') this.$emit('update:activeTool', '')
} else { } else {
if (this.activeTool) { if (this.activeTool) {
if (toolName === CrosshairsTool.toolName) {
toolGroup.setToolDisabled(this.activeTool)
} else {
toolGroup.setToolPassive(this.activeTool) toolGroup.setToolPassive(this.activeTool)
} }
}
toolGroup.setToolActive(toolName, { toolGroup.setToolActive(toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }] bindings: [{ mouseButton: MouseBindings.Primary }]
}) })
@ -442,6 +471,18 @@ export default {
item.bidirectionalView = view item.bidirectionalView = view
this.resetViewport() this.resetViewport()
}, },
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");
if (!an) return false
let referencedImageId = an.metadata.referencedImageId;
let sliceIndex = metaData.get('generalImageModule', referencedImageId).instanceNumber
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId)
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIndex - 1 });
}
},
viewSegmentGroup(item) { viewSegmentGroup(item) {
let view = !item.view let view = !item.view
this.viewprotIds.forEach(id => { this.viewprotIds.forEach(id => {
@ -479,16 +520,7 @@ export default {
this.segmentationId = item.segmentationId; this.segmentationId = item.segmentationId;
this.segmentIndex = item.segmentIndex; this.segmentIndex = item.segmentIndex;
segmentation.segmentIndex.setActiveSegmentIndex(item.segmentationId, item.segmentIndex); segmentation.segmentIndex.setActiveSegmentIndex(item.segmentationId, item.segmentIndex);
if (item.bidirectional) { this.jumpBidirectional(item)
let an = annotation.state.getAllAnnotations().find(i => i.metadata.segmentationId === item.segmentationId && i.metadata.segmentIndex === item.segmentIndex && i.metadata.toolName === "SegmentBidirectional");
if (!an) return false
let referencedImageId = an.metadata.referencedImageId;
let sliceIndex = metaData.get('generalImageModule', referencedImageId).instanceNumber
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId)
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIndex - 1 });
}
if (item.lock) { if (item.lock) {
this.$emit('setToolsPassive') this.$emit('setToolsPassive')
} }
@ -498,7 +530,7 @@ export default {
this.viewprotIds.forEach(id => { this.viewprotIds.forEach(id => {
segmentation.activeSegmentation.setActiveSegmentation(id, this.segmentationId) segmentation.activeSegmentation.setActiveSegmentation(id, this.segmentationId)
}) })
this.selectSegment({ segmentIndex: 1 }) this.selectSegment({ segmentationId: this.segmentationId, segmentIndex: 1 })
// this.changeSegmentConfig() // this.changeSegmentConfig()
}, },
async addSegmentGroup() { async addSegmentGroup() {
@ -507,6 +539,7 @@ export default {
let obj = { let obj = {
name: this.$t('trials:reading:Segmentations:name:SegmentGroup') + (this.segmentList.length + 1), name: this.$t('trials:reading:Segmentations:name:SegmentGroup') + (this.segmentList.length + 1),
view: true, view: true,
segmentationId: null,
segments: [] segments: []
} }
let segmentationId = await this.addOrUpdateSegmentation({ name: obj.name }) let segmentationId = await this.addOrUpdateSegmentation({ name: obj.name })
@ -798,7 +831,8 @@ export default {
const RecommendedDisplayCIELabValue = dcmjs.data.Colors.rgb2DICOMLAB( const RecommendedDisplayCIELabValue = dcmjs.data.Colors.rgb2DICOMLAB(
color.slice(0, 3).map(value => value / 255) color.slice(0, 3).map(value => value / 255)
).map(value => Math.round(value)); ).map(value => Math.round(value));
let SegmentLabel = group.segments.find(item => item.segmentIndex === segmentIndex).SegmentLabel let segment = group.segments.find(item => item.segmentIndex === segmentIndex)
let SegmentLabel = segment.SegmentLabel
const segmentMetadata = { const segmentMetadata = {
SegmentNumber: segmentIndex.toString(), SegmentNumber: segmentIndex.toString(),
SegmentLabel: SegmentLabel, SegmentLabel: SegmentLabel,
@ -816,24 +850,42 @@ export default {
CodeMeaning: "Tissue" CodeMeaning: "Tissue"
} }
}; };
labelmap3D.metadata[segmentIndex] = segmentMetadata; if (segment.bidirectional && segment.stats) labelmap3D.metadata[segmentIndex] = segmentMetadata;
}); });
if (labelmap3D.metadata.length <= 0) {
return false
}
const generatedSegmentation = const generatedSegmentation =
Cornerstone3D.Segmentation.generateSegmentation( Cornerstone3D.Segmentation.generateSegmentation(
referencedImages, referencedImages,
labelmap3D, labelmap3D,
metaData metaData
); );
console.log(generatedSegmentation.dataset, 'generatedSegmentation.dataset')
if (!isFile) { if (!isFile) {
const buffer = Buffer.from(dcmjs.data.datasetToDict(generatedSegmentation.dataset).write()); const buffer = Buffer.from(dcmjs.data.datasetToDict(generatedSegmentation.dataset).write());
let blob = new Blob([buffer], { type: "application/dicom" }); let blob = new Blob([buffer], { type: "application/dicom" });
return blob return blob
} else { } else {
downloadDICOMData(generatedSegmentation.dataset, `${group.name}.dcm`); this.downloadDICOMData(generatedSegmentation.dataset, `${group.name}.dcm`);
} }
}, },
downloadDICOMData(bufferOrDataset, filename) {
let blob;
if (bufferOrDataset instanceof ArrayBuffer) {
blob = new Blob([bufferOrDataset], { type: 'application/dicom' });
} else {
if (!bufferOrDataset._meta) {
throw new Error('Dataset must have a _meta property');
}
blob = dcmjs.data.datasetToBlob(bufferOrDataset);
}
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
link.click();
},
// SEG // SEG
beginScanFiles(e) { beginScanFiles(e) {
let files = e.target.files let files = e.target.files
@ -936,14 +988,11 @@ export default {
CStUtils.segmentation.setBrushSizeForToolGroup(toolGroupId, this.brushSize); CStUtils.segmentation.setBrushSizeForToolGroup(toolGroupId, this.brushSize);
}, },
async createSegmentation(segmentationId) { async createSegmentation(segmentationId) {
// const derivedSegmentationImages = const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
// await imageLoader.createAndCacheDerivedLabelmapImages( const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
// this.series.ImageIds toolGroup.setToolActive(
// ); LabelMapEditWithContourTool.toolName,
);
// const derivedSegmentationImageIds = derivedSegmentationImages.map(
// image => image.imageId
// );
if (!cache.getVolume(segmentationId)) { if (!cache.getVolume(segmentationId)) {
await volumeLoader.createAndCacheDerivedLabelmapVolume( await volumeLoader.createAndCacheDerivedLabelmapVolume(
this.series.SeriesInstanceUid, this.series.SeriesInstanceUid,
@ -960,7 +1009,6 @@ export default {
type: cornerstoneTools.Enums.SegmentationRepresentations type: cornerstoneTools.Enums.SegmentationRepresentations
.Labelmap, .Labelmap,
data: { data: {
// imageIds: derivedSegmentationImageIds
volumeId: segmentationId volumeId: segmentationId
} }
} }
@ -969,11 +1017,13 @@ export default {
}, },
segmentationModifiedCallback(evt) { segmentationModifiedCallback(evt) {
const { detail } = evt; const { detail } = evt;
// console.log(detail)
if (detail.segmentIndex === 0) { if (detail.segmentIndex === 0) {
this.drawing = true this.drawing = true
this.isDel = true this.isDel = true
} }
if (!detail || !detail.segmentIndex || detail.segmentIndex === 255) { // || !detail.segmentIndex
if (!detail || detail.segmentIndex === 255) {
return; return;
} }
this.drawing = true this.drawing = true
@ -986,13 +1036,12 @@ export default {
}); });
if (mode === 'individual') { if (mode === 'individual') {
// Handle individual mode where stats is an object with segment indices as keys
const segmentStats = stats; const segmentStats = stats;
for (const segmentIndex of indices) { for (const segmentIndex of indices) {
if (segmentStats[segmentIndex]) { if (segmentStats[segmentIndex]) {
const segmentStat = segmentStats[segmentIndex]; const segmentStat = segmentStats[segmentIndex];
console.log(segmentStat, 'segmentStat') // console.log(segmentStat, 'segmentStat')
segmentStat.count.label = 'Voxels'; segmentStat.count.label = 'Voxels';
let segmentGroup = this.segmentList.find(item => item.segmentationId === segmentationId) let segmentGroup = this.segmentList.find(item => item.segmentationId === segmentationId)
let segment = segmentGroup.segments.find(item => item.segmentIndex === segmentIndex) let segment = segmentGroup.segments.find(item => item.segmentIndex === segmentIndex)
@ -1017,6 +1066,7 @@ export default {
}, },
]) ])
}) })
this.$emit('setToolsPassive')
}, },
contentMouseup() { contentMouseup() {
// console.log("segment contentMouseup") // console.log("segment contentMouseup")
@ -1131,11 +1181,14 @@ export default {
}) })
// //
let blob = this.exportSegmentation(segmentGroup.segmentationId, segmentGroup) let blob = this.exportSegmentation(segmentGroup.segmentationId, segmentGroup)
if (blob) {
let path = `/${this.$route.query.trialId}/Segment/${this.visitInfo.SubjectId let path = `/${this.$route.query.trialId}/Segment/${this.visitInfo.SubjectId
}/${this.visitInfo.VisistId}/${this.series.StudyId }/${this.visitInfo.VisistId}/${this.series.StudyId
}/${this.series.Id}/${segmentGroup.name}.dcm` }/${this.series.Id}/${segmentGroup.name}.dcm`
const result = await this.OSSclient.put(path, blob) const result = await this.OSSclient.put(path, blob)
segmentGroup.segUrl = this.$getObjectName(result.url) segmentGroup.segUrl = this.$getObjectName(result.url)
}
this.addOrUpdateSegmentation({ name: segmentGroup.name, id: segmentGroup.segmentationId, url: segmentGroup.segUrl }) this.addOrUpdateSegmentation({ name: segmentGroup.name, id: segmentGroup.segmentationId, url: segmentGroup.segUrl })
segmentGroup.segments.forEach(s => { segmentGroup.segments.forEach(s => {
this.addOrUpdateSegment({ name: s.SegmentLabel, color: s.color, segmentIndex: s.segmentIndex, segmentationId: s.segmentationId, segmentJson: JSON.stringify({ stats: s.stats, bidirectional: s.bidirectional }), id: s.id }) this.addOrUpdateSegment({ name: s.SegmentLabel, color: s.color, segmentIndex: s.segmentIndex, segmentationId: s.segmentationId, segmentJson: JSON.stringify({ stats: s.stats, bidirectional: s.bidirectional }), id: s.id })

View File

@ -608,7 +608,7 @@ export default {
}) })
DicomEvent.$on('setTableQuestionAnswer', async (DATA) => { DicomEvent.$on('setTableQuestionAnswer', async (DATA) => {
let { id, answer, ParentQsId, data } = DATA let { id, answer, ParentQsId, data } = DATA
console.log(ParentQsId, this.question.Id) // console.log(ParentQsId, this.question.Id)
if (this.question.Id === ParentQsId) { if (this.question.Id === ParentQsId) {
this.QuestionsForm[id] = answer this.QuestionsForm[id] = answer
if (data.RowId) { if (data.RowId) {
@ -1456,13 +1456,13 @@ export default {
getAnnotationProp(annotation, prop) { getAnnotationProp(annotation, prop) {
if (!annotation) return if (!annotation) return
let referencedImageId = null let referencedImageId = null
if (annotation.from) { if (annotation.from || annotation.metadata.volumeId) {
referencedImageId = `${annotation?.metadata?.volumeId}?sliceIndex=${annotation?.metadata?.sliceIndex}&viewPlaneNormal=${annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')}` referencedImageId = `${annotation?.metadata?.volumeId}?sliceIndex=${annotation?.metadata?.sliceIndex}&viewPlaneNormal=${annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? Number(0).toFixed(3) : Number(i).toFixed(3)).join(',')}`
} else { } else {
referencedImageId = annotation?.metadata?.referencedImageId referencedImageId = annotation?.metadata?.referencedImageId
} }
if (!referencedImageId) return null if (!referencedImageId) return null
const cacheKey = annotation.from ? `volumeId:${referencedImageId}` : `imageId:${referencedImageId}` const cacheKey = annotation.from || annotation.metadata.volumeId ? `volumeId:${referencedImageId}` : `imageId:${referencedImageId}`
const points = ['x', 'y', 'z']; const points = ['x', 'y', 'z'];
const cachedStats = annotation.markTool === "ArrowAnnotate" ? annotation.data?.handles?.points[0] : annotation.data?.cachedStats?.[cacheKey] const cachedStats = annotation.markTool === "ArrowAnnotate" ? annotation.data?.handles?.points[0] : annotation.data?.cachedStats?.[cacheKey]
const hasProp = cachedStats const hasProp = cachedStats

View File

@ -442,7 +442,7 @@ export default {
VIEW_SEGMENT: 23, // VIEW_SEGMENT: 23, //
REMOVE_SEGMENT: 24, // REMOVE_SEGMENT: 24, //
} }
console.log(obj, 'obj') // console.log(obj, 'obj')
const { question, operateStateEnum, rowId, answer } = obj const { question, operateStateEnum, rowId, answer } = obj
const { Id, IsTableQuestion, ImageTool, ImageToolAttribute, ParentQsId, RowId, QuestionName, QuestionEnName } = question const { Id, IsTableQuestion, ImageTool, ImageToolAttribute, ParentQsId, RowId, QuestionName, QuestionEnName } = question
@ -506,7 +506,7 @@ export default {
} }
}, },
handleBindSegment(obj) { handleBindSegment(obj) {
this.$emit("openSegmentForm", { visitInfo: this.visitInfo }) this.$emit("openSegmentForm", { visitInfo: this.visitInfo, operateStateEnum: obj.operateStateEnum, isTableQuestion: this.isTableQuestion, operateQuestionId: this.operateQuestionId, operateRowId: this.operateRowId })
}, },
handleSegmentSave(obj) { handleSegmentSave(obj) {
let imageToolAttribute = this.imageToolAttribute let imageToolAttribute = this.imageToolAttribute
@ -1227,13 +1227,13 @@ export default {
getAnnotationProp(annotation, prop) { getAnnotationProp(annotation, prop) {
if (!annotation || !prop) return if (!annotation || !prop) return
let referencedImageId = null let referencedImageId = null
if (annotation.from) { if (annotation.from || annotation.metadata.volumeId) {
referencedImageId = `${annotation?.metadata?.volumeId}?sliceIndex=${annotation?.metadata?.sliceIndex}&viewPlaneNormal=${annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')}` referencedImageId = `${annotation?.metadata?.volumeId}?sliceIndex=${annotation?.metadata?.sliceIndex}&viewPlaneNormal=${annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? Number(0).toFixed(3) : Number(i).toFixed(3)).join(',')}`
} else { } else {
referencedImageId = annotation?.metadata?.referencedImageId referencedImageId = annotation?.metadata?.referencedImageId
} }
if (!referencedImageId) return null if (!referencedImageId) return null
const cacheKey = annotation.from ? `volumeId:${referencedImageId}` : `imageId:${referencedImageId}` const cacheKey = annotation.from || annotation.metadata.volumeId ? `volumeId:${referencedImageId}` : `imageId:${referencedImageId}`
const points = ['x', 'y', 'z'] const points = ['x', 'y', 'z']
const cachedStats = annotation.markTool === "ArrowAnnotate" ? annotation.data?.handles?.points[0] : annotation.data?.cachedStats?.[cacheKey] const cachedStats = annotation.markTool === "ArrowAnnotate" ? annotation.data?.handles?.points[0] : annotation.data?.cachedStats?.[cacheKey]
const hasProp = cachedStats const hasProp = cachedStats