ohif-viewer/extensions/dicom-microscopy/src/utils/loadSR.js

208 lines
6.4 KiB
JavaScript
Raw Normal View History

feat(microscopy): add dicom microscopy extension and mode (#3247) * skeleton for dicom-microscopy extension * skeleton for microscopy mode * [feat] ported @radicalimaging/microscopy-dicom to OHIF's default extension * [feat] ported microscopy mode from private repo * added new definitions to the package.json * webpack configuration update for microscopy extension * register new icons for microscopy tools * fixes to the microscopy extension and mode * trivial error fix - typescript type import error * link microscopy extension with default OHIF app plugin config * demo config fix * fix logs * remove unsed imports * [fix] loading of microscopy * [fix] webworker script loading, normalizing denaturalized dataset * found the latest version of dicom-microscopy-viewer that works with current OHIF extension : 0.35.2 * hide thumbnail pane by default, as we have issues with * comments * remove unused code * [feat] microscopy - annotation selection * [feat] microscopy - edit annotation label * wip * [bugfix] dicom-microscopy tool * [bugfix] dicom microscopy annotations * [fix] mixed-content blocking caused by BulkDataURI * [fix] microscopy measurements panel - center button * [fix] microscopy measurements panel - styling * [fix] microscopy - controls * fix local loading of microscopy * fix local loading of dicom microscopy * fix typo - indexof to indexOf * [fix] remove unused icons * remove commented out lines from webpack configuration * platform/viewer/public/config/default.js - revert accidental changes * [refactor] redirecting to microscopy mode on Local mode * attribution to DMV and SLIM viewer * [fix] code review feedbacks * fix thumbnails * [fix] microscopy - fix old publisher.publish() to PubSubService._broadcastEvent() * [fix] interactions, webpack config, roi selection * [feat] microscopy - remove select tool from UI * microscopy author * [fix] saving and loading SR * [bugfix] - missing publish() function in MicroscopyService, replace with _broadcastEvent * remove author section from readme * refactor SR saving feature * fix webpack config after merge * [bugfix] repeated import * fix e2e * webpack configuration * hide "Create report" button for microscopy
2023-04-28 02:53:52 +02:00
import dcmjs from 'dcmjs';
import DCM_CODE_VALUES from './dcmCodeValues';
import toArray from './toArray';
const MeasurementReport =
dcmjs.adapters.DICOMMicroscopyViewer.MeasurementReport;
// Define as async so that it returns a promise, expected by the ViewportGrid
export default async function loadSR(
microscopyService,
microscopySRDisplaySet,
referencedDisplaySet
) {
const naturalizedDataset = microscopySRDisplaySet.metadata;
const { StudyInstanceUID, FrameOfReferenceUID } = referencedDisplaySet;
const managedViewers = microscopyService.getManagedViewersForStudy(
StudyInstanceUID
);
if (!managedViewers || !managedViewers.length) {
return;
}
microscopySRDisplaySet.isLoaded = true;
const { rois, labels } = await _getROIsFromToolState(
naturalizedDataset,
FrameOfReferenceUID
);
const managedViewer = managedViewers[0];
for (let i = 0; i < rois.length; i++) {
// NOTE: When saving Microscopy SR, we are attaching identifier property
// to each ROI, and when read for display, it is coming in as "TEXT"
// evaluation.
// As the Dicom Microscopy Viewer will override styles for "Text" evalutations
// to hide all other geometries, we are going to manually remove that
// evaluation item.
const roi = rois[i];
const roiSymbols = Object.getOwnPropertySymbols(roi);
const _properties = roiSymbols.find(s => s.description === 'properties');
const properties = roi[_properties];
properties['evaluations'] = [];
managedViewer.addRoiGraphicWithLabel(roi, labels[i]);
}
}
async function _getROIsFromToolState(naturalizedDataset, FrameOfReferenceUID) {
const toolState = MeasurementReport.generateToolState(naturalizedDataset);
const tools = Object.getOwnPropertyNames(toolState);
const DICOMMicroscopyViewer = await import(
/* webpackChunkName: "dicom-microscopy-viewer" */ 'dicom-microscopy-viewer'
);
const measurementGroupContentItems = _getMeasurementGroups(
naturalizedDataset
);
const rois = [];
const labels = [];
tools.forEach(t => {
const toolSpecificToolState = toolState[t];
let scoord3d;
const capsToolType = t.toUpperCase();
const measurementGroupContentItemsForTool = measurementGroupContentItems.filter(
mg => {
const imageRegionContentItem = toArray(mg.ContentSequence).find(
ci =>
ci.ConceptNameCodeSequence.CodeValue ===
DCM_CODE_VALUES.IMAGE_REGION
);
return imageRegionContentItem.GraphicType === capsToolType;
}
);
toolSpecificToolState.forEach((coordinates, index) => {
const properties = {};
const options = {
coordinates,
frameOfReferenceUID: FrameOfReferenceUID,
};
if (t === 'Polygon') {
scoord3d = new DICOMMicroscopyViewer.scoord3d.Polygon(options);
} else if (t === 'Polyline') {
scoord3d = new DICOMMicroscopyViewer.scoord3d.Polyline(options);
} else if (t === 'Point') {
scoord3d = new DICOMMicroscopyViewer.scoord3d.Point(options);
} else if (t === 'Ellipse') {
scoord3d = new DICOMMicroscopyViewer.scoord3d.Ellipse(options);
} else {
throw new Error('Unsupported tool type');
}
const measurementGroup = measurementGroupContentItemsForTool[index];
const findingGroup = toArray(measurementGroup.ContentSequence).find(
ci => ci.ConceptNameCodeSequence.CodeValue === DCM_CODE_VALUES.FINDING
);
const trackingGroup = toArray(measurementGroup.ContentSequence).find(
ci =>
ci.ConceptNameCodeSequence.CodeValue ===
DCM_CODE_VALUES.TRACKING_UNIQUE_IDENTIFIER
);
/**
* Extract presentation state from tracking identifier.
* Currently is stored in SR but should be stored in its tags.
*/
if (trackingGroup) {
const regExp = /\(([^)]+)\)/;
const matches = regExp.exec(trackingGroup.TextValue);
if (matches && matches[1]) {
properties.presentationState = JSON.parse(matches[1]);
properties.marker = properties.presentationState.marker;
}
}
let measurements = toArray(measurementGroup.ContentSequence).filter(ci =>
[
DCM_CODE_VALUES.LENGTH,
DCM_CODE_VALUES.AREA,
DCM_CODE_VALUES.SHORT_AXIS,
DCM_CODE_VALUES.LONG_AXIS,
DCM_CODE_VALUES.ELLIPSE_AREA,
].includes(ci.ConceptNameCodeSequence.CodeValue)
);
let evaluations = toArray(measurementGroup.ContentSequence).filter(ci =>
[DCM_CODE_VALUES.TRACKING_UNIQUE_IDENTIFIER].includes(
ci.ConceptNameCodeSequence.CodeValue
)
);
/**
* TODO: Resolve bug in DCMJS.
* ConceptNameCodeSequence should be a sequence with only one item.
*/
evaluations = evaluations.map(evaluation => {
const e = { ...evaluation };
e.ConceptNameCodeSequence = toArray(e.ConceptNameCodeSequence);
return e;
});
/**
* TODO: Resolve bug in DCMJS.
* ConceptNameCodeSequence should be a sequence with only one item.
*/
measurements = measurements.map(measurement => {
const m = { ...measurement };
m.ConceptNameCodeSequence = toArray(m.ConceptNameCodeSequence);
return m;
});
if (measurements && measurements.length) {
properties.measurements = measurements;
console.debug('[SR] retrieving measurements...', measurements);
}
if (evaluations && evaluations.length) {
properties.evaluations = evaluations;
console.debug('[SR] retrieving evaluations...', evaluations);
}
const roi = new DICOMMicroscopyViewer.roi.ROI({ scoord3d, properties });
rois.push(roi);
if (findingGroup) {
labels.push(findingGroup.ConceptCodeSequence.CodeValue);
} else {
labels.push('');
}
});
});
return { rois, labels };
}
function _getMeasurementGroups(naturalizedDataset) {
const { ContentSequence } = naturalizedDataset;
const imagingMeasurementsContentItem = ContentSequence.find(
ci =>
ci.ConceptNameCodeSequence.CodeValue ===
DCM_CODE_VALUES.IMAGING_MEASUREMENTS
);
const measurementGroupContentItems = toArray(
imagingMeasurementsContentItem.ContentSequence
).filter(
ci =>
ci.ConceptNameCodeSequence.CodeValue === DCM_CODE_VALUES.MEASUREMENT_GROUP
);
return measurementGroupContentItems;
}