feat(segmentation mode): Add create, and export SEG with Brushes (#3632)
This commit is contained in:
1 parent
896866e9d0
commit
48bbd6281a
186 files changed
+6801
-2883
No files matched your search
@@ -18,7 +18,6 @@ import toggleStackImageSync from './utils/stackSync/toggleStackImageSync';
|
||||
import { getFirstAnnotationSelected } from './utils/measurementServiceMappings/utils/selection';
|
||||
import getActiveViewportEnabledElement from './utils/getActiveViewportEnabledElement';
|
||||
import { CornerstoneServices } from './types';
|
||||
import ImageOverlayViewerTool from './tools/ImageOverlayViewerTool';
|
||||
|
||||
function commandsModule({
|
||||
servicesManager,
|
||||
@@ -266,7 +265,6 @@ function commandsModule({
|
||||
toolbarServiceRecordInteraction: props => {
|
||||
toolbarService.recordInteraction(props);
|
||||
},
|
||||
|
||||
setToolActive: ({ toolName, toolGroupId = null, toggledState }) => {
|
||||
if (toolName === 'Crosshairs') {
|
||||
const activeViewportToolGroup = toolGroupService.getToolGroup(null);
|
||||
|
||||
@@ -13,12 +13,12 @@ function WrappedCinePlayer({ enabledVPElement, viewportId, servicesManager }) {
|
||||
const handleCineClose = () => {
|
||||
toolbarService.recordInteraction({
|
||||
groupId: 'MoreTools',
|
||||
itemId: 'cine',
|
||||
interactionType: 'toggle',
|
||||
commands: [
|
||||
{
|
||||
commandName: 'toggleCine',
|
||||
commandOptions: {},
|
||||
toolName: 'cine',
|
||||
context: 'CORNERSTONE',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -137,7 +137,7 @@ const cornerstoneExtension: Types.Extensions.Extension = {
|
||||
export type { PublicViewportOptions };
|
||||
export {
|
||||
measurementMappingUtils,
|
||||
CornerstoneExtensionTypes,
|
||||
CornerstoneExtensionTypes as Types,
|
||||
toolNames,
|
||||
getActiveViewportEnabledElement,
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@ const DEFAULT_CONTEXT_MENU_CLICKS = {
|
||||
{
|
||||
commandName: 'showCornerstoneContextMenu',
|
||||
commandOptions: {
|
||||
requireNearbyToolData: true,
|
||||
menuId: 'measurementsContextMenu',
|
||||
},
|
||||
},
|
||||
@@ -62,8 +63,20 @@ function initContextMenu({
|
||||
const customizations =
|
||||
customizationService.get('cornerstoneViewportClickCommands') || DEFAULT_CONTEXT_MENU_CLICKS;
|
||||
const toRun = customizations[name];
|
||||
|
||||
if (!toRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only find nearbyToolData if required, for the click (which closes the context menu
|
||||
// we don't need to find nearbyToolData)
|
||||
let nearbyToolData = null;
|
||||
if (toRun.commands.some(command => command.commandOptions?.requireNearbyToolData)) {
|
||||
nearbyToolData = findNearbyToolData(commandsManager, evt);
|
||||
}
|
||||
|
||||
const options = {
|
||||
nearbyToolData: findNearbyToolData(commandsManager, evt),
|
||||
nearbyToolData,
|
||||
event: evt,
|
||||
};
|
||||
commandsManager.run(toRun, options);
|
||||
|
||||
@@ -25,6 +25,9 @@ import {
|
||||
annotation,
|
||||
ReferenceLinesTool,
|
||||
TrackballRotateTool,
|
||||
CircleScissorsTool,
|
||||
RectangleScissorsTool,
|
||||
SphereScissorsTool,
|
||||
} from '@cornerstonejs/tools';
|
||||
|
||||
import CalibrationLineTool from './tools/CalibrationLineTool';
|
||||
@@ -59,6 +62,9 @@ export default function initCornerstoneTools(configuration = {}) {
|
||||
addTool(ReferenceLinesTool);
|
||||
addTool(CalibrationLineTool);
|
||||
addTool(TrackballRotateTool);
|
||||
addTool(CircleScissorsTool);
|
||||
addTool(RectangleScissorsTool);
|
||||
addTool(SphereScissorsTool);
|
||||
addTool(ImageOverlayViewerTool);
|
||||
|
||||
// Modify annotation tools to use dashed lines on SR
|
||||
@@ -101,6 +107,9 @@ const toolNames = {
|
||||
ReferenceLines: ReferenceLinesTool.toolName,
|
||||
CalibrationLine: CalibrationLineTool.toolName,
|
||||
TrackballRotateTool: TrackballRotateTool.toolName,
|
||||
CircleScissors: CircleScissorsTool.toolName,
|
||||
RectangleScissors: RectangleScissorsTool.toolName,
|
||||
SphereScissors: SphereScissorsTool.toolName,
|
||||
ImageOverlayViewer: ImageOverlayViewerTool.toolName,
|
||||
};
|
||||
|
||||
|
||||
+3
-2
@@ -210,7 +210,7 @@ class CornerstoneCacheService {
|
||||
}
|
||||
|
||||
private _shouldRenderSegmentation(displaySets) {
|
||||
const { segmentationService } = this.servicesManager.services;
|
||||
const { segmentationService, displaySetService } = this.servicesManager.services;
|
||||
|
||||
const viewportDisplaySetInstanceUIDs = displaySets.map(
|
||||
({ displaySetInstanceUID }) => displaySetInstanceUID
|
||||
@@ -222,10 +222,11 @@ class CornerstoneCacheService {
|
||||
|
||||
for (const segmentation of segmentations) {
|
||||
const segDisplaySetInstanceUID = segmentation.displaySetInstanceUID;
|
||||
const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
|
||||
|
||||
const shouldDisplaySeg = segmentationService.shouldRenderSegmentation(
|
||||
viewportDisplaySetInstanceUIDs,
|
||||
segDisplaySetInstanceUID
|
||||
segDisplaySet.instances[0].FrameOfReferenceUID
|
||||
);
|
||||
|
||||
if (shouldDisplaySeg) {
|
||||
|
||||
@@ -99,43 +99,51 @@ class SegmentationService extends PubSubService {
|
||||
};
|
||||
|
||||
/**
|
||||
* It adds a segment to a segmentation, basically just setting the properties for
|
||||
* the segment.
|
||||
* @param segmentationId - The ID of the segmentation you want to add a
|
||||
* segment to.
|
||||
* @param segmentIndex - The index of the segment to add.
|
||||
* @param properties - The properties of the segment to add including
|
||||
* -- label: the label of the segment
|
||||
* -- color: the color of the segment
|
||||
* -- opacity: the opacity of the segment
|
||||
* -- visibility: the visibility of the segment (boolean)
|
||||
* -- isLocked: whether the segment is locked for editing
|
||||
* -- active: whether the segment is currently the active segment to be edited
|
||||
* Adds a new segment to the specified segmentation.
|
||||
* @param segmentationId - The ID of the segmentation to add the segment to.
|
||||
* @param config - An object containing the configuration options for the new segment.
|
||||
* - segmentIndex: (optional) The index of the segment to add. If not provided, the next available index will be used.
|
||||
* - toolGroupId: (optional) The ID of the tool group to associate the new segment with. If not provided, the first available tool group will be used.
|
||||
* - properties: (optional) An object containing the properties of the new segment.
|
||||
* - label: (optional) The label of the new segment. If not provided, a default label will be used.
|
||||
* - color: (optional) The color of the new segment in RGB format. If not provided, a default color will be used.
|
||||
* - opacity: (optional) The opacity of the new segment. If not provided, a default opacity will be used.
|
||||
* - visibility: (optional) Whether the new segment should be visible. If not provided, the segment will be visible by default.
|
||||
* - isLocked: (optional) Whether the new segment should be locked for editing. If not provided, the segment will not be locked by default.
|
||||
* - active: (optional) Whether the new segment should be the active segment to be edited. If not provided, the segment will not be active by default.
|
||||
*/
|
||||
public addSegment(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
toolGroupId?: string,
|
||||
properties?: {
|
||||
label?: string;
|
||||
color?: ohifTypes.RGB;
|
||||
opacity?: number;
|
||||
visibility?: boolean;
|
||||
isLocked?: boolean;
|
||||
active?: boolean;
|
||||
}
|
||||
config: {
|
||||
segmentIndex?: number;
|
||||
toolGroupId?: string;
|
||||
properties?: {
|
||||
label?: string;
|
||||
color?: ohifTypes.RGB;
|
||||
opacity?: number;
|
||||
visibility?: boolean;
|
||||
isLocked?: boolean;
|
||||
active?: boolean;
|
||||
};
|
||||
} = {}
|
||||
): void {
|
||||
if (segmentIndex === 0) {
|
||||
if (config?.segmentIndex === 0) {
|
||||
throw new Error('Segment index 0 is reserved for "no label"');
|
||||
}
|
||||
|
||||
toolGroupId = toolGroupId ?? this._getFirstToolGroupId();
|
||||
const toolGroupId = config.toolGroupId ?? this._getFirstToolGroupId();
|
||||
|
||||
const { segmentationRepresentationUID, segmentation } = this._getSegmentationInfo(
|
||||
segmentationId,
|
||||
toolGroupId
|
||||
);
|
||||
|
||||
let segmentIndex = config.segmentIndex;
|
||||
if (!segmentIndex) {
|
||||
// grab the next available segment index
|
||||
segmentIndex = segmentation.segments.length === 0 ? 1 : segmentation.segments.length;
|
||||
}
|
||||
|
||||
if (this._getSegmentInfo(segmentation, segmentIndex)) {
|
||||
throw new Error(`Segment ${segmentIndex} already exists`);
|
||||
}
|
||||
@@ -147,7 +155,7 @@ class SegmentationService extends PubSubService {
|
||||
);
|
||||
|
||||
segmentation.segments[segmentIndex] = {
|
||||
label: properties.label,
|
||||
label: config.properties?.label ?? `Segment ${segmentIndex}`,
|
||||
segmentIndex: segmentIndex,
|
||||
color: [rgbaColor[0], rgbaColor[1], rgbaColor[2]],
|
||||
opacity: rgbaColor[3],
|
||||
@@ -157,9 +165,12 @@ class SegmentationService extends PubSubService {
|
||||
|
||||
segmentation.segmentCount++;
|
||||
|
||||
// make the newly added segment the active segment
|
||||
this._setActiveSegment(segmentationId, segmentIndex);
|
||||
|
||||
const suppressEvents = true;
|
||||
if (properties !== undefined) {
|
||||
const { color: newColor, opacity, isLocked, visibility, active } = properties;
|
||||
if (config.properties !== undefined) {
|
||||
const { color: newColor, opacity, isLocked, visibility, active } = config.properties;
|
||||
|
||||
if (newColor !== undefined) {
|
||||
this._setSegmentColor(segmentationId, segmentIndex, newColor, toolGroupId, suppressEvents);
|
||||
@@ -281,17 +292,21 @@ class SegmentationService extends PubSubService {
|
||||
);
|
||||
}
|
||||
|
||||
public setSegmentLockedForSegmentation(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
isLocked: boolean
|
||||
): void {
|
||||
public setSegmentLocked(segmentationId: string, segmentIndex: number, isLocked: boolean): void {
|
||||
const suppressEvents = false;
|
||||
this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
|
||||
}
|
||||
|
||||
public setSegmentLabel(segmentationId: string, segmentIndex: number, segmentLabel: string): void {
|
||||
this._setSegmentLabel(segmentationId, segmentIndex, segmentLabel);
|
||||
/**
|
||||
* Toggles the locked state of a segment in a segmentation.
|
||||
* @param segmentationId - The ID of the segmentation.
|
||||
* @param segmentIndex - The index of the segment to toggle.
|
||||
*/
|
||||
public toggleSegmentLocked(segmentationId: string, segmentIndex: number): void {
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
const segment = this._getSegmentInfo(segmentation, segmentIndex);
|
||||
const isLocked = !segment.isLocked;
|
||||
this._setSegmentLocked(segmentationId, segmentIndex, isLocked);
|
||||
}
|
||||
|
||||
public setSegmentColor(
|
||||
@@ -353,7 +368,7 @@ class SegmentationService extends PubSubService {
|
||||
this._setActiveSegmentationForToolGroup(segmentationId, toolGroupId, suppressEvents);
|
||||
}
|
||||
|
||||
public setActiveSegmentForSegmentation(segmentationId: string, segmentIndex: number): void {
|
||||
public setActiveSegment(segmentationId: string, segmentIndex: number): void {
|
||||
this._setActiveSegment(segmentationId, segmentIndex, false);
|
||||
}
|
||||
|
||||
@@ -434,12 +449,14 @@ class SegmentationService extends PubSubService {
|
||||
},
|
||||
]);
|
||||
|
||||
// Define a new color LUT and associate it with this segmentation.
|
||||
// Todo: need to be generalized to accept custom color LUTs
|
||||
const newColorLUT = this.generateNewColorLUT();
|
||||
const newColorLUTIndex = this.getNextColorLUTIndex();
|
||||
|
||||
cstSegmentation.config.color.addColorLUT(newColorLUT, newColorLUTIndex);
|
||||
// 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,
|
||||
@@ -448,8 +465,8 @@ class SegmentationService extends PubSubService {
|
||||
activeSegmentIndex: segmentation.activeSegmentIndex ?? null,
|
||||
segmentCount: segmentation.segmentCount ?? 0,
|
||||
isActive: false,
|
||||
colorLUTIndex: newColorLUTIndex,
|
||||
isVisible: true,
|
||||
colorLUTIndex,
|
||||
};
|
||||
|
||||
cachedSegmentation = this.segmentations[segmentationId];
|
||||
@@ -736,6 +753,97 @@ class SegmentationService extends PubSubService {
|
||||
return this.addOrUpdateSegmentation(segmentation, suppressEvents);
|
||||
}
|
||||
|
||||
// Todo: this should not run on the main thread
|
||||
public calculateCentroids = (
|
||||
segmentationId: string,
|
||||
segmentIndex?: number
|
||||
): Map<number, { x: number; y: number; z: number; world: number[] }> => {
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
const volume = this.getLabelmapVolume(segmentationId);
|
||||
const { dimensions, imageData } = volume;
|
||||
const scalarData = volume.getScalarData();
|
||||
const [dimX, dimY, numFrames] = dimensions;
|
||||
const frameLength = dimX * dimY;
|
||||
|
||||
const segmentIndices = segmentIndex
|
||||
? [segmentIndex]
|
||||
: segmentation.segments
|
||||
.filter(segment => segment?.segmentIndex)
|
||||
.map(segment => segment.segmentIndex);
|
||||
|
||||
const segmentIndicesSet = new Set(segmentIndices);
|
||||
|
||||
const centroids = new Map();
|
||||
for (const index of segmentIndicesSet) {
|
||||
centroids.set(index, { x: 0, y: 0, z: 0, count: 0 });
|
||||
}
|
||||
|
||||
let voxelIndex = 0;
|
||||
for (let frame = 0; frame < numFrames; frame++) {
|
||||
for (let p = 0; p < frameLength; p++) {
|
||||
const segmentIndex = scalarData[voxelIndex++];
|
||||
if (segmentIndicesSet.has(segmentIndex)) {
|
||||
const centroid = centroids.get(segmentIndex);
|
||||
centroid.x += p % dimX;
|
||||
centroid.y += (p / dimX) | 0;
|
||||
centroid.z += frame;
|
||||
centroid.count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = new Map();
|
||||
for (const [index, centroid] of centroids) {
|
||||
const count = centroid.count;
|
||||
const normalizedCentroid = {
|
||||
x: centroid.x / count,
|
||||
y: centroid.y / count,
|
||||
z: centroid.z / count,
|
||||
};
|
||||
normalizedCentroid.world = imageData.indexToWorld([
|
||||
normalizedCentroid.x,
|
||||
normalizedCentroid.y,
|
||||
normalizedCentroid.z,
|
||||
]);
|
||||
result.set(index, normalizedCentroid);
|
||||
}
|
||||
|
||||
this.setCentroids(segmentationId, result);
|
||||
return result;
|
||||
};
|
||||
|
||||
private setCentroids = (
|
||||
segmentationId: string,
|
||||
centroids: Map<number, { image: number[]; world?: number[] }>
|
||||
): void => {
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
const imageData = this.getLabelmapVolume(segmentationId).imageData; // Assuming this method returns imageData
|
||||
|
||||
if (!segmentation.cachedStats) {
|
||||
segmentation.cachedStats = { segmentCenter: {} };
|
||||
} else if (!segmentation.cachedStats.segmentCenter) {
|
||||
segmentation.cachedStats.segmentCenter = {};
|
||||
}
|
||||
|
||||
for (const [segmentIndex, centroid] of centroids) {
|
||||
let world = centroid.world;
|
||||
|
||||
// If world coordinates are not provided, calculate them
|
||||
if (!world || world.length === 0) {
|
||||
world = imageData.indexToWorld(centroid.image);
|
||||
}
|
||||
|
||||
segmentation.cachedStats.segmentCenter[segmentIndex] = {
|
||||
center: {
|
||||
image: centroid.image,
|
||||
world: world,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
this.addOrUpdateSegmentation(segmentation, true, true);
|
||||
};
|
||||
|
||||
public jumpToSegmentCenter(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
@@ -749,6 +857,10 @@ class SegmentationService extends PubSubService {
|
||||
const { toolGroupService } = this.servicesManager.services;
|
||||
const center = this._getSegmentCenter(segmentationId, segmentIndex);
|
||||
|
||||
if (!center?.world) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { world } = center;
|
||||
|
||||
// todo: generalize
|
||||
@@ -831,6 +943,7 @@ class SegmentationService extends PubSubService {
|
||||
displaySetInstanceUID: string,
|
||||
options?: {
|
||||
segmentationId: string;
|
||||
FrameOfReferenceUID: string;
|
||||
label: string;
|
||||
}
|
||||
): Promise<string> => {
|
||||
@@ -865,6 +978,8 @@ class SegmentationService extends PubSubService {
|
||||
// We should set it as active by default, as it created for display
|
||||
isActive: true,
|
||||
type: representationType,
|
||||
FrameOfReferenceUID:
|
||||
options?.FrameOfReferenceUID || displaySet.instances?.[0]?.FrameOfReferenceUID,
|
||||
representationData: {
|
||||
LABELMAP: {
|
||||
volumeId: segmentationId,
|
||||
@@ -892,7 +1007,8 @@ class SegmentationService extends PubSubService {
|
||||
toolGroupId: string,
|
||||
segmentationId: string,
|
||||
hydrateSegmentation = false,
|
||||
representationType = csToolsEnums.SegmentationRepresentations.Labelmap
|
||||
representationType = csToolsEnums.SegmentationRepresentations.Labelmap,
|
||||
suppressEvents = false
|
||||
): Promise<void> => {
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
|
||||
@@ -959,13 +1075,19 @@ class SegmentationService extends PubSubService {
|
||||
);
|
||||
}
|
||||
|
||||
if (isLocked !== undefined) {
|
||||
if (isLocked) {
|
||||
this._setSegmentLocked(segmentationId, segmentIndex, isLocked, suppressEvents);
|
||||
}
|
||||
}
|
||||
|
||||
if (!suppressEvents) {
|
||||
this._broadcastEvent(this.EVENTS.SEGMENTATION_UPDATED, {
|
||||
segmentation,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public setSegmentRGBAColorForSegmentation = (
|
||||
public setSegmentRGBAColor = (
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
rgbaColor,
|
||||
@@ -1008,11 +1130,11 @@ class SegmentationService extends PubSubService {
|
||||
if (!segmentation) {
|
||||
throw new Error(`Segmentation with segmentationId ${segmentationId} not found.`);
|
||||
}
|
||||
|
||||
this._setDisplaySetIsHydrated(segmentationId, true);
|
||||
|
||||
segmentation.hydrated = true;
|
||||
|
||||
// Not all segmentations have dipslaysets, some of them are derived in the client
|
||||
this._setDisplaySetIsHydrated(segmentationId, true);
|
||||
|
||||
if (!suppressEvents) {
|
||||
this._broadcastEvent(this.EVENTS.SEGMENTATION_UPDATED, {
|
||||
segmentation,
|
||||
@@ -1021,8 +1143,13 @@ class SegmentationService extends PubSubService {
|
||||
};
|
||||
|
||||
private _setDisplaySetIsHydrated(displaySetUID: string, isHydrated: boolean): void {
|
||||
const { DisplaySetService: displaySetService } = this.servicesManager.services;
|
||||
const { displaySetService } = this.servicesManager.services;
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetUID);
|
||||
|
||||
if (!displaySet) {
|
||||
return;
|
||||
}
|
||||
|
||||
displaySet.isHydrated = isHydrated;
|
||||
displaySetService.setDisplaySetMetadataInvalidated(displaySetUID, false);
|
||||
}
|
||||
@@ -1167,7 +1294,6 @@ class SegmentationService extends PubSubService {
|
||||
}
|
||||
|
||||
const { colorLUTIndex } = segmentation;
|
||||
|
||||
this._removeSegmentationFromCornerstone(segmentationId);
|
||||
|
||||
// Delete associated colormap
|
||||
@@ -1181,8 +1307,12 @@ class SegmentationService extends PubSubService {
|
||||
if (wasActive) {
|
||||
const remainingSegmentations = this._getSegmentations();
|
||||
|
||||
if (remainingSegmentations.length) {
|
||||
const { id } = remainingSegmentations[0];
|
||||
const remainingHydratedSegmentations = remainingSegmentations.filter(
|
||||
segmentation => segmentation.hydrated
|
||||
);
|
||||
|
||||
if (remainingHydratedSegmentations.length) {
|
||||
const { id } = remainingHydratedSegmentations[0];
|
||||
|
||||
this._setActiveSegmentationForToolGroup(id, this._getFirstToolGroupId(), false);
|
||||
}
|
||||
@@ -1312,15 +1442,11 @@ class SegmentationService extends PubSubService {
|
||||
return cstSegmentation.state.getSegmentationRepresentations(toolGroupId);
|
||||
};
|
||||
|
||||
public setSegmentLabelForSegmentation(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
label: string
|
||||
) {
|
||||
this._setSegmentLabelForSegmentation(segmentationId, segmentIndex, label);
|
||||
public setSegmentLabel(segmentationId: string, segmentIndex: number, label: string) {
|
||||
this._setSegmentLabel(segmentationId, segmentIndex, label);
|
||||
}
|
||||
|
||||
private _setSegmentLabelForSegmentation(
|
||||
private _setSegmentLabel(
|
||||
segmentationId: string,
|
||||
segmentIndex: number,
|
||||
label: string,
|
||||
@@ -1348,8 +1474,8 @@ class SegmentationService extends PubSubService {
|
||||
}
|
||||
}
|
||||
|
||||
public shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segDisplaySetInstanceUID) {
|
||||
if (!viewportDisplaySetInstanceUIDs || !viewportDisplaySetInstanceUIDs.length) {
|
||||
public shouldRenderSegmentation(viewportDisplaySetInstanceUIDs, segmentationFrameOfReferenceUID) {
|
||||
if (!viewportDisplaySetInstanceUIDs?.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1357,10 +1483,6 @@ class SegmentationService extends PubSubService {
|
||||
|
||||
let shouldDisplaySeg = false;
|
||||
|
||||
const segDisplaySet = displaySetService.getDisplaySetByUID(segDisplaySetInstanceUID);
|
||||
|
||||
const segFrameOfReferenceUID = this._getFrameOfReferenceUIDForSeg(segDisplaySet);
|
||||
|
||||
// check if the displaySet is sharing the same frameOfReferenceUID
|
||||
// with the new segmentation
|
||||
for (const displaySetInstanceUID of viewportDisplaySetInstanceUIDs) {
|
||||
@@ -1370,7 +1492,7 @@ class SegmentationService extends PubSubService {
|
||||
// don't want to show the segmentation for all the frames
|
||||
if (
|
||||
displaySet.isReconstructable &&
|
||||
displaySet?.images?.[0]?.FrameOfReferenceUID === segFrameOfReferenceUID
|
||||
displaySet?.images?.[0]?.FrameOfReferenceUID === segmentationFrameOfReferenceUID
|
||||
) {
|
||||
shouldDisplaySeg = true;
|
||||
break;
|
||||
@@ -1717,7 +1839,7 @@ class SegmentationService extends PubSubService {
|
||||
const segmentationRepresentations =
|
||||
this.getSegmentationRepresentationsForToolGroup(toolGroupId);
|
||||
|
||||
if (segmentationRepresentations.length === 0) {
|
||||
if (!segmentationRepresentations?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@ type Segmentation = {
|
||||
isActive: boolean;
|
||||
// if the segmentation is visible in the viewer
|
||||
isVisible: boolean;
|
||||
// the frame of reference UID of the segmentation
|
||||
FrameOfReferenceUID: string;
|
||||
// the label of the segmentation
|
||||
label: string;
|
||||
// the number of segments in the segmentation
|
||||
|
||||
@@ -97,9 +97,9 @@ export default class ToolGroupService {
|
||||
}
|
||||
|
||||
public getActiveToolForViewport(viewportId: string): string {
|
||||
const toolGroup = ToolGroupManager.getToolGroupForViewport(viewportId);
|
||||
const toolGroup = this.getToolGroupForViewport(viewportId);
|
||||
if (!toolGroup) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
return toolGroup.getActivePrimaryMouseButtonTool();
|
||||
@@ -184,13 +184,9 @@ export default class ToolGroupService {
|
||||
this._setToolsMode(toolGroup, tools);
|
||||
}
|
||||
|
||||
public createToolGroupAndAddTools(
|
||||
toolGroupId: string,
|
||||
tools: Array<Tool>,
|
||||
configs: any = {}
|
||||
): Types.IToolGroup {
|
||||
public createToolGroupAndAddTools(toolGroupId: string, tools: Array<Tool>): Types.IToolGroup {
|
||||
const toolGroup = this.createToolGroup(toolGroupId);
|
||||
this.addToolsToToolGroup(toolGroupId, tools, configs);
|
||||
this.addToolsToToolGroup(toolGroupId, tools);
|
||||
return toolGroup;
|
||||
}
|
||||
|
||||
@@ -239,34 +235,6 @@ export default class ToolGroupService {
|
||||
toolInstance.configuration = config;
|
||||
}
|
||||
|
||||
private _getToolNames(toolGroupTools: Tools): string[] {
|
||||
const toolNames = [];
|
||||
if (toolGroupTools.active) {
|
||||
toolGroupTools.active.forEach(tool => {
|
||||
toolNames.push(tool.toolName);
|
||||
});
|
||||
}
|
||||
if (toolGroupTools.passive) {
|
||||
toolGroupTools.passive.forEach(tool => {
|
||||
toolNames.push(tool.toolName);
|
||||
});
|
||||
}
|
||||
|
||||
if (toolGroupTools.enabled) {
|
||||
toolGroupTools.enabled.forEach(tool => {
|
||||
toolNames.push(tool.toolName);
|
||||
});
|
||||
}
|
||||
|
||||
if (toolGroupTools.disabled) {
|
||||
toolGroupTools.disabled.forEach(tool => {
|
||||
toolNames.push(tool.toolName);
|
||||
});
|
||||
}
|
||||
|
||||
return toolNames;
|
||||
}
|
||||
|
||||
private _setToolsMode(toolGroup, tools) {
|
||||
const { active, passive, enabled, disabled } = tools;
|
||||
|
||||
@@ -295,17 +263,33 @@ export default class ToolGroupService {
|
||||
}
|
||||
}
|
||||
|
||||
private _addTools(toolGroup, tools, configs) {
|
||||
const toolNames = this._getToolNames(tools);
|
||||
toolNames.forEach(toolName => {
|
||||
// Initialize the toolConfig if no configuration is provided
|
||||
const toolConfig = configs[toolName] ?? {};
|
||||
private _addTools(toolGroup, tools) {
|
||||
const addTools = tools => {
|
||||
tools.forEach(({ toolName, parentTool, configuration }) => {
|
||||
if (parentTool) {
|
||||
toolGroup.addToolInstance(toolName, parentTool, {
|
||||
...configuration,
|
||||
});
|
||||
} else {
|
||||
toolGroup.addTool(toolName, { ...configuration });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// if (volumeUID) {
|
||||
// toolConfig.volumeUID = volumeUID;
|
||||
// }
|
||||
if (tools.active) {
|
||||
addTools(tools.active);
|
||||
}
|
||||
|
||||
toolGroup.addTool(toolName, { ...toolConfig });
|
||||
});
|
||||
if (tools.passive) {
|
||||
addTools(tools.passive);
|
||||
}
|
||||
|
||||
if (tools.enabled) {
|
||||
addTools(tools.enabled);
|
||||
}
|
||||
|
||||
if (tools.disabled) {
|
||||
addTools(tools.disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,7 @@ import {
|
||||
import { utilities as csToolsUtils, Enums as csToolsEnums } from '@cornerstonejs/tools';
|
||||
import { IViewportService } from './IViewportService';
|
||||
import { RENDERING_ENGINE_ID } from './constants';
|
||||
import ViewportInfo, {
|
||||
ViewportOptions,
|
||||
DisplaySetOptions,
|
||||
PublicViewportOptions,
|
||||
} from './Viewport';
|
||||
import ViewportInfo, { DisplaySetOptions, PublicViewportOptions } from './Viewport';
|
||||
import { StackViewportData, VolumeViewportData } from '../../types/CornerstoneCacheService';
|
||||
import { Presentation, Presentations } from '../../types/Presentation';
|
||||
|
||||
@@ -586,10 +582,23 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise, check if the hydrated segmentations are in the same FOR
|
||||
// otherwise, check if the hydrated segmentations are in the same FrameOfReferenceUID
|
||||
// as the primary displaySet, if so add the representation (since it was not there)
|
||||
const { id: segDisplaySetInstanceUID, type } = segmentation;
|
||||
const segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
|
||||
const { id: segDisplaySetInstanceUID } = segmentation;
|
||||
let segFrameOfReferenceUID = this._getFrameOfReferenceUID(segDisplaySetInstanceUID);
|
||||
|
||||
if (!segFrameOfReferenceUID) {
|
||||
// if the segmentation displaySet does not have a FrameOfReferenceUID, we might check the
|
||||
// segmentation itself maybe it has a FrameOfReferenceUID
|
||||
const { FrameOfReferenceUID } = segmentation;
|
||||
if (FrameOfReferenceUID) {
|
||||
segFrameOfReferenceUID = FrameOfReferenceUID;
|
||||
}
|
||||
}
|
||||
|
||||
if (!segFrameOfReferenceUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
let shouldDisplaySeg = false;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { metaData } from '@cornerstonejs/core';
|
||||
import { VolumeViewport, metaData } from '@cornerstonejs/core';
|
||||
import { utilities } from '@cornerstonejs/core';
|
||||
import { IStackViewport, IVolumeViewport, Point3 } from '@cornerstonejs/core/dist/esm/types';
|
||||
import { AnnotationDisplayTool, drawing } from '@cornerstonejs/tools';
|
||||
import { guid } from '@ohif/core/src/utils';
|
||||
|
||||
@@ -46,6 +47,15 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool {
|
||||
this._cachedOverlayMetadata = new Map();
|
||||
};
|
||||
|
||||
protected getReferencedImageId(viewport: IStackViewport | IVolumeViewport): string {
|
||||
if (viewport instanceof VolumeViewport) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetId = this.getTargetId(viewport);
|
||||
return targetId.split('imageId:')[1];
|
||||
}
|
||||
|
||||
renderAnnotation = (enabledElement, svgDrawingHelper) => {
|
||||
const { viewport } = enabledElement;
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ class DicomLoaderService {
|
||||
|
||||
if (
|
||||
(!imageInstance && !nonImageInstance) ||
|
||||
!nonImageInstance.imageId.startsWith('dicomfile')
|
||||
!nonImageInstance.imageId?.startsWith('dicomfile')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user