Initial commit from prod-batam
This commit is contained in:
184
extensions/dicom-microscopy/src/utils/loadSR.ts
Normal file
184
extensions/dicom-microscopy/src/utils/loadSR.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
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(microscopyService, 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" evaluations
|
||||
// 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(microscopyService, naturalizedDataset, FrameOfReferenceUID) {
|
||||
const toolState = MeasurementReport.generateToolState(naturalizedDataset);
|
||||
const tools = Object.getOwnPropertyNames(toolState);
|
||||
// Does a dynamic import to prevent webpack from rebuilding the library
|
||||
const DICOMMicroscopyViewer = await microscopyService.importDicomMicroscopyViewer();
|
||||
|
||||
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.log('[SR] retrieving measurements...', measurements);
|
||||
}
|
||||
|
||||
if (evaluations && evaluations.length) {
|
||||
properties.evaluations = evaluations;
|
||||
console.log('[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;
|
||||
}
|
||||
Reference in New Issue
Block a user