fix(multiframe): metadata handling of NM studies and loading order (#4554)

This commit is contained in:
Alireza authored and GitHub committed 2024-11-28 13:22:40 -05:00
1 parent 6729534a1a
commit 7624ccb5e4
57 files changed
+482 -662

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.2.13",
"@cornerstonejs/dicom-image-loader": "^2.2.20",
"@ohif/ui": "3.10.0-beta.10",
"cornerstone-math": "0.1.9",
"dicom-parser": "^1.8.21"
+8 -12
View File
@@ -8,7 +8,6 @@ import toNumber from '../utils/toNumber';
import combineFrameInstance from '../utils/combineFrameInstance';
class MetadataProvider {
private readonly studies: Map<string, any> = new Map();
private readonly imageURIToUIDs: Map<string, any> = new Map();
private readonly imageUIDsByImageId: Map<string, any> = new Map();
// Can be used to store custom metadata for a specific type.
@@ -17,6 +16,10 @@ class MetadataProvider {
private readonly customMetadata: Map<string, any> = new Map();
addImageIdToUIDs(imageId, uids) {
if (!imageId) {
throw new Error('MetadataProvider::Empty imageId');
}
// This method is a fallback for when you don't have WADO-URI or WADO-RS.
// You can add instances fetched by any method by calling addInstance, and hook an imageId to point at it here.
// An example would be dicom hosted at some random site.
@@ -35,6 +38,10 @@ class MetadataProvider {
}
_getInstance(imageId) {
if (!imageId) {
throw new Error('MetadataProvider::Empty imageId');
}
const uids = this.getUIDsFromImageID(imageId);
if (!uids) {
@@ -455,17 +462,6 @@ class MetadataProvider {
}
getUIDsFromImageID(imageId) {
if (!imageId) {
throw new Error('MetadataProvider::Empty imageId');
}
// TODO: adding csiv here is not really correct. Probably need to use
// metadataProvider.addImageIdToUIDs(imageId, {
// StudyInstanceUID,
// SeriesInstanceUID,
// SOPInstanceUID,
// })
// somewhere else
const cachedUIDs = this.imageUIDsByImageId.get(imageId);
if (cachedUIDs) {
return cachedUIDs;
@@ -280,7 +280,7 @@ export default class CustomizationService extends PubSubService {
return customization;
}
const parent = this.getCustomization(customizationType);
const result = parent ? Object.assign(Object.create(parent), customization) : customization;
const result = parent ? Object.assign({}, parent, customization) : customization;
// Execute an nested type information
return result.transform?.(this) || result;
}
@@ -679,7 +679,7 @@ class StudyPrefetcherService extends PubSubService {
this._displaySetLoadingStates.clear();
this._imageIdsToDisplaySetsMap.clear();
this._inflightRequests.clear();
this.imageLoadPoolManager.clearRequestStack(IMAGE_REQUEST_TYPE);
this.imageLoadPoolManager.clearRequestStack(this.requestType);
this._broadcastEvent(this.EVENTS.SERVICE_STOPPED, {});
}
+1
View File
@@ -81,6 +81,7 @@ declare global {
}
export interface Config {
studyBrowserMode?: 'all' | 'primary';
routerBasename?: string;
customizationService?: CustomizationServiceType;
extensions?: string[];
+55 -18
View File
@@ -1,3 +1,5 @@
import { vec3 } from 'gl-matrix';
/**
* Combine the Per instance frame data, the shared frame data
* and the root data objects.
@@ -9,25 +11,28 @@
* single frame data. (eg frame is undefined is the same as frame===1).
*/
const combineFrameInstance = (frame, instance) => {
const { PerFrameFunctionalGroupsSequence, SharedFunctionalGroupsSequence, NumberOfFrames } =
instance;
const {
PerFrameFunctionalGroupsSequence,
SharedFunctionalGroupsSequence,
NumberOfFrames,
SpacingBetweenSlices,
} = instance;
if (PerFrameFunctionalGroupsSequence || NumberOfFrames > 1) {
const frameNumber = Number.parseInt(frame || 1);
const shared = (
SharedFunctionalGroupsSequence ? Object.values(SharedFunctionalGroupsSequence[0]) : []
)
.filter(it => !!it)
.map(it => it[0])
.filter(it => it !== undefined && typeof it === 'object');
const perFrame = (
PerFrameFunctionalGroupsSequence
? Object.values(PerFrameFunctionalGroupsSequence[frameNumber - 1])
: []
)
.filter(it => !!it)
.map(it => it[0])
.filter(it => it !== undefined && typeof it === 'object');
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
@@ -35,9 +40,40 @@ const combineFrameInstance = (frame, instance) => {
instance.ImageOrientationPatient =
instance.DetectorInformationSequence[0].ImageOrientationPatient;
}
let ImagePositionPatientToUse = instance.ImagePositionPatient;
if (!instance.ImagePositionPatient && instance.DetectorInformationSequence) {
instance.ImagePositionPatient = instance.DetectorInformationSequence[0].ImagePositionPatient;
const imagePositionPatient = instance.DetectorInformationSequence[0].ImagePositionPatient;
const imageOrientationPatient = instance.ImageOrientationPatient;
// Calculate the position for the current frame
if (imageOrientationPatient && SpacingBetweenSlices) {
const rowOrientation = vec3.fromValues(
imageOrientationPatient[0],
imageOrientationPatient[1],
imageOrientationPatient[2]
);
const colOrientation = vec3.fromValues(
imageOrientationPatient[3],
imageOrientationPatient[4],
imageOrientationPatient[5]
);
const normalVector = vec3.cross(vec3.create(), rowOrientation, colOrientation);
const position = vec3.scaleAndAdd(
vec3.create(),
imagePositionPatient,
normalVector,
SpacingBetweenSlices * (frameNumber - 1)
);
ImagePositionPatientToUse = [position[0], position[1], position[2]];
}
}
console.debug('🚀 ~ ImagePositionPatientToUse:', ImagePositionPatientToUse);
const newInstance = Object.assign(instance, { frameNumber: frameNumber });
@@ -52,7 +88,8 @@ const combineFrameInstance = (frame, instance) => {
// back to the dicomMetaStore so we don't have to do this again.
return {
...newInstance,
ImagePositionPatient: newInstance.ImagePositionPatient ?? [0, 0, frameNumber],
ImagePositionPatient: ImagePositionPatientToUse ??
newInstance.ImagePositionPatient ?? [0, 0, frameNumber],
};
} else {
return instance;