feat: Annotation and Measurements support on multi-frame DICOM (#2973)

* added utilities to get frameNumber from imageId and add frameNumber to per-frame instance copy

* Fix measurements' display texts on the Measurements Panel to have the correct instance number and frame number

* fixed minor React bugs (errors on console)

* fixed React's console bugs that are logged for some series that doesn't have "description"

* bug fix - multi-frame files was not loading frames correctly. It was loading Frame 1 twice, and not loading the last frame. Due to wrong frame number indexing (frame number begins with 1, not 0)

* metadata parser fix - providing default values of imagePlaneModule

* measurement SR support on multi-frame DICOM

* bug fix - jumping to the selected measurment on multi-frame DICOM

* upgrade dcmjs dependency to 2.8.1

* StudySummary component - allow "description" to be null

* make getUIDsFromImageID() method public from MetadataProvider

* imageId usage fixes to be more stable

* change the variable name to be more meaningful

* fix metaProvider importing

* for(...of) instead of for(i=0;i<length;..)
as that doesn't assume anything about the layout/design and just gets next until done.

* use Array.findIndex instead of plain for loop

* use ReferencedSOPSequence[0] - because the ReferencedSOPSequence is an array that happens to have attributes of child zero when of length 1, but you shouldn't count on that.

* DisplaySetService.getDisplaySetForSOPInstanceUID() - added optional frameNumber parameter for future usage : now they are just ignored as we are not supporting multiframe splits

* simple code refactoring

* refactoring for checking undefined values - mappedAnnotations

* remove unreachable code

* code refactoring  - prefer conditional chaining

* fix how we access imageIds of viewport (StackViewport)
This commit is contained in:
md-prog authored and GitHub committed 2022-10-13 11:07:56 -04:00
1 parent af09578544
commit b93067b6a2
30 files changed
+276 -132

No files matched your search

@@ -6,7 +6,7 @@ import { adapters } from 'dcmjs';
const { CodeScheme: Cornerstone3DCodeScheme } = adapters.Cornerstone3D;
const { ImageSet } = classes;
const { ImageSet, MetadataProvider: metadataProvider } = classes;
// TODO ->
// Add SR thumbnail
// Make viewport
@@ -214,20 +214,26 @@ function _checkIfCanAddMeasurementsToDisplaySet(
newDisplaySet
);
for (let i = 0; i < images.length; i++) {
for (const imageId of imageIdsForDisplaySet) {
if (!unloadedMeasurements.length) {
// All measurements loaded.
break;
return;
}
const image = images[i];
const { SOPInstanceUID } = image;
if (SOPInstanceUIDs.includes(SOPInstanceUID)) {
const imageId = imageIdsForDisplaySet[i];
const { SOPInstanceUID, frameNumber } = metadataProvider.getUIDsFromImageID(
imageId
);
if (SOPInstanceUIDs.includes(SOPInstanceUID)) {
for (let j = unloadedMeasurements.length - 1; j >= 0; j--) {
const measurement = unloadedMeasurements[j];
if (_measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID)) {
if (
_measurementReferencesSOPInstanceUID(
measurement,
SOPInstanceUID,
frameNumber
)
) {
addMeasurement(
measurement,
imageId,
@@ -241,9 +247,23 @@ function _checkIfCanAddMeasurementsToDisplaySet(
}
}
function _measurementReferencesSOPInstanceUID(measurement, SOPInstanceUID) {
function _measurementReferencesSOPInstanceUID(
measurement,
SOPInstanceUID,
frameNumber
) {
const { coords } = measurement;
// NOTE: The ReferencedFrameNumber can be multiple values according to the DICOM
// Standard. But for now, we will support only one ReferenceFrameNumber.
const ReferencedFrameNumber =
(measurement.coords[0].ReferencedSOPSequence &&
measurement.coords[0].ReferencedSOPSequence[0]?.ReferencedFrameNumber) ||
1;
if (frameNumber && Number(frameNumber) !== Number(ReferencedFrameNumber))
return false;
for (let j = 0; j < coords.length; j++) {
const coord = coords[j];
const { ReferencedSOPInstanceUID } = coord.ReferencedSOPSequence;
@@ -45,6 +45,12 @@ export default function addMeasurement(
const annotationManager = annotation.state.getDefaultAnnotationManager();
// Create Cornerstone3D Annotation from measurement
const frameNumber =
(measurement.coords[0].ReferencedSOPSequence &&
measurement.coords[0].ReferencedSOPSequence[0]?.ReferencedFrameNumber) ||
1;
const SRAnnotation: Types.Annotation = {
annotationUID: measurement.TrackingUniqueIdentifier,
metadata: {
@@ -61,6 +67,7 @@ export default function addMeasurement(
TrackingUniqueIdentifier: measurementData.TrackingUniqueIdentifier,
renderableData: measurementData.renderableData,
},
frameNumber: frameNumber,
},
};
@@ -75,6 +82,7 @@ export default function addMeasurement(
// It'd be super weird if it didn't anyway as a SCOORD.
measurement.ReferencedSOPInstanceUID =
measurement.coords[0].ReferencedSOPSequence.ReferencedSOPInstanceUID;
measurement.frameNumber = frameNumber;
delete measurement.coords;
}