Skip to content

Commit

Permalink
fix(DICOM Overlay): The overlay data wasn't being refreshed on change (
Browse files Browse the repository at this point in the history
  • Loading branch information
wayfarer3130 authored Nov 21, 2023
1 parent c4e22c2 commit 00e7519
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 27 deletions.
2 changes: 2 additions & 0 deletions extensions/cornerstone/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -140,5 +141,6 @@ export {
CornerstoneExtensionTypes as Types,
toolNames,
getActiveViewportEnabledElement,
ImageOverlayViewerTool,
};
export default cornerstoneExtension;
48 changes: 24 additions & 24 deletions extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx
Original file line number Diff line number Diff line change
@@ -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]
Expand All @@ -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 = {},
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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);
});
Expand Down Expand Up @@ -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;
Expand All @@ -178,7 +182,7 @@ class ImageOverlayViewerTool extends AnnotationDisplayTool {

const dataUrl = this._renderOverlayToDataUrl(
{ width: overlay.columns, height: overlay.rows },
color,
overlay.color || color,
pixelData
);

Expand All @@ -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;
}

/**
Expand Down
40 changes: 40 additions & 0 deletions extensions/cornerstone/src/tools/OverlayPlaneModuleProvider.ts
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion platform/app/.webpack/webpack.pwa.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion testdata
Submodule testdata updated 303 files

0 comments on commit 00e7519

Please sign in to comment.