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 GitHub
parent b0af8ec553
commit 7745c092bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 78 additions and 55 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -143,14 +143,25 @@ class CornerstoneCacheService {
return newViewportData; return newViewportData;
} }
private _getStackViewportData( private async _getStackViewportData(
dataSource, dataSource,
displaySets, displaySets,
initialImageIndex, initialImageIndex,
viewportType: Enums.ViewportType viewportType: Enums.ViewportType
): StackViewportData { ): Promise<StackViewportData> {
// For Stack Viewport we don't have fusion currently const overlayDisplaySets = displaySets.filter(ds => ds.isOverlayDisplaySet);
const displaySet = displaySets[0]; 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); let stackImageIds = this.stackImageIds.get(displaySet.displaySetInstanceUID);
@ -159,23 +170,23 @@ class CornerstoneCacheService {
this.stackImageIds.set(displaySet.displaySetInstanceUID, stackImageIds); 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 = { return {
viewportType,
data: {
StudyInstanceUID, StudyInstanceUID,
displaySetInstanceUID, displaySetInstanceUID,
isCompositeStack, isCompositeStack,
imageIds: stackImageIds, imageIds: stackImageIds,
}, initialImageIndex,
};
});
return {
viewportType,
data: StackViewportData,
}; };
if (typeof initialImageIndex === 'number') {
StackViewportData.data.initialImageIndex = initialImageIndex;
}
return StackViewportData;
} }
private async _getVolumeViewportData( private async _getVolumeViewportData(

View File

@ -557,9 +557,14 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
): Promise<void> { ): Promise<void> {
const displaySetOptions = viewportInfo.getDisplaySetOptions(); 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 = let initialImageIndexToUse =
presentations?.positionPresentation?.initialImageIndex ?? initialImageIndex; presentations?.positionPresentation?.initialImageIndex ?? initialImageIndex;
@ -588,6 +593,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
} }
} }
this._handleOverlays(viewport);
return viewport.setStack(imageIds, initialImageIndexToUse).then(() => { return viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
viewport.setProperties({ ...properties }); viewport.setProperties({ ...properties });
this.setPresentations(viewport.id, presentations); this.setPresentations(viewport.id, presentations);
@ -772,24 +779,7 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
this.setPresentations(viewport.id, presentations); this.setPresentations(viewport.id, presentations);
// load any secondary displaySets this._handleOverlays(viewport);
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);
}
const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id); const toolGroup = toolGroupService.getToolGroupForViewport(viewport.id);
csToolsUtils.segmentation.triggerSegmentationRender(toolGroup.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( private _addSegmentationRepresentationToToolGroupIfNecessary(
displaySetInstanceUIDs: string[], displaySetInstanceUIDs: string[],
viewport: any viewport: any

View File

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