Files
ohif-viewer/platform/core/src/classes/MetadataProvider.js
T

412 lines
13 KiB
JavaScript
Raw Normal View History

2019-08-13 15:36:46 -04:00
import { parsingUtils } from '../lib/parsingUtils';
const FUNCTION = 'function';
class MetadataProvider {
constructor() {
// Define the main "metadataLookup" private property as an immutable property.
Object.defineProperty(this, 'metadataLookup', {
configurable: false,
enumerable: false,
writable: false,
value: new Map(),
});
// Local reference to provider function bound to current instance.
Object.defineProperty(this, '_provider', {
configurable: false,
enumerable: false,
writable: true,
value: null,
});
}
/**
* Cornerstone Metadata provider to store image meta data
* Data from instances, series, and studies are associated with
* imageIds to facilitate usage of this information by Cornerstone's Tools
*
* e.g. the imagePlane metadata object contains instance information about
* row/column pixel spacing, patient position, and patient orientation. It
* is used in CornerstoneTools to position reference lines and orientation markers.
*
* @param {String} imageId The Cornerstone ImageId
* @param {Object} data An object containing instance, series, and study metadata
*/
addMetadata(imageId, data) {
const instanceMetadata = data.instance;
const seriesMetadata = data.series;
const studyMetadata = data.study;
const numImages = data.numImages;
const metadata = {};
metadata.frameNumber = data.frameNumber;
metadata.study = {
accessionNumber: studyMetadata.accessionNumber,
patientId: studyMetadata.patientId,
studyInstanceUid: studyMetadata.studyInstanceUid,
studyDate: studyMetadata.studyDate,
studyTime: studyMetadata.studyTime,
studyDescription: studyMetadata.studyDescription,
institutionName: studyMetadata.institutionName,
patientHistory: studyMetadata.patientHistory,
};
metadata.series = {
seriesDescription: seriesMetadata.seriesDescription,
seriesNumber: seriesMetadata.seriesNumber,
seriesDate: seriesMetadata.seriesDate,
seriesTime: seriesMetadata.seriesTime,
modality: seriesMetadata.modality,
seriesInstanceUid: seriesMetadata.seriesInstanceUid,
numImages: numImages,
};
metadata.instance = instanceMetadata;
metadata.patient = {
name: studyMetadata.patientName,
id: studyMetadata.patientId,
birthDate: studyMetadata.patientBirthDate,
sex: studyMetadata.patientSex,
age: studyMetadata.patientAge,
};
// If there is sufficient information, populate
// the imagePlane object for easier use in the Viewer
metadata.imagePlane = this.getImagePlane(instanceMetadata);
// Add the metadata to the imageId lookup object
this.metadataLookup.set(imageId, metadata);
}
/**
* Return the metadata for the given imageId
* @param {String} imageId The Cornerstone ImageId
* @returns image metadata
*/
getMetadata(imageId) {
return this.metadataLookup.get(imageId);
}
/**
* Adds a set of metadata to the Cornerstone metadata provider given a specific
* imageId, type, and dataset
*
* @param imageId
* @param type (e.g. series, instance, tagDisplay)
* @param data
*/
addSpecificMetadata(imageId, type, data) {
const metadata = {};
metadata[type] = data;
const oldMetadata = this.metadataLookup.get(imageId);
this.metadataLookup.set(imageId, Object.assign(oldMetadata, metadata));
}
getFromImage(image, type, tag, attrName, defaultValue) {
let value;
if (image.data) {
value = this.getFromDataSet(image.data, type, tag);
} else {
value = image.instance[attrName];
}
return value === null ? defaultValue : value;
}
getFromDataSet(dataSet, type, tag) {
if (!dataSet) {
return;
}
const fn = dataSet[type];
if (!fn) {
return;
}
return fn.call(dataSet, tag);
}
getFrameIncrementPointer(image) {
const dataSet = image.data;
let frameInstancePointer = '';
if (parsingUtils.isValidDataSet(dataSet)) {
const frameInstancePointerNames = {
x00181063: 'frameTime',
x00181065: 'frameTimeVector',
};
// (0028,0009) = Frame Increment Pointer
const frameInstancePointerTag = parsingUtils.attributeTag(
dataSet,
'x00280009'
);
frameInstancePointer = frameInstancePointerNames[frameInstancePointerTag];
} else {
frameInstancePointer = image.instance.frameIncrementPointer;
}
return frameInstancePointer || '';
}
getFrameTimeVector(image) {
const dataSet = image.data;
if (parsingUtils.isValidDataSet(dataSet)) {
// Frame Increment Pointer points to Frame Time Vector (0018,1065) field
return parsingUtils.floatArray(dataSet, 'x00181065');
}
return image.instance.frameTimeVector;
}
getFrameTime(image) {
const dataSet = image.data;
if (parsingUtils.isValidDataSet(dataSet)) {
// Frame Increment Pointer points to Frame Time (0018,1063) field or is not defined (for addtional flexibility).
// Yet another value is possible for this field (5200,9230 for Multi-frame Functional Groups)
// but that case is currently not supported.
return dataSet.floatString('x00181063', -1);
}
return image.instance.frameTime;
}
/**
* Updates the related metadata for missing fields given a specified image
*
* @param image
*/
updateMetadata(image) {
const imageMetadata = this.metadataLookup.get(image.imageId);
if (!imageMetadata) {
return;
}
imageMetadata.patient.age =
imageMetadata.patient.age ||
this.getFromDataSet(image.data, 'string', 'x00101010');
imageMetadata.instance.rows = imageMetadata.instance.rows || image.rows;
imageMetadata.instance.columns =
imageMetadata.instance.columns || image.columns;
imageMetadata.instance.sopClassUid =
imageMetadata.instance.sopClassUid ||
this.getFromDataSet(image.data, 'string', 'x00080016');
imageMetadata.instance.sopInstanceUid =
imageMetadata.instance.sopInstanceUid ||
this.getFromDataSet(image.data, 'string', 'x00080018');
imageMetadata.instance.pixelSpacing =
imageMetadata.instance.pixelSpacing ||
this.getFromDataSet(image.data, 'string', 'x00280030');
imageMetadata.instance.frameOfReferenceUID =
imageMetadata.instance.frameOfReferenceUID ||
this.getFromDataSet(image.data, 'string', 'x00200052');
imageMetadata.instance.imageOrientationPatient =
imageMetadata.instance.imageOrientationPatient ||
this.getFromDataSet(image.data, 'string', 'x00200037');
imageMetadata.instance.imagePositionPatient =
imageMetadata.instance.imagePositionPatient ||
this.getFromDataSet(image.data, 'string', 'x00200032');
imageMetadata.instance.sliceThickness =
imageMetadata.instance.sliceThickness ||
this.getFromDataSet(image.data, 'string', 'x00180050');
imageMetadata.instance.sliceLocation =
imageMetadata.instance.sliceLocation ||
this.getFromDataSet(image.data, 'string', 'x00201041');
imageMetadata.instance.tablePosition =
imageMetadata.instance.tablePosition ||
this.getFromDataSet(image.data, 'string', 'x00189327');
imageMetadata.instance.spacingBetweenSlices =
imageMetadata.instance.spacingBetweenSlices ||
this.getFromDataSet(image.data, 'string', 'x00180088');
imageMetadata.instance.lossyImageCompression =
imageMetadata.instance.lossyImageCompression ||
this.getFromDataSet(image.data, 'string', 'x00282110');
imageMetadata.instance.lossyImageCompressionRatio =
imageMetadata.instance.lossyImageCompressionRatio ||
this.getFromDataSet(image.data, 'string', 'x00282112');
imageMetadata.instance.frameIncrementPointer =
imageMetadata.instance.frameIncrementPointer ||
this.getFromDataSet(image.data, 'string', 'x00280009');
imageMetadata.instance.frameTime =
imageMetadata.instance.frameTime ||
this.getFromDataSet(image.data, 'string', 'x00181063');
imageMetadata.instance.frameTimeVector =
imageMetadata.instance.frameTimeVector ||
this.getFromDataSet(image.data, 'string', 'x00181065');
if (
(image.data || image.instance) &&
!imageMetadata.instance.multiframeMetadata
) {
imageMetadata.instance.multiframeMetadata = this.getMultiframeModuleMetadata(
image
);
}
imageMetadata.imagePlane =
imageMetadata.imagePlane || this.getImagePlane(imageMetadata.instance);
}
/**
* Constructs and returns the imagePlane given the metadata instance
*
* @param metadataInstance The metadata instance (InstanceMetadata class) containing information to construct imagePlane
* @returns imagePlane The constructed imagePlane to be used in viewer easily
*/
getImagePlane(instance) {
if (
!instance.rows ||
!instance.columns ||
!instance.pixelSpacing ||
!instance.frameOfReferenceUID ||
!instance.imageOrientationPatient ||
!instance.imagePositionPatient
) {
return;
}
const imageOrientation = instance.imageOrientationPatient.split('\\');
const imagePosition = instance.imagePositionPatient.split('\\');
let columnPixelSpacing = 1.0;
let rowPixelSpacing = 1.0;
if (instance.pixelSpacing) {
const split = instance.pixelSpacing.split('\\');
rowPixelSpacing = parseFloat(split[0]);
columnPixelSpacing = parseFloat(split[1]);
}
return {
frameOfReferenceUID: instance.frameOfReferenceUID,
rows: instance.rows,
columns: instance.columns,
rowCosines: [
parseFloat(imageOrientation[0]),
parseFloat(imageOrientation[1]),
parseFloat(imageOrientation[2]),
],
columnCosines: [
parseFloat(imageOrientation[3]),
parseFloat(imageOrientation[4]),
parseFloat(imageOrientation[5]),
],
imagePositionPatient: [
parseFloat(imagePosition[0]),
parseFloat(imagePosition[1]),
parseFloat(imagePosition[2]),
],
rowPixelSpacing,
columnPixelSpacing,
};
}
/**
* This function extracts miltiframe information from a dicomParser.DataSet object.
*
* @param dataSet {Object} An instance of dicomParser.DataSet object where multiframe information can be found.
* @return {Object} An object containing multiframe image metadata (frameIncrementPointer, frameTime, frameTimeVector, etc).
*/
getMultiframeModuleMetadata(image) {
const imageInfo = {
isMultiframeImage: false,
frameIncrementPointer: null,
numberOfFrames: 0,
frameTime: 0,
frameTimeVector: null,
averageFrameRate: 0, // backwards compatibility only... it might be useless in the future
};
let frameTime;
const numberOfFrames = this.getFromImage(
image,
'intString',
'x00280008',
'numberOfFrames',
-1
);
if (numberOfFrames > 0) {
// set multi-frame image indicator
imageInfo.isMultiframeImage = true;
imageInfo.numberOfFrames = numberOfFrames;
// (0028,0009) = Frame Increment Pointer
const frameIncrementPointer = this.getFrameIncrementPointer(image);
if (frameIncrementPointer === 'frameTimeVector') {
// Frame Increment Pointer points to Frame Time Vector (0018,1065) field
const frameTimeVector = this.getFrameTimeVector(image);
if (frameTimeVector instanceof Array && frameTimeVector.length > 0) {
imageInfo.frameIncrementPointer = frameIncrementPointer;
imageInfo.frameTimeVector = frameTimeVector;
frameTime =
frameTimeVector.reduce((a, b) => a + b) / frameTimeVector.length;
imageInfo.averageFrameRate = 1000 / frameTime;
}
} else if (
frameIncrementPointer === 'frameTime' ||
frameIncrementPointer === ''
) {
frameTime = this.getFrameTime(image);
if (frameTime > 0) {
imageInfo.frameIncrementPointer = frameIncrementPointer;
imageInfo.frameTime = frameTime;
imageInfo.averageFrameRate = 1000 / frameTime;
}
}
}
return imageInfo;
}
/**
* Get a bound reference to the provider function.
*/
getProvider() {
let provider = this._provider;
if (typeof this._provider !== FUNCTION) {
provider = this.provider.bind(this);
this._provider = provider;
}
return provider;
}
/**
* Looks up metadata for Cornerstone Tools given a specified type and imageId
* A type may be, e.g. 'study', or 'patient', or 'imagePlaneModule'. These types
* are keys in the stored metadata objects.
*
* @param type
* @param imageId
* @returns {Object} Relevant metadata of the specified type
*/
provider(type, imageId) {
const imageMetadata = this.metadataLookup.get(imageId);
if (!imageMetadata) {
return;
}
if (imageMetadata.hasOwnProperty(type)) {
return imageMetadata[type];
}
}
}
export default MetadataProvider;