Merge branch 'uat'
continuous-integration/drone/push Build is passing Details

rj
wangxiaoshuang 2026-06-29 13:26:45 +08:00
commit 105bd48201
4 changed files with 79 additions and 15 deletions

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="1782284218093" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4982" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 51.2a460.8 460.8 0 1 1 0 921.6 460.8 460.8 0 0 1 0-921.6z m0 51.2a409.6 409.6 0 1 0 0 819.2 409.6 409.6 0 0 0 0-819.2z" fill="#ffffff" p-id="4983"></path><path d="M512 358.4c70.4 0 137.1136 5.2736 196.7616 14.6432l27.0336 4.6592C877.1584 403.9168 972.8 454.2464 972.8 512c0 57.7536-95.6416 108.032-237.0048 134.2976l-27.0336 4.608A1275.1872 1275.1872 0 0 1 512 665.6c-70.4 0-137.1136-5.2736-196.7616-14.6432l-27.0336-4.6592C146.8416 620.0832 51.2 569.7536 51.2 512c0-57.7536 95.6416-108.032 237.0048-134.2976l27.0336-4.608c55.0912-8.704 116.1728-13.824 180.5824-14.592L512 358.4z m0 51.2c-31.5904 0-62.3104 1.1776-91.8528 3.328l-29.0816 2.5088-28.2112 3.072c-23.1424 2.7648-45.2096 6.144-66.048 9.984l-24.4224 4.7616-23.0912 5.12-21.76 5.5296C150.3744 464.5376 102.4 490.8544 102.4 512c0 21.1456 47.9744 47.4624 125.1328 68.096l21.76 5.4784 23.0912 5.1712 24.4224 4.7616c20.8384 3.84 42.9056 7.168 66.048 9.984l28.16 3.072 29.1328 2.5088a1261.4656 1261.4656 0 0 0 183.7056 0l29.0816-2.5088 28.2112-3.072c23.1424-2.7648 45.2096-6.144 66.048-9.984l24.4224-4.7616 23.0912-5.12 21.76-5.5296C873.6256 559.4624 921.6 533.1456 921.6 512c0-21.1456-47.9744-47.4624-125.1328-68.096l-21.76-5.4784-23.0912-5.1712-24.4224-4.7616c-20.8384-3.84-42.9056-7.168-66.048-9.984l-28.16-3.072-29.1328-2.5088c-24.576-1.792-50.0736-2.9184-76.1344-3.2256L512 409.6z" fill="#ffffff" p-id="4984"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -3076,6 +3076,8 @@ export default {
} }
} else if (this.activeTool === FusionJumpToPointTool.toolName) { } else if (this.activeTool === FusionJumpToPointTool.toolName) {
this.setFusionMipJumpEnabled(false) this.setFusionMipJumpEnabled(false)
} else if (this.activeTool === 'SphericalBrush') {
toolGroup.setToolDisabled('ThresholdSphere')
} else { } else {
toolGroup.setToolPassive(this.activeTool) toolGroup.setToolPassive(this.activeTool)
} }

View File

