From 8297171f4a5735d43eebc48d6face67e1aeb2d21 Mon Sep 17 00:00:00 2001 From: Erik Ziegler Date: Tue, 9 Feb 2016 13:19:11 +0100 Subject: [PATCH] Update CornerstoneWADOImageLoader to 0.9.0, tried to get DIMSE + WADO-URI working --- LesionTracker/defaultSettings.js | 2 +- OHIFViewer/defaultSettings.js | 2 +- .../client/cornerstoneWADOImageLoader.js | 126 +++++++++++------- Packages/dimseservice/server/DIMSE.js | 3 +- .../components/studyDateList/studyDateList.js | 2 +- .../server/methods/getStudyMetadata.js | 13 +- .../server/services/dimse/instances.js | 2 +- .../server/services/dimse/retrieveMetadata.js | 17 ++- config/dockerDCM4CHE.json | 2 +- 9 files changed, 103 insertions(+), 66 deletions(-) diff --git a/LesionTracker/defaultSettings.js b/LesionTracker/defaultSettings.js index 8b33acb90..033d2f0a3 100644 --- a/LesionTracker/defaultSettings.js +++ b/LesionTracker/defaultSettings.js @@ -1,5 +1,5 @@ Meteor.startup(function() { - if (Meteor.settings) { + if (Object.keys(Meteor.settings).length > 1) { console.log('Using custom LesionTracker settings'); console.log(Meteor.settings); return; diff --git a/OHIFViewer/defaultSettings.js b/OHIFViewer/defaultSettings.js index ef9d5f259..6e90d6895 100644 --- a/OHIFViewer/defaultSettings.js +++ b/OHIFViewer/defaultSettings.js @@ -1,5 +1,5 @@ Meteor.startup(function() { - if (Meteor.settings) { + if (Object.keys(Meteor.settings).length > 1) { console.log('Using custom LesionTracker settings: '); console.log(Meteor.settings); return; diff --git a/Packages/cornerstone/client/cornerstoneWADOImageLoader.js b/Packages/cornerstone/client/cornerstoneWADOImageLoader.js index 29390416b..9986a745e 100644 --- a/Packages/cornerstone/client/cornerstoneWADOImageLoader.js +++ b/Packages/cornerstone/client/cornerstoneWADOImageLoader.js @@ -1,4 +1,4 @@ -/*! cornerstone-wado-image-loader - v0.8.1 - 2016-02-07 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneWADOImageLoader */ +/*! cornerstone-wado-image-loader - v0.9.0 - 2016-02-08 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneWADOImageLoader */ // // This is a cornerstone image loader for WADO-URI requests. It has limited support for compressed // transfer syntaxes, check here to see what is currently supported: @@ -85,10 +85,10 @@ if(typeof cornerstoneWADOImageLoader === 'undefined'){ throw "no color space conversion for photometric interpretation " + photometricInterpretation; } deferred.resolve(imageData); - return deferred; + return deferred.promise(); } catch (error) { deferred.reject(error); - return deferred; + return deferred.promise(); } } @@ -380,7 +380,7 @@ if(typeof cornerstoneWADOImageLoader === 'undefined'){ } }; - return deferred; + return deferred.promise(); } function isJPEGBaseline8Bit(dataSet) { @@ -550,6 +550,11 @@ if(typeof cornerstoneWADOImageLoader === 'undefined'){ else if( transferSyntax === "1.2.840.10008.1.2.1") { return cornerstoneWADOImageLoader.extractUncompressedPixels(dataSet, frame); } + // Explicit VR Big Endian (retired) + else if ( transferSyntax === "1.2.840.10008.1.2.2" ) + { + return cornerstoneWADOImageLoader.extractUncompressedPixels(dataSet, frame, true); + } // JPEG 2000 Lossless else if(transferSyntax === "1.2.840.10008.1.2.4.90") { @@ -599,6 +604,9 @@ if(typeof cornerstoneWADOImageLoader === 'undefined'){ } else { + if(console && console.log) { + console.log("Image cannot be decoded due to Unsupported transfer syntax " + transferSyntax); + } throw "no decoder for transfer syntax " + transferSyntax; } } @@ -3288,17 +3296,37 @@ var JpegImage = (function jpegImage() { (function ($, cornerstone, cornerstoneWADOImageLoader) { "use strict"; - function extractUncompressedPixels(dataSet, frame) + + function swap16(val) { + return ((val & 0xFF) << 8) + | ((val >> 8) & 0xFF); + } + + + function extractUncompressedPixels(dataSet, frame, bigEndian) { var pixelFormat = cornerstoneWADOImageLoader.getPixelFormat(dataSet); + var imageFrame = getImageFrame(dataSet, frame, pixelFormat); + // byte swap 16 bit data if bigEndian + if(bigEndian && (pixelFormat === 2 || pixelFormat === 3)) { + for(var i=0; i < imageFrame.length; i++) { + imageFrame[i] = swap16(imageFrame[i]); + } + } + return imageFrame; + } + + function getImageFrame(dataSet, frame, pixelFormat) { + // Note - we may want to sanity check the rows * columns * bitsAllocated * samplesPerPixel against the buffer size var pixelDataElement = dataSet.elements.x7fe00010; var height = dataSet.uint16('x00280010'); var width = dataSet.uint16('x00280011'); var samplesPerPixel = dataSet.uint16('x00280002'); var pixelDataOffset = pixelDataElement.dataOffset; var numPixels = width * height * samplesPerPixel; - // Note - we may want to sanity check the rows * columns * bitsAllocated * samplesPerPixel against the buffer size - + if (!numPixels) { + throw "Sanity check failed when calculating the number of pixels"; + } var frameOffset = 0; if(pixelFormat === 1) { frameOffset = pixelDataOffset + frame * numPixels; @@ -3307,11 +3335,13 @@ var JpegImage = (function jpegImage() { else if(pixelFormat === 2) { frameOffset = pixelDataOffset + frame * numPixels * 2; return new Uint16Array(dataSet.byteArray.buffer, frameOffset, numPixels); + return imageFrame; } else if(pixelFormat === 3) { frameOffset = pixelDataOffset + frame * numPixels * 2; return new Int16Array(dataSet.byteArray.buffer, frameOffset, numPixels); } + throw "Unknown pixel format"; } cornerstoneWADOImageLoader.extractUncompressedPixels = extractUncompressedPixels; @@ -3373,26 +3403,28 @@ var JpegImage = (function jpegImage() { "use strict"; - function getPixelSpacing(dataSet) - { - // NOTE - these are not required for all SOP Classes - // so we return them as undefined. We also do not - // deal with the complexity associated with projection - // radiographs here and leave that to a higher layer - var pixelSpacing = dataSet.string('x00280030'); - if(pixelSpacing && pixelSpacing.length > 0) { - var split = pixelSpacing.split('\\'); - return { - row: parseFloat(split[0]), - column: parseFloat(split[1]) - }; - } - else { - return { - row: undefined, - column: undefined - }; + function getPixelSpacing(dataSet) { + // NOTE - these are not required for all SOP Classes + // so we return them as undefined. We also do not + // deal with the complexity associated with projection + // radiographs here and leave that to a higher layer + var pixelSpacing = dataSet.string('x00280030'); + if (pixelSpacing && pixelSpacing.length > 0) { + var split = pixelSpacing.split('\\'); + + // Make sure that neither pixel spacing value is 0 or undefined + if (parseFloat(split[0]) && parseFloat(split[1])) { + return { + row: parseFloat(split[0]), + column: parseFloat(split[1]) + }; } + } + + return { + row: undefined, + column: undefined + }; } // module exports cornerstoneWADOImageLoader.getPixelSpacing = getPixelSpacing; @@ -3411,8 +3443,8 @@ var JpegImage = (function jpegImage() { }; if(dataSet.elements.x00281052 && dataSet.elements.x00281053) { - result.intercept = dataSet.floatString('x00281052'); - result.slope = dataSet.floatString('x00281053'); + result.intercept = dataSet.floatString('x00281052') || result.intercept; + result.slope = dataSet.floatString('x00281053') || result.slope; } return result; @@ -3480,14 +3512,14 @@ var JpegImage = (function jpegImage() { }, function(error) { deferred.reject(error); }); - return deferred; + return deferred.promise(); } var fileIndex = parseInt(url); var file = cornerstoneWADOImageLoader.fileManager.get(fileIndex); if(file === undefined) { deferred.reject('unknown file index ' + url); - return deferred; + return deferred.promise(); } @@ -3508,19 +3540,19 @@ var JpegImage = (function jpegImage() { }, function(error) { deferred.reject(error); }); - return deferred; + return deferred.promise(); } var imagePromise = cornerstoneWADOImageLoader.createImageObject(dataSet, imageId, frame); imagePromise.then(function(image) { deferred.resolve(image); - }, function() { - deferred.reject(); + }, function(error) { + deferred.reject(error); }); }; fileReader.readAsArrayBuffer(file); - return deferred; + return deferred.promise(); } // registery dicomweb and wadouri image loader prefixes @@ -3697,7 +3729,7 @@ var JpegImage = (function jpegImage() { }; xhr.send(); - return deferred; + return deferred.promise(); }; }(cornerstoneWADOImageLoader)); /** @@ -3746,7 +3778,7 @@ var JpegImage = (function jpegImage() { var image = cornerstoneWADOImageLoader.imageManager.get(index); if(image === undefined) { deferred.reject('unknown imageId'); - return deferred; + return deferred.promise(); } var mediaType;// = 'image/dicom+jp2'; @@ -3781,7 +3813,7 @@ var JpegImage = (function jpegImage() { deferred.reject(reason); }); - return deferred; + return deferred.promise(); } // registery dicomweb and wadouri image loader prefixes @@ -4044,7 +4076,7 @@ var JpegImage = (function jpegImage() { } catch(err) { deferred.reject(err); - return deferred; + return deferred.promise(); } imageDataPromise.then(function(imageData) { @@ -4095,7 +4127,8 @@ var JpegImage = (function jpegImage() { sharedCacheKey: sharedCacheKey }; - if(image.windowCenter === undefined) { + if(image.windowCenter === undefined || isNaN(image.windowCenter) || + image.windowWidth === undefined || isNaN(image.windowWidth)) { image.windowWidth = 255; image.windowCenter = 128; } @@ -4104,7 +4137,7 @@ var JpegImage = (function jpegImage() { deferred.reject(error); }); - return deferred; + return deferred.promise(); } // module exports @@ -4171,7 +4204,7 @@ var JpegImage = (function jpegImage() { bytesPerPixel = getBytesPerPixel(dataSet); } catch(error) { deferred.reject(error); - return deferred; + return deferred.promise(); } var numPixels = rows * columns; @@ -4188,7 +4221,7 @@ var JpegImage = (function jpegImage() { } catch(err) { deferred.reject(err); - return deferred; + return deferred.promise(); } var minMax = cornerstoneWADOImageLoader.getMinMax(storedPixelData); @@ -4241,7 +4274,8 @@ var JpegImage = (function jpegImage() { // TODO: deal with pixel padding and all of the various issues by setting it to min pixel value (or lower) // TODO: Mask out overlays embedded in pixel data above high bit - if(image.windowCenter === undefined) { + if(image.windowCenter === undefined || isNaN(image.windowCenter) || + image.windowWidth === undefined || isNaN(image.windowWidth)) { var maxVoi = image.maxPixelValue * image.slope + image.intercept; var minVoi = image.minPixelValue * image.slope + image.intercept; image.windowWidth = maxVoi - minVoi; @@ -4249,7 +4283,7 @@ var JpegImage = (function jpegImage() { } deferred.resolve(image); - return deferred; + return deferred.promise(); } // module exports @@ -4260,7 +4294,7 @@ var JpegImage = (function jpegImage() { "use strict"; // module exports - cornerstoneWADOImageLoader.version = '0.8.1'; + cornerstoneWADOImageLoader.version = '0.9.0'; }(cornerstoneWADOImageLoader)); (function ($, cornerstone, cornerstoneWADOImageLoader) { @@ -4316,7 +4350,7 @@ var JpegImage = (function jpegImage() { xhr.send(); - return deferred; + return deferred.promise(); } cornerstoneWADOImageLoader.internal.xhrRequest = xhrRequest; diff --git a/Packages/dimseservice/server/DIMSE.js b/Packages/dimseservice/server/DIMSE.js index bd266d117..fcc4671af 100755 --- a/Packages/dimseservice/server/DIMSE.js +++ b/Packages/dimseservice/server/DIMSE.js @@ -167,7 +167,7 @@ DIMSE.retrieveInstances = function(studyInstanceUID, seriesInstanceUID, params) 0x0008103E: '', 0x00200011: '', 0x00080016: '', - 0x00080018: '', + 0x00080018: '', // sopInstanceUid. This is missing from the results? 0x00200013: '', 0x00280010: '', 0x00280011: '', @@ -181,6 +181,7 @@ DIMSE.retrieveInstances = function(studyInstanceUID, seriesInstanceUID, params) var instances = []; result.on('result', function(msg) { + console.log(msg); instances.push(msg); }); diff --git a/Packages/lesiontracker/client/components/studyDateList/studyDateList.js b/Packages/lesiontracker/client/components/studyDateList/studyDateList.js index d522cb34a..30123f62f 100644 --- a/Packages/lesiontracker/client/components/studyDateList/studyDateList.js +++ b/Packages/lesiontracker/client/components/studyDateList/studyDateList.js @@ -17,7 +17,7 @@ Template.studyDateList.helpers({ patientId: currentStudyInBrowser.patientId }, { sort: { - studyDate: 1 + studyDate: -1 } }); diff --git a/Packages/viewerbase/server/methods/getStudyMetadata.js b/Packages/viewerbase/server/methods/getStudyMetadata.js index cb9beae51..4e8d2f8d8 100644 --- a/Packages/viewerbase/server/methods/getStudyMetadata.js +++ b/Packages/viewerbase/server/methods/getStudyMetadata.js @@ -10,10 +10,13 @@ Meteor.methods({ throw 'No properly configured server was available over DICOMWeb'; } - // Get the server data. This is user-defined in the - // config.json files used to run the Meteor server - var server = Meteor.settings.dicomWeb.endpoints[0]; - - return Services.WADO.RetrieveMetadata(server, studyInstanceUid); + if (Meteor.settings.dicomWeb && Meteor.settings.defaultServiceType === 'dicomWeb') { + // Get the server data. This is user-defined in the + // config.json files used to run the Meteor server + var server = Meteor.settings.dicomWeb.endpoints[0]; + return Services.WADO.RetrieveMetadata(server, studyInstanceUid); + } else if (Meteor.settings.dimse && Meteor.settings.defaultServiceType === 'dimse') { + return Services.DIMSE.RetrieveMetadata(studyInstanceUid); + } } }); diff --git a/Packages/viewerbase/server/services/dimse/instances.js b/Packages/viewerbase/server/services/dimse/instances.js index 08cd68f94..b18e72e61 100755 --- a/Packages/viewerbase/server/services/dimse/instances.js +++ b/Packages/viewerbase/server/services/dimse/instances.js @@ -37,7 +37,7 @@ function resultDataToStudyMetadata(resultData) { var serverRoot = host + ':' + port; log.info('INSTANCE'); - log.info(instance); + //log.info(instance); var sopInstanceUid = instance[0x00080018]; var uri = serverRoot + '/studies/' + studyInstanceUid + '/series/' + seriesInstanceUid + '/instances/' + sopInstanceUid + '/frames/1'; diff --git a/Packages/viewerbase/server/services/dimse/retrieveMetadata.js b/Packages/viewerbase/server/services/dimse/retrieveMetadata.js index baeb39ee4..71171908d 100755 --- a/Packages/viewerbase/server/services/dimse/retrieveMetadata.js +++ b/Packages/viewerbase/server/services/dimse/retrieveMetadata.js @@ -26,6 +26,7 @@ function getSourceImageInstanceUid(instance) { * @returns {{seriesList: Array, patientName: *, patientId: *, accessionNumber: *, studyDate: *, modalities: *, studyDescription: *, imageCount: *, studyInstanceUid: *}} */ function resultDataToStudyMetadata(studyInstanceUid, resultData) { + console.log('resultDataToStudyMetadata'); var seriesMap = {}; var seriesList = []; @@ -69,7 +70,8 @@ function resultDataToStudyMetadata(studyInstanceUid, resultData) { var sopInstanceUid = instance[0x00080018]; log.info('instance'); - log.info(instance); + //console.log('instance'); + //console.log(instance); var instanceSummary = { imageType: instance[0x00080008], sopClassUid: instance[0x00080016], @@ -99,12 +101,9 @@ function resultDataToStudyMetadata(studyInstanceUid, resultData) { frameRate: parseFloat(instance[0x00181063]) }; - var host = Meteor.settings.dimse.host, - port = Meteor.settings.dimse.port; - - var serverRoot = host + ':' + port; - - instanceSummary.wadorsuri = serverRoot + '/studies/' + studyInstanceUid + '/series/' + seriesInstanceUid + '/instances/' + sopInstanceUid + '/frames/1'; + // Retrieve the actual data over WADO-URI + var server = Meteor.settings.dicomWeb.endpoints[0]; + instanceSummary.wadouri = server.wadoUriRoot + '?requestType=WADO&studyUID=' + studyInstanceUid + '&seriesUID=' + seriesInstanceUid + '&objectUID=' + sopInstanceUid + "&contentType=application%2Fdicom"; series.instances.push(instanceSummary); }); @@ -156,7 +155,7 @@ Services.DIMSE.RetrieveMetadata = function(studyInstanceUid) { study.studyInstanceUid = studyInstanceUid; - console.log(study); - console.log(study.seriesList[0].instances[0]); + //console.log(study); + //console.log(study.seriesList[0].instances[0]); return study; }; diff --git a/config/dockerDCM4CHE.json b/config/dockerDCM4CHE.json index 01739c3c6..64c07f9b3 100644 --- a/config/dockerDCM4CHE.json +++ b/config/dockerDCM4CHE.json @@ -6,7 +6,7 @@ "wadoUriRootNOTE" : "either this uri is not correct for wado-uri or wado-uri is not configured on orthanc currently", "wadoUriRoot" : "http://192.168.99.100:8080/dcm4chee-arc/aets/DCM4CHEE/wado", "qidoRoot": "http://192.168.99.100:8080/dcm4chee-arc/aets/DCM4CHEE/rs", - "wadoRoot": "http://192.168.99.100:8080/dcm4chee-arc/aets/DCM4CHEE/wado", + "wadoRoot": "http://192.168.99.100:8080/dcm4chee-arc/aets/DCM4CHEE/rs/wado/DCM4CHEE", "qidoSupportsIncludeField": true, "imageRendering" : "wadouri", "requestOptions" : {