fix(DICOM Overlay): The overlay data wasn't being refreshed on change (#3793)

This commit is contained in:
Bill Wallace 2023-11-21 09:05:25 -05:00 committed by GitHub
parent c4e22c251f
commit 00e751933a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 69 additions and 27 deletions

View File

@ -29,6 +29,7 @@ import { id } from './id';
import * as csWADOImageLoader from './initWADOImageLoader.js';
import { measurementMappingUtils } from './utils/measurementServiceMappings';
import type { PublicViewportOptions } from './services/ViewportService/Viewport';
import ImageOverlayViewerTool from './tools/ImageOverlayViewerTool';
const Component = React.lazy(() => {
return import(/* webpackPrefetch: true */ './Viewport/OHIFCornerstoneViewport');
@ -140,5 +141,6 @@ export {
CornerstoneExtensionTypes as Types,
toolNames,
getActiveViewportEnabledElement,
ImageOverlayViewerTool,
};
export default cornerstoneExtension;

View File

@ -1,8 +1,8 @@
import { VolumeViewport, metaData } from '@cornerstonejs/core';
import { utilities } from '@cornerstonejs/core';
import { VolumeViewport, metaData, 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';
import OverlayPlaneModuleProvider from './OverlayPlaneModuleProvider';
interface CachedStat {
color: number[]; // [r, g, b, a]
@ -27,8 +27,12 @@ interface CachedStat {
*/
class ImageOverlayViewerTool extends AnnotationDisplayTool {
static toolName = 'ImageOverlayViewer';
private _cachedOverlayMetadata: Map<string, any[]> = new Map();
private _cachedStats: { [key: string]: CachedStat } = {};
/**
* The overlay plane module provider add method is exposed here to be used
* when updating the overlay for this tool to use for displaying data.
*/
public static addOverlayPlaneModule = OverlayPlaneModuleProvider.add;
constructor(
toolProps = {},
@ -42,10 +46,7 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool {
super(toolProps, defaultToolProps);
}
onSetToolDisabled = (): void => {
this._cachedStats = {};
this._cachedOverlayMetadata = new Map();
};
onSetToolDisabled = (): void => { };
protected getReferencedImageId(viewport: IStackViewport | IVolumeViewport): string {
if (viewport instanceof VolumeViewport) {
@ -64,9 +65,8 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool {
return;
}
const overlays =
this._cachedOverlayMetadata.get(imageId) ??
metaData.get('overlayPlaneModule', imageId)?.overlays;
const overlayMetadata = metaData.get('overlayPlaneModule', imageId);
const overlays = overlayMetadata?.overlays;
// no overlays
if (!overlays?.length) {
@ -79,9 +79,10 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool {
overlay.y ||= 0;
});
this._cachedOverlayMetadata.set(imageId, overlays);
// Will clear cached stat data when the overlay data changes
ImageOverlayViewerTool.addOverlayPlaneModule(imageId, overlayMetadata);
this._getCachedStat(imageId, overlays, this.configuration.fillColor).then(cachedStat => {
this._getCachedStat(imageId, overlayMetadata, this.configuration.fillColor).then(cachedStat => {
cachedStat.overlays.forEach(overlay => {
this._renderOverlay(enabledElement, svgDrawingHelper, overlay);
});
@ -152,15 +153,18 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool {
private async _getCachedStat(
imageId: string,
overlayMetadata: any[],
overlayMetadata,
color: number[]
): Promise<CachedStat> {
if (this._cachedStats[imageId] && this._isSameColor(this._cachedStats[imageId].color, color)) {
return this._cachedStats[imageId];
const missingOverlay = overlayMetadata.overlays.filter(
overlay => overlay.pixelData && !overlay.dataUrl
);
if (missingOverlay.length === 0) {
return overlayMetadata;
}
const overlays = await Promise.all(
overlayMetadata
overlayMetadata.overlays
.filter(overlay => overlay.pixelData)
.map(async (overlay, idx) => {
let pixelData = null;
@ -178,7 +182,7 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool {
const dataUrl = this._renderOverlayToDataUrl(
{ width: overlay.columns, height: overlay.rows },
color,
overlay.color || color,
pixelData
);
@ -190,13 +194,9 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool {
};
})
);
overlayMetadata.overlays = overlays;
this._cachedStats[imageId] = {
color: color,
overlays: overlays.filter(overlay => overlay),
};
return this._cachedStats[imageId];
return overlayMetadata;
}
/**

View File

@ -0,0 +1,40 @@
import { metaData } from '@cornerstonejs/core';
const _cachedOverlayMetadata: Map<string, any[]> = new Map();
/**
* Image Overlay Viewer tool is not a traditional tool that requires user interactin.
* But it is used to display Pixel Overlays. And it will provide toggling capability.
*
* The documentation for Overlay Plane Module of DICOM can be found in [C.9.2 of
* Part-3 of DICOM standard](https://dicom.nema.org/medical/dicom/2018b/output/chtml/part03/sect_C.9.2.html)
*
* Image Overlay rendered by this tool can be toggled on and off using
* toolGroup.setToolEnabled() and toolGroup.setToolDisabled()
*/
const OverlayPlaneModuleProvider = {
/** Adds the metadata for overlayPlaneModule */
add: (imageId, metadata) => {
if (_cachedOverlayMetadata.get(imageId) === metadata) {
// This is a no-op here as the tool re-caches the data
return;
}
_cachedOverlayMetadata.set(imageId, metadata);
},
/** Standard getter for metadata */
get: (type: string, query: string | string[]) => {
if (Array.isArray(query)) {
return;
}
if (type !== 'overlayPlaneModule') {
return;
}
return _cachedOverlayMetadata.get(query);
},
};
// Needs to be higher priority than default provider
metaData.addProvider(OverlayPlaneModuleProvider.get, 10_000);
export default OverlayPlaneModuleProvider;

View File

@ -37,7 +37,7 @@
"preinstall": "node preinstall.js",
"start": "yarn run dev",
"test": "yarn run test:unit",
"test:data": "git submodule update --init",
"test:data": "git submodule update --init -r",
"test-watch": "jest --collectCoverage --watchAll",
"test:unit": "jest --collectCoverage",
"test:unit:ci": "lerna run test:unit:ci --parallel --stream",

View File

@ -154,7 +154,7 @@ module.exports = (env, argv) => {
{
directory: '../../testdata',
staticOptions: {
extensions: ['gz', 'br'],
extensions: ['gz', 'br', 'mht'],
index: ['index.json.gz', 'index.mht.gz'],
redirect: true,
setHeaders,

@ -1 +1 @@
Subproject commit 4d59660c2883ed749a680e5fb6d4624ab54c9422
Subproject commit c9892af83d5dc4b06fac6ff1ca77246467571ec0