fix(multiframe): metadata handling of NM studies and loading order (#4554)
This commit is contained in:
1 parent
6729534a1a
commit
7624ccb5e4
57 files changed
+482
-662
No files matched your search
@@ -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,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, {});
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ declare global {
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
studyBrowserMode?: 'all' | 'primary';
|
||||
routerBasename?: string;
|
||||
customizationService?: CustomizationServiceType;
|
||||
extensions?: string[];
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user