ohif-viewer/extensions/dicom-microscopy/src/utils/constructSR.ts

214 lines
7.0 KiB
TypeScript
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 DEVICE_OBSERVER_UID from './DEVICE_OBSERVER_UID';
/**
*
* @param {*} metadata - Microscopy Image instance metadata
* @param {*} SeriesDescription - SR description
* @param {*} annotations - Annotations
*
* @return Comprehensive3DSR dataset
*/
export default function constructSR(
metadata,
{ SeriesDescription, SeriesNumber },
annotations
) {
// Handle malformed data
if (!metadata.SpecimenDescriptionSequence) {
metadata.SpecimenDescriptionSequence = {
SpecimenUID: metadata.SeriesInstanceUID,
SpecimenIdentifier: metadata.SeriesDescription,
};
}
const { SpecimenDescriptionSequence } = metadata;
// construct Comprehensive3DSR dataset
const observationContext = new dcmjs.sr.templates.ObservationContext({
observerPersonContext: new dcmjs.sr.templates.ObserverContext({
observerType: new dcmjs.sr.coding.CodedConcept({
value: '121006',
schemeDesignator: 'DCM',
meaning: 'Person',
}),
observerIdentifyingAttributes: new dcmjs.sr.templates.PersonObserverIdentifyingAttributes(
{
name: '@ohif/extension-dicom-microscopy',
}
),
}),
observerDeviceContext: new dcmjs.sr.templates.ObserverContext({
observerType: new dcmjs.sr.coding.CodedConcept({
value: '121007',
schemeDesignator: 'DCM',
meaning: 'Device',
}),
observerIdentifyingAttributes: new dcmjs.sr.templates.DeviceObserverIdentifyingAttributes(
{
uid: DEVICE_OBSERVER_UID,
}
),
}),
subjectContext: new dcmjs.sr.templates.SubjectContext({
subjectClass: new dcmjs.sr.coding.CodedConcept({
value: '121027',
schemeDesignator: 'DCM',
meaning: 'Specimen',
}),
subjectClassSpecificContext: new dcmjs.sr.templates.SubjectContextSpecimen(
{
uid: SpecimenDescriptionSequence.SpecimenUID,
identifier:
SpecimenDescriptionSequence.SpecimenIdentifier ||
metadata.SeriesInstanceUID,
containerIdentifier:
metadata.ContainerIdentifier || metadata.SeriesInstanceUID,
}
),
}),
});
const imagingMeasurements = [];
for (let i = 0; i < annotations.length; i++) {
const { roiGraphic: roi, label } = annotations[i];
let {
measurements,
evaluations,
marker,
presentationState,
} = roi.properties;
console.debug('[SR] storing marker...', marker);
console.debug('[SR] storing measurements...', measurements);
console.debug('[SR] storing evaluations...', evaluations);
console.debug('[SR] storing presentation state...', presentationState);
if (presentationState) presentationState.marker = marker;
/** Avoid incompatibility with dcmjs */
measurements = measurements.map((measurement: any) => {
const ConceptName = Array.isArray(measurement.ConceptNameCodeSequence)
? measurement.ConceptNameCodeSequence[0]
: measurement.ConceptNameCodeSequence;
const MeasuredValue = Array.isArray(measurement.MeasuredValueSequence)
? measurement.MeasuredValueSequence[0]
: measurement.MeasuredValueSequence;
const MeasuredValueUnits = Array.isArray(
MeasuredValue.MeasurementUnitsCodeSequence
)
? MeasuredValue.MeasurementUnitsCodeSequence[0]
: MeasuredValue.MeasurementUnitsCodeSequence;
return new dcmjs.sr.valueTypes.NumContentItem({
name: new dcmjs.sr.coding.CodedConcept({
meaning: ConceptName.CodeMeaning,
value: ConceptName.CodeValue,
schemeDesignator: ConceptName.CodingSchemeDesignator,
}),
value: MeasuredValue.NumericValue,
unit: new dcmjs.sr.coding.CodedConcept({
value: MeasuredValueUnits.CodeValue,
meaning: MeasuredValueUnits.CodeMeaning,
schemeDesignator: MeasuredValueUnits.CodingSchemeDesignator,
}),
});
});
/** Avoid incompatibility with dcmjs */
evaluations = evaluations.map((evaluation: any) => {
const ConceptName = Array.isArray(evaluation.ConceptNameCodeSequence)
? evaluation.ConceptNameCodeSequence[0]
: evaluation.ConceptNameCodeSequence;
return new dcmjs.sr.valueTypes.TextContentItem({
name: new dcmjs.sr.coding.CodedConcept({
value: ConceptName.CodeValue,
meaning: ConceptName.CodeMeaning,
schemeDesignator: ConceptName.CodingSchemeDesignator,
}),
value: evaluation.TextValue,
relationshipType: evaluation.RelationshipType,
});
});
const identifier = `ROI #${i + 1}`;
const group = new dcmjs.sr.templates.PlanarROIMeasurementsAndQualitativeEvaluations(
{
trackingIdentifier: new dcmjs.sr.templates.TrackingIdentifier({
uid: roi.uid,
identifier: presentationState
? identifier.concat(`(${JSON.stringify(presentationState)})`)
: identifier,
}),
referencedRegion: new dcmjs.sr.contentItems.ImageRegion3D({
graphicType: roi.scoord3d.graphicType,
graphicData: roi.scoord3d.graphicData,
frameOfReferenceUID: roi.scoord3d.frameOfReferenceUID,
}),
findingType: new dcmjs.sr.coding.CodedConcept({
value: label,
schemeDesignator: '@ohif/extension-dicom-microscopy',
meaning: 'FREETEXT',
}),
/** Evaluations will conflict with current tracking identifier */
/** qualitativeEvaluations: evaluations, */
measurements,
}
);
imagingMeasurements.push(...group);
}
const measurementReport = new dcmjs.sr.templates.MeasurementReport({
languageOfContentItemAndDescendants: new dcmjs.sr.templates.LanguageOfContentItemAndDescendants(
{}
),
observationContext,
procedureReported: new dcmjs.sr.coding.CodedConcept({
value: '112703',
schemeDesignator: 'DCM',
meaning: 'Whole Slide Imaging',
}),
imagingMeasurements,
});
const dataset = new dcmjs.sr.documents.Comprehensive3DSR({
content: measurementReport[0],
evidence: [metadata],
seriesInstanceUID: dcmjs.data.DicomMetaDictionary.uid(),
seriesNumber: SeriesNumber,
seriesDescription:
SeriesDescription || 'Whole slide imaging structured report',
sopInstanceUID: dcmjs.data.DicomMetaDictionary.uid(),
instanceNumber: 1,
manufacturer: 'dcmjs-org',
});
dataset.SpecificCharacterSet = 'ISO_IR 192';
const fileMetaInformationVersionArray = new Uint8Array(2);
fileMetaInformationVersionArray[1] = 1;
dataset._meta = {
FileMetaInformationVersion: {
Value: [fileMetaInformationVersionArray.buffer], // TODO
vr: 'OB',
},
MediaStorageSOPClassUID: dataset.sopClassUID,
MediaStorageSOPInstanceUID: dataset.sopInstanceUID,
TransferSyntaxUID: {
Value: ['1.2.840.10008.1.2.1'],
vr: 'UI',
},
ImplementationClassUID: {
Value: [dcmjs.data.DicomMetaDictionary.uid()],
vr: 'UI',
},
ImplementationVersionName: {
Value: ['@ohif/extension-dicom-microscopy'],
vr: 'SH',
},
};
return dataset;
}