feat: Add interleaved HTJ2K and volume progressive loading (#4276)

This commit is contained in:
Bill Wallace authored and GitHub committed 2024-07-05 12:33:07 -04:00
1 parent 2845f07806
commit a2084f319b
11 files changed
+336 -17

No files matched your search

+25
View File
@@ -42,6 +42,7 @@ import { createFrameViewSynchronizer } from './synchronizers/frameViewSynchroniz
const { helpers: volumeLoaderHelpers } = csStreamingImageVolumeLoader;
const { getDynamicVolumeInfo } = volumeLoaderHelpers ?? {};
const { imageRetrieveMetadataProvider } = cornerstone.utilities;
const Component = React.lazy(() => {
return import(/* webpackPrefetch: true */ './Viewport/OHIFCornerstoneViewport');
@@ -55,6 +56,15 @@ const OHIFCornerstoneViewport = props => {
);
};
const stackRetrieveOptions = {
retrieveOptions: {
single: {
streaming: true,
decodeLevel: 1,
},
},
};
/**
*
*/
@@ -80,6 +90,21 @@ const cornerstoneExtension: Types.Extensions.Extension = {
toolbarService.registerEventForToolbarUpdate(cornerstone.eventTarget, [
cornerstoneTools.Enums.Events.TOOL_ACTIVATED,
]);
// Configure the interleaved/HTJ2K loader
imageRetrieveMetadataProvider.clear();
// The default volume interleaved options are to interleave the
// image retrieve, but don't perform progressive loading per image
// This interleaves images and replicates them for low-resolution depth volume
// reconstruction, which progressively improves
imageRetrieveMetadataProvider.add(
'volume',
cornerstone.ProgressiveRetrieveImages.interleavedRetrieveStages
);
// The default stack loading option is to progressive load HTJ2K images
// There are other possible options, but these need more thought about
// how to define them.
imageRetrieveMetadataProvider.add('stack', stackRetrieveOptions);
},
onModeExit: ({ servicesManager }: withAppTypes): void => {
+6 -4
View File
@@ -99,7 +99,7 @@ export default async function init({
hangingProtocolService,
viewportGridService,
stateSyncService,
studyPrefetcherService
studyPrefetcherService,
} = servicesManager.services;
window.services = servicesManager.services;
@@ -180,10 +180,12 @@ export default async function init({
); // this provider is required for Calibration tool
metaData.addProvider(metadataProvider.get.bind(metadataProvider), 9999);
// These are set reasonably low to allow for interleaved retrieves and slower
// connections.
imageLoadPoolManager.maxNumRequests = {
interaction: appConfig?.maxNumRequests?.interaction || 100,
thumbnail: appConfig?.maxNumRequests?.thumbnail || 75,
prefetch: appConfig?.maxNumRequests?.prefetch || 10,
interaction: appConfig?.maxNumRequests?.interaction || 10,
thumbnail: appConfig?.maxNumRequests?.thumbnail || 5,
prefetch: appConfig?.maxNumRequests?.prefetch || 5,
};
initWADOImageLoader(userAuthenticationService, appConfig, extensionManager);
@@ -31,6 +31,7 @@ function PanelStudyBrowser({
const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = useState([
...StudyInstanceUIDs,
]);
const [hasLoadedViewports, setHasLoadedViewports] = useState(false);
const [studyDisplayList, setStudyDisplayList] = useState([]);
const [displaySets, setDisplaySets] = useState([]);
const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = useState({});
@@ -108,6 +109,18 @@ function PanelStudyBrowser({
// // ~~ Initial Thumbnails
useEffect(() => {
if (!hasLoadedViewports) {
if (activeViewportId) {
// Once there is an active viewport id, it means the layout is ready
// so wait a bit of time to allow the viewports preferential loading
// which improves user experience of responsiveness significantly on slower
// systems.
window.setTimeout(() => setHasLoadedViewports(true), 250);
}
return;
}
const currentDisplaySets = displaySetService.activeDisplaySets;
currentDisplaySets.forEach(async dSet => {
const newImageSrcEntry = {};
@@ -126,7 +139,14 @@ function PanelStudyBrowser({
return { ...prevState, ...newImageSrcEntry };
});
});
}, [StudyInstanceUIDs, dataSource, displaySetService, getImageSrc]);
}, [
StudyInstanceUIDs,
dataSource,
displaySetService,
getImageSrc,
hasLoadedViewports,
activeViewportId,
]);
// ~~ displaySets
useEffect(() => {
@@ -144,6 +164,9 @@ function PanelStudyBrowser({
const SubscriptionDisplaySetsAdded = displaySetService.subscribe(
displaySetService.EVENTS.DISPLAY_SETS_ADDED,
data => {
if (!hasLoadedViewports) {
return;
}
const { displaySetsAdded, options } = data;
displaySetsAdded.forEach(async dSet => {
const newImageSrcEntry = {};
@@ -213,7 +236,7 @@ function PanelStudyBrowser({
const shouldCollapseStudy = expandedStudyInstanceUIDs.includes(StudyInstanceUID);
const updatedExpandedStudyInstanceUIDs = shouldCollapseStudy
? // eslint-disable-next-line prettier/prettier
[...expandedStudyInstanceUIDs.filter(stdyUid => stdyUid !== StudyInstanceUID)]
[...expandedStudyInstanceUIDs.filter(stdyUid => stdyUid !== StudyInstanceUID)]
: [...expandedStudyInstanceUIDs, StudyInstanceUID];
setExpandedStudyInstanceUIDs(updatedExpandedStudyInstanceUIDs);
+1 -1
View File
@@ -31,7 +31,7 @@ function getPanelModule({ commandsManager, extensionManager, servicesManager })
}),
},
{
name: 'measure',
name: 'measurements',
iconName: 'tab-linear',
iconLabel: 'Measure',
label: i18n.t('SidePanel:Measurements'),
@@ -43,6 +43,7 @@ function PanelStudyBrowserTracking({
...StudyInstanceUIDs,
]);
const [studyDisplayList, setStudyDisplayList] = useState([]);
const [hasLoadedViewports, setHasLoadedViewports] = useState(false);
const [displaySets, setDisplaySets] = useState([]);
const [displaySetsLoadingState, setDisplaySetsLoadingState] = useState({});
const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = useState({});
@@ -128,6 +129,18 @@ function PanelStudyBrowserTracking({
// ~~ Initial Thumbnails
useEffect(() => {
if (!hasLoadedViewports) {
if (activeViewportId) {
// Once there is an active viewport id, it means the layout is ready
// so wait a bit of time to allow the viewports preferential loading
// which improves user experience of responsiveness significantly on slower
// systems.
window.setTimeout(() => setHasLoadedViewports(true), 250);
}
return;
}
const currentDisplaySets = displaySetService.activeDisplaySets;
if (!currentDisplaySets.length) {
@@ -152,7 +165,7 @@ function PanelStudyBrowserTracking({
return { ...prevState, ...newImageSrcEntry };
});
});
}, [displaySetService, dataSource, getImageSrc]);
}, [displaySetService, dataSource, getImageSrc, activeViewportId, hasLoadedViewports]);
// ~~ displaySets
useEffect(() => {
@@ -209,6 +222,9 @@ function PanelStudyBrowserTracking({
const SubscriptionDisplaySetsAdded = displaySetService.subscribe(
displaySetService.EVENTS.DISPLAY_SETS_ADDED,
data => {
if (!hasLoadedViewports) {
return;
}
const { displaySetsAdded, options } = data;
displaySetsAdded.forEach(async dSet => {
const displaySetInstanceUID = dSet.displaySetInstanceUID;
@@ -428,7 +444,7 @@ function PanelStudyBrowserTracking({
onClickUntrack={displaySetInstanceUID => {
onClickUntrack(displaySetInstanceUID);
}}
onClickThumbnail={() => {}}
onClickThumbnail={() => { }}
onDoubleClickThumbnail={onDoubleClickThumbnailHandler}
activeDisplaySetInstanceUIDs={activeViewportDisplaySetInstanceUIDs}
/>