feat(hp): enable OHIF to run with partial metadata for large studies at the cost of less effective hanging protocol (#3804)
Co-authored-by: rodrigobasilio2022 <rodrigo.basilio@radicalimaging.com> Co-authored-by: rodrigobasilio2022 <114958722+rodrigobasilio2022@users.noreply.github.com>
This commit is contained in:
9 files changed
+240
-74
No files matched your search
@@ -13,7 +13,7 @@ import { history } from '../../utils/history';
|
||||
import loadModules from '../../pluginImports';
|
||||
import isSeriesFilterUsed from '../../utils/isSeriesFilterUsed';
|
||||
|
||||
const { getSplitParam } = utils;
|
||||
const { getSplitParam, sortingCriteria } = utils;
|
||||
const { TimingEnum } = Types;
|
||||
|
||||
/**
|
||||
@@ -26,11 +26,33 @@ const { TimingEnum } = Types;
|
||||
* @returns array of subscriptions to cancel
|
||||
*/
|
||||
function defaultRouteInit(
|
||||
{ servicesManager, studyInstanceUIDs, dataSource, filters },
|
||||
{ servicesManager, studyInstanceUIDs, dataSource, filters, appConfig },
|
||||
hangingProtocolId
|
||||
) {
|
||||
const { displaySetService, hangingProtocolService, uiNotificationService } =
|
||||
const { displaySetService, hangingProtocolService, uiNotificationService, customizationService } =
|
||||
servicesManager.services;
|
||||
/**
|
||||
* Function to apply the hanging protocol when the minimum number of display sets were
|
||||
* received or all display sets retrieval were completed
|
||||
* @returns
|
||||
*/
|
||||
function applyHangingProtocol() {
|
||||
const displaySets = displaySetService.getActiveDisplaySets();
|
||||
|
||||
if (!displaySets || !displaySets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Gets the studies list to use
|
||||
const studies = getStudies(studyInstanceUIDs, displaySets);
|
||||
|
||||
// study being displayed, and is thus the "active" study.
|
||||
const activeStudy = studies[0];
|
||||
|
||||
// run the hanging protocol matching on the displaySets with the predefined
|
||||
// hanging protocol in the mode configuration
|
||||
hangingProtocolService.run({ studies, activeStudy, displaySets }, hangingProtocolId);
|
||||
}
|
||||
|
||||
const unsubscriptions = [];
|
||||
const issuedWarningSeries = [];
|
||||
@@ -55,6 +77,7 @@ function defaultRouteInit(
|
||||
duration: 7000,
|
||||
});
|
||||
}
|
||||
|
||||
displaySetService.makeDisplaySets(seriesMetadata.instances, madeInClient);
|
||||
}
|
||||
);
|
||||
@@ -63,10 +86,15 @@ function defaultRouteInit(
|
||||
|
||||
log.time(TimingEnum.STUDY_TO_DISPLAY_SETS);
|
||||
log.time(TimingEnum.STUDY_TO_FIRST_IMAGE);
|
||||
|
||||
const allRetrieves = studyInstanceUIDs.map(StudyInstanceUID =>
|
||||
dataSource.retrieve.series.metadata({
|
||||
StudyInstanceUID,
|
||||
filters,
|
||||
returnPromises: true,
|
||||
sortCriteria:
|
||||
customizationService.get('sortingCriteria') ||
|
||||
sortingCriteria.seriesSortCriteria.seriesInfoSortingCriteria,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -77,31 +105,34 @@ function defaultRouteInit(
|
||||
});
|
||||
});
|
||||
|
||||
// The hanging protocol matching service is fairly expensive to run multiple
|
||||
// times, and doesn't allow partial matches to be made (it will simply fail
|
||||
// to display anything if a required match fails), so we wait here until all metadata
|
||||
// is retrieved (which will synchronously trigger the display set creation)
|
||||
// until we run the hanging protocol matching service.
|
||||
|
||||
Promise.allSettled(allRetrieves).then(() => {
|
||||
Promise.allSettled(allRetrieves).then(promises => {
|
||||
log.timeEnd(TimingEnum.STUDY_TO_DISPLAY_SETS);
|
||||
log.time(TimingEnum.DISPLAY_SETS_TO_FIRST_IMAGE);
|
||||
log.time(TimingEnum.DISPLAY_SETS_TO_ALL_IMAGES);
|
||||
const displaySets = displaySetService.getActiveDisplaySets();
|
||||
|
||||
if (!displaySets || !displaySets.length) {
|
||||
return;
|
||||
const allPromises = [];
|
||||
const remainingPromises = [];
|
||||
|
||||
function startRemainingPromises(remainingPromises) {
|
||||
remainingPromises.forEach(p => p.forEach(p => p.start()));
|
||||
}
|
||||
|
||||
// Gets the studies list to use
|
||||
const studies = getStudies(studyInstanceUIDs, displaySets);
|
||||
promises.forEach(promise => {
|
||||
const retrieveSeriesMetadataPromise = promise.value;
|
||||
if (Array.isArray(retrieveSeriesMetadataPromise)) {
|
||||
const { requiredSeries, remaining } = hangingProtocolService.filterSeriesRequiredForRun(
|
||||
hangingProtocolId,
|
||||
retrieveSeriesMetadataPromise
|
||||
);
|
||||
const requiredSeriesPromises = requiredSeries.map(promise => promise.start());
|
||||
allPromises.push(Promise.allSettled(requiredSeriesPromises));
|
||||
remainingPromises.push(remaining);
|
||||
}
|
||||
});
|
||||
|
||||
// study being displayed, and is thus the "active" study.
|
||||
const activeStudy = studies[0];
|
||||
|
||||
// run the hanging protocol matching on the displaySets with the predefined
|
||||
// hanging protocol in the mode configuration
|
||||
hangingProtocolService.run({ studies, activeStudy, displaySets }, hangingProtocolId);
|
||||
Promise.allSettled(allPromises).then(applyHangingProtocol);
|
||||
startRemainingPromises(remainingPromises);
|
||||
applyHangingProtocol();
|
||||
});
|
||||
|
||||
return unsubscriptions;
|
||||
@@ -401,6 +432,7 @@ export default function ModeRoute({
|
||||
studyInstanceUIDs,
|
||||
dataSource,
|
||||
filters,
|
||||
appConfig,
|
||||
},
|
||||
hangingProtocolIdToUse
|
||||
);
|
||||
@@ -482,4 +514,5 @@ ModeRoute.propTypes = {
|
||||
extensionManager: PropTypes.object,
|
||||
servicesManager: PropTypes.object,
|
||||
hotkeysManager: PropTypes.object,
|
||||
commandsManager: PropTypes.object,
|
||||
};
|
||||
@@ -214,6 +214,32 @@ export default class HangingProtocolService extends PubSubService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the series required for running a hanging protocol.
|
||||
*
|
||||
* This can be extended in the future with more complex selection rules e.g.
|
||||
* N series of a given type, and M of a different type, such as all CT series,
|
||||
* and all SR, and then everything else.
|
||||
*
|
||||
* @param protocolId - The ID of the hanging protocol.
|
||||
* @param seriesPromises - An array of promises representing the series.
|
||||
* @returns An object containing the required series and the remaining series.
|
||||
*/
|
||||
public filterSeriesRequiredForRun(protocolId, seriesPromises) {
|
||||
if (Array.isArray(protocolId)) {
|
||||
protocolId = protocolId[0];
|
||||
}
|
||||
const minSeriesLoadedToRunHP =
|
||||
this.getProtocolById(protocolId)?.hpInitiationCriteria?.minSeriesLoaded ||
|
||||
seriesPromises.length;
|
||||
const requiredSeries = seriesPromises.slice(0, minSeriesLoadedToRunHP);
|
||||
const remaining = seriesPromises.slice(minSeriesLoadedToRunHP);
|
||||
return {
|
||||
requiredSeries,
|
||||
remaining,
|
||||
};
|
||||
}
|
||||
|
||||
/** Gets the protocol with id 'default' */
|
||||
public getDefaultProtocol(): HangingProtocol.Protocol {
|
||||
return this.getProtocolById('default');
|
||||
@@ -543,8 +569,8 @@ export default class HangingProtocolService extends PubSubService {
|
||||
viewportId: existingViewportId
|
||||
? existingViewportId
|
||||
: index === 0
|
||||
? 'default'
|
||||
: uuidv4(),
|
||||
? 'default'
|
||||
: uuidv4(),
|
||||
},
|
||||
displaySets: viewport.displaySets || [],
|
||||
};
|
||||
|
||||
@@ -293,6 +293,17 @@ export type Protocol = {
|
||||
*/
|
||||
numberOfPriorsReferenced?: number;
|
||||
syncDataForViewports?: boolean;
|
||||
/**
|
||||
* Set of minimal conditions necessary to run the hanging protocol.
|
||||
*/
|
||||
hpInitiationCriteria?: {
|
||||
/* If configured, sets the minimum number of series needed to run the hanging
|
||||
* protocol and start displaying images. Used when OHIF needs to handle studies
|
||||
* with several series and it is required that the first image should be loaded
|
||||
* faster.
|
||||
*/
|
||||
minSeriesLoaded: number;
|
||||
};
|
||||
};
|
||||
|
||||
/** Used to dynamically generate protocols.
|
||||
|
||||
Reference in new issue
Block a user