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:
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user