@ -19,7 +19,7 @@
<div :title="$t('trials:Segmentations:tools:thresholecircle')" <div :title="$t('trials:Segmentations:tools:thresholecircle')"
:class="['tool-item', ThresholdTools.includes(activeTool) && segmentList.length > 0 ? 'tool-item-active' : '']" :class="['tool-item', ThresholdTools.includes(activeTool) && segmentList.length > 0 ? 'tool-item-active' : '']"
:style="{ cursor: isMPR || segmentList.length <= 0 || (curSegment && curSegment.lock) || ['viewport-MPR-1', 'viewport-MPR-2'].includes(`${viewportKey}-${activeViewportIndex}`) ? 'not-allowed' : 'pointer' }" :style="{ cursor: isMPR || segmentList.length <= 0 || (curSegment && curSegment.lock) || ['viewport-MPR-1', 'viewport-MPR-2'].includes(`${viewportKey}-${activeViewportIndex}`) ? 'not-allowed' : 'pointer' }"
@click.prevent="initThreshold"> @click.prevent="initThreshold()">
<svg-icon icon-class="thresholecircle" class="svg-icon" /> <svg-icon icon-class="thresholecircle" class="svg-icon" />
</div> </div>
<div :title="$t('trials:Segmentations:tools:circularbrush')" <div :title="$t('trials:Segmentations:tools:circularbrush')"
@ -28,6 +28,12 @@
@click.prevent="setToolActive('CircularBrush')"> @click.prevent="setToolActive('CircularBrush')">
<svg-icon icon-class="circularbrush" class="svg-icon" /> <svg-icon icon-class="circularbrush" class="svg-icon" />
</div> </div>
<div :title="$t('trials:Segmentations:tools:SphericalBrush')"
:class="['tool-item', activeTool === 'SphericalBrush' && segmentList.length > 0 ? 'tool-item-active' : '']"
:style="{ cursor: isMPR || segmentList.length <= 0 || (curSegment && curSegment.lock) || ['viewport-MPR-1', 'viewport-MPR-2'].includes(`${viewportKey}-${activeViewportIndex}`) ? 'not-allowed' : 'pointer' }"
@click.prevent="initThreshold('SphericalBrush')">
<svg-icon icon-class="sphericalBrush" class="svg-icon" />
</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: isMPR || segmentList.length <= 0 || (curSegment && curSegment.lock) || ['viewport-MPR-1', 'viewport-MPR-2'].includes(`${viewportKey}-${activeViewportIndex}`) ? 'not-allowed' : 'pointer' }" :style="{ cursor: isMPR || segmentList.length <= 0 || (curSegment && curSegment.lock) || ['viewport-MPR-1', 'viewport-MPR-2'].includes(`${viewportKey}-${activeViewportIndex}`) ? 'not-allowed' : 'pointer' }"
:title="$t('trials:Segmentations:tools:Eraser')" :title="$t('trials:Segmentations:tools:Eraser')"
@ -40,7 +46,7 @@
</div> </div>
<div class="ConfigBox"> <div class="ConfigBox">
<div class="EraserConfig" <div class="EraserConfig"
v-if="activeTool === 'CircularEraser' || activeTool === 'CircularBrush' || ThresholdTools.includes(activeTool)"> v-if="activeTool === 'SphericalBrush' || activeTool === 'CircularEraser' || activeTool === 'CircularBrush' || ThresholdTools.includes(activeTool)">
<span>{{ $t('trials:reading:Segmentations:title:EraserConfigSection') }}</span> <span>{{ $t('trials:reading:Segmentations:title:EraserConfigSection') }}</span>
<el-select v-model="sliderMax" placeholder="" size="small" @change="handleSliderChange"> <el-select v-model="sliderMax" placeholder="" size="small" @change="handleSliderChange">
<el-option v-for="item in sliderSection" :key="item.id" :label="item.label" <el-option v-for="item in sliderSection" :key="item.id" :label="item.label"
@ -48,8 +54,8 @@
</el-option> </el-option>
</el-select> </el-select>
</div> </div>
<div class="EraserConfig" <div class="EraserConfig RadiusConfig"
v-if="activeTool === 'CircularEraser' || activeTool === 'CircularBrush' || ThresholdTools.includes(activeTool)"> v-if="activeTool === 'SphericalBrush' || activeTool === 'CircularEraser' || activeTool === 'CircularBrush' || ThresholdTools.includes(activeTool)">
<span>{{ $t('trials:reading:Segmentations:title:EraserConfig') }}</span> <span>{{ $t('trials:reading:Segmentations:title:EraserConfig') }}</span>
<el-slider v-model="brushSize" show-input :step="sliderStep" :max="sliderMax" input-size="mini" <el-slider v-model="brushSize" show-input :step="sliderStep" :max="sliderMax" input-size="mini"
:show-input-controls="false" /> :show-input-controls="false" />
@ -656,8 +662,13 @@ export default {
handleClickPopover(item) { handleClickPopover(item) {
this.popoverId = `popover-${item.segmentationId}_${item.segmentIndex}` this.popoverId = `popover-${item.segmentationId}_${item.segmentIndex}`
}, },
initThreshold() { initThreshold(key = null) {
if (this.isMPR) return false if (this.isMPR) return false
if (key === 'SphericalBrush') {
this.setToolActive(this.ThresholdTools[1], 'SphericalBrush')
this.setSphericalBrushConfig()
return false
}
if (!this.ThresholdTools.includes(this.activeTool)) { if (!this.ThresholdTools.includes(this.activeTool)) {
this.setToolActive(this.ThresholdTools[0]) this.setToolActive(this.ThresholdTools[0])
this.thresholdType = this.ThresholdTools[0] this.thresholdType = this.ThresholdTools[0]
@ -777,7 +788,7 @@ export default {
} }
}, },
setToolActive(toolName) { setToolActive(toolName, name = null) {
if (this.segmentList.length <= 0) return false if (this.segmentList.length <= 0) return false
if (this.curSegment.lock) return false if (this.curSegment.lock) return false
if (this.isMPR) return false if (this.isMPR) return false
@ -806,10 +817,15 @@ export default {
// if (toolName === 'CircularEraser') { // if (toolName === 'CircularEraser') {
// console.log(toolGroup.getToolInstance(toolName)) // console.log(toolGroup.getToolInstance(toolName))
// } // }
this.$emit('update:activeTool', toolName) if (name) {
this.setBrushSize(toolName) this.$emit('update:activeTool', name)
if (this.ThresholdTools.includes(toolName)) { this.setBrushSize(name)
this.setBrushThreshold() } else {
this.$emit('update:activeTool', toolName)
this.setBrushSize(toolName)
if (this.ThresholdTools.includes(toolName)) {
this.setBrushThreshold()
}
} }
} }
@ -1516,6 +1532,21 @@ export default {
if (toolName === 'ThresholdSphere') { if (toolName === 'ThresholdSphere') {
this.setDynamicRadius() this.setDynamicRadius()
} }
if (toolName === 'SphericalBrush') {
this.setSphericalBrushConfig()
}
},
setSphericalBrushConfig() {
let volume = cache.getVolume(this.series.SeriesInstanceUid);
let { spacing, numFrames } = volume
let constant = numFrames * spacing[2] / 100
let dynamicRadius = Math.ceil(this.brushSize * constant)
let obj = {
dynamicRadius: dynamicRadius,
isDynamic: false,
range: [-10000, 100000]
}
this.setBrushThreshold(obj)
}, },
setDynamicRadius() { setDynamicRadius() {
let volume = cache.getVolume(this.series.SeriesInstanceUid); let volume = cache.getVolume(this.series.SeriesInstanceUid);
@ -1523,7 +1554,7 @@ export default {
let constant = numFrames * spacing[2] / 100 let constant = numFrames * spacing[2] / 100
this.brushThreshold.dynamicRadius = Math.ceil(this.brushSize * constant) this.brushThreshold.dynamicRadius = Math.ceil(this.brushSize * constant)
}, },
setBrushThreshold() { setBrushThreshold(OBJ = null) {
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}` const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
let brushThreshold = { let brushThreshold = {
isDynamic: this.brushThreshold.isDynamic, isDynamic: this.brushThreshold.isDynamic,
@ -1532,6 +1563,7 @@ export default {
if (!this.brushThreshold.isDynamic) { if (!this.brushThreshold.isDynamic) {
brushThreshold.range = this.brushThreshold.range brushThreshold.range = this.brushThreshold.range
} }
if (OBJ) brushThreshold = OBJ
CStUtils.segmentation.setBrushThresholdForToolGroup(toolGroupId, brushThreshold); CStUtils.segmentation.setBrushThresholdForToolGroup(toolGroupId, brushThreshold);
}, },
async createSegmentation(segmentationId) { async createSegmentation(segmentationId) {
@ -1566,7 +1598,7 @@ export default {
}, },
segmentationModifiedCallback(evt) { segmentationModifiedCallback(evt) {
const { detail } = evt; const { detail } = evt;
let tools = [...this.ThresholdTools, ...this.tools] let tools = [...this.ThresholdTools, ...this.tools, 'SphericalBrush']
if (!detail || detail.segmentIndex === 255 || !this.activeTool || !tools.includes(this.activeTool)) { if (!detail || detail.segmentIndex === 255 || !this.activeTool || !tools.includes(this.activeTool)) {
return; return;
} }
@ -1584,12 +1616,26 @@ export default {
console.log(stats) console.log(stats)
if (mode === 'individual') { if (mode === 'individual') {
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';
if (this.series.Modality === 'PT') {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId);
let imageIds = viewport.getImageIds(this.series.SeriesInstanceUid)
let imageId = imageIds[0]
const suvFactor = metaData.get('scalingModule', imageId) || {};
console.log(suvFactor, 'suvFactor')
segmentStat.sulpeak = {
label: "Mean Pixel",
name: "sulpeak",
unit: "SUL",
value: suvFactor.suvlbm && suvFactor.suvbw ? segmentStat.mean.value * suvFactor.suvlbm / suvFactor.suvbw : 0,
}
}
let segmentGroup = this.segmentList.find(item => item.segmentationId === segmentationId) let segmentGroup = this.segmentList.find(item => item.segmentationId === segmentationId)
if (segmentGroup) { if (segmentGroup) {
let segment = segmentGroup.segments.find(item => item.segmentIndex === segmentIndex) let segment = segmentGroup.segments.find(item => item.segmentIndex === segmentIndex)
@ -1617,12 +1663,12 @@ export default {
contentMouseup() { contentMouseup() {
try { try {
// console.log("segment contentMouseup") // console.log("segment contentMouseup")
if (!this.drawing) return false
if (this.timeoutId) { if (this.timeoutId) {
clearTimeout(this.timeoutId); clearTimeout(this.timeoutId);
this.timeoutId = null; this.timeoutId = null;
} }
this.timeoutId = setTimeout(() => { this.timeoutId = setTimeout(() => {
if (!this.drawing) return false
this.timeoutId = null; this.timeoutId = null;
this.drawing = false; this.drawing = false;
let segmentGroup = this.segmentList.find(item => item.segmentationId === this.segmentationId) let segmentGroup = this.segmentList.find(item => item.segmentationId === this.segmentationId)
@ -2498,6 +2544,21 @@ export default {
.EraserConfig { .EraserConfig {
margin: 5px 0; margin: 5px 0;
::v-deep .el-input-number {
width: 90px;
}
::v-deep .el-input-number.is-without-controls .el-input__inner {
padding: 0 10px;
width: 90px;
}
::v-deep .el-radio-button__inner {
width: 105px;
}
}
.RadiusConfig {
::v-deep .el-input-number { ::v-deep .el-input-number {
width: 50px; width: 50px;
} }

View File

@ -409,7 +409,7 @@ const config = {
'name': 'Labelmap分割', 'name': 'Labelmap分割',
'icon': 'labelmap', 'icon': 'labelmap',
'toolName': 'Labelmap', 'toolName': 'Labelmap',
'props': ['max', 'min', 'volume', 'count', 'mean', 'stdDev', 'length', 'width'], 'props': ['max', 'min', 'volume', 'count', 'mean', 'stdDev', 'length', 'width', 'sulpeak'],
'i18nKey': 'trials:reading:button:Labelmap', 'i18nKey': 'trials:reading:button:Labelmap',
'isDisabled': false, 'isDisabled': false,
'disabledReason': '' 'disabledReason': ''