fix(rt): dont convert to volume for RTSTRUCT (#4157)

Co-authored-by: Alireza <ar.sedghi@gmail.com>
This commit is contained in:
Ibrahim 2024-05-28 11:13:14 -04:00 committed by Alireza
parent 471e6a1559
commit 634abf353f
9 changed files with 78 additions and 55 deletions

View File

@ -111,7 +111,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
{...props}
displaySets={[referencedDisplaySet, rtDisplaySet]}
viewportOptions={{
viewportType: 'volume',
viewportType: 'stack',
toolGroupId: toolGroupId,
orientation: viewportOptions.orientation,
viewportId: viewportOptions.viewportId,

View File

@ -213,7 +213,7 @@ const commandsModule = ({
updateViewportsForSegmentationRendering({
viewportId,
servicesManager,
referencedDisplaySetInstanceUID: displaySet.referencedDisplaySetInstanceUID,
displaySet,
loadFn: async () => {
const segDisplaySet = displaySet;
const suppressEvents = false;

View File

@ -10,7 +10,7 @@ import { Enums, cache } from '@cornerstonejs/core';
* @param params.viewportId - ID of the viewport to be updated.
* @param params.loadFn - Function to load the segmentation data.
* @param params.servicesManager - The services manager.
* @param params.referencedDisplaySetInstanceUID - Optional UID for the referenced display set instance.
* @param params.displaySet - the display set.
*
* @returns Returns true upon successful update of viewports for segmentation rendering.
*/
@ -18,12 +18,12 @@ async function updateViewportsForSegmentationRendering({
viewportId,
loadFn,
servicesManager,
referencedDisplaySetInstanceUID,
displaySet,
}: {
viewportId: string;
loadFn: () => Promise<string>;
servicesManager: AppTypes.ServicesManager;
referencedDisplaySetInstanceUID?: string;
displaySet?: any;
}) {
const { cornerstoneViewportService, segmentationService, viewportGridService } =
servicesManager.services;
@ -31,13 +31,13 @@ async function updateViewportsForSegmentationRendering({
const viewport = getTargetViewport({ viewportId, viewportGridService });
const targetViewportId = viewport.viewportOptions.viewportId;
referencedDisplaySetInstanceUID =
referencedDisplaySetInstanceUID || viewport?.displaySetInstanceUIDs[0];
const referencedDisplaySetInstanceUID =
displaySet?.referencedDisplaySetInstanceUID || viewport?.displaySetInstanceUIDs[0];
const updatedViewports = getUpdatedViewportsForSegmentation({
servicesManager,
viewportId,
referencedDisplaySetInstanceUID,
displaySet,
});
// create Segmentation callback which needs to be waited until
@ -56,7 +56,7 @@ async function updateViewportsForSegmentationRendering({
updatedViewports.forEach(async viewport => {
viewport.viewportOptions = {
...viewport.viewportOptions,
viewportType: 'volume',
viewportType: displaySet?.Modality === 'RTSTRUCT' ? 'stack' : 'volume',
needsRerendering: true,
};
const viewportId = viewport.viewportId;
@ -66,7 +66,7 @@ async function updateViewportsForSegmentationRendering({
// only run the createSegmentationForVolume for the targetViewportId
// since the rest will get handled by cornerstoneViewportService
if (volumeExists && viewportId === targetViewportId) {
if ((volumeExists || displaySet.Modality === 'RTSTRUCT') && viewportId === targetViewportId) {
await createSegmentationForVolume();
return;
}
@ -126,14 +126,14 @@ const getTargetViewport = ({ viewportId, viewportGridService }) => {
* @param {Object} params - Parameters for the function.
* @param params.viewportId - the ID of the viewport to be updated.
* @param params.servicesManager - The services manager
* @param params.referencedDisplaySetInstanceUID - Optional UID for the referenced display set instance.
* @param params.displaySet - the display set.
*
* @returns {Array} Returns an array of viewports that require updates for segmentation rendering.
*/
function getUpdatedViewportsForSegmentation({
viewportId,
servicesManager,
referencedDisplaySetInstanceUID,
displaySet,
}: withAppTypes) {
const { hangingProtocolService, displaySetService, segmentationService, viewportGridService } =
servicesManager.services;
@ -146,7 +146,7 @@ function getUpdatedViewportsForSegmentation({
const displaySetInstanceUIDs = viewports.get(targetViewportId).displaySetInstanceUIDs;
const referenceDisplaySetInstanceUID =
referencedDisplaySetInstanceUID || displaySetInstanceUIDs[0];
displaySet?.referencedDisplaySetInstanceUID || displaySetInstanceUIDs[0];
const referencedDisplaySet = displaySetService.getDisplaySetByUID(referenceDisplaySetInstanceUID);
const segmentationFrameOfReferenceUID = referencedDisplaySet.instances[0].FrameOfReferenceUID;
@ -175,7 +175,7 @@ function getUpdatedViewportsForSegmentation({
viewportId,
displaySetInstanceUIDs: viewport.displaySetInstanceUIDs,
viewportOptions: {
viewportType: 'volume',
viewportType: displaySet.Modality === 'RTSTRUCT' ? 'stack' : 'volume',
needsRerendering: true,
},
});

View File

@ -317,7 +317,7 @@ function CustomizableViewportOverlay({
function _getViewportInstances(viewportData) {
const imageIds = [];
if (viewportData.viewportType === Enums.ViewportType.STACK) {
imageIds.push(viewportData.data.imageIds[0]);
imageIds.push(viewportData.data[0].imageIds[0]);
} else if (viewportData.viewportType === Enums.ViewportType.ORTHOGRAPHIC) {
const volumes = viewportData.data;
volumes.forEach(volume => {
@ -357,7 +357,7 @@ const getInstanceNumber = (viewportData, viewportId, imageIndex, cornerstoneView
};
function _getInstanceNumberFromStack(viewportData, imageIndex) {
const imageIds = viewportData.data.imageIds;
const imageIds = viewportData.data[0].imageIds;
const imageId = imageIds[imageIndex];
if (!imageId) {

View File

@ -48,7 +48,7 @@ function CornerstoneImageScrollbar({
setImageSliceData({
imageIndex: imageIndex,
numberOfSlices: viewportData.data.imageIds.length,
numberOfSlices: viewportData.data[0].imageIds.length,
});
return;
@ -78,7 +78,7 @@ function CornerstoneImageScrollbar({
// find the index of imageId in the imageIds
setImageSliceData({
imageIndex: newImageIdIndex,
numberOfSlices: viewportData.data.imageIds.length,
numberOfSlices: viewportData.data[0].imageIds.length,
});
};

View File

@ -67,7 +67,7 @@ function ViewportOrientationMarkers({
let rowCosines, columnCosines;
if (viewportData.viewportType === 'stack') {
const imageIndex = imageSliceData.imageIndex;
const imageId = viewportData.data.imageIds?.[imageIndex];
const imageId = viewportData.data[0].imageIds?.[imageIndex];
// Workaround for below TODO stub
if (!imageId) {

View File

@ -129,14 +129,25 @@ class CornerstoneCacheService {
return newViewportData;
}
private _getStackViewportData(
private async _getStackViewportData(
dataSource,
displaySets,
initialImageIndex,
viewportType: Enums.ViewportType
): StackViewportData {
// For Stack Viewport we don't have fusion currently
const displaySet = displaySets[0];
): Promise<StackViewportData> {
const overlayDisplaySets = displaySets.filter(ds => ds.isOverlayDisplaySet);
const nonOverlayDisplaySets = displaySets.filter(ds => !ds.isOverlayDisplaySet);
// load overlays if they are not loaded
for (const overlayDisplaySet of overlayDisplaySets) {
if (overlayDisplaySet.load && overlayDisplaySet.load instanceof Function) {
const { userAuthenticationService } = this.servicesManager.services;
const headers = userAuthenticationService.getAuthorizationHeader();
await overlayDisplaySet.load({ headers });
}
}
const displaySet = nonOverlayDisplaySets[0];
let stackImageIds = this.stackImageIds.get(displaySet.displaySetInstanceUID);
@ -145,23 +156,23 @@ class CornerstoneCacheService {
this.stackImageIds.set(displaySet.displaySetInstanceUID, stackImageIds);
}
const { displaySetInstanceUID, StudyInstanceUID, isCompositeStack } = displaySet;
// Ensuring the first non-overlay `displaySet` is always the primary one
const StackViewportData = [displaySet, ...overlayDisplaySets].map(ds => {
const { displaySetInstanceUID, StudyInstanceUID, isCompositeStack } = ds;
const StackViewportData: StackViewportData = {
viewportType,
data: {
return {
StudyInstanceUID,
displaySetInstanceUID,
isCompositeStack,
imageIds: stackImageIds,
},
initialImageIndex,
};
});
return {
viewportType,
data: StackViewportData,
};
if (typeof initialImageIndex === 'number') {
StackViewportData.data.initialImageIndex = initialImageIndex;
}
return StackViewportData;
}
private async _getVolumeViewportData(

View File

@ -557,9 +557,14 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
): Promise<void> {
const displaySetOptions = viewportInfo.getDisplaySetOptions();
const { imageIds, initialImageIndex, displaySetInstanceUID } = viewportData.data;
const displaySetInstanceUIDs = viewportData.data.map(data => data.displaySetInstanceUID);
this.viewportsDisplaySets.set(viewport.id, [displaySetInstanceUID]);
// based on the cache service construct always the first one is the non-overlay
// and the rest are overlays
this.viewportsDisplaySets.set(viewport.id, [...displaySetInstanceUIDs]);
const { initialImageIndex, imageIds } = viewportData.data[0];
let initialImageIndexToUse =
presentations?.positionPresentation?.initialImageIndex ?? initialImageIndex;
@ -588,6 +593,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
}
}
this._handleOverlays(viewport);
return viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
viewport.setProperties({ ...properties });
this.setPresentations(viewport.id, presentations);
@ -772,24 +779,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
this.setPresentations(viewport.id, presentations);
// load any secondary displaySets
const displaySetInstanceUIDs = this.viewportsDisplaySets.get(viewport.id);
// can be SEG or RTSTRUCT for now
const overlayDisplaySet = displaySetInstanceUIDs
.map(displaySetService.getDisplaySetByUID)
.find(displaySet => displaySet?.isOverlayDisplaySet);
if (overlayDisplaySet) {
this.addOverlayRepresentationForDisplaySet(overlayDisplaySet, viewport);
} else {
// If the displaySet is not a SEG displaySet we assume it is a primary displaySet
// and we can look into hydrated segmentations to check if any of them are
// associated with the primary displaySet
// get segmentations only returns the hydrated segmentations
this._addSegmentationRepresentationToToolGroupIfNecessary(displaySetInstanceUIDs, viewport);
}
this._handleOverlays(viewport);
const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id);
csToolsUtils.segmentation.triggerSegmentationRender(toolGroup.id);
@ -809,6 +799,28 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
});
}
private _handleOverlays(viewport: Types.IStackViewport | Types.IVolumeViewport) {
const { displaySetService } = this.servicesManager.services;
// load any secondary displaySets
const displaySetInstanceUIDs = this.viewportsDisplaySets.get(viewport.id);
// can be SEG or RTSTRUCT for now
const overlayDisplaySet = displaySetInstanceUIDs
.map(displaySetService.getDisplaySetByUID)
.find(displaySet => displaySet?.isOverlayDisplaySet);
if (overlayDisplaySet) {
this.addOverlayRepresentationForDisplaySet(overlayDisplaySet, viewport);
} else {
// If the displaySet is not a SEG displaySet we assume it is a primary displaySet
// and we can look into hydrated segmentations to check if any of them are
// associated with the primary displaySet
// get segmentations only returns the hydrated segmentations
this._addSegmentationRepresentationToToolGroupIfNecessary(displaySetInstanceUIDs, viewport);
}
}
private _addSegmentationRepresentationToToolGroupIfNecessary(
displaySetInstanceUIDs: string[],
viewport: any

View File

@ -22,7 +22,7 @@ type VolumeData = {
type StackViewportData = {
viewportType: Enums.ViewportType;
data: StackData;
data: StackData[];
};
type VolumeViewportData = {