Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just exposes the tool externally to be used. Otherwise it becomes difficult to add new metadata.

};
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
Loading