Integrate web workers and the latest cornerstone libraries

- Upgrade cornerstone to v0.10.1
- Upgrade cornerstoneTools to v0.8.1
- Upgrade cornerstoneWADOImageLoader to v0.14.1
- Upgrade dicomParser to v1.7.4
- Upgrade hammerjs to v2.0.8
This commit is contained in:
Evren Ozkan 2017-02-13 15:49:02 -05:00
parent 8fa437b2c2
commit 33f9b8672b
12 changed files with 8565 additions and 7785 deletions

View File

@ -0,0 +1,21 @@
import { Meteor } from 'meteor/meteor';
import { cornerstoneWADOImageLoader } from 'meteor/ohif:cornerstone';
Meteor.startup(function() {
const maxWebWorkers = Math.max(navigator.hardwareConcurrency - 1, 1);
const config = {
maxWebWorkers: maxWebWorkers,
startWebWorkersOnDemand: true,
webWorkerPath : Meteor.absoluteUrl('packages/ohif_cornerstone/public/js/cornerstoneWADOImageLoaderWebWorker.es5.js'),
taskConfiguration: {
'decodeTask' : {
loadCodecsOnStartup : true,
initializeCodecsOnStartup: false,
codecsPath: Meteor.absoluteUrl('packages/ohif_cornerstone/public/js/cornerstoneWADOImageLoaderCodecs.es5.js'),
usePDFJS: false
}
}
};
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
});

View File

