ohif-viewer/extensions/dicom-microscopy/src/utils/constructSR.ts
M.D 075008c674
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-27 20:53:52 -04:00

214 lines
7.0 KiB
TypeScript

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