fix(segmentation): upgrade cs3d to fix various segmentation bugs (#3885)
This commit is contained in:
1 parent
a8a0bdb2df
commit
b1efe40aa1
26 files changed
+241
-171
No files matched your search
@@ -168,6 +168,8 @@ function OHIFCornerstoneRTViewport(props) {
|
||||
}, [servicesManager, viewportId, rtDisplaySet, rtIsLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
// I'm not sure what is this, since in RT we support Overlapping segmnets
|
||||
// via contours
|
||||
const { unsubscribe } = segmentationService.subscribe(
|
||||
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
|
||||
evt => {
|
||||
|
||||
@@ -44,9 +44,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^1.40.3",
|
||||
"@cornerstonejs/tools": "^1.40.3",
|
||||
"@kitware/vtk.js": "27.3.1",
|
||||
"@cornerstonejs/adapters": "^1.43.7",
|
||||
"@cornerstonejs/tools": "^1.43.7",
|
||||
"@kitware/vtk.js": "29.3.0",
|
||||
"react-color": "^2.19.3"
|
||||
}
|
||||
}
|
||||
@@ -189,6 +189,15 @@ async function _loadSegments({ extensionManager, servicesManager, segDisplaySet,
|
||||
}
|
||||
});
|
||||
|
||||
if (results.overlappingSegments) {
|
||||
uiNotificationService.show({
|
||||
title: 'Overlapping Segments',
|
||||
message:
|
||||
'Unsupported overlapping segments detected, segmentation rendering results may be incorrect.',
|
||||
type: 'warning',
|
||||
});
|
||||
}
|
||||
|
||||
if (!usedRecommendedDisplayCIELabValue) {
|
||||
// Display a notification about the non-utilization of RecommendedDisplayCIELabValue
|
||||
uiNotificationService.show({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createReportAsync } from '@ohif/extension-default';
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { SegmentationGroupTable, LegacyButtonGroup, LegacyButton } from '@ohif/ui';
|
||||
import { SegmentationGroupTable } from '@ohif/ui';
|
||||
|
||||
import callInputDialog from './callInputDialog';
|
||||
import callColorPickerDialog from './colorPickerDialog';
|
||||
|
||||
@@ -37,7 +37,7 @@ const initialState = {
|
||||
},
|
||||
ThresholdBrush: {
|
||||
brushSize: 15,
|
||||
thresholdRange: [-500, 500],
|
||||
thresholdRange: null,
|
||||
},
|
||||
activeTool: null,
|
||||
};
|
||||
@@ -88,6 +88,8 @@ function SegmentationToolbox({ servicesManager, extensionManager }) {
|
||||
|
||||
const setToolActive = useCallback(
|
||||
toolName => {
|
||||
initializeThresholdValue(toolName);
|
||||
|
||||
toolbarService.recordInteraction({
|
||||
interactionType: 'tool',
|
||||
commands: [
|
||||
@@ -178,6 +180,24 @@ function SegmentationToolbox({ servicesManager, extensionManager }) {
|
||||
[toolGroupService]
|
||||
);
|
||||
|
||||
function initializeThresholdValue(toolName: any) {
|
||||
if (state.ThresholdBrush.thresholdRange === null) {
|
||||
// set the default threshold range from the tool configuration
|
||||
const toolGroupIds = toolGroupService.getToolGroupIds();
|
||||
const toolGroupId = toolGroupIds[0];
|
||||
const toolGroup = toolGroupService.getToolGroup(toolGroupId);
|
||||
const toolConfig = toolGroup.getToolConfiguration(toolName);
|
||||
const defaultThresholdRange = toolConfig?.strategySpecificConfiguration?.THRESHOLD?.threshold;
|
||||
dispatch({
|
||||
type: ACTIONS.SET_TOOL_CONFIG,
|
||||
payload: {
|
||||
tool: 'ThresholdBrush',
|
||||
config: { thresholdRange: defaultThresholdRange },
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const onBrushSizeChange = useCallback(
|
||||
(valueAsStringOrNumber, toolCategory) => {
|
||||
const value = Number(valueAsStringOrNumber);
|
||||
@@ -200,8 +220,8 @@ function SegmentationToolbox({ servicesManager, extensionManager }) {
|
||||
const handleRangeChange = useCallback(
|
||||
newRange => {
|
||||
if (
|
||||
newRange[0] === state.ThresholdBrush.thresholdRange[0] &&
|
||||
newRange[1] === state.ThresholdBrush.thresholdRange[1]
|
||||
newRange[0] === state.ThresholdBrush.thresholdRange?.[0] &&
|
||||
newRange[1] === state.ThresholdBrush.thresholdRange?.[1]
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -213,7 +233,7 @@ function SegmentationToolbox({ servicesManager, extensionManager }) {
|
||||
const toolGroup = toolGroupService.getToolGroup(toolGroupId);
|
||||
toolGroup.setToolConfiguration(toolName, {
|
||||
strategySpecificConfiguration: {
|
||||
THRESHOLD_INSIDE_CIRCLE: {
|
||||
THRESHOLD: {
|
||||
threshold: newRange,
|
||||
},
|
||||
},
|
||||
@@ -365,7 +385,7 @@ function SegmentationToolbox({ servicesManager, extensionManager }) {
|
||||
<InputDoubleRange
|
||||
values={state.ThresholdBrush.thresholdRange}
|
||||
onChange={handleRangeChange}
|
||||
minValue={-1000}
|
||||
minValue={-1000} // Todo: these should be configurable
|
||||
maxValue={1000}
|
||||
step={1}
|
||||
showLabel={true}
|
||||
|
||||
@@ -172,14 +172,6 @@ function OHIFCornerstoneSEGViewport(props) {
|
||||
if (evt.segDisplaySet.displaySetInstanceUID === segDisplaySet.displaySetInstanceUID) {
|
||||
setSegIsLoading(false);
|
||||
}
|
||||
|
||||
if (evt.overlappingSegments) {
|
||||
uiNotificationService.show({
|
||||
title: 'Overlapping Segments',
|
||||
message: 'Overlapping segments detected which is not currently supported',
|
||||
type: 'warning',
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -44,9 +44,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^1.40.3",
|
||||
"@cornerstonejs/core": "^1.40.3",
|
||||
"@cornerstonejs/tools": "^1.40.3",
|
||||
"@cornerstonejs/adapters": "^1.43.7",
|
||||
"@cornerstonejs/core": "^1.43.7",
|
||||
"@cornerstonejs/tools": "^1.43.7",
|
||||
"classnames": "^2.3.2"
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "^1.2.2",
|
||||
"@cornerstonejs/codec-openjph": "^2.4.2",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.40.3",
|
||||
"@cornerstonejs/dicom-image-loader": "^1.43.7",
|
||||
"@ohif/core": "3.8.0-beta.42",
|
||||
"@ohif/ui": "3.8.0-beta.42",
|
||||
"dcmjs": "^0.29.12",
|
||||
@@ -52,11 +52,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@cornerstonejs/adapters": "^1.40.3",
|
||||
"@cornerstonejs/core": "^1.40.3",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^1.40.3",
|
||||
"@cornerstonejs/tools": "^1.40.3",
|
||||
"@kitware/vtk.js": "27.3.1",
|
||||
"@cornerstonejs/adapters": "^1.43.7",
|
||||
"@cornerstonejs/core": "^1.43.7",
|
||||
"@cornerstonejs/streaming-image-volume-loader": "^1.43.7",
|
||||
"@cornerstonejs/tools": "^1.43.7",
|
||||
"@kitware/vtk.js": "29.3.01",
|
||||
"html2canvas": "^1.4.1",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"lodash.merge": "^4.6.2",
|
||||
|
||||
@@ -253,10 +253,10 @@ export default async function init({
|
||||
|
||||
/**
|
||||
* Runs error handler for failed requests.
|
||||
* @param event
|
||||
* @param event
|
||||
*/
|
||||
const imageLoadFailedHandler = ({ detail }) => {
|
||||
const handler = errorHandler.getHTTPErrorHandler()
|
||||
const handler = errorHandler.getHTTPErrorHandler();
|
||||
handler(detail.error);
|
||||
};
|
||||
|
||||
@@ -290,7 +290,7 @@ export default async function init({
|
||||
});
|
||||
eventTarget.addEventListener(EVENTS.IMAGE_LOAD_FAILED, imageLoadFailedHandler);
|
||||
eventTarget.addEventListener(EVENTS.IMAGE_LOAD_ERROR, imageLoadFailedHandler);
|
||||
|
||||
|
||||
function elementEnabledHandler(evt) {
|
||||
const { element } = evt.detail;
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import cloneDeep from 'lodash.clonedeep';
|
||||
|
||||
import { Types as OhifTypes, ServicesManager, PubSubService } from '@ohif/core';
|
||||
import {
|
||||
cache,
|
||||
@@ -11,7 +9,6 @@ import {
|
||||
volumeLoader,
|
||||
} from '@cornerstonejs/core';
|
||||
import {
|
||||
CONSTANTS as cstConstants,
|
||||
Enums as csToolsEnums,
|
||||
segmentation as cstSegmentation,
|
||||
Types as cstTypes,
|
||||
@@ -23,7 +20,6 @@ import { easeInOutBell, reverseEaseInOutBell } from '../../utils/transitions';
|
||||
import { Segment, Segmentation, SegmentationConfig } from './SegmentationServiceTypes';
|
||||
import { mapROIContoursToRTStructData } from './RTSTRUCT/mapROIContoursToRTStructData';
|
||||
|
||||
const { COLOR_LUT } = cstConstants;
|
||||
const LABELMAP = csToolsEnums.SegmentationRepresentations.Labelmap;
|
||||
const CONTOUR = csToolsEnums.SegmentationRepresentations.Contour;
|
||||
|
||||
@@ -465,15 +461,6 @@ class SegmentationService extends PubSubService {
|
||||
},
|
||||
]);
|
||||
|
||||
// if first segmentation, we can use the default colorLUT, otherwise
|
||||
// we need to generate a new one and use a new colorLUT
|
||||
const colorLUTIndex = 0;
|
||||
if (Object.keys(this.segmentations).length !== 0) {
|
||||
const newColorLUT = this.generateNewColorLUT();
|
||||
const colorLUTIndex = this.getNextColorLUTIndex();
|
||||
cstSegmentation.config.color.addColorLUT(newColorLUT, colorLUTIndex);
|
||||
}
|
||||
|
||||
this.segmentations[segmentationId] = {
|
||||
...segmentation,
|
||||
label: segmentation.label || '',
|
||||
@@ -482,7 +469,6 @@ class SegmentationService extends PubSubService {
|
||||
segmentCount: segmentation.segmentCount ?? 0,
|
||||
isActive: false,
|
||||
isVisible: true,
|
||||
colorLUTIndex,
|
||||
};
|
||||
|
||||
cachedSegmentation = this.segmentations[segmentationId];
|
||||
@@ -1037,8 +1023,6 @@ class SegmentationService extends PubSubService {
|
||||
segmentation.hydrated = true;
|
||||
}
|
||||
|
||||
const { colorLUTIndex } = segmentation;
|
||||
|
||||
// Based on the segmentationId, set the colorLUTIndex.
|
||||
const segmentationRepresentationUIDs = await cstSegmentation.addSegmentationRepresentations(
|
||||
toolGroupId,
|
||||
@@ -1057,12 +1041,6 @@ class SegmentationService extends PubSubService {
|
||||
segmentationRepresentationUIDs[0]
|
||||
);
|
||||
|
||||
cstSegmentation.config.color.setColorLUT(
|
||||
toolGroupId,
|
||||
segmentationRepresentationUIDs[0],
|
||||
colorLUTIndex
|
||||
);
|
||||
|
||||
// add the segmentation segments properly
|
||||
for (const segment of segmentation.segments) {
|
||||
if (segment === null || segment === undefined) {
|
||||
@@ -1530,7 +1508,6 @@ class SegmentationService extends PubSubService {
|
||||
segments: [],
|
||||
isVisible: true,
|
||||
isActive: false,
|
||||
colorLUTIndex: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2124,23 +2101,6 @@ class SegmentationService extends PubSubService {
|
||||
return viewportInfo.getToolGroupId();
|
||||
};
|
||||
|
||||
private getNextColorLUTIndex = (): number => {
|
||||
let i = 0;
|
||||
while (true) {
|
||||
if (cstSegmentation.state.getColorLUT(i) === undefined) {
|
||||
return i;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
};
|
||||
|
||||
private generateNewColorLUT() {
|
||||
const newColorLUT = cloneDeep(COLOR_LUT);
|
||||
|
||||
return newColorLUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts object of objects to array.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Types, Enums } from '@cornerstonejs/core';
|
||||
import { Types as UITypes } from '@ohif/ui';
|
||||
import { Types as CoreTypes } from '@ohif/core';
|
||||
import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
|
||||
import getCornerstoneBlendMode from '../../utils/getCornerstoneBlendMode';
|
||||
import getCornerstoneOrientation from '../../utils/getCornerstoneOrientation';
|
||||
@@ -18,7 +18,7 @@ export type ViewportOptions = {
|
||||
toolGroupId: string;
|
||||
viewportId: string;
|
||||
// Presentation ID to store/load presentation state from
|
||||
presentationIds?: UITypes.PresentationIds;
|
||||
presentationIds?: CoreTypes.PresentationIds;
|
||||
orientation?: Enums.OrientationAxis;
|
||||
background?: Types.Point3;
|
||||
displayArea?: Types.DisplayArea;
|
||||
@@ -36,7 +36,7 @@ export type PublicViewportOptions = {
|
||||
id?: string;
|
||||
viewportType?: string;
|
||||
toolGroupId?: string;
|
||||
presentationIds?: UITypes.PresentationIds;
|
||||
presentationIds?: CoreTypes.PresentationIds;
|
||||
viewportId?: string;
|
||||
orientation?: Enums.OrientationAxis;
|
||||
background?: Types.Point3;
|
||||
@@ -71,7 +71,7 @@ export type DisplaySetOptions = {
|
||||
voiInverted: boolean;
|
||||
blendMode?: Enums.BlendModes;
|
||||
slabThickness?: number;
|
||||
colormap?: { name: string, opacity?: number };
|
||||
colormap?: { name: string; opacity?: number };
|
||||
displayPreset?: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/** Store presentation data for either stack viewports or volume viewports */
|
||||
import { Types } from '@cornerstonejs/core';
|
||||
import { Types as UITypes } from '@ohif/ui';
|
||||
import { Types as CoreTypes } from '@ohif/core';
|
||||
|
||||
/**
|
||||
* Has information on the presentation of the viewport.
|
||||
*/
|
||||
export interface Presentation extends Types.StackViewportProperties {
|
||||
presentationIds: UITypes.PresentationIds;
|
||||
presentationIds: CoreTypes.PresentationIds;
|
||||
viewportType: string;
|
||||
initialImageIndex: number;
|
||||
camera: Types.ICamera;
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "^1.40.3",
|
||||
"@cornerstonejs/tools": "^1.40.3",
|
||||
"@cornerstonejs/core": "^1.43.7",
|
||||
"@cornerstonejs/tools": "^1.43.7",
|
||||
"@ohif/core": "3.8.0-beta.42",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.8.0-beta.42",
|
||||
"@ohif/ui": "3.8.0-beta.42",
|
||||
|
||||
Reference in new issue
Block a user