diff --git a/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/PanelROIThresholdSegmentation.tsx b/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/PanelROIThresholdSegmentation.tsx index 0d47b93f5..1b946c6cf 100644 --- a/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/PanelROIThresholdSegmentation.tsx +++ b/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/PanelROIThresholdSegmentation.tsx @@ -1,7 +1,6 @@ import React, { useEffect, useState, useCallback, useReducer } from 'react'; import PropTypes from 'prop-types'; import { SegmentationTable, Button, Icon } from '@ohif/ui'; -import classnames from 'classnames'; import { useTranslation } from 'react-i18next'; import segmentationEditHandler from './segmentationEditHandler'; @@ -10,14 +9,16 @@ import ROIThresholdConfiguration, { ROI_STAT, } from './ROIThresholdConfiguration'; -const LOWER_THRESHOLD_DEFAULT = 0; -const UPPER_THRESHOLD_DEFAULT = 100; +const LOWER_CT_THRESHOLD_DEFAULT = -1000; +const UPPER_CT_THRESHOLD_DEFAULT = 500; +const LOWER_PT_THRESHOLD_DEFAULT = 5; +const UPPER_PT_THRESHOLD_DEFAULT = 50; const WEIGHT_DEFAULT = 0.41; // a default weight for suv max often used in the literature const DEFAULT_STRATEGY = ROI_STAT; function reducer(state, action) { const { payload } = action; - const { strategy, lower, upper, weight } = payload; + const { strategy, ctLower, ctUpper, ptLower, ptUpper, weight } = payload; switch (action.type) { case 'setStrategy': @@ -28,8 +29,10 @@ function reducer(state, action) { case 'setThreshold': return { ...state, - lower: lower ? lower : state.lower, - upper: upper ? upper : state.upper, + ctLower: ctLower ? ctLower : state.ctLower, + ctUpper: ctUpper ? ctUpper : state.ctUpper, + ptLower: ptLower ? ptLower : state.ptLower, + ptUpper: ptUpper ? ptUpper : state.ptUpper, }; case 'setWeight': return { @@ -57,8 +60,10 @@ export default function PanelRoiThresholdSegmentation({ const [config, dispatch] = useReducer(reducer, { strategy: DEFAULT_STRATEGY, - lower: LOWER_THRESHOLD_DEFAULT, - upper: UPPER_THRESHOLD_DEFAULT, + ctLower: LOWER_CT_THRESHOLD_DEFAULT, + ctUpper: UPPER_CT_THRESHOLD_DEFAULT, + ptLower: LOWER_PT_THRESHOLD_DEFAULT, + ptUpper: UPPER_PT_THRESHOLD_DEFAULT, weight: WEIGHT_DEFAULT, }); @@ -171,91 +176,93 @@ export default function PanelRoiThresholdSegmentation({ }, [segmentations, selectedSegmentationId]); return ( -
-
-
- + +
+
{ + setShowConfig(!showConfig); }} > - {labelmapLoading ? 'loading ...' : 'New Label'} - - -
-
{ - setShowConfig(!showConfig); - }} - > -
- {t('ROI Threshold Configuration')} +
+ {t('ROI Threshold Configuration')} +
-
- {showConfig && ( - - )} - {/* show segmentation table */} -
- {segmentations?.length ? ( - { - runCommand('setSegmentationActiveForToolGroups', { - segmentationId: id, - }); - setSelectedSegmentationId(id); - }} - onToggleVisibility={id => { - segmentationService.toggleSegmentationVisibility(id); - }} - onToggleVisibilityAll={ids => { - ids.map(id => { - segmentationService.toggleSegmentationVisibility(id); - }); - }} - onDelete={id => { - segmentationService.remove(id); - }} - onEdit={id => { - segmentationEditHandler({ - id, - servicesManager, - }); - }} + {showConfig && ( + - ) : null} -
- {tmtvValue !== null ? ( -
- - {'TMTV:'} - -
{`${tmtvValue} mL`}
+ )} + {/* show segmentation table */} +
+ {segmentations?.length ? ( + { + runCommand('setSegmentationActiveForToolGroups', { + segmentationId: id, + }); + setSelectedSegmentationId(id); + }} + onToggleVisibility={id => { + segmentationService.toggleSegmentationVisibility(id); + }} + onToggleVisibilityAll={ids => { + ids.map(id => { + segmentationService.toggleSegmentationVisibility(id); + }); + }} + onDelete={id => { + segmentationService.remove(id); + }} + onEdit={id => { + segmentationEditHandler({ + id, + servicesManager, + }); + }} + /> + ) : null}
- ) : null} - + {tmtvValue !== null ? ( +
+ + {'TMTV:'} + +
{`${tmtvValue} mL`}
+
+ ) : null} + +
{'User Guide'}
-
+ ); } diff --git a/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/ROIThresholdConfiguration.tsx b/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/ROIThresholdConfiguration.tsx index d4c048cf5..cd55a3cb0 100644 --- a/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/ROIThresholdConfiguration.tsx +++ b/extensions/tmtv/src/Panels/PanelROIThresholdSegmentation/ROIThresholdConfiguration.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Input, Select, Button, ButtonGroup } from '@ohif/ui'; +import { Input, Label, Select, Button, ButtonGroup } from '@ohif/ui'; import { useTranslation } from 'react-i18next'; export const ROI_STAT = 'roi_stat'; @@ -14,9 +14,9 @@ function ROIThresholdConfiguration({ config, dispatch, runCommand }) { const { t } = useTranslation('ROIThresholdConfiguration'); return ( -
+
-
+
{ - dispatch({ - type: 'setThreshold', - payload: { - lower: e.target.value, - }, - }); - }} - /> - { - dispatch({ - type: 'setThreshold', - payload: { - upper: e.target.value, - }, - }); - }} - /> +
+ + + + + + + + + + + + + + +
+ +
+ + +
+ { + dispatch({ + type: 'setThreshold', + payload: { + ctLower: e.target.value, + }, + }); + }} + /> + { + dispatch({ + type: 'setThreshold', + payload: { + ctUpper: e.target.value, + }, + }); + }} + /> +
+
+ + +
+ { + dispatch({ + type: 'setThreshold', + payload: { + ptLower: e.target.value, + }, + }); + }} + /> + { + dispatch({ + type: 'setThreshold', + payload: { + ptUpper: e.target.value, + }, + }); + }} + /> +
+
)}
diff --git a/extensions/tmtv/src/commandsModule.js b/extensions/tmtv/src/commandsModule.js index 37de4dea8..b633d231c 100644 --- a/extensions/tmtv/src/commandsModule.js +++ b/extensions/tmtv/src/commandsModule.js @@ -173,12 +173,20 @@ const commandsModule = ({ ); const { representationData } = segmentation; - const { volumeId: segVolumeId } = representationData[LABELMAP]; + const { + displaySetMatchDetails: matchDetails, + } = hangingProtocolService.getMatchDetails(); + const volumeLoaderScheme = 'cornerstoneStreamingImageVolume'; // Loader id which defines which volume loader to use + const ctDisplaySet = matchDetails.get('ctDisplaySet'); + const ctVolumeId = `${volumeLoaderScheme}:${ctDisplaySet.displaySetInstanceUID}`; // VolumeId with loader id + volume id + + const { volumeId: segVolumeId } = representationData[LABELMAP]; const { referencedVolumeId } = cs.cache.getVolume(segVolumeId); const labelmapVolume = cs.cache.getVolume(segmentationId); const referencedVolume = cs.cache.getVolume(referencedVolumeId); + const ctReferencedVolume = cs.cache.getVolume(ctVolumeId); if (!referencedVolume) { throw new Error('No Reference volume found'); @@ -201,23 +209,20 @@ const commandsModule = ({ return; } - const { lower, upper } = getThresholdValues( + const { ptLower, ptUpper, ctLower, ctUpper } = getThresholdValues( annotationUIDs, - referencedVolume, + [referencedVolume, ctReferencedVolume], config ); - const configToUse = { - lower, - upper, - overwrite: true, - }; - return csTools.utilities.segmentation.rectangleROIThresholdVolumeByRange( annotationUIDs, labelmapVolume, - [referencedVolume], - configToUse + [ + { volume: referencedVolume, lower: ptLower, upper: ptUpper }, + { volume: ctReferencedVolume, lower: ctLower, upper: ctUpper }, + ], + { overwrite: true } ); }, calculateSuvPeak: ({ labelmap }) => { diff --git a/extensions/tmtv/src/utils/getThresholdValue.ts b/extensions/tmtv/src/utils/getThresholdValue.ts index 3f597433b..a54171f6a 100644 --- a/extensions/tmtv/src/utils/getThresholdValue.ts +++ b/extensions/tmtv/src/utils/getThresholdValue.ts @@ -1,19 +1,7 @@ import * as csTools from '@cornerstonejs/tools'; -function getThresholdValues( - annotationUIDs, - referencedVolume, - config -): { lower: number; upper: number } { - if (config.strategy === 'range') { - return { - lower: Number(config.lower), - upper: Number(config.upper), - }; - } - +function getRoiStats(referencedVolume, annotations) { // roiStats - const { weight } = config; const { imageData } = referencedVolume; const values = imageData .getPointData() @@ -24,10 +12,6 @@ function getThresholdValues( const { fn, baseValue } = _getStrategyFn('max'); let value = baseValue; - const annotations = annotationUIDs.map(annotationUID => - csTools.annotation.state.getAnnotation(annotationUID) - ); - const boundsIJK = csTools.utilities.rectangleROITool.getBoundsIJKFromRectangleAnnotations( annotations, referencedVolume @@ -43,10 +27,36 @@ function getThresholdValues( } } } + return value; +} + +function getThresholdValues( + annotationUIDs, + referencedVolumes, + config +): { ptLower: number; ptUpper: number; ctLower: number; ctUpper: number } { + if (config.strategy === 'range') { + return { + ptLower: Number(config.ptLower), + ptUpper: Number(config.ptUpper), + ctLower: Number(config.ctLower), + ctUpper: Number(config.ctUpper), + }; + } + + const { weight } = config; + const annotations = annotationUIDs.map(annotationUID => + csTools.annotation.state.getAnnotation(annotationUID) + ); + + const ptValue = getRoiStats(referencedVolumes[0], annotations); + const ctValue = getRoiStats(referencedVolumes[1], annotations); return { - lower: weight * value, - upper: +Infinity, + ctLower: weight * ctValue, + ctUpper: +Infinity, + ptLower: weight * ptValue, + ptUpper: +Infinity, }; } diff --git a/platform/viewer/netlify.toml b/platform/viewer/netlify.toml index 4f86618bb..468eca951 100644 --- a/platform/viewer/netlify.toml +++ b/platform/viewer/netlify.toml @@ -12,7 +12,7 @@ base = "" build = "yarn run build:viewer:ci" publish = "dist" - ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF . ../ui/ ../core/ ../i18n ../../extensions/ ../../modes" + ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF . ../ui/ ../core/ ../i18n" # NODE_VERSION in root `.nvmrc` takes priority