Skip to content

Commit

Permalink
feat(metadata): adding support for multiframe metadata in wadors and …
Browse files Browse the repository at this point in the history
…wadouri (#582)
  • Loading branch information
sedghi authored Apr 28, 2023
1 parent 06ba425 commit 7ae983c
Show file tree
Hide file tree
Showing 13 changed files with 454 additions and 55 deletions.
3 changes: 3 additions & 0 deletions packages/dicomImageLoader/.webpack/webpack-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ module.exports = {
exclude: [/(node_modules)/, /(codecs)/],
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true,
},
},
},
{
Expand Down
7 changes: 6 additions & 1 deletion packages/dicomImageLoader/.webpack/webpack-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,12 @@ module.exports = {
},
],
},
plugins: [new webpack.ProgressPlugin()],
plugins: [
new webpack.ProgressPlugin(),
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
experiments: {
asyncWebAssembly: true,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function isNMReconstructable(imageSubType) {
return imageSubType === 'RECON TOMO' || imageSubType === 'RECON GATED TOMO';
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,16 @@ function combineFrameInstance(frameNumber, instance) {
frameNumber
);

return Object.assign(
rest,
{ '00280008': NumberOfFrames },
...Object.values(shared),
...Object.values(perFrame)
);
const newInstance = Object.assign(instance, { frameNumber });

// merge the shared first then the per frame to override
[...shared, ...perFrame].forEach((item) => {
Object.entries(item).forEach(([key, value]) => {
newInstance[key] = value;
});
});

return Object.assign(rest, { '00280008': NumberOfFrames }, newInstance);
}

return instance;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import getTagValue from '../getTagValue';
import getValue from './getValue';
import isNMReconstructable from '../../isNMReconstructable';
import getNumberValues from './getNumberValues';

function isNMModality(metaData) {
const modality = getValue(metaData['00080060']);

return modality.includes('NM');
}

/**
* Get a subpart of Image Type dicom tag defined by index
* @param {*} metaData
* @param {*} index 0 based index of the subtype
*/
function getImageTypeSubItemFromMetadata(metaData, index) {
const imageType = getTagValue(metaData['00080008'], false);

if (imageType) {
// const subTypes = imageType.split('\\');

// if (subTypes.length > index) {
// return subTypes[index];
// }
return imageType[index];
}

return undefined;
}
/**
* Extracts the orientation from NM multiframe metadata, if image type
* equal to RECON TOMO or RECON GATED TOMO
* @param {*} metaData
* @returns
*/
function extractOrientationFromNMMultiframeMetadata(metaData) {
let imageOrientationPatient;
const imageSubType = getImageTypeSubItemFromMetadata(metaData, 2);

if (imageSubType && isNMReconstructable(imageSubType)) {
const detectorInformationSequence = getTagValue(metaData['00540022']);

if (detectorInformationSequence) {
imageOrientationPatient = getNumberValues(
detectorInformationSequence['00200037'],
6
);
}
}

return imageOrientationPatient;
}

/**
* Extracts the position from NM multiframe dataset, if image type
* equal to RECON TOMO or RECON GATED TOMO
* @param {*} metaData
* @returns
*/
function extractPositionFromNMMultiframeMetadata(metaData) {
let imagePositionPatient;
const imageSubType = getImageTypeSubItemFromMetadata(metaData, 2);

if (imageSubType && isNMReconstructable(imageSubType)) {
const detectorInformationSequence = getTagValue(metaData['00540022']);

if (detectorInformationSequence) {
imagePositionPatient = getNumberValues(
detectorInformationSequence['00200032'],
3
);
}
}

return imagePositionPatient;
}

export {
extractOrientationFromNMMultiframeMetadata,
extractPositionFromNMMultiframeMetadata,
isNMModality,
getImageTypeSubItemFromMetadata,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import getNumberValues from './getNumberValues';
import {
extractOrientationFromNMMultiframeMetadata,
extractPositionFromNMMultiframeMetadata,
isNMModality,
} from './NMHelpers';

/**
* Extract orientation information from a metadata. It tries to get the orientation
* from the Detector Information Sequence (for NM images) if image type equal
* to RECON TOMO or RECON GATED TOMO
* @param {*} metaData
* @returns
*/
function extractOrientationFromMetadata(metaData) {
let imageOrientationPatient = getNumberValues(metaData['00200037'], 6);

// If orientation not valid to this point, trying to get the orientation
// from the Detector Information Sequence (for NM images) with image type
// equal to RECON TOMO or RECON GATED TOMO

if (!imageOrientationPatient && isNMModality(metaData)) {
imageOrientationPatient =
extractOrientationFromNMMultiframeMetadata(metaData);
}

return imageOrientationPatient;
}

/**
* Extract position information from a metaData. It tries to get the position
* from the Detector Information Sequence (for NM images) if image type equal
* to RECON TOMO or RECON GATED TOMO
* @param {*} metaData
* @returns
*/
function extractPositionFromMetadata(metaData) {
let imagePositionPatient = getNumberValues(metaData['00200032'], 3);

// If position not valid to this point, trying to get the position
// from the Detector Information Sequence (for NM images)
if (!imagePositionPatient && isNMModality(metaData)) {
imagePositionPatient = extractPositionFromNMMultiframeMetadata(metaData);
}

return imagePositionPatient;
}

export { extractOrientationFromMetadata, extractPositionFromMetadata };

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@ import getNumberValue from './getNumberValue';
import getOverlayPlaneModule from './getOverlayPlaneModule';
import metaDataManager from '../metaDataManager';
import getValue from './getValue';
//import fixNMMetadata from './fixNMMetadata';
import {
getMultiframeInformation,
getFrameInformation,
} from '../combineFrameInstance';
import multiframeMetadata from '../retrieveMultiframeMetadata';
import {
extractOrientationFromMetadata,
extractPositionFromMetadata,
} from './extractPositioningFromMetadata';
import { getImageTypeSubItemFromMetadata } from './NMHelpers';
import isNMReconstructable from '../../isNMReconstructable';

function metaDataProvider(type, imageId) {
if (type === 'multiframeModule') {
Expand Down Expand Up @@ -83,10 +88,28 @@ function metaDataProvider(type, imageId) {
};
}

if (type === 'nmMultiframeGeometryModule') {
const modality = getValue(metaData['00080060']);
const imageSubType = getImageTypeSubItemFromMetadata(metaData, 2);

return {
modality,
imageType: getValue(metaData['00080008']),
imageSubType,
imageOrientationPatient: extractOrientationFromMetadata(metaData),
imagePositionPatient: extractPositionFromMetadata(metaData),
sliceThickness: getNumberValue(metaData['00180050']),
pixelSpacing: getNumberValues(metaData['00280030'], 2),
numberOfFrames: getNumberValue(metaData['00280008']),
isNMReconstructable:
isNMReconstructable(imageSubType) && modality.includes('NM'),
};
}

if (type === 'imagePlaneModule') {
//metaData = fixNMMetadata(metaData);
const imageOrientationPatient = getNumberValues(metaData['00200037'], 6);
const imagePositionPatient = getNumberValues(metaData['00200032'], 3);
const imageOrientationPatient = extractOrientationFromMetadata(metaData);
const imagePositionPatient = extractPositionFromMetadata(metaData);
const pixelSpacing = getNumberValues(metaData['00280030'], 2);

let columnPixelSpacing = null;
Expand Down
46 changes: 28 additions & 18 deletions packages/dicomImageLoader/src/imageLoader/wadors/metaDataManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { combineFrameInstance } from './combineFrameInstance';
import multiframeMetadata from './retrieveMultiframeMetadata';

let metadataByImageURI = [];
let multiframeMetadataByImageURI = {};

function add(imageId: string, metadata: WADORSMetaData) {
const imageURI = imageIdToURI(imageId);
Expand All @@ -21,42 +22,51 @@ function add(imageId: string, metadata: WADORSMetaData) {
function get(imageId: string): WADORSMetaData {
const imageURI = imageIdToURI(imageId);

// dealing first with the non multiframe information
let metadata = metadataByImageURI[imageURI];
// Check if the metadata is already available
const metadata = metadataByImageURI[imageURI];

if (metadata) {
if (!metadata.isMultiframe) {
return metadata;
}
if (metadata && !metadata?.isMultiframe) {
// Return the metadata for single-frame images
return metadata;
}

let frame = 1;
const cachedMetadata = multiframeMetadataByImageURI[imageURI];

if (!metadata) {
// in this case it could indicate a multiframe imageid
// Try to get the first frame metadata, where is stored the multiframe info
const firstFrameInfo =
multiframeMetadata._retrieveMultiframeMetadata(imageURI);

metadata = firstFrameInfo.metadata;
frame = firstFrameInfo.frame;
if (cachedMetadata) {
return cachedMetadata;
}

if (metadata) {
metadata = combineFrameInstance(frame, metadata);
// Try to get the metadata for a specific frame of a multiframe image
const retrievedMetadata =
multiframeMetadata._retrieveMultiframeMetadata(imageURI);

if (!retrievedMetadata || !retrievedMetadata.metadata) {
return;
}

return metadata;
const { metadata: firstFrameMetadata, frame } = retrievedMetadata;

if (firstFrameMetadata) {
// Combine the metadata from the first frame with the metadata from the specified frame
const combined = combineFrameInstance(frame, firstFrameMetadata);

multiframeMetadataByImageURI[imageURI] = combined;

return combined;
}
}

function remove(imageId) {
const imageURI = imageIdToURI(imageId);

metadataByImageURI[imageURI] = undefined;

multiframeMetadataByImageURI[imageURI] = undefined;
}

function purge() {
metadataByImageURI = [];
multiframeMetadataByImageURI = {};
}

export { metadataByImageURI };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ function combineFrameInstanceDataset(frameNumber, dataSet) {
PerFrameFunctionalGroupsSequence,
SharedFunctionalGroupsSequence,
otherElements,
otherAttributtes,
} = getMultiframeInformation(dataSet);

if (PerFrameFunctionalGroupsSequence || NumberOfFrames > 1) {
Expand All @@ -100,15 +99,17 @@ function combineFrameInstanceDataset(frameNumber, dataSet) {
);

// creating a new copy of the dataset to remove the two multiframe dicom tags
const newDataset = {
...otherAttributtes,
const newElements = {
elements: {
...otherElements,
...shared,
...perFrame,
},
};

const clonedDataset = Object.create(dataSet);
const newDataset = Object.assign(clonedDataset, newElements);

return newDataset;
}

Expand Down
Loading

0 comments on commit 7ae983c

Please sign in to comment.