From 1122594d2b03f7953ef68424337fa6561e7c37d3 Mon Sep 17 00:00:00 2001 From: "Emanuel F. Oliveira" Date: Fri, 28 Oct 2016 15:44:32 -0200 Subject: [PATCH] OHIF-20 Moving multi-frame related functionality to metaDataProvider / Created new library with parsing utilities. --- .../imageViewerViewport.js | 8 +-- Packages/viewerbase/lib/metaDataProvider.js | 66 +++++++++++++++++++ Packages/viewerbase/lib/parsingUtils.js | 41 ++++++++++++ 3 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 Packages/viewerbase/lib/parsingUtils.js diff --git a/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js b/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js index 0aca248ef..121bd6033 100644 --- a/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js +++ b/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js @@ -213,15 +213,15 @@ function loadDisplaySetIntoViewport(data, templateData) { cornerstoneTools.addToolState(element, 'stack', stack); // Set the default CINE settings - var frameRate = 1000 / displaySet.images[0].frameTime; + var multiframeMetadata = instance.multiframeMetadata; var cineToolData = { loop: OHIF.viewer.cine.loop, - framesPerSecond: frameRate || OHIF.viewer.cine.framesPerSecond + framesPerSecond: multiframeMetadata.averageFrameRate || OHIF.viewer.cine.framesPerSecond }; cornerstoneTools.addToolState(element, 'playClip', cineToolData); // Autoplay datasets that have framerates set - if (frameRate) { + if (multiframeMetadata.isMultiframeImage && multiframeMetadata.averageFrameRate > 0) { cornerstoneTools.playClip(element); } @@ -386,7 +386,6 @@ function setDisplaySet(data, displaySetInstanceUid, templateData) { var study = data.study; if (!study || !study.displaySets) { throw 'Study does not exist or has no display sets'; - return; } study.displaySets.every(displaySet => { @@ -401,7 +400,6 @@ function setDisplaySet(data, displaySetInstanceUid, templateData) { // If we didn't find anything, stop here if (!data.displaySet) { throw 'Display set not found in specified study!'; - return; } // Otherwise, load pass the data object into loadSeriesIntoViewport diff --git a/Packages/viewerbase/lib/metaDataProvider.js b/Packages/viewerbase/lib/metaDataProvider.js index f033fd195..ba6b0f367 100644 --- a/Packages/viewerbase/lib/metaDataProvider.js +++ b/Packages/viewerbase/lib/metaDataProvider.js @@ -1,3 +1,5 @@ +import {parsingUtils} from './parsingUtils' + var metaDataLookup = {}; /** @@ -102,6 +104,7 @@ updateMetaData = function(image) { 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); }; @@ -150,6 +153,69 @@ function getImagePlane(instance) { }; } +/** + * 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 diff --git a/Packages/viewerbase/lib/parsingUtils.js b/Packages/viewerbase/lib/parsingUtils.js new file mode 100644 index 000000000..a078ebdfa --- /dev/null +++ b/Packages/viewerbase/lib/parsingUtils.js @@ -0,0 +1,41 @@ + +import {dicomParser} from 'meteor/cornerstone'; + +export const parsingUtils = { + isValidDataSet: function(data) { + return (data instanceof dicomParser.DataSet); + }, + attributeTag: function(data, tag) { + if (this.isValidDataSet(data) && tag in data.elements) { + let element = data.elements[tag]; + if (element && element.length === 4) { + let parser = data.byteArrayParser.readUint16, + bytes = data.byteArray, + offset = element.dataOffset; + return 'x' + ('00000000' + (parser(bytes, offset) * 256 * 256 + parser(bytes, offset + 2)).toString(16)).substr(-8); + } + } + return null; + }, + multiValue: function(data, tag, parser) { + if (this.isValidDataSet(data) && tag in data.elements) { + let element = data.elements[tag]; + if (element && element.length > 0) { + let string = dicomParser.readFixedString(data.byteArray, element.dataOffset, element.length); + if (typeof string === 'string' && string.length > 0) { + if (typeof parser !== 'function') { + parser = null; + } + return string.split('\\').map(function(value) { + value = value.trim(); + return parser !== null ? parser(value) : value; + }); + } + } + } + return null; + }, + floatArray: function(data, tag) { + return this.multiValue(data, tag, parseFloat); + } +};