@ -0,0 +1,21 @@
import { Meteor } from 'meteor/meteor';
import { cornerstoneWADOImageLoader } from 'meteor/ohif:cornerstone';
Meteor.startup(function() {
const maxWebWorkers = Math.max(navigator.hardwareConcurrency - 1, 1);
const config = {
maxWebWorkers: maxWebWorkers,
startWebWorkersOnDemand: true,
webWorkerPath : Meteor.absoluteUrl('packages/ohif_cornerstone/public/js/cornerstoneWADOImageLoaderWebWorker.es5.js'),
taskConfiguration: {
'decodeTask' : {
loadCodecsOnStartup : true,
initializeCodecsOnStartup: false,
codecsPath: Meteor.absoluteUrl('packages/ohif_cornerstone/public/js/cornerstoneWADOImageLoaderCodecs.es5.js'),
usePDFJS: false
}
}
};
cornerstoneWADOImageLoader.webWorkerManager.initialize(config);
});

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/*! cornerstoneTools - v0.7.9 - 2016-10-29 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneTools */
/*! cornerstoneTools - v0.8.1 - 2017-02-11 | (c) 2014 Chris Hafey | https://github.com/chafey/cornerstoneTools */
// Begin Source: src/header.js
if (typeof cornerstone === 'undefined') {
cornerstone = {};
@ -22,70 +22,81 @@ if (typeof cornerstoneTools === 'undefined') {
'use strict';
var scrollTimeout;
var scrollTimeoutDelay = 1;
function mouseWheel(e) {
clearTimeout(scrollTimeout);
// !!!HACK/NOTE/WARNING!!!
// for some reason I am getting mousewheel and DOMMouseScroll events on my
// mac os x mavericks system when middle mouse button dragging.
// I couldn't find any info about this so this might break other systems
// webkit hack
if (e.originalEvent.type === 'mousewheel' && e.originalEvent.wheelDeltaY === 0) {
return;
}
// firefox hack
if (e.originalEvent.type === 'DOMMouseScroll' && e.originalEvent.axis === 1) {
return;
}
scrollTimeout = setTimeout(function() {
var element = e.target.parentNode;
var element = e.currentTarget;
if (!e.deltaY) {
return;
}
var x;
var y;
var x;
var y;
if (e.pageX !== undefined && e.pageY !== undefined) {
x = e.pageX;
y = e.pageY;
}
if (e.pageX !== undefined && e.pageY !== undefined) {
x = e.pageX;
y = e.pageY;
} else if (e.originalEvent &&
e.originalEvent.pageX !== undefined &&
e.originalEvent.pageY !== undefined) {
x = e.originalEvent.pageX;
y = e.originalEvent.pageY;
} else {
// IE9 & IE10
x = e.x;
y = e.y;
}
var startingCoords = cornerstone.pageToPixel(element, x, y);
var startingCoords = cornerstone.pageToPixel(element, x, y);
var wheelDeltaPixels;
var pixelsPerLine = 40;
var pixelsPerPage = 800;
e = window.event || e; // old IE support
if (e.deltaMode === 2) {
// DeltaY is in Pages
wheelDeltaPixels = e.deltaY * pixelsPerPage;
} else if (e.deltaMode === 1) {
// DeltaY is in Lines
wheelDeltaPixels = e.deltaY * pixelsPerLine;
} else {
// DeltaY is already in Pixels
wheelDeltaPixels = e.deltaY;
}
var wheelDelta;
if (e.originalEvent && e.originalEvent.wheelDelta) {
wheelDelta = -e.originalEvent.wheelDelta;
} else if (e.originalEvent && e.originalEvent.deltaY) {
wheelDelta = -e.originalEvent.deltaY;
} else if (e.originalEvent && e.originalEvent.detail) {
wheelDelta = -e.originalEvent.detail;
} else {
wheelDelta = e.wheelDelta;
}
var direction = e.deltaY < 0 ? -1 : 1;
var direction = wheelDelta < 0 ? -1 : 1;
var mouseWheelData = {
element: element,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
direction: direction,
wheelDeltaPixels: wheelDeltaPixels,
pageX: x,
pageY: y,
imageX: startingCoords.x,
imageY: startingCoords.y
};
var mouseWheelData = {
element: element,
viewport: cornerstone.getViewport(element),
image: cornerstone.getEnabledElement(element).image,
direction: direction,
pageX: x,
pageY: y,
imageX: startingCoords.x,
imageY: startingCoords.y
};
$(element).trigger('CornerstoneToolsMouseWheel', mouseWheelData);
}, scrollTimeoutDelay);
$(element).trigger('CornerstoneToolsMouseWheel', mouseWheelData);
}
var mouseWheelEvents = 'mousewheel DOMMouseScroll';
function enable(element) {
// Prevent handlers from being attached multiple times
disable(element);
cornerstoneTools.addWheelListener(element, mouseWheel);
$(element).on(mouseWheelEvents, mouseWheel);
}
function disable(element) {
cornerstoneTools.removeWheelListener(element, mouseWheel);
$(element).unbind(mouseWheelEvents, mouseWheel);
}
// module exports
@ -1440,8 +1451,6 @@ if (typeof cornerstoneTools === 'undefined') {
'use strict';
function mouseWheelTool(mouseWheelCallback) {
var configuration = {};
var toolInterface = {
activate: function(element) {
$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);
@ -1451,9 +1460,7 @@ if (typeof cornerstoneTools === 'undefined') {
},
disable: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);},
enable: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);},
deactivate: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);},
getConfiguration: function() { return configuration;},
setConfiguration: function(config) {configuration = config;}
deactivate: function(element) {$(element).off('CornerstoneToolsMouseWheel', mouseWheelCallback);}
};
return toolInterface;
}
@ -4747,8 +4754,18 @@ if (typeof cornerstoneTools === 'undefined') {
}
function dragCallback(e, eventData) {
eventData.viewport.translation.x += (eventData.deltaPoints.page.x / eventData.viewport.scale);
eventData.viewport.translation.y += (eventData.deltaPoints.page.y / eventData.viewport.scale);
// FIXME: Copied from Cornerstone src/internal/calculateTransform.js, should be exposed from there.
var widthScale = eventData.viewport.scale;
var heightScale = eventData.viewport.scale;
if (eventData.image.rowPixelSpacing < eventData.image.columnPixelSpacing) {
widthScale = widthScale * (eventData.image.columnPixelSpacing / eventData.image.rowPixelSpacing);
} else if (eventData.image.columnPixelSpacing < eventData.image.rowPixelSpacing) {
heightScale = heightScale * (eventData.image.rowPixelSpacing / eventData.image.columnPixelSpacing);
}
eventData.viewport.translation.x += (eventData.deltaPoints.page.x / widthScale);
eventData.viewport.translation.y += (eventData.deltaPoints.page.y / heightScale);
cornerstone.setViewport(eventData.element, eventData.viewport);
return false; // false = causes jquery to preventDefault() and stopPropagation() this event
}
@ -4926,6 +4943,7 @@ if (typeof cornerstoneTools === 'undefined') {
var measurementData = {
visible: true,
active: true,
invalidated: true,
handles: {
start: {
x: mouseEventData.currentPoints.image.x,
@ -4938,6 +4956,14 @@ if (typeof cornerstoneTools === 'undefined') {
y: mouseEventData.currentPoints.image.y,
highlight: true,
active: true
},
textBox: {
active: false,
hasMoved: false,
movesIndependently: false,
drawnIndependently: true,
allowedOutsideImage: true,
hasBoundingBox: true
}
}
};
@ -5000,25 +5026,35 @@ if (typeof cornerstoneTools === 'undefined') {
};
}
function onImageRendered(e, eventData) {
function numberWithCommas(x) {
// http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
var parts = x.toString().split('.');
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return parts.join('.');
}
function onImageRendered(e, eventData) {
// if we have no toolData for this element, return immediately as there is nothing to do
var toolData = cornerstoneTools.getToolState(e.currentTarget, toolType);
if (toolData === undefined) {
if (!toolData) {
return;
}
// we have tool data for this element - iterate over each one and draw it
var image = eventData.image;
var element = eventData.element;
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
var config = cornerstoneTools.rectangleRoi.getConfiguration();
var context = eventData.canvasContext.canvas.getContext('2d');
context.setTransform(1, 0, 0, 1, 0, 0);
//activation color
var color;
var lineWidth = cornerstoneTools.toolStyle.getToolWidth();
var font = cornerstoneTools.textStyle.getFont();
var fontHeight = cornerstoneTools.textStyle.getFontSize();
var config = cornerstoneTools.rectangleRoi.getConfiguration();
// Retrieve the image modality from its metadata, if available
var modalityTag = 'x00080060';
var modality;
if (image.data) {
modality = image.data.string(modalityTag);
}
// If we have tool data for this element - iterate over each set and draw it
for (var i = 0; i < toolData.data.length; i++) {
context.save();
@ -5031,65 +5067,261 @@ if (typeof cornerstoneTools === 'undefined') {
context.shadowOffsetY = config.shadowOffsetY || 1;
}
//differentiate the color of activation tool
if (data.active) {
color = cornerstoneTools.toolColors.getActiveColor();
} else {
color = cornerstoneTools.toolColors.getToolColor();
}
// Check which color the rendered tool should be
var color = cornerstoneTools.toolColors.getColorIfActive(data.active);
// draw the rectangle
var handleStartCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.start);
var handleEndCanvas = cornerstone.pixelToCanvas(eventData.element, data.handles.end);
// Convert Image coordinates to Canvas coordinates given the element
var handleStartCanvas = cornerstone.pixelToCanvas(element, data.handles.start);
var handleEndCanvas = cornerstone.pixelToCanvas(element, data.handles.end);
var widthCanvas = Math.abs(handleStartCanvas.x - handleEndCanvas.x);
var heightCanvas = Math.abs(handleStartCanvas.y - handleEndCanvas.y);
// Retrieve the bounds of the ellipse (left, top, width, and height)
// in Canvas coordinates
var leftCanvas = Math.min(handleStartCanvas.x, handleEndCanvas.x);
var topCanvas = Math.min(handleStartCanvas.y, handleEndCanvas.y);
var centerX = (handleStartCanvas.x + handleEndCanvas.x) / 2;
var centerY = (handleStartCanvas.y + handleEndCanvas.y) / 2;
var widthCanvas = Math.abs(handleStartCanvas.x - handleEndCanvas.x);
var heightCanvas = Math.abs(handleStartCanvas.y - handleEndCanvas.y);
// Draw the rectangle on the canvas
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.rect(leftCanvas, topCanvas, widthCanvas, heightCanvas);
context.stroke();
// draw the handles
cornerstoneTools.drawHandles(context, eventData, data.handles, color);
// If the tool configuration specifies to only draw the handles on hover / active,
// follow this logic
if (config && config.drawHandlesOnHover) {
// Draw the handles if the tool is active
if (data.active === true) {
cornerstoneTools.drawHandles(context, eventData, data.handles, color);
} else {
// If the tool is inactive, draw the handles only if each specific handle is being
// hovered over
var handleOptions = {
drawHandlesIfActive: true
};
cornerstoneTools.drawHandles(context, eventData, data.handles, color, handleOptions);
}
} else {
// If the tool has no configuration settings, always draw the handles
cornerstoneTools.drawHandles(context, eventData, data.handles, color);
}
// Calculate the mean, stddev, and area
// TODO: calculate this in web worker for large pixel counts...
// Define variables for the area and mean/standard deviation
var area,
meanStdDev,
meanStdDevSUV;
var width = Math.abs(data.handles.start.x - data.handles.end.x);
var height = Math.abs(data.handles.start.y - data.handles.end.y);
var left = Math.min(data.handles.start.x, data.handles.end.x);
var top = Math.min(data.handles.start.y, data.handles.end.y);
var pixels = cornerstone.getPixels(eventData.element, left, top, width, height);
// Perform a check to see if the tool has been invalidated. This is to prevent
// unnecessary re-calculation of the area, mean, and standard deviation if the
// image is re-rendered but the tool has not moved (e.g. during a zoom)
if (!data.invalidated) {
// If the data is not invalidated, retrieve it from the toolData
meanStdDev = data.meanStdDev;
meanStdDevSUV = data.meanStdDevSUV;
area = data.area;
} else {
// If the data has been invalidated, we need to calculate it again
var ellipse = {
left: left,
top: top,
width: width,
height: height
// Retrieve the bounds of the ellipse in image coordinates
var ellipse = {
left: Math.min(data.handles.start.x, data.handles.end.x),
top: Math.min(data.handles.start.y, data.handles.end.y),
width: Math.abs(data.handles.start.x - data.handles.end.x),
height: Math.abs(data.handles.start.y - data.handles.end.y)
};
// First, make sure this is not a color image, since no mean / standard
// deviation will be calculated for color images.
if (!image.color) {
// Retrieve the array of pixels that the ellipse bounds cover
var pixels = cornerstone.getPixels(element, ellipse.left, ellipse.top, ellipse.width, ellipse.height);
// Calculate the mean & standard deviation from the pixels and the ellipse details
meanStdDev = calculateMeanStdDev(pixels, ellipse);
if (modality === 'PT') {
// If the image is from a PET scan, use the DICOM tags to
// calculate the SUV from the mean and standard deviation.
// Note that because we are using modality pixel values from getPixels, and
// the calculateSUV routine also rescales to modality pixel values, we are first
// returning the values to storedPixel values before calcuating SUV with them.
// TODO: Clean this up? Should we add an option to not scale in calculateSUV?
meanStdDevSUV = {
mean: cornerstoneTools.calculateSUV(image, (meanStdDev.mean - image.intercept) / image.slope),
stdDev: cornerstoneTools.calculateSUV(image, (meanStdDev.stdDev - image.intercept) / image.slope)
};
}
// If the mean and standard deviation values are sane, store them for later retrieval
if (meanStdDev && !isNaN(meanStdDev.mean)) {
data.meanStdDev = meanStdDev;
data.meanStdDevSUV = meanStdDevSUV;
}
}
// Retrieve the pixel spacing values, and if they are not
// real non-zero values, set them to 1
var columnPixelSpacing = image.columnPixelSpacing || 1;
var rowPixelSpacing = image.rowPixelSpacing || 1;
// Calculate the image area from the ellipse dimensions and pixel spacing
area = (ellipse.width * columnPixelSpacing) * (ellipse.height * rowPixelSpacing);
// If the area value is sane, store it for later retrieval
if (!isNaN(area)) {
data.area = area;
}
// Set the invalidated flag to false so that this data won't automatically be recalculated
data.invalidated = false;
}
// Define an array to store the rows of text for the textbox
var textLines = [];
// If the mean and standard deviation values are present, display them
if (meanStdDev && meanStdDev.mean) {
// If the modality is CT, add HU to denote Hounsfield Units
var moSuffix = '';
if (modality === 'CT') {
moSuffix = ' HU';
}
// Create a line of text to display the mean and any units that were specified (i.e. HU)
var meanText = 'Mean: ' + numberWithCommas(meanStdDev.mean.toFixed(2)) + moSuffix;
// Create a line of text to display the standard deviation and any units that were specified (i.e. HU)
var stdDevText = 'StdDev: ' + numberWithCommas(meanStdDev.stdDev.toFixed(2)) + moSuffix;
// If this image has SUV values to display, concatenate them to the text line
if (meanStdDevSUV && meanStdDevSUV.mean !== undefined) {
var SUVtext = ' SUV: ';
meanText += SUVtext + numberWithCommas(meanStdDevSUV.mean.toFixed(2));
stdDevText += SUVtext + numberWithCommas(meanStdDevSUV.stdDev.toFixed(2));
}
// Add these text lines to the array to be displayed in the textbox
textLines.push(meanText);
textLines.push(stdDevText);
}
// If the area is a sane value, display it
if (area) {
// Determine the area suffix based on the pixel spacing in the image.
// If pixel spacing is present, use millimeters. Otherwise, use pixels.
// This uses Char code 178 for a superscript 2
var suffix = ' mm' + String.fromCharCode(178);
if (!image.rowPixelSpacing || !image.columnPixelSpacing) {
suffix = ' pixels' + String.fromCharCode(178);
}
// Create a line of text to display the area and its units
var areaText = 'Area: ' + numberWithCommas(area.toFixed(2)) + suffix;
// Add this text line to the array to be displayed in the textbox
textLines.push(areaText);
}
// If the textbox has not been moved by the user, it should be displayed on the right-most
// side of the tool.
if (!data.handles.textBox.hasMoved) {
// Find the rightmost side of the ellipse at its vertical center, and place the textbox here
// Note that this calculates it in image coordinates
data.handles.textBox.x = Math.max(data.handles.start.x, data.handles.end.x);
data.handles.textBox.y = (data.handles.start.y + data.handles.end.y) / 2;
}
// Convert the textbox Image coordinates into Canvas coordinates
var textCoords = cornerstone.pixelToCanvas(element, data.handles.textBox);
// Set options for the textbox drawing function
var options = {
centering: {
x: false,
y: true
}
};
var meanStdDev = calculateMeanStdDev(pixels, ellipse);
var area = (width * eventData.image.columnPixelSpacing) * (height * eventData.image.rowPixelSpacing);
var areaText = 'Area: ' + area.toFixed(2) + ' mm^2';
// Draw the textbox and retrieves it's bounding box for mouse-dragging and highlighting
var boundingBox = cornerstoneTools.drawTextBox(context, textLines, textCoords.x,
textCoords.y, color, options);
// Draw text
context.font = font;
// Store the bounding box data in the handle for mouse-dragging and highlighting
data.handles.textBox.boundingBox = boundingBox;
var textSize = context.measureText(area);
// If the textbox has moved, we would like to draw a line linking it with the tool
// This section decides where to draw this line to on the Ellipse based on the location
// of the textbox relative to the ellipse.
if (data.handles.textBox.hasMoved) {
// Draw dashed link line between tool and text
var textX = centerX < (eventData.image.columns / 2) ? centerX + (widthCanvas / 2): centerX - (widthCanvas / 2) - textSize.width;
var textY = centerY < (eventData.image.rows / 2) ? centerY + (heightCanvas / 2): centerY - (heightCanvas / 2);
// The initial link position is at the center of the
// textbox.
var link = {
start: {},
end: {
x: textCoords.x,
y: textCoords.y
}
};
// First we calculate the ellipse points (top, left, right, and bottom)
var ellipsePoints = [ {
// Top middle point of ellipse
x: leftCanvas + widthCanvas / 2,
y: topCanvas
}, {
// Left middle point of ellipse
x: leftCanvas,
y: topCanvas + heightCanvas / 2
}, {
// Bottom middle point of ellipse
x: leftCanvas + widthCanvas / 2,
y: topCanvas + heightCanvas
}, {
// Right middle point of ellipse
x: leftCanvas + widthCanvas,
y: topCanvas + heightCanvas / 2
} ];
// We obtain the link starting point by finding the closest point on the ellipse to the
// center of the textbox
link.start = cornerstoneMath.point.findClosestPoint(ellipsePoints, link.end);
// Next we calculate the corners of the textbox bounding box
var boundingBoxPoints = [ {
// Top middle point of bounding box
x: boundingBox.left + boundingBox.width / 2,
y: boundingBox.top
}, {
// Left middle point of bounding box
x: boundingBox.left,
y: boundingBox.top + boundingBox.height / 2
}, {
// Bottom middle point of bounding box
x: boundingBox.left + boundingBox.width / 2,
y: boundingBox.top + boundingBox.height
}, {
// Right middle point of bounding box
x: boundingBox.left + boundingBox.width,
y: boundingBox.top + boundingBox.height / 2
}, ];
// Now we recalculate the link endpoint by identifying which corner of the bounding box
// is closest to the start point we just calculated.
link.end = cornerstoneMath.point.findClosestPoint(boundingBoxPoints, link.start);
// Finally we draw the dashed linking line
context.beginPath();
context.strokeStyle = color;
context.lineWidth = lineWidth;
context.setLineDash([ 2, 3 ]);
context.moveTo(link.start.x, link.start.y);
context.lineTo(link.end.x, link.end.y);
context.stroke();
}
context.fillStyle = color;
cornerstoneTools.drawTextBox(context, 'Mean: ' + meanStdDev.mean.toFixed(2), textX, textY - fontHeight - 5, color);
cornerstoneTools.drawTextBox(context, 'StdDev: ' + meanStdDev.stdDev.toFixed(2), textX, textY, color);
cornerstoneTools.drawTextBox(context, areaText, textX, textY + fontHeight + 5, color);
context.restore();
}
}
@ -8025,43 +8257,8 @@ if (typeof cornerstoneTools === 'undefined') {
'use strict';
// this module defines a way for tools to access various metadata about an imageId. This layer of abstraction exists
// so metadata can be provided to the tools in different ways (e.g. by parsing DICOM P10 or by a WADO-RS document)
// NOTE: We may want to push this function down into the cornerstone core library, not sure yet...
var providers = [];
function addProvider( provider) {
providers.push(provider);
}
function removeProvider( provider) {
var index = providers.indexOf(provider);
if (index === -1) {
return;
}
providers.splice(index, 1);
}
function getMetaData(type, imageId) {
var result;
$.each(providers, function(index, provider) {
result = provider(type, imageId);
if (result !== undefined) {
return true;
}
});
return result;
}
// module/private exports
cornerstoneTools.metaData = {
addProvider: addProvider,
removeProvider: removeProvider,
get: getMetaData
};
cornerstoneTools.metaData = cornerstone.metaData;
})($, cornerstone, cornerstoneTools);
// End Source; src/metaData.js
@ -8379,12 +8576,7 @@ if (typeof cornerstoneTools === 'undefined') {
}
setTimeout(function() {
var requestDetails = getNextRequest();
if (!requestDetails) {
return;
}
sendRequest(requestDetails);
startGrabbing();
}, grabDelay);
}
@ -8418,11 +8610,29 @@ if (typeof cornerstoneTools === 'undefined') {
return;
}
function requestTypeToLoadPriority(requestDetails) {
if (requestDetails.type === 'prefetch') {
return -5;
} else if (requestDetails.type === 'interactive') {
return 0;
} else if (requestDetails.type === 'thumbnail') {
return 5;
}
}
var priority = requestTypeToLoadPriority(requestDetails);
var loader;
if (requestDetails.preventCache === true) {
loader = cornerstone.loadImage(imageId);
loader = cornerstone.loadImage(imageId, {
priority: priority,
type: requestDetails.type
});
} else {
loader = cornerstone.loadAndCacheImage(imageId);
loader = cornerstone.loadAndCacheImage(imageId, {
priority: priority,
type: requestDetails.type
});
}
// Load and cache the image
@ -8441,10 +8651,6 @@ if (typeof cornerstoneTools === 'undefined') {
function startGrabbing() {
// Begin by grabbing X images
if (awake) {
return;
}
var maxSimultaneousRequests = cornerstoneTools.getMaxSimultaneousRequests();
maxNumRequests = {
@ -8453,7 +8659,11 @@ if (typeof cornerstoneTools === 'undefined') {
prefetch: Math.max(maxSimultaneousRequests - 1, 1)
};
for (var i = 0; i < maxSimultaneousRequests; i++) {
var currentRequests = numRequests.interaction +
numRequests.thumbnail +
numRequests.prefetch;
var requestsToSend = maxSimultaneousRequests - currentRequests;
for (var i = 0; i < requestsToSend; i++) {
var requestDetails = getNextRequest();
if (requestDetails) {
sendRequest(requestDetails);
@ -8887,14 +9097,8 @@ Display scroll progress bar across bottom of image.
// Stop prefetching if the ImageCacheFull event is fired from cornerstone
// console.log('CornerstoneImageCacheFull full, stopping');
var element = e.data.element;
var stackPrefetchData;
try {
stackPrefetchData = cornerstoneTools.getToolState(element, toolType);
} catch(error) {
return;
}
var stackPrefetchData = cornerstoneTools.getToolState(element, toolType);
if (!stackPrefetchData || !stackPrefetchData.data || !stackPrefetchData.data.length) {
return;
}
@ -8911,15 +9115,7 @@ Display scroll progress bar across bottom of image.
// it to the indicesToRequest list so that it will be retrieved later if the
// currentImageIdIndex is changed to an image nearby
var element = e.data.element;
var stackData;
try {
// It will throw an exception in some cases (eg: thumbnails)
stackData = cornerstoneTools.getToolState(element, 'stack');
} catch(error) {
return;
}
var stackData = cornerstoneTools.getToolState(element, 'stack');
if (!stackData || !stackData.data || !stackData.data.length) {
return;
}
@ -9069,9 +9265,7 @@ Display scroll progress bar across bottom of image.
}
function mouseWheelCallback(e, eventData) {
var config = cornerstoneTools.stackScroll.getConfiguration();
var pixelsPerImage = config.wheelDeltaPixelsPerImage || 100;
var images = eventData.direction * Math.max(1, Math.round(Math.abs(eventData.wheelDeltaPixels) / pixelsPerImage));
var images = -eventData.direction;
cornerstoneTools.scroll(eventData.element, images);
}
@ -9110,19 +9304,6 @@ Display scroll progress bar across bottom of image.
cornerstoneTools.stackScroll = cornerstoneTools.simpleMouseButtonTool(mouseDownCallback);
cornerstoneTools.stackScrollWheel = cornerstoneTools.mouseWheelTool(mouseWheelCallback);
var stackScrollWheelConfig = {
// Smaller numbers lead to faster scrolling
// 100 is the default here because in my empirical tests,
// a single tick of my mouse produces a value of 100 pixels
// on Firefox on Windows. This is the largest I noticed in my
// tests. In some cases (e.g. >500 image stacks), the user
// may want to speed up stack scrolling. Lowering this value
// can do this.
wheelDeltaPixelsPerImage: 100
};
cornerstoneTools.stackScrollWheel.setConfiguration(stackScrollWheelConfig);
var options = {
eventData: {
deltaY: 0
@ -10837,9 +11018,10 @@ Display scroll progress bar across bottom of image.
var toolType = 'timeSeriesPlayer';
/**
* Starts playing a clip or adjusts the frame rate of an already playing clip. framesPerSecond is
* optional and defaults to 30 if not specified. A negative framesPerSecond will play the clip in reverse.
* The element must be a stack of images
* Starts playing a clip of different time series of the same image or adjusts the frame rate of an
* already playing clip. framesPerSecond is optional and defaults to 30 if not specified. A negative
* framesPerSecond will play the clip in reverse.
* The element must have time series
* @param element
* @param framesPerSecond
*/
@ -11403,6 +11585,10 @@ Display scroll progress bar across bottom of image.
return config.maxSimultaneousRequests;
}
return getDefaultSimultaneousRequests();
}
function getDefaultSimultaneousRequests() {
var infoString = getBrowserInfo();
var info = infoString.split(' ');
var browserName = info[0];
@ -11426,6 +11612,7 @@ Display scroll progress bar across bottom of image.
}
// module exports
cornerstoneTools.getDefaultSimultaneousRequests = getDefaultSimultaneousRequests;
cornerstoneTools.getMaxSimultaneousRequests = getMaxSimultaneousRequests;
cornerstoneTools.getBrowserInfo = getBrowserInfo;
cornerstoneTools.isMobileDevice = isMobileDevice;
@ -11730,14 +11917,6 @@ Display scroll progress bar across bottom of image.
'use strict';
function isNumber(value) {
return typeof value === 'number' && !isNaN(value);
}
function isInteger(number) {
return number % 1 === 0;
}
function scrollToIndex(element, newImageIdIndex) {
var toolData = cornerstoneTools.getToolState(element, 'stack');
if (!toolData || !toolData.data || !toolData.data.length) {
@ -11746,12 +11925,6 @@ Display scroll progress bar across bottom of image.
var stackData = toolData.data[0];
if (!isNumber(newImageIdIndex)) {
throw 'scrollToIndex: index provided is not numeric: ' + newImageIdIndex;
} else if (isNumber(newImageIdIndex) && !isInteger(newImageIdIndex)) {
throw 'scrollToIndex: index provided is not an integer: ' + newImageIdIndex;
}
// Allow for negative indexing
if (newImageIdIndex < 0) {
newImageIdIndex += stackData.imageIds.length;
@ -11815,16 +11988,19 @@ Display scroll progress bar across bottom of image.
}
}
var requestPoolManager = cornerstoneTools.requestPoolManager;
var type = 'interaction';
requestPoolManager.clearRequestStack(type);
// Convert the preventCache value in stack data to a boolean
var preventCache = !!stackData.preventCache;
requestPoolManager.addRequest(element, newImageId, type, preventCache, doneCallback, failCallback);
requestPoolManager.startGrabbing();
var imagePromise;
if (preventCache) {
imagePromise = cornerstone.loadImage(newImageId);
} else {
imagePromise = cornerstone.loadAndCacheImage(newImageId);
}
imagePromise.then(doneCallback, failCallback);
// Make sure we kick off any changed download request pools
cornerstoneTools.requestPoolManager.startGrabbing();
$(element).trigger('CornerstoneStackScroll', eventData);
}
@ -11877,127 +12053,3 @@ Display scroll progress bar across bottom of image.
})(cornerstone, cornerstoneTools);
// End Source; src/util/setContextToDisplayFontSize.js
// Begin Source: src/util/wheelListeners.js
(function(cornerstoneTools) {
'use strict';
// Thanks to Andrei Kashcha (@anvaka)
// https://github.com/anvaka/wheel/blob/master/index.js
/**
* This module unifies handling of mouse whee event across different browsers
*
* See https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel?redirectlocale=en-US&redirectslug=DOM%2FMozilla_event_reference%2Fwheel
* for more details
*
* Usage:
* var addWheelListener = require('wheel').addWheelListener;
* var removeWheelListener = require('wheel').removeWheelListener;
* addWheelListener(domElement, function (e) {
* // mouse wheel event
* });
* removeWheelListener(domElement, function);
*/
// by default we shortcut to 'addEventListener':
// creates a global "addWheelListener" method
// example: addWheelListener( elem, function( e ) { console.log( e.deltaY ); e.preventDefault(); } );
var prefix = '',
_addEventListener,
_removeEventListener,
support;
function detectEventModel(window, document) {
if (window && window.addEventListener) {
_addEventListener = 'addEventListener';
_removeEventListener = 'removeEventListener';
} else {
_addEventListener = 'attachEvent';
_removeEventListener = 'detachEvent';
prefix = 'on';
}
if (document) {
// detect available wheel event
support = 'onwheel' in document.createElement('div') ? 'wheel' : // Modern browsers support "wheel"
document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least "mousewheel"
'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox
} else {
support = 'wheel';
}
}
function addWheelListener(elem, callback, useCapture) {
detectEventModel(window, document);
_addWheelListener(elem, support, callback, useCapture);
// handle MozMousePixelScroll in older Firefox
if (support === 'DOMMouseScroll') {
_addWheelListener(elem, 'MozMousePixelScroll', callback, useCapture);
}
}
function removeWheelListener(elem, callback, useCapture) {
detectEventModel(window, document);
_removeWheelListener(elem, support, callback, useCapture);
// handle MozMousePixelScroll in older Firefox
if (support === 'DOMMouseScroll') {
_removeWheelListener(elem, 'MozMousePixelScroll', callback, useCapture);
}
}
function _removeWheelListener(elem, eventName, callback, useCapture) {
elem[_removeEventListener](prefix + eventName, callback, useCapture || false);
}
function _addWheelListener(elem, eventName, callback, useCapture) {
elem[_addEventListener](prefix + eventName, support === 'wheel' ? callback : function(originalEvent) {
if (!originalEvent) {
originalEvent = window.event;
}
// create a normalized event object
var event = {
// keep a ref to the original event object
originalEvent: originalEvent,
target: originalEvent.target || originalEvent.srcElement,
type: 'wheel',
deltaMode: originalEvent.type === 'MozMousePixelScroll' ? 0 : 1,
deltaX: 0,
deltaY: 0,
deltaZ: 0,
preventDefault: function() {
return originalEvent.preventDefault ? originalEvent.preventDefault() : false;
}
};
// calculate deltaY (and deltaX) according to the event
if (support === 'mousewheel') {
event.deltaY = -1 / 40 * originalEvent.wheelDelta;
// Webkit also support wheelDeltaX
if (originalEvent.wheelDeltaX) {
event.deltaX = -1 / 40 * originalEvent.wheelDeltaX;
}
} else {
event.deltaY = originalEvent.detail;
}
// it's time to fire the callback
return callback(event);
}, useCapture || false);
}
// Module exports
cornerstoneTools.addWheelListener = addWheelListener;
cornerstoneTools.removeWheelListener = removeWheelListener;
})(cornerstoneTools);
// End Source; src/util/wheelListeners.js

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/*! dicom-parser - v1.7.1 - 2016-06-09 | (c) 2014 Chris Hafey | https://github.com/chafey/dicomParser */
/*! dicom-parser - v1.7.4 - 2016-08-18 | (c) 2014 Chris Hafey | https://github.com/chafey/dicomParser */
(function (root, factory) {
// node.js
@ -194,20 +194,6 @@ var dicomParser = (function (dicomParser)
dataSet.byteArray[position + 1] === 0xD9);
}
// Each JPEG image has a start of image marker
function isStartOfImageMarker(dataSet, position) {
return (dataSet.byteArray[position] === 0xFF &&
dataSet.byteArray[position + 1] === 0xD8);
}
function isFragmentStartOfImage(dataSet, pixelDataElement, fragmentIndex) {
var fragment = pixelDataElement.fragments[fragmentIndex];
if(isStartOfImageMarker(dataSet, fragment.position)) {
return true;
}
return false;
}
function isFragmentEndOfImage(dataSet, pixelDataElement, fragmentIndex) {
var fragment = pixelDataElement.fragments[fragmentIndex];
// Need to check the last two bytes and the last three bytes for marker since odd length
@ -222,12 +208,7 @@ var dicomParser = (function (dicomParser)
function findLastImageFrameFragmentIndex(dataSet, pixelDataElement, startFragment) {
for(var fragmentIndex=startFragment; fragmentIndex < pixelDataElement.fragments.length; fragmentIndex++) {
if(isFragmentEndOfImage(dataSet, pixelDataElement, fragmentIndex)) {
// if not last fragment, peek ahead to make sure the next fragment has a start of image marker just to
// be safe
if(fragmentIndex === pixelDataElement.fragments.length - 1 ||
isFragmentStartOfImage(dataSet, pixelDataElement, fragmentIndex+1)) {
return fragmentIndex;
}
return fragmentIndex;
}
}
}
@ -275,10 +256,6 @@ var dicomParser = (function (dicomParser)
var basicOffsetTable = [];
var startFragmentIndex = 0;
// sanity check first fragment has a JPEG start of image marker
if(!isFragmentStartOfImage(dataSet, pixelDataElement, startFragmentIndex)) {
throw 'first fragment does not have JPEG start of image marker';
}
while(true) {
// Add the offset for the start fragment
@ -1417,6 +1394,65 @@ var dicomParser = (function (dicomParser)
* Internal helper functions for parsing DICOM elements
*/
var dicomParser = (function (dicomParser)
{
"use strict";
if(dicomParser === undefined)
{
dicomParser = {};
}
/**
* reads from the byte stream until it finds the magic number for the Sequence Delimitation Item item
* and then sets the length of the element
* @param byteStream
* @param element
*/
dicomParser.findAndSetUNElementLength = function(byteStream, element)
{
if(byteStream === undefined)
{
throw "dicomParser.findAndSetUNElementLength: missing required parameter 'byteStream'";
}
var itemDelimitationItemLength = 8; // group, element, length
var maxPosition = byteStream.byteArray.length - itemDelimitationItemLength;
while(byteStream.position <= maxPosition)
{
var groupNumber;
groupNumber = byteStream.readUint16();
if(groupNumber === 0xfffe)
{
var elementNumber;
elementNumber = byteStream.readUint16();
if(elementNumber === 0xe0dd)
{
// NOTE: It would be better to also check for the length to be 0 as part of the check above
// but we will just log a warning for now
var itemDelimiterLength;
itemDelimiterLength = byteStream.readUint32(); // the length
if(itemDelimiterLength !== 0) {
byteStream.warnings('encountered non zero length following item delimiter at position' + byteStream.position - 4 + " while reading element of undefined length with tag ' + element.tag");
}
element.length = byteStream.position - element.dataOffset;
return;
}
}
}
// No item delimitation item - silently set the length to the end of the buffer and set the position past the end of the buffer
element.length = byteStream.byteArray.length - element.dataOffset;
byteStream.seek(byteStream.byteArray.length - byteStream.position);
};
return dicomParser;
}(dicomParser));
/**
* Internal helper functions for parsing DICOM elements
*/
var dicomParser = (function (dicomParser)
{
"use strict";
@ -1891,11 +1927,16 @@ var dicomParser = (function (dicomParser)
dicomParser.readSequenceItemsExplicit(byteStream, element, warnings);
return element;
}
if(element.length === 4294967295)
{
if(element.tag === 'x7fe00010') {
dicomParser.findEndOfEncapsulatedElement(byteStream, element, warnings);
return element;
} else if(element.vr === 'UN') {
dicomParser.findAndSetUNElementLength(byteStream, element);
return element;
} else {
dicomParser.findItemDelimitationItemAndSetElementLength(byteStream, element);
return element;
@ -2633,7 +2674,7 @@ var dicomParser = (function (dicomParser)
dicomParser = {};
}
dicomParser.version = "1.7.1";
dicomParser.version = "1.7.3";
return dicomParser;
}(dicomParser));

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -25,12 +25,6 @@ Package.onUse(function(api) {
api.addFiles('client/cornerstoneWADOImageLoader.js', 'client', {
bare: true
});
api.addFiles('client/libopenjpeg.js', 'client', {
bare: true
});
api.addFiles('client/libCharLS.js', 'client', {
bare: true
});
api.addFiles('client/dicomParser.js', 'client', {
bare: true
});
@ -41,6 +35,9 @@ Package.onUse(function(api) {
bare: true
});
api.addAssets('public/js/cornerstoneWADOImageLoaderCodecs.es5.js', 'client');
api.addAssets('public/js/cornerstoneWADOImageLoaderWebWorker.es5.js', 'client');
api.export('cornerstone', 'client');
api.export('cornerstoneMath', 'client');
api.export('cornerstoneTools', 'client');

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,861 @@
/*! cornerstone-wado-image-loader - v0.14.1 - 2017-02-11 | (c) 2016 Chris Hafey | https://github.com/chafey/cornerstoneWADOImageLoader */
cornerstoneWADOImageLoaderWebWorker = {
registerTaskHandler : undefined
};
(function () {
// an object of task handlers
var taskHandlers = {};
// Flag to ensure web worker is only initialized once
var initialized = false;
// the configuration object passed in when the web worker manager is initialized
var config;
/**
* Initialization function that loads additional web workers and initializes them
* @param data
*/
function initialize(data) {
//console.log('web worker initialize ', data.workerIndex);
// prevent initialization from happening more than once
if(initialized) {
return;
}
// save the config data
config = data.config;
// load any additional web worker tasks
if(data.config.webWorkerTaskPaths) {
for(var i=0; i < data.config.webWorkerTaskPaths.length; i++) {
self.importScripts(data.config.webWorkerTaskPaths[i]);
}
}
// initialize each task handler
Object.keys(taskHandlers).forEach(function(key) {
taskHandlers[key].initialize(config.taskConfiguration);
});
// tell main ui thread that we have completed initialization
self.postMessage({
taskType: 'initialize',
status: 'success',
result: {
},
workerIndex: data.workerIndex
});
initialized = true;
}
/**
* Function exposed to web worker tasks to register themselves
* @param taskHandler
*/
cornerstoneWADOImageLoaderWebWorker.registerTaskHandler = function(taskHandler) {
if(taskHandlers[taskHandler.taskType]) {
console.log('attempt to register duplicate task handler "', taskHandler.taskType, '"');
return false;
}
taskHandlers[taskHandler.taskType] = taskHandler;
if(initialized) {
taskHandler.initialize(config.taskConfiguration);
}
};
/**
* Function to load a new web worker task with updated configuration
* @param data
*/
function loadWebWorkerTask(data) {
config = data.config;
self.importScripts(data.sourcePath);
}
/**
* Web worker message handler - dispatches messages to the registered task handlers
* @param msg
*/
self.onmessage = function(msg) {
//console.log('web worker onmessage', msg.data);
// handle initialize message
if(msg.data.taskType === 'initialize') {
initialize(msg.data);
return;
}
// handle loadWebWorkerTask message
if(msg.data.taskType === 'loadWebWorkerTask') {
loadWebWorkerTask(msg.data);
return;
}
// dispatch the message if there is a handler registered for it
if(taskHandlers[msg.data.taskType]) {
taskHandlers[msg.data.taskType].handler(msg.data, function(result, transferList) {
self.postMessage({
taskType: msg.data.taskType,
status: 'success',
result: result,
workerIndex: msg.data.workerIndex
}, transferList);
});
return;
}
// not task handler registered - send a failure message back to ui thread
console.log('no task handler for ', msg.data.taskType);
console.log(taskHandlers);
self.postMessage({
taskType: msg.data.taskType,
status: 'failed - no task handler registered',
workerIndex: msg.data.workerIndex
});
};
}());
cornerstoneWADOImageLoader = {};
(function () {
// flag to ensure codecs are loaded only once
var codecsLoaded = false;
// the configuration object for the decodeTask
var decodeConfig;
/**
* Function to control loading and initializing the codecs
* @param config
*/
function loadCodecs(config) {
// prevent loading codecs more than once
if (codecsLoaded) {
return;
}
// Load the codecs
//console.time('loadCodecs');
self.importScripts(config.decodeTask.codecsPath);
codecsLoaded = true;
//console.timeEnd('loadCodecs');
// Initialize the codecs
if (config.decodeTask.initializeCodecsOnStartup) {
//console.time('initializeCodecs');
cornerstoneWADOImageLoader.initializeJPEG2000(config.decodeTask);
cornerstoneWADOImageLoader.initializeJPEGLS(config.decodeTask);
//console.timeEnd('initializeCodecs');
}
}
/**
* Task initialization function
*/
function decodeTaskInitialize(config) {
decodeConfig = config;
if (config.decodeTask.loadCodecsOnStartup) {
loadCodecs(config);
}
}
function calculateMinMax(imageFrame) {
if (imageFrame.smallestPixelValue !== undefined && imageFrame.largestPixelValue !== undefined) {
return;
}
var minMax = cornerstoneWADOImageLoader.getMinMax(imageFrame.pixelData);
imageFrame.smallestPixelValue = minMax.min;
imageFrame.largestPixelValue = minMax.max;
}
/**
* Task handler function
*/
function decodeTaskHandler(data, doneCallback) {
// Load the codecs if they aren't already loaded
loadCodecs(decodeConfig);
var imageFrame = data.data.imageFrame;
// convert pixel data from ArrayBuffer to Uint8Array since web workers support passing ArrayBuffers but
// not typed arrays
var pixelData = new Uint8Array(data.data.pixelData);
cornerstoneWADOImageLoader.decodeImageFrame(
imageFrame,
data.data.transferSyntax,
pixelData,
decodeConfig.decodeTask,
data.data.options);
calculateMinMax(imageFrame);
// convert from TypedArray to ArrayBuffer since web workers support passing ArrayBuffers but not
// typed arrays
imageFrame.pixelData = imageFrame.pixelData.buffer;
// invoke the callback with our result and pass the pixelData in the transferList to move it to
// UI thread without making a copy
doneCallback(imageFrame, [imageFrame.pixelData]);
}
// register our task
cornerstoneWADOImageLoaderWebWorker.registerTaskHandler({
taskType: 'decodeTask',
handler: decodeTaskHandler,
initialize: decodeTaskInitialize
});
}());
/**
*/
(function (cornerstoneWADOImageLoader) {
"use strict";
function decodeImageFrame(imageFrame, transferSyntax, pixelData, decodeConfig, options) {
var start = new Date().getTime();
// Implicit VR Little Endian
if(transferSyntax === "1.2.840.10008.1.2") {
imageFrame = cornerstoneWADOImageLoader.decodeLittleEndian(imageFrame, pixelData);
}
// Explicit VR Little Endian
else if(transferSyntax === "1.2.840.10008.1.2.1") {
imageFrame = cornerstoneWADOImageLoader.decodeLittleEndian(imageFrame, pixelData);
}
// Explicit VR Big Endian (retired)
else if (transferSyntax === "1.2.840.10008.1.2.2" ) {
imageFrame = cornerstoneWADOImageLoader.decodeBigEndian(imageFrame, pixelData);
}
// Deflate transfer syntax (deflated by dicomParser)
else if(transferSyntax === '1.2.840.10008.1.2.1.99') {
imageFrame = cornerstoneWADOImageLoader.decodeLittleEndian(imageFrame, pixelData);
}
// RLE Lossless
else if (transferSyntax === "1.2.840.10008.1.2.5" )
{
imageFrame = cornerstoneWADOImageLoader.decodeRLE(imageFrame, pixelData);
}
// JPEG Baseline lossy process 1 (8 bit)
else if (transferSyntax === "1.2.840.10008.1.2.4.50")
{
imageFrame = cornerstoneWADOImageLoader.decodeJPEGBaseline(imageFrame, pixelData);
}
// JPEG Baseline lossy process 2 & 4 (12 bit)
else if (transferSyntax === "1.2.840.10008.1.2.4.51")
{
imageFrame = cornerstoneWADOImageLoader.decodeJPEGBaseline(imageFrame, pixelData);
}
// JPEG Lossless, Nonhierarchical (Processes 14)
else if (transferSyntax === "1.2.840.10008.1.2.4.57")
{
imageFrame = cornerstoneWADOImageLoader.decodeJPEGLossless(imageFrame, pixelData);
}
// JPEG Lossless, Nonhierarchical (Processes 14 [Selection 1])
else if (transferSyntax === "1.2.840.10008.1.2.4.70" )
{
imageFrame = cornerstoneWADOImageLoader.decodeJPEGLossless(imageFrame, pixelData);
}
// JPEG-LS Lossless Image Compression
else if (transferSyntax === "1.2.840.10008.1.2.4.80" )
{
imageFrame = cornerstoneWADOImageLoader.decodeJPEGLS(imageFrame, pixelData);
}
// JPEG-LS Lossy (Near-Lossless) Image Compression
else if (transferSyntax === "1.2.840.10008.1.2.4.81" )
{
imageFrame = cornerstoneWADOImageLoader.decodeJPEGLS(imageFrame, pixelData);
}
// JPEG 2000 Lossless
else if (transferSyntax === "1.2.840.10008.1.2.4.90")
{
imageFrame = cornerstoneWADOImageLoader.decodeJPEG2000(imageFrame, pixelData, decodeConfig, options);
}
// JPEG 2000 Lossy
else if (transferSyntax === "1.2.840.10008.1.2.4.91")
{
imageFrame = cornerstoneWADOImageLoader.decodeJPEG2000(imageFrame, pixelData, decodeConfig, options);
}
/* Don't know if these work...
// JPEG 2000 Part 2 Multicomponent Image Compression (Lossless Only)
else if(transferSyntax === "1.2.840.10008.1.2.4.92")
{
return cornerstoneWADOImageLoader.decodeJPEG2000(dataSet, frame);
}
// JPEG 2000 Part 2 Multicomponent Image Compression
else if(transferSyntax === "1.2.840.10008.1.2.4.93")
{
return cornerstoneWADOImageLoader.decodeJPEG2000(dataSet, frame);
}
*/
else
{
if(console && console.log) {
console.log("Image cannot be decoded due to Unsupported transfer syntax " + transferSyntax);
}
throw "no decoder for transfer syntax " + transferSyntax;
}
var end = new Date().getTime();
imageFrame.decodeTimeInMS = end - start;
return imageFrame;
}
cornerstoneWADOImageLoader.decodeImageFrame = decodeImageFrame;
}(cornerstoneWADOImageLoader));
/**
*/
(function (cornerstoneWADOImageLoader) {
function swap16(val) {
return ((val & 0xFF) << 8)
| ((val >> 8) & 0xFF);
}
function decodeBigEndian(imageFrame, pixelData) {
if(imageFrame.bitsAllocated === 16) {
var arrayBuffer = pixelData.buffer;
var offset = pixelData.byteOffset;
var length = pixelData.length;
// if pixel data is not aligned on even boundary, shift it so we can create the 16 bit array
// buffers on it
if(offset % 2) {
arrayBuffer = arrayBuffer.slice(offset);
offset = 0;
}
if(imageFrame.pixelRepresentation === 0) {
imageFrame.pixelData = new Uint16Array(arrayBuffer, offset, length / 2);
} else {
imageFrame.pixelData = new Int16Array(arrayBuffer, offset, length / 2);
}
// Do the byte swap
for(var i=0; i < imageFrame.pixelData.length; i++) {
imageFrame[i] = swap16(imageFrame.pixelData[i]);
}
} else if(imageFrame.bitsAllocated === 8) {
imageFrame.pixelData = pixelData;
}
return imageFrame;
}
// module exports
cornerstoneWADOImageLoader.decodeBigEndian = decodeBigEndian;
}(cornerstoneWADOImageLoader));
(function (cornerstoneWADOImageLoader) {
"use strict";
function decodeJpx(imageFrame, pixelData) {
var jpxImage = new JpxImage();
jpxImage.parse(pixelData);
var tileCount = jpxImage.tiles.length;
if(tileCount !== 1) {
throw 'JPEG2000 decoder returned a tileCount of ' + tileCount + ', when 1 is expected';
}
imageFrame.columns = jpxImage.width;
imageFrame.rows = jpxImage.height;
imageFrame.pixelData = jpxImage.tiles[0].items;
return imageFrame;
}
var openJPEG;
function decodeOpenJPEG(data, bytesPerPixel, signed) {
var dataPtr = openJPEG._malloc(data.length);
openJPEG.writeArrayToMemory(data, dataPtr);
// create param outpout
var imagePtrPtr=openJPEG._malloc(4);
var imageSizePtr=openJPEG._malloc(4);
var imageSizeXPtr=openJPEG._malloc(4);
var imageSizeYPtr=openJPEG._malloc(4);
var imageSizeCompPtr=openJPEG._malloc(4);
var t0 = Date.now();
var ret = openJPEG.ccall('jp2_decode','number', ['number', 'number', 'number', 'number', 'number', 'number', 'number'],
[dataPtr, data.length, imagePtrPtr, imageSizePtr, imageSizeXPtr, imageSizeYPtr, imageSizeCompPtr]);
// add num vomp..etc
if(ret !== 0){
console.log('[opj_decode] decoding failed!');
openJPEG._free(dataPtr);
openJPEG._free(openJPEG.getValue(imagePtrPtr, '*'));
openJPEG._free(imageSizeXPtr);
openJPEG._free(imageSizeYPtr);
openJPEG._free(imageSizePtr);
openJPEG._free(imageSizeCompPtr);
return undefined;
}
var imagePtr = openJPEG.getValue(imagePtrPtr, '*')
var image = {
length : openJPEG.getValue(imageSizePtr,'i32'),
sx : openJPEG.getValue(imageSizeXPtr,'i32'),
sy : openJPEG.getValue(imageSizeYPtr,'i32'),
nbChannels : openJPEG.getValue(imageSizeCompPtr,'i32'), // hard coded for now
perf_timetodecode : undefined,
pixelData : undefined
};
// Copy the data from the EMSCRIPTEN heap into the correct type array
var length = image.sx*image.sy*image.nbChannels;
var src32 = new Int32Array(openJPEG.HEAP32.buffer, imagePtr, length);
if(bytesPerPixel === 1) {
if(Uint8Array.from) {
image.pixelData = Uint8Array.from(src32);
} else {
image.pixelData = new Uint8Array(length);
for(var i=0; i < length; i++) {
image.pixelData[i] = src32[i];
}
}
} else {
if (signed) {
if(Int16Array.from) {
image.pixelData = Int16Array.from(src32);
} else {
image.pixelData = new Int16Array(length);
for(var i=0; i < length; i++) {
image.pixelData[i] = src32[i];
}
}
} else {
if(Uint16Array.from) {
image.pixelData = Uint16Array.from(src32);
} else {
image.pixelData = new Uint16Array(length);
for(var i=0; i < length; i++) {
image.pixelData[i] = src32[i];
}
}
}
}
var t1 = Date.now();
image.perf_timetodecode = t1-t0;
// free
openJPEG._free(dataPtr);
openJPEG._free(imagePtrPtr);
openJPEG._free(imagePtr);
openJPEG._free(imageSizePtr);
openJPEG._free(imageSizeXPtr);
openJPEG._free(imageSizeYPtr);
openJPEG._free(imageSizeCompPtr);
return image;
}
function decodeOpenJpeg2000(imageFrame, pixelData) {
var bytesPerPixel = imageFrame.bitsAllocated <= 8 ? 1 : 2;
var signed = imageFrame.pixelRepresentation === 1;
var image = decodeOpenJPEG(pixelData, bytesPerPixel, signed);
imageFrame.columns = image.sx;
imageFrame.rows = image.sy;
imageFrame.pixelData = image.pixelData;
if(image.nbChannels > 1) {
imageFrame.photometricInterpretation = "RGB";
}
return imageFrame;
}
function initializeJPEG2000(decodeConfig) {
// check to make sure codec is loaded
if(!decodeConfig.usePDFJS) {
if(typeof OpenJPEG === 'undefined') {
throw 'OpenJPEG decoder not loaded';
}
}
if (!openJPEG) {
openJPEG = OpenJPEG();
if (!openJPEG || !openJPEG._jp2_decode) {
throw 'OpenJPEG failed to initialize';
}
}
}
function decodeJPEG2000(imageFrame, pixelData, decodeConfig, options)
{
options = options || {};
initializeJPEG2000(decodeConfig);
if(options.usePDFJS || decodeConfig.usePDFJS) {
// OHIF image-JPEG2000 https://github.com/OHIF/image-JPEG2000
//console.log('PDFJS')
return decodeJpx(imageFrame, pixelData);
} else {
// OpenJPEG2000 https://github.com/jpambrun/openjpeg
//console.log('OpenJPEG')
return decodeOpenJpeg2000(imageFrame, pixelData);
}
}
cornerstoneWADOImageLoader.decodeJPEG2000 = decodeJPEG2000;
cornerstoneWADOImageLoader.initializeJPEG2000 = initializeJPEG2000;
}(cornerstoneWADOImageLoader));
(function (cornerstoneWADOImageLoader) {
"use strict";
function decodeJPEGBaseline(imageFrame, pixelData)
{
// check to make sure codec is loaded
if(typeof JpegImage === 'undefined') {
throw 'No JPEG Baseline decoder loaded';
}
var jpeg = new JpegImage();
jpeg.parse(pixelData);
if(imageFrame.bitsAllocated === 8) {
imageFrame.pixelData = jpeg.getData(imageFrame.columns, imageFrame.rows);
return imageFrame;
}
else if(imageFrame.bitsAllocated === 16) {
imageFrame.pixelData = jpeg.getData16(imageFrame.columns, imageFrame.rows);
return imageFrame;
}
}
cornerstoneWADOImageLoader.decodeJPEGBaseline = decodeJPEGBaseline;
}(cornerstoneWADOImageLoader));
"use strict";
(function (cornerstoneWADOImageLoader) {
function decodeJPEGLossless(imageFrame, pixelData) {
// check to make sure codec is loaded
if(typeof jpeg === 'undefined' ||
typeof jpeg.lossless === 'undefined' ||
typeof jpeg.lossless.Decoder === 'undefined') {
throw 'No JPEG Lossless decoder loaded';
}
var byteOutput = imageFrame.bitsAllocated <= 8 ? 1 : 2;
//console.time('jpeglossless');
var buffer = pixelData.buffer;
var decoder = new jpeg.lossless.Decoder();
var decompressedData = decoder.decode(buffer, buffer.byteOffset, buffer.length, byteOutput);
//console.timeEnd('jpeglossless');
if (imageFrame.pixelRepresentation === 0) {
if (imageFrame.bitsAllocated === 16) {
imageFrame.pixelData = new Uint16Array(decompressedData.buffer);
return imageFrame;
} else {
// untested!
imageFrame.pixelData = new Uint8Array(decompressedData.buffer);
return imageFrame;
}
} else {
imageFrame.pixelData = new Int16Array(decompressedData.buffer);
return imageFrame;
}
}
// module exports
cornerstoneWADOImageLoader.decodeJPEGLossless = decodeJPEGLossless;
}(cornerstoneWADOImageLoader));
"use strict";
(function (cornerstoneWADOImageLoader) {
var charLS;
function jpegLSDecode(data, isSigned) {
// prepare input parameters
var dataPtr = charLS._malloc(data.length);
charLS.writeArrayToMemory(data, dataPtr);
// prepare output parameters
var imagePtrPtr=charLS._malloc(4);
var imageSizePtr=charLS._malloc(4);
var widthPtr=charLS._malloc(4);
var heightPtr=charLS._malloc(4);
var bitsPerSamplePtr=charLS._malloc(4);
var stridePtr=charLS._malloc(4);
var allowedLossyErrorPtr =charLS._malloc(4);
var componentsPtr=charLS._malloc(4);
var interleaveModePtr=charLS._malloc(4);
// Decode the image
var result = charLS.ccall(
'jpegls_decode',
'number',
['number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number'],
[dataPtr, data.length, imagePtrPtr, imageSizePtr, widthPtr, heightPtr, bitsPerSamplePtr, stridePtr, componentsPtr, allowedLossyErrorPtr, interleaveModePtr]
);
// Extract result values into object
var image = {
result : result,
width : charLS.getValue(widthPtr,'i32'),
height : charLS.getValue(heightPtr,'i32'),
bitsPerSample : charLS.getValue(bitsPerSamplePtr,'i32'),
stride : charLS.getValue(stridePtr,'i32'),
components : charLS.getValue(componentsPtr, 'i32'),
allowedLossyError : charLS.getValue(allowedLossyErrorPtr, 'i32'),
interleaveMode: charLS.getValue(interleaveModePtr, 'i32'),
pixelData: undefined
};
// Copy image from emscripten heap into appropriate array buffer type
var imagePtr = charLS.getValue(imagePtrPtr, '*');
if(image.bitsPerSample <= 8) {
image.pixelData = new Uint8Array(image.width * image.height * image.components);
image.pixelData.set(new Uint8Array(charLS.HEAP8.buffer, imagePtr, image.pixelData.length));
} else {
// I have seen 16 bit signed images, but I don't know if 16 bit unsigned is valid, hoping to get
// answer here:
// https://github.com/team-charls/charls/issues/14
if(isSigned) {
image.pixelData = new Int16Array(image.width * image.height * image.components);
image.pixelData.set(new Int16Array(charLS.HEAP16.buffer, imagePtr, image.pixelData.length));
} else {
image.pixelData = new Uint16Array(image.width * image.height * image.components);
image.pixelData.set(new Uint16Array(charLS.HEAP16.buffer, imagePtr, image.pixelData.length));
}
}
// free memory and return image object
charLS._free(dataPtr);
charLS._free(imagePtr);
charLS._free(imagePtrPtr);
charLS._free(imageSizePtr);
charLS._free(widthPtr);
charLS._free(heightPtr);
charLS._free(bitsPerSamplePtr);
charLS._free(stridePtr);
charLS._free(componentsPtr);
charLS._free(interleaveModePtr);
return image;
}
function initializeJPEGLS() {
// check to make sure codec is loaded
if(typeof CharLS === 'undefined') {
throw 'No JPEG-LS decoder loaded';
}
// Try to initialize CharLS
// CharLS https://github.com/chafey/charls
if(!charLS) {
charLS = CharLS();
if(!charLS || !charLS._jpegls_decode) {
throw 'JPEG-LS failed to initialize';
}
}
}
function decodeJPEGLS(imageFrame, pixelData)
{
initializeJPEGLS();
var image = jpegLSDecode(pixelData, imageFrame.pixelRepresentation === 1);
//console.log(image);
// throw error if not success or too much data
if(image.result !== 0 && image.result !== 6) {
throw 'JPEG-LS decoder failed to decode frame (error code ' + image.result + ')';
}
imageFrame.columns = image.width;
imageFrame.rows = image.height;
imageFrame.pixelData = image.pixelData;
return imageFrame;
}
// module exports
cornerstoneWADOImageLoader.decodeJPEGLS = decodeJPEGLS;
cornerstoneWADOImageLoader.initializeJPEGLS = initializeJPEGLS;
}(cornerstoneWADOImageLoader));
/**
*/
(function (cornerstoneWADOImageLoader) {
function decodeLittleEndian(imageFrame, pixelData) {
if(imageFrame.bitsAllocated === 16) {
var arrayBuffer = pixelData.buffer;
var offset = pixelData.byteOffset;
var length = pixelData.length;
// if pixel data is not aligned on even boundary, shift it so we can create the 16 bit array
// buffers on it
if(offset % 2) {
arrayBuffer = arrayBuffer.slice(offset);
offset = 0;
}
if(imageFrame.pixelRepresentation === 0) {
imageFrame.pixelData = new Uint16Array(arrayBuffer, offset, length / 2);
} else {
imageFrame.pixelData = new Int16Array(arrayBuffer, offset, length / 2);
}
} else if(imageFrame.bitsAllocated === 8) {
imageFrame.pixelData = pixelData;
}
return imageFrame;
}
// module exports
cornerstoneWADOImageLoader.decodeLittleEndian = decodeLittleEndian;
}(cornerstoneWADOImageLoader));
/**
*/
(function (cornerstoneWADOImageLoader) {
function decodeRLE(imageFrame, pixelData) {
if(imageFrame.bitsAllocated === 8) {
return decode8(imageFrame, pixelData);
} else if( imageFrame.bitsAllocated === 16) {
return decode16(imageFrame, pixelData);
} else {
throw 'unsupported pixel format for RLE'
}
}
function decode8(imageFrame, pixelData ) {
var frameData = pixelData;
var frameSize = imageFrame.rows * imageFrame.columns;
var outFrame = new ArrayBuffer(frameSize*imageFrame.samplesPerPixel);
var header=new DataView(frameData.buffer, frameData.byteOffset);
var data=new DataView( frameData.buffer, frameData.byteOffset );
var out=new DataView( outFrame );
var outIndex=0;
var numSegments = header.getInt32(0,true);
for( var s=0 ; s < numSegments ; ++s ) {
outIndex = s;
var inIndex=header.getInt32( (s+1)*4,true);
var maxIndex=header.getInt32( (s+2)*4,true);
if( maxIndex===0 )
maxIndex = frameData.length;
var endOfSegment = frameSize * numSegments;
while( inIndex < maxIndex ) {
var n=data.getInt8(inIndex++);
if( n >=0 && n <=127 ) {
// copy n bytes
for( var i=0 ; i < n+1 && outIndex < endOfSegment; ++i ) {
out.setInt8(outIndex, data.getInt8(inIndex++));
outIndex+=imageFrame.samplesPerPixel;
}
} else if( n<= -1 && n>=-127 ) {
var value=data.getInt8(inIndex++);
// run of n bytes
for( var j=0 ; j < -n+1 && outIndex < endOfSegment; ++j ) {
out.setInt8(outIndex, value );
outIndex+=imageFrame.samplesPerPixel;
}
} else if (n===-128)
; // do nothing
}
}
imageFrame.pixelData = new Uint8Array(outFrame);
return imageFrame;
}
function decode16( imageFrame, pixelData ) {
var frameData = pixelData;
var frameSize = imageFrame.rows * imageFrame.columns;
var outFrame = new ArrayBuffer(frameSize*imageFrame.samplesPerPixel*2);
var header=new DataView(frameData.buffer, frameData.byteOffset);
var data=new DataView( frameData.buffer, frameData.byteOffset );
var out=new DataView( outFrame );
var numSegments = header.getInt32(0,true);
for( var s=0 ; s < numSegments ; ++s ) {
var outIndex=0;
var highByte=( s===0 ? 1 : 0);
var inIndex=header.getInt32( (s+1)*4,true);
var maxIndex=header.getInt32( (s+2)*4,true);
if( maxIndex===0 )
maxIndex = frameData.length;
while( inIndex < maxIndex ) {
var n=data.getInt8(inIndex++);
if( n >=0 && n <=127 ) {
for( var i=0 ; i < n+1 && outIndex < frameSize ; ++i ) {
out.setInt8( (outIndex*2)+highByte, data.getInt8(inIndex++) );
outIndex++;
}
} else if( n<= -1 && n>=-127 ) {
var value=data.getInt8(inIndex++);
for( var j=0 ; j < -n+1 && outIndex < frameSize ; ++j ) {
out.setInt8( (outIndex*2)+highByte, value );
outIndex++;
}
} else if (n===-128)
; // do nothing
}
}
if(imageFrame.pixelRepresentation === 0) {
imageFrame.pixelData = new Uint16Array(outFrame);
} else {
imageFrame.pixelData = new Int16Array(outFrame);
}
return imageFrame;
}
// module exports
cornerstoneWADOImageLoader.decodeRLE = decodeRLE;
}(cornerstoneWADOImageLoader));
(function (cornerstoneWADOImageLoader) {
"use strict";
function getMinMax(storedPixelData)
{
// we always calculate the min max values since they are not always
// present in DICOM and we don't want to trust them anyway as cornerstone
// depends on us providing reliable values for these
var min = storedPixelData[0];
var max = storedPixelData[0];
var storedPixel;
var numPixels = storedPixelData.length;
for(var index = 1; index < numPixels; index++) {
storedPixel = storedPixelData[index];
min = Math.min(min, storedPixel);
max = Math.max(max, storedPixel);
}
return {
min: min,
max: max
};
}
// module exports
cornerstoneWADOImageLoader.getMinMax = getMinMax;
}(cornerstoneWADOImageLoader));