fix: Having sop instance in a per-frame or shared attribute breaks load (#4560)

Co-authored-by: sedghi <ar.sedghi@gmail.com>
This commit is contained in:
Bill Wallaceandsedghi authored and GitHub committed 2025-01-15 15:16:50 -05:00
1 parent 27c040fdda
commit cded082617
15 files changed
+136 -71

No files matched your search

+1 -1
View File
@@ -37,7 +37,7 @@
"@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2",
"@cornerstonejs/codec-openjpeg": "^1.2.4",
"@cornerstonejs/codec-openjph": "^2.4.5",
"@cornerstonejs/dicom-image-loader": "^2.14.7",
"@cornerstonejs/dicom-image-loader": "^2.15.3",
"@ohif/ui": "3.10.0-beta.59",
"cornerstone-math": "0.1.9",
"dicom-parser": "^1.8.21"
@@ -545,6 +545,7 @@ const WADO_IMAGE_LOADER = {
frameOfReferenceUID: instance.FrameOfReferenceUID,
rows: toNumber(instance.Rows),
columns: toNumber(instance.Columns),
spacingBetweenSlices: toNumber(instance.SpacingBetweenSlices),
imageOrientationPatient: toNumber(ImageOrientationPatient) || [0, 1, 0, 0, 0, -1],
rowCosines: toNumber(rowCosines || [0, 1, 0]),
isDefaultValueSetForRowCosine: toNumber(rowCosines) ? false : true,
+60 -30
View File
@@ -1,4 +1,5 @@
import { vec3 } from 'gl-matrix';
import { dicomSplit } from './dicomSplit';
/**
* Combine the Per instance frame data, the shared frame data
@@ -15,24 +16,13 @@ const combineFrameInstance = (frame, instance) => {
PerFrameFunctionalGroupsSequence,
SharedFunctionalGroupsSequence,
NumberOfFrames,
SpacingBetweenSlices,
ImageType,
} = instance;
instance.ImageType = dicomSplit(ImageType);
if (PerFrameFunctionalGroupsSequence || NumberOfFrames > 1) {
const frameNumber = Number.parseInt(frame || 1);
const shared = SharedFunctionalGroupsSequence
? Object.values(SharedFunctionalGroupsSequence[0])
.filter(Boolean)
.map(it => it[0])
.filter(it => typeof it === 'object')
: [];
const perFrame = PerFrameFunctionalGroupsSequence
? Object.values(PerFrameFunctionalGroupsSequence[frameNumber - 1])
.filter(Boolean)
.map(it => it[0])
.filter(it => typeof it === 'object')
: [];
// this is to fix NM multiframe datasets with position and orientation
// information inside DetectorInformationSequence
@@ -44,8 +34,12 @@ const combineFrameInstance = (frame, instance) => {
let ImagePositionPatientToUse = instance.ImagePositionPatient;
if (!instance.ImagePositionPatient && instance.DetectorInformationSequence) {
const imagePositionPatient = instance.DetectorInformationSequence[0].ImagePositionPatient;
const imageOrientationPatient = instance.ImageOrientationPatient;
let imagePositionPatient = instance.DetectorInformationSequence[0].ImagePositionPatient;
let imageOrientationPatient = instance.ImageOrientationPatient;
imagePositionPatient = imagePositionPatient.map(it => Number(it));
imageOrientationPatient = imageOrientationPatient.map(it => Number(it));
const SpacingBetweenSlices = Number(instance.SpacingBetweenSlices);
// Calculate the position for the current frame
if (imageOrientationPatient && SpacingBetweenSlices) {
@@ -73,26 +67,62 @@ const combineFrameInstance = (frame, instance) => {
ImagePositionPatientToUse = [position[0], position[1], position[2]];
}
}
const sharedInstance = createCombinedValue(instance, SharedFunctionalGroupsSequence?.[0]);
const newInstance = createCombinedValue(
sharedInstance,
PerFrameFunctionalGroupsSequence?.[frameNumber]
);
const newInstance = Object.assign(instance, { frameNumber: frameNumber });
// merge the shared first then the per frame to override
[...shared, ...perFrame].forEach(item => {
Object.entries(item).forEach(([key, value]) => {
newInstance[key] = value;
});
Object.defineProperty(newInstance, 'ImagePositionPatient', {
value: ImagePositionPatientToUse ?? newInstance.ImagePositionPatient ?? [0, 0, frameNumber],
writable: true,
enumerable: true,
configurable: true,
});
// Todo: we should cache this combined instance somewhere, maybe add it
// back to the dicomMetaStore so we don't have to do this again.
return {
...newInstance,
ImagePositionPatient: ImagePositionPatientToUse ??
newInstance.ImagePositionPatient ?? [0, 0, frameNumber],
};
Object.defineProperty(newInstance, 'frameNumber', {
value: frameNumber,
writable: true,
enumerable: true,
configurable: true,
});
return newInstance;
} else {
return instance;
}
};
function createCombinedValue(parent, functionalGroups) {
const newInstance = Object.create(parent);
if (!functionalGroups) {
return newInstance;
}
if (functionalGroups._sharedValue) {
return functionalGroups._sharedValue;
}
const shared = functionalGroups
? Object.values(functionalGroups)
.filter(Boolean)
.map(it => it[0])
.filter(it => typeof it === 'object')
: [];
// merge the shared first then the per frame to override
[...shared].forEach(item => {
if (item.SOPInstanceUID) {
// This sub-item is a previous value information item, so don't merge it
return;
}
Object.entries(item).forEach(([key, value]) => {
newInstance[key] = value;
});
});
Object.defineProperty(functionalGroups, '_sharedValue', {
value: newInstance,
writable: false,
enumerable: false,
});
return newInstance;
}
export default combineFrameInstance;
+5
View File
@@ -0,0 +1,5 @@
export function dicomSplit(value) {
return (
(Array.isArray(value) && value) || (typeof value === 'string' && value.split('\\')) || value
);
}