分割添加阈值工具
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
0273601f7b
commit
5c76bda1e7
|
|
@ -0,0 +1 @@
|
|||
<svg width="24px" height="24px" viewBox="0 0 24 24" class="h-6 w-6"><g id="tool-seg-threshold" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><rect id="Rectangle" x="0" y="0" width="24" height="24"></rect><g id="Group" transform="translate(2.4184, 14.3673)" stroke="currentColor" stroke-width="1.5"><line x1="16.5816327" y1="2.13265306" x2="19.0816327" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><line x1="0.0816326531" y1="2.13265306" x2="12.0816327" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><circle id="Oval" cx="14.2244898" cy="2.09183673" r="2.09183673"></circle></g><g id="Group" transform="translate(11.7092, 7.4592) scale(-1, 1) translate(-11.7092, -7.4592)translate(1.9184, 5.3673)" stroke="currentColor" stroke-width="1.5"><line x1="16" y1="2.13265306" x2="19.0816327" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><line x1="0.0816326531" y1="2.13265306" x2="11" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><circle id="Oval" cx="13.2244898" cy="2.09183673" r="2.09183673"></circle></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -273,11 +273,7 @@
|
|||
</div>
|
||||
<!-- 伪彩 -->
|
||||
<template v-if="readingTool === 2">
|
||||
<colorMap
|
||||
v-show="isFusion"
|
||||
ref="colorMap"
|
||||
:modality="fusionOverlayModality"
|
||||
@setColorMap="setColorMap"
|
||||
<colorMap v-show="isFusion" ref="colorMap" :modality="fusionOverlayModality" @setColorMap="setColorMap"
|
||||
@voiChange="voiChange" />
|
||||
</template>
|
||||
</div>
|
||||
|
|
@ -308,7 +304,7 @@
|
|||
:class="['viewports-box', isFusion || isMPR ? 'viewports-box-down' : '', fullScreenIndex !== null ? 'viewports-box-full-screen' : '']"
|
||||
:style="gridStyle">
|
||||
<div v-for="(v, index) in cellsMax" v-show="index < cells.length" :key="`viewport-${index}`"
|
||||
:class="['grid-cell', isMPR && index === 2 ? 'grid-cell-3' : '', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||
:class="['grid-cell', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
||||
<VolumeViewport :ref="`viewport-${index}`" :data-viewport-uid="`viewport-${index}`"
|
||||
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index"
|
||||
|
|
@ -325,7 +321,7 @@
|
|||
:style="gridStyleMPR">
|
||||
<div v-for="(v, index) in 3" :key="`viewport-MPR-${index}`" v-show="index < cells.length"
|
||||
:style="cellStyle"
|
||||
:class="['grid-cell', isMPR && index === 0 ? 'grid-cell-3' : '', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||
:class="['grid-cell', index === 0 ? 'grid-cell-3' : '', index === activeViewportIndex ? 'cell_active' : '', index === fullScreenIndex ? 'cell-full-screen' : '']"
|
||||
@dblclick="toggleFullScreen($event, index)" @click="activeViewport(index)">
|
||||
<MPRViewport :ref="`viewport-MPR-${index}`" :data-viewport-uid="`viewport-MPR-${index}`"
|
||||
:rendering-engine-id="renderingEngineId" :viewport-id="`viewport-MPR-${index}`"
|
||||
|
|
@ -369,10 +365,11 @@
|
|||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('trials:reading:dicom3D:tabs:segment')" name="segment">
|
||||
<Segmentations ref="Segmentations" :visitInfo="taskInfo" :isMPR="isMPR"
|
||||
:volumeToolGroupId="volumeToolGroupId" :viewportKey="viewportKey"
|
||||
:rendering-engine-id="renderingEngineId" :activeViewportIndex="activeViewportIndex"
|
||||
:activeTool.sync="activeTool" :actionConfiguration="actionConfiguration"
|
||||
@setToolsPassive="setToolsPassive" @resetQuestion="resetQuestion" />
|
||||
:volumeToolGroupId="volumeToolGroupId" :viewportKey="viewportKey" :global-loading.sync="loading"
|
||||
:loadingText.sync="loadingText" :rendering-engine-id="renderingEngineId"
|
||||
:activeViewportIndex="activeViewportIndex" :activeTool.sync="activeTool"
|
||||
:actionConfiguration="actionConfiguration" @setToolsPassive="setToolsPassive"
|
||||
@resetQuestion="resetQuestion" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template v-else>
|
||||
|
|
@ -1379,6 +1376,20 @@ export default {
|
|||
|
||||
}
|
||||
)
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@
|
|||
@click.prevent="setToolActive('LabelMapEditWithContour')">
|
||||
<svg-icon icon-class="contour" class="svg-icon" />
|
||||
</div>
|
||||
<div :title="$t('trials:Segmentations:tools:thresholecircle')"
|
||||
:class="['tool-item', ThresholdTools.includes(activeTool) && segmentList.length > 0 ? 'tool-item-active' : '']"
|
||||
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }"
|
||||
@click.prevent="initThreshold">
|
||||
<svg-icon icon-class="thresholecircle" class="svg-icon" />
|
||||
</div>
|
||||
<div :title="$t('trials:Segmentations:tools:circularbrush')"
|
||||
:class="['tool-item', activeTool === 'CircularBrush' && segmentList.length > 0 ? 'tool-item-active' : '']"
|
||||
:style="{ cursor: segmentList.length <= 0 || (curSegment && curSegment.lock) ? 'not-allowed' : 'pointer' }"
|
||||
|
|
@ -28,11 +34,41 @@
|
|||
</div> -->
|
||||
</div>
|
||||
<div class="ConfigBox">
|
||||
<div class="EraserConfig" v-if="activeTool === 'CircularEraser' || activeTool === 'CircularBrush'">
|
||||
<div class="EraserConfig"
|
||||
v-if="activeTool === 'CircularEraser' || activeTool === 'CircularBrush' || ThresholdTools.includes(activeTool)">
|
||||
<span>{{ $t('trials:reading:Segmentations:title:EraserConfig') }}</span>
|
||||
<el-slider v-model="brushSize" show-input :step="1" :max="100" input-size="mini"
|
||||
:show-input-controls="false" />
|
||||
</div>
|
||||
<div class="EraserConfig" v-if="ThresholdTools.includes(activeTool)">
|
||||
<span>{{ $t('trials:reading:Segmentations:title:thresholdType') }}</span>
|
||||
<el-radio-group v-model="thresholdType" @input="(key) => setToolActive(key)">
|
||||
<el-radio-button :label="tool" v-for="tool of ThresholdTools" :key="tool" size="mini">
|
||||
{{ $t(`trials:reading:Segmentations:radioButton:${tool}`) }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="EraserConfig" v-if="ThresholdTools.includes(activeTool)">
|
||||
<span>{{ $t('trials:reading:Segmentations:title:isDynamic') }}</span>
|
||||
<el-radio-group v-model="brushThreshold.isDynamic">
|
||||
<el-radio-button :label="true" size="mini">
|
||||
{{ $t('trials:reading:Segmentations:radioButton:Dynamic') }}
|
||||
</el-radio-button>
|
||||
<el-radio-button :label="false" size="mini">
|
||||
{{ $t('trials:reading:Segmentations:radioButton:Rang') }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="EraserConfig" v-if="ThresholdTools.includes(activeTool) && !brushThreshold.isDynamic">
|
||||
<span>{{ $t('trials:reading:Segmentations:title:range') }}</span>
|
||||
<div style="display: flex;align-items: center;">
|
||||
<el-input-number v-model="brushThreshold.range[0]" :controls="false" :step="1"
|
||||
:max="brushThreshold.range[1]" size="small" />
|
||||
<span style="margin: 0 10px;">--</span>
|
||||
<el-input-number v-model="brushThreshold.range[1]" :controls="false" :step="1"
|
||||
:min="brushThreshold.range[0]" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="Segment">
|
||||
|
|
@ -84,7 +120,7 @@
|
|||
</el-switch>
|
||||
<span style="margin-left: 5px;">{{
|
||||
$t('trials:reading:Segmentations:title:InactiveSegmentationsShow')
|
||||
}}</span>
|
||||
}}</span>
|
||||
</div>
|
||||
<!-- <div class="SegmentConfig" v-if="SegmentConfig.InactiveSegmentations.show">
|
||||
<span>{{ $t('trials:reading:Segmentations:title:Opacity') }}</span>
|
||||
|
|
@ -112,7 +148,7 @@
|
|||
<i slot="reference" class="el-icon-more" style="cursor: pointer;color:#fff" />
|
||||
</el-popover>
|
||||
<el-select v-model="segmentationId" placeholder="" @change="selectSegmentGroup">
|
||||
<el-option v-for="item in segmentList" :key="item.segmentationId" :label="item.name"
|
||||
<el-option v-for="item in segmentList" :key="`${item.segmentationId}`" :label="item.name"
|
||||
:value="item.segmentationId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
|
@ -155,7 +191,7 @@
|
|||
<div v-for="k in statsKey" :key="k" class="statsBox">
|
||||
<span>{{ k }}</span>
|
||||
<span v-if="item.stats[k]">{{ Number(item.stats[k].value).toFixed(2)
|
||||
}}<i>{{ item.stats[k].unit }}</i></span>
|
||||
}}<i>{{ item.stats[k].unit }}</i></span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="serialNum" slot="reference">{{ item.segmentIndex }}</div>
|
||||
|
|
@ -262,6 +298,14 @@ export default {
|
|||
type: String,
|
||||
required: true
|
||||
},
|
||||
loadingText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
globalLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -269,6 +313,13 @@ export default {
|
|||
series: {},
|
||||
activeNames: ['tools', 'Segment'],
|
||||
brushSize: 10,
|
||||
brushThreshold: {
|
||||
dynamicRadius: 0,
|
||||
isDynamic: false,
|
||||
range: [200, 1000]
|
||||
},
|
||||
ThresholdTools: ['ThresholdCircle', 'ThresholdSphere'],
|
||||
thresholdType: null,
|
||||
showSegmentConfig: false,
|
||||
SegmentConfig: {
|
||||
renderOutline: true,
|
||||
|
|
@ -344,12 +395,25 @@ export default {
|
|||
},
|
||||
brushSize: {
|
||||
handler() {
|
||||
this.setBrushSize()
|
||||
this.setBrushSize(this.activeTool)
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
brushThreshold: {
|
||||
handler() {
|
||||
this.setBrushThreshold()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initThreshold() {
|
||||
if (!this.ThresholdTools.includes(this.activeTool)) {
|
||||
this.setToolActive(this.ThresholdTools[0])
|
||||
this.thresholdType = this.ThresholdTools[0]
|
||||
}
|
||||
|
||||
},
|
||||
createSegmentConfiguration(segmentIndex, segmentationId, otherSegments) {
|
||||
const containedSegmentIndices = otherSegments
|
||||
? { has: (segmentIndex) => otherSegments.indexOf(segmentIndex) !== -1 }
|
||||
|
|
@ -469,8 +533,12 @@ export default {
|
|||
toolGroup.setToolActive(toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Primary }]
|
||||
})
|
||||
this.setBrushSize()
|
||||
this.$emit('update:activeTool', toolName)
|
||||
this.setBrushSize(toolName)
|
||||
if (this.ThresholdTools.includes(toolName)) {
|
||||
this.setBrushThreshold()
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
changeShowSegmentConfig() {
|
||||
|
|
@ -504,12 +572,12 @@ export default {
|
|||
} else {
|
||||
const points = an.data.handles.points;
|
||||
const worldPoint = points[0]; // 取一个点
|
||||
let imageData = cache.getVolume(this.series.SeriesInstanceUid).imageData
|
||||
let volume = cache.getVolume(this.series.SeriesInstanceUid)
|
||||
let { imageData, numFrames } = volume
|
||||
const ijk = imageData.worldToIndex(worldPoint);
|
||||
const sliceIndex = Math.abs(Math.round(ijk[2]));
|
||||
let imageIds = viewport.getImageIds(this.series.SeriesInstanceUid)
|
||||
// console.log(sliceIndex, 'sliceIndex')
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: imageIds.length - sliceIndex - 1 });
|
||||
csUtils.jumpToSlice(viewport.element, { imageIndex: numFrames - sliceIndex - 1 });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1052,9 +1120,30 @@ export default {
|
|||
}
|
||||
)
|
||||
},
|
||||
setBrushSize() {
|
||||
setBrushSize(toolName = null) {
|
||||
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
CStUtils.segmentation.setBrushSizeForToolGroup(toolGroupId, this.brushSize);
|
||||
if (toolName === 'ThresholdSphere') {
|
||||
this.setDynamicRadius()
|
||||
}
|
||||
},
|
||||
setDynamicRadius() {
|
||||
let volume = cache.getVolume(this.series.SeriesInstanceUid);
|
||||
let { spacing, numFrames } = volume
|
||||
let constant = numFrames * spacing[2] / 100
|
||||
this.brushThreshold.dynamicRadius = Math.ceil(this.brushSize * constant)
|
||||
// console.log(this.brushThreshold.dynamicRadius)
|
||||
},
|
||||
setBrushThreshold() {
|
||||
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
let brushThreshold = {
|
||||
isDynamic: this.brushThreshold.isDynamic,
|
||||
dynamicRadius: this.brushThreshold.dynamicRadius,
|
||||
}
|
||||
if (!this.brushThreshold.isDynamic) {
|
||||
brushThreshold.range = this.brushThreshold.range
|
||||
}
|
||||
CStUtils.segmentation.setBrushThresholdForToolGroup(toolGroupId, brushThreshold);
|
||||
},
|
||||
async createSegmentation(segmentationId) {
|
||||
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
|
||||
|
|
@ -1070,18 +1159,21 @@ export default {
|
|||
}
|
||||
)
|
||||
}
|
||||
segmentation.addSegmentations([
|
||||
{
|
||||
segmentationId,
|
||||
representation: {
|
||||
type: cornerstoneTools.Enums.SegmentationRepresentations
|
||||
.Labelmap,
|
||||
data: {
|
||||
volumeId: segmentationId
|
||||
if (!segmentation.state.getSegmentation(segmentationId)) {
|
||||
segmentation.addSegmentations([
|
||||
{
|
||||
segmentationId,
|
||||
representation: {
|
||||
type: cornerstoneTools.Enums.SegmentationRepresentations
|
||||
.Labelmap,
|
||||
data: {
|
||||
volumeId: segmentationId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
]);
|
||||
}
|
||||
|
||||
},
|
||||
segmentationModifiedCallback(evt) {
|
||||
const { detail } = evt;
|
||||
|
|
@ -1097,6 +1189,7 @@ export default {
|
|||
this.drawing = true
|
||||
},
|
||||
async calculateStatistics(indices, segmentationId, mode) {
|
||||
if (!segmentation.state.getSegmentation(segmentationId)) return false
|
||||
const stats = await segmentationUtils.getStatistics({
|
||||
segmentationId,
|
||||
segmentIndices: indices,
|
||||
|
|
@ -1151,16 +1244,19 @@ export default {
|
|||
if (!this.isDel) {
|
||||
this.calculateStatistics([this.segmentIndex], this.segmentationId, 'individual');
|
||||
let segmentGroup = this.segmentList.find(item => item.segmentationId === this.segmentationId)
|
||||
let segment = segmentGroup.segments.find(item => item.segmentIndex === this.segmentIndex)
|
||||
this.getBidirectional([segment])
|
||||
let segment = segmentGroup ? segmentGroup.segments.find(item => item.segmentIndex === this.segmentIndex) : null
|
||||
if (segment) this.getBidirectional([segment])
|
||||
} else {
|
||||
let segmentGroup = this.segmentList.find(item => item.segmentationId === this.segmentationId)
|
||||
let segmentIndexs = []
|
||||
segmentGroup.segments.forEach(item => {
|
||||
segmentIndexs.push(item.segmentIndex)
|
||||
})
|
||||
this.getBidirectional(segmentGroup.segments)
|
||||
this.calculateStatistics(segmentIndexs, this.segmentationId, 'individual');
|
||||
if (segmentGroup && segmentGroup.segments && segmentGroup.segments.length > 0) {
|
||||
let segmentIndexs = []
|
||||
segmentGroup.segments.forEach(item => {
|
||||
segmentIndexs.push(item.segmentIndex)
|
||||
})
|
||||
this.getBidirectional(segmentGroup.segments)
|
||||
this.calculateStatistics(segmentIndexs, this.segmentationId, 'individual');
|
||||
}
|
||||
|
||||
}
|
||||
this.isDel = false
|
||||
|
||||
|
|
@ -1243,6 +1339,8 @@ export default {
|
|||
let confirm = await this.$confirm(this.$t("segment:confirm:questionNeedChange"))
|
||||
if (!confirm) return false
|
||||
}
|
||||
this.$emit("update:globalLoading", true)
|
||||
this.$emit("update:loadingText", this.$t("segment:loadingText:saveSegmentation"))
|
||||
for (let i = 0; i < this.segmentList.length; i++) {
|
||||
let segmentGroup = this.segmentList[i]
|
||||
// 将所有分割进行锁定
|
||||
|
|
@ -1266,6 +1364,7 @@ export default {
|
|||
})
|
||||
}
|
||||
if (bindingList.length > 0) this.saveSegmentBindingAndAnswer(bindingList)
|
||||
this.$emit("update:globalLoading", false)
|
||||
} catch (err) {
|
||||
this.loading = false
|
||||
console.log(err)
|
||||
|
|
@ -1310,6 +1409,7 @@ export default {
|
|||
let res = await getSegmentationList(data);
|
||||
this.loading = false;
|
||||
if (res.IsSuccess) {
|
||||
this.segmentList = []
|
||||
let list = res.Result.CurrentPageData;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i]
|
||||
|
|
@ -1356,7 +1456,6 @@ export default {
|
|||
})
|
||||
}
|
||||
}
|
||||
// console.log(segmentation.state.getSegmentations(), 'segmentation.state.getSegmentations()')
|
||||
} catch (err) {
|
||||
this.loading = false
|
||||
console.log(err)
|
||||
|
|
@ -1701,6 +1800,19 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
.EraserConfig {
|
||||
margin: 5px 0;
|
||||
|
||||
::v-deep .el-input-number {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
::v-deep .el-input-number.is-without-controls .el-input__inner {
|
||||
padding: 0 5px;
|
||||
width: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.SegmentConfig {
|
||||
padding: 5px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,7 +270,8 @@ export default {
|
|||
const viewport = renderingEngine.getViewport(this.viewportId)
|
||||
const zoom = viewport.getZoom()
|
||||
this.imageInfo.zoom = zoom.toFixed(4)
|
||||
let imageId = viewport.getCurrentImageId()
|
||||
let imageIds = viewport.getImageIds(this.volumeId)
|
||||
let imageId = imageIds[0]
|
||||
if (imageId) {
|
||||
const imagePlaneModule = metaData.get('imagePlaneModule', imageId)
|
||||
this.imageInfo.imageOrientationPatient = imagePlaneModule.imageOrientationPatient
|
||||
|
|
|
|||
Loading…
Reference in New Issue