242 lines
9.6 KiB
JavaScript
242 lines
9.6 KiB
JavaScript
import {parsingUtils} from './parsingUtils'
|
|
|
|
var metaDataLookup = {};
|
|
|
|
/**
|
|
* 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 = function(imageId, data) {
|
|
var instanceMetaData = data.instance;
|
|
var seriesMetaData = data.series;
|
|
var studyMetaData = data.study;
|
|
var imageIndex = data.imageIndex;
|
|
var numImages = data.numImages;
|
|
|
|
var metaData = {};
|
|
|
|
metaData.study = {
|
|
patientId: studyMetaData.patientId,
|
|
studyInstanceUid: studyMetaData.studyInstanceUid,
|
|
studyDate: studyMetaData.studyDate,
|
|
studyTime: studyMetaData.studyTime,
|
|
studyDescription: studyMetaData.studyDescription,
|
|
institutionName: studyMetaData.institutionName
|
|
};
|
|
|
|
metaData.series = {
|
|
seriesDescription: seriesMetaData.seriesDescription,
|
|
seriesNumber: seriesMetaData.seriesNumber,
|
|
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
|
|
};
|
|
|
|
// If there is sufficient information, populate
|
|
// the imagePlane object for easier use in the Viewer
|
|
metaData.imagePlane = getImagePlane(instanceMetaData);
|
|
|
|
// Add the metaData to the imageId lookup object
|
|
metaDataLookup[imageId] = metaData;
|
|
};
|
|
|
|
/**
|
|
* 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 = function(imageId, type, data) {
|
|
var metaData = {};
|
|
metaData[type] = data;
|
|
|
|
metaDataLookup[imageId] = $.extend(metaDataLookup[imageId], metaData);
|
|
};
|
|
|
|
/**
|
|
* Updates the related metaData for missing fields given a specified image
|
|
*
|
|
* @param image
|
|
*/
|
|
updateMetaData = function(image) {
|
|
var imageMetaData = metaDataLookup[image.imageId];
|
|
if (!imageMetaData) {
|
|
return;
|
|
}
|
|
|
|
imageMetaData.instance.sopClassUid = imageMetaData.instance.sopClassUid || image.data.string('x00080016');
|
|
imageMetaData.instance.sopInstanceUid = imageMetaData.instance.sopInstanceUid || image.data.string('x00080018');
|
|
|
|
imageMetaData.instance.rows = imageMetaData.instance.rows || image.rows;
|
|
imageMetaData.instance.columns = imageMetaData.instance.columns || image.columns;
|
|
imageMetaData.instance.pixelSpacing = imageMetaData.instance.pixelSpacing || image.data.string('x00280030');
|
|
imageMetaData.instance.frameOfReferenceUID = imageMetaData.instance.frameOfReferenceUID || image.data.string('x00200052');
|
|
imageMetaData.instance.imageOrientationPatient = imageMetaData.instance.imageOrientationPatient || image.data.string('x00200037');
|
|
imageMetaData.instance.imagePositionPatient = imageMetaData.instance.imagePositionPatient || image.data.string('x00200032');
|
|
|
|
imageMetaData.instance.sliceThickness = imageMetaData.instance.sliceThickness || image.data.string('x00180050');
|
|
imageMetaData.instance.sliceLocation = imageMetaData.instance.sliceLocation || image.data.string('x00201041');
|
|
imageMetaData.instance.tablePosition = imageMetaData.instance.tablePosition || image.data.string('x00189327');
|
|
imageMetaData.instance.spacingBetweenSlices = imageMetaData.instance.spacingBetweenSlices || image.data.string('x00180088');
|
|
|
|
imageMetaData.instance.lossyImageCompression = imageMetaData.instance.lossyImageCompression || image.data.string('x00282110');
|
|
imageMetaData.instance.lossyImageCompressionRatio = imageMetaData.instance.lossyImageCompressionRatio || image.data.string('x00282112');
|
|
|
|
imageMetaData.instance.frameIncrementPointer = imageMetaData.instance.frameIncrementPointer || image.data.string('x00280009');
|
|
imageMetaData.instance.frameTime = imageMetaData.instance.frameTime || image.data.string('x00181063');
|
|
imageMetaData.instance.frameTimeVector = imageMetaData.instance.frameTimeVector || image.data.string('x00181065');
|
|
imageMetaData.instance.multiframeMetadata = getMultiframeModuleMetaData(image.data);
|
|
|
|
imageMetaData.imagePlane = imageMetaData.imagePlane || getImagePlane(imageMetaData.instance);
|
|
};
|
|
|
|
/**
|
|
* Constructs and returns the imagePlane given the instance
|
|
*
|
|
* @param instance The instance containing information to construct imagePlane
|
|
* @returns imagePlane The constructed imagePlane to be used in viewer easily
|
|
*/
|
|
function getImagePlane(instance) {
|
|
if (!instance.rows || !instance.columns || !instance.pixelSpacing ||
|
|
!instance.frameOfReferenceUID || !instance.imageOrientationPatient ||
|
|
!instance.imagePositionPatient) {
|
|
return;
|
|
}
|
|
|
|
var imageOrientation = instance.imageOrientationPatient.split('\\');
|
|
var imagePosition = instance.imagePositionPatient.split('\\');
|
|
|
|
var columnPixelSpacing = 1.0;
|
|
var rowPixelSpacing = 1.0;
|
|
if (instance.pixelSpacing) {
|
|
var split = instance.pixelSpacing.split('\\');
|
|
rowPixelSpacing = parseFloat(split[0]);
|
|
columnPixelSpacing = parseFloat(split[1]);
|
|
}
|
|
|
|
return {
|
|
frameOfReferenceUID:
|
|
instance.frameOfReferenceUID,
|
|
rows:
|
|
instance.rows,
|
|
columns:
|
|
instance.columns,
|
|
rowCosines:
|
|
new cornerstoneMath.Vector3(parseFloat(imageOrientation[0]), parseFloat(imageOrientation[1]), parseFloat(imageOrientation[2])),
|
|
columnCosines:
|
|
new cornerstoneMath.Vector3(parseFloat(imageOrientation[3]), parseFloat(imageOrientation[4]), parseFloat(imageOrientation[5])),
|
|
imagePositionPatient:
|
|
new cornerstoneMath.Vector3(parseFloat(imagePosition[0]), parseFloat(imagePosition[1]), parseFloat(imagePosition[2])),
|
|
rowPixelSpacing:
|
|
rowPixelSpacing,
|
|
columnPixelSpacing:
|
|
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 = function(dataSet) {
|
|
|
|
var numberOfFrames,
|
|
frameIncrementPointer,
|
|
frameTime,
|
|
frameTimeVector,
|
|
imageInfo = {
|
|
isMultiframeImage: false,
|
|
frameIncrementPointer: null,
|
|
numberOfFrames: 0,
|
|
frameTime: 0,
|
|
frameTimeVector: null,
|
|
averageFrameRate: 0 // backwards compatibility only... it might be useless in the future
|
|
};
|
|
|
|
if (parsingUtils.isValidDataSet(dataSet)) {
|
|
|
|
// (0028,0008) = Number of Frames
|
|
numberOfFrames = dataSet.intString('x00280008', -1);
|
|
if (numberOfFrames > 0) {
|
|
|
|
// set multi-frame image indicator
|
|
imageInfo.isMultiframeImage = true;
|
|
imageInfo.numberOfFrames = numberOfFrames;
|
|
|
|
// (0028,0009) = Frame Increment Pointer
|
|
frameIncrementPointer = parsingUtils.attributeTag(dataSet, 'x00280009') || '';
|
|
|
|
if (frameIncrementPointer === 'x00181065') {
|
|
// Frame Increment Pointer points to Frame Time Vector (0018,1065) field
|
|
frameTimeVector = parsingUtils.floatArray(dataSet, 'x00181065');
|
|
if (frameTimeVector instanceof Array && frameTimeVector.length > 0) {
|
|
imageInfo.frameIncrementPointer = 'frameTimeVector';
|
|
imageInfo.frameTimeVector = frameTimeVector;
|
|
frameTime = frameTimeVector.reduce((a, b) => a + b) / frameTimeVector.length;
|
|
imageInfo.averageFrameRate = 1000 / frameTime;
|
|
}
|
|
} else if (frameIncrementPointer === 'x00181063' || frameIncrementPointer === '') {
|
|
// 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.
|
|
frameTime = dataSet.floatString('x00181063', -1);
|
|
if (frameTime > 0) {
|
|
imageInfo.frameIncrementPointer = 'frameTime';
|
|
imageInfo.frameTime = frameTime;
|
|
imageInfo.averageFrameRate = 1000 / frameTime;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return imageInfo;
|
|
|
|
};
|
|
|
|
/**
|
|
* Looks up metaData for Cornerstone Tools given a specified type and imageId
|
|
* A type may be, e.g. 'study', or 'patient', or 'imagePlane'. These types
|
|
* are keys in the stored metaData objects.
|
|
*
|
|
* @param type
|
|
* @param imageId
|
|
* @returns {Object} Relevant metaData of the specified type
|
|
*/
|
|
function provider(type, imageId) {
|
|
var imageMetaData = metaDataLookup[imageId];
|
|
if (!imageMetaData) {
|
|
return;
|
|
}
|
|
|
|
if (imageMetaData.hasOwnProperty(type)) {
|
|
return imageMetaData[type];
|
|
}
|
|
}
|
|
|
|
Meteor.startup(function() {
|
|
cornerstoneTools.metaData.addProvider(provider);
|
|
});
|