Merged LesionTracker and OHIF Viewer, refactored out common components

This commit is contained in:
Erik Ziegler committed 2015-10-13 14:14:50 +02:00
1 parent 34adbe3fbc
commit b774c22593
144 files changed
+27820

No files matched your search

@@ -0,0 +1,5 @@
/*AccountsTemplates.configure({
forbidClientAccountCreation: true // do not allow account creation
});
*/
+18
View File
@@ -0,0 +1,18 @@
createStacks = function(study) {
var stacks = [];
// TODO: Split by multi-frame, modality, image size, etc
study.seriesList.forEach(function(series) {
if (!series.instances) {
return;
}
// Don't display thumbnails for non-image modalities
// All imaging modalities must have a valid value for rows (or columns)
var anInstance = series.instances[0];
if (!anInstance || !anInstance.rows) {
return;
}
stacks.push(series);
});
return stacks;
};
@@ -0,0 +1,17 @@
/**
* Converts the properties to URL query parameters. Based on:
* http://stackoverflow.com/questions/111529/create-query-parameters-in-javascript
*
* @param data
* @returns {string}
*/
encodeQueryData = function(data)
{
var ret = [];
for (var d in data) {
if(data[d]) {
ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d]));
}
}
return ret.join("&");
};
@@ -0,0 +1,39 @@
function checkToken(token, data, dataOffset) {
if(dataOffset + token.length > data.length) {
//console.log('dataOffset >> ', dataOffset);
return false;
}
var endIndex = dataOffset;
for(var i = 0; i < token.length; i++) {
if(token[i] !== data[endIndex++]) {
if(endIndex > 520000) {
console.log('token=',uint8ArrayToString(token));
console.log('data=', uint8ArrayToString(data, dataOffset, endIndex-dataOffset));
console.log('miss at %d %s dataOffset=%d', i, String.fromCharCode(data[endIndex]), endIndex);
console.log('miss at %d %s dataOffset=%d', i, String.fromCharCode(token[endIndex]), endIndex);
}
return false;
}
}
return true;
}
findIndexOfString = function(data, str, offset) {
offset = offset || 0;
var token = stringToUint8Array(str);
for(var i=offset; i < data.length; i++) {
if(token[0] === data[i]) {
//console.log('match @', i);
if(checkToken(token, data, i)) {
return i;
}
}
}
return -1;
}
@@ -0,0 +1,5 @@
getActiveViewportElement = function() {
// TODO: Fix this, just hardcoded for now
var element = $('.imageViewerViewport').get(0);
return element;
};
+10
View File
@@ -0,0 +1,10 @@
getImageId = function(instance) {
if (!instance) {
return;
}
if (instance.wadouri) {
return 'dicomweb:' + instance.wadouri; // WADO-URI
} else {
return getWADORSImageId(instance); // WADO-RS Retrieve Frame
}
};
@@ -0,0 +1,45 @@
getWADORSImageId = function(instance) {
var columnPixelSpacing = 1.0;
var rowPixelSpacing = 1.0;
if (instance.pixelSpacing) {
var split = instance.pixelSpacing.split('\\');
rowPixelSpacing = parseFloat(split[0]);
columnPixelSpacing = parseFloat(split[1]);
}
var windowWidth;
var windowCenter;
if (instance.windowWidth && instance.windowCenter) {
windowWidth = parseFloat(instance.windowWidth.split('\\')[0]);
windowCenter = parseFloat(instance.windowCenter.split('\\')[0]);
}
var image = {
uri: instance.wadorsuri,
//imageId : '',
//minPixelValue : 0,
//maxPixelValue : 255,
slope: instance.rescaleSlope,
intercept: instance.rescaleIntercept,
windowCenter : windowCenter,
windowWidth : windowWidth,
//render: cornerstone.renderColorImage,
//getPixelData: getPixelData,
//getImageData: getImageData,
//getCanvas: getCanvas,
rows: instance.rows,
columns: instance.columns,
height: instance.rows,
width: instance.columns,
color: false,
columnPixelSpacing: columnPixelSpacing,
rowPixelSpacing: rowPixelSpacing,
invert: false,
sizeInBytes: instance.rows * instance.columns * (instance.bitsAllocated / 8),
instance: instance
};
var imageId = cornerstoneWADORSImageLoader.addImage(image);
return imageId;
};
@@ -0,0 +1,16 @@
UI.registerHelper('formatDA', function (context, format, options) {
if (!context) {
return undefined;
}
/*var date = DICOMUtilities.toDate(context, '', false);
if(!date) {
return context;
}*/
var date = context;
var dateAsMoment = moment(date);
var strFormat = "MMM D, YYYY";
if (options) {
strFormat = format;
}
return dateAsMoment.format(strFormat);
});
@@ -0,0 +1,5 @@
UI.registerHelper('formatNumberPrecision', function(context, precision, options) {
if(context) {
return parseFloat(context).toFixed(precision);
}
});
@@ -0,0 +1,20 @@
UI.registerHelper('formatDA', function (context, format, options) {
if (!context) {
return undefined;
}
var pn = context;
var str = '';
if(pn.prefix) {
str = pn.prefix + ' ';
}
str += (pn.familyName || '') + ', ';
str += (pn.givenName || '');
if(pn.middleName) {
str += ' ' + pn.middleName;
}
if(pn.suffix) {
str += ' ' + pn.suffix;
}
return str;
});
@@ -0,0 +1,13 @@
UI.registerHelper('formatTM', function(context, format, options) {
if (!context) {
return undefined;
}
var date = context;
var dateAsMoment = moment(date);
var strFormat = "HH:mm:ss";
if (options) {
strFormat = format;
}
return dateAsMoment.format(strFormat);
});
+5
View File
@@ -0,0 +1,5 @@
isTouchDevice = function() {
return (('ontouchstart' in window) ||
(navigator.MaxTouchPoints > 0) ||
(navigator.msMaxTouchPoints > 0));
};
@@ -0,0 +1,82 @@
var metaDataLookup = {};
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 = {
date: instanceMetaData.studyDate,
time: instanceMetaData.studyTime,
description: instanceMetaData.studyDescription
};
metaData.series = {
description: seriesMetaData.seriesDescription,
number: seriesMetaData.seriesNumber,
modality: seriesMetaData.modality,
instanceUid: seriesMetaData.instanceUid,
numImages: numImages
};
metaData.instance = {
number: instanceMetaData.instanceNumber,
index: imageIndex
};
metaData.patient = {
name: instanceMetaData.patientName,
id: instanceMetaData.patientId
};
// If there is sufficient information, populate
// the imagePlane object for easier use in the Viewer
if (instanceMetaData.frameOfReferenceUID &&
instanceMetaData.imageOrientationPatient &&
instanceMetaData.imagePositionPatient) {
var imageOrientation = instanceMetaData.imageOrientationPatient.split('\\');
var imagePosition = instanceMetaData.imagePositionPatient.split('\\');
var columnPixelSpacing = 1.0;
var rowPixelSpacing = 1.0;
if (instanceMetaData.pixelSpacing) {
var split = instanceMetaData.pixelSpacing.split('\\');
rowPixelSpacing = parseFloat(split[0]);
columnPixelSpacing = parseFloat(split[1]);
}
metaData.imagePlane = {
frameOfReferenceUID: instanceMetaData.frameOfReferenceUID,
rows: instanceMetaData.rows,
columns: instanceMetaData.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,
};
}
// Add the metaData to the imageId lookup object
metaDataLookup[imageId] = metaData;
};
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);
});
@@ -0,0 +1,42 @@
/**
* This function destroys and rerenders the imageViewerViewport template.
* It uses the data provided (object containing seriesIndex and studyIndex) to
* load a new series into the produced viewport.
*
* @param element
* @param data An object containing a seriesIndex and studyIndex for a study to load into this viewport
*/
rerenderViewportWithNewSeries = function(element, data) {
var container = $(element).parents('.viewportContainer').get(0);
var viewportIndex = $(container).index();
data.viewportIndex = viewportIndex;
// Update the dictionary of loaded series for the specified viewport
OHIF.viewer.imageViewerLoadedSeriesDictionary[viewportIndex] = {
seriesInstanceUid: data.seriesInstanceUid,
studyInstanceUid: data.studyInstanceUid
};
// Remove the hover styling
$(element).find('canvas').not('.magnifyTool').removeClass("faded");
// Reset the current image index for this viewport
delete OHIF.viewer.imageViewerCurrentImageIdIndexDictionary[viewportIndex];
// Remove the whole template, add in the new one
var viewportContainer = $(element).parents('.removable');
var newViewportContainer = document.createElement('div');
newViewportContainer.className = 'removable';
// Remove the parent element of the template
// This is a workaround since otherwise Blaze UI onDestroyed doesn't fire
viewportContainer.remove();
container.appendChild(newViewportContainer);
// Render and insert the template
UI.renderWithData(Template.imageViewerViewport, data, newViewportContainer);
Session.set('loadedSeriesDictionary', OHIF.viewer.imageViewerLoadedSeriesDictionary);
};
+15
View File
@@ -0,0 +1,15 @@
sortStudy = function(study) {
if (!study) {
return;
}
study.seriesList.sort(function(a, b) {
return a.seriesNumber - b.seriesNumber;
});
study.seriesList.forEach(function(series){
series.instances.sort(function(a, b) {
return a.instanceNumber - b.instanceNumber;
});
});
};
@@ -0,0 +1,7 @@
stringToUint8Array = function(str) {
var uint=new Uint8Array(str.length);
for(var i=0,j=str.length;i<j;i++){
uint[i]=str.charCodeAt(i);
}
return uint;
};
+150
View File
@@ -0,0 +1,150 @@
var activeTool;
var tools = {};
var initialized = false;
function configureTools() {
// Set the configuration for the multitouch pan tool
var multiTouchPanConfig = {
testPointers: function(eventData) {
return (eventData.numPointers >= 3);
}
};
cornerstoneTools.panMultiTouch.setConfiguration(multiTouchPanConfig);
}
toolManager = {
init: function() {
toolManager.addTool('wwwc', {
mouse: cornerstoneTools.wwwc,
touch: cornerstoneTools.wwwcTouchDrag
});
toolManager.addTool('zoom', {
mouse: cornerstoneTools.zoom,
touch: cornerstoneTools.zoomTouchDrag
});
toolManager.addTool('wwwcRegion', {
mouse: cornerstoneTools.wwwcRegion,
touch: cornerstoneTools.wwwcRegionTouch
});
toolManager.addTool('dragProbe', {
mouse: cornerstoneTools.dragProbe,
touch: cornerstoneTools.dragProbeTouch
});
toolManager.addTool('pan', {
mouse: cornerstoneTools.pan,
touch: cornerstoneTools.panTouchDrag
});
toolManager.addTool('stackScroll', {
mouse: cornerstoneTools.stackScroll,
touch: cornerstoneTools.stackScrollTouchDrag
});
toolManager.addTool('length', {
mouse: cornerstoneTools.length,
touch: cornerstoneTools.lengthTouch
});
toolManager.addTool('angle', {
mouse: cornerstoneTools.simpleAngle,
touch: cornerstoneTools.simpleAngleTouch
});
toolManager.addTool('magnify', {
mouse: cornerstoneTools.magnify,
touch: cornerstoneTools.magnifyTouchDrag
});
toolManager.addTool('ellipticalRoi', {
mouse: cornerstoneTools.ellipticalRoi,
touch: cornerstoneTools.ellipticalRoiTouch
});
toolManager.addTool('rectangleRoi', {
mouse: cornerstoneTools.rectangleRoi,
touch: cornerstoneTools.rectangleRoiTouch
});
toolManager.addTool('annotate', {
mouse: cornerstoneTools.arrowAnnotate,
touch: cornerstoneTools.arrowAnnotateTouch
});
activeTool = OHIF.viewer.defaultTool;
configureTools();
initialized = true;
},
addTool: function(name, base) {
tools[name] = base;
},
setActiveTool: function(tool) {
if (!initialized) {
toolManager.init();
}
$('#toolRow .btn-group button').removeClass('active');
var toolButton = document.getElementById(tool);
toolButton.classList.add('active');
$('.imageViewerViewport').each(function(index, element) {
var canvases = $(element).find('canvas');
if (!element.classList.contains('empty') && canvases.length > 0) {
// First, deactivate the current active tool
tools[activeTool].mouse.deactivate(element, 1);
tools[activeTool].touch.deactivate(element);
// First, get the stack toolData
var toolData = cornerstoneTools.getToolState(element, 'stack');
if (toolData === undefined || toolData.data === undefined || toolData.data.length === 0) {
return;
}
// Get the imageIds for this element
var imageIds = toolData.data[0].imageIds;
// Deactivate all the middle mouse, right click, and scroll wheel tools
cornerstoneTools.pan.deactivate(element);
cornerstoneTools.zoom.deactivate(element);
cornerstoneTools.zoomWheel.deactivate(element);
cornerstoneTools.stackScrollWheel.deactivate(element);
// Reactivate the relevant scrollwheel tool for this element
if (imageIds.length > 1) {
// scroll is the default tool for middle mouse wheel for stacks
cornerstoneTools.stackScrollWheel.activate(element);
} else {
// zoom is the default tool for middle mouse wheel for single images (non stacks)
cornerstoneTools.zoomWheel.activate(element);
}
// This block ensures that the middle mouse and scroll tools keep working
if (tool === 'pan') {
cornerstoneTools.pan.activate(element, 3); // 3 means left mouse button and middle mouse button
cornerstoneTools.zoom.activate(element, 4); // zoom is the default tool for right mouse button
} else if (tool === 'zoom') {
cornerstoneTools.pan.activate(element, 2); // pan is the default tool for middle mouse button
cornerstoneTools.zoom.activate(element, 5); // 5 means left mouse button and right mouse button
} else {
// Reactivate the middle mouse and right click tools
cornerstoneTools.pan.activate(element, 2); // pan is the default tool for middle mouse button
cornerstoneTools.zoom.activate(element, 4); // zoom is the default tool for right mouse button
// Activate the chosen tool
tools[tool].mouse.activate(element, 1);
}
tools[tool].touch.activate(element);
cornerstoneTools.zoomTouchPinch.activate(element);
cornerstoneTools.panMultiTouch.activate(element);
}
});
activeTool = tool;
},
getActiveTool: function() {
if (!activeTool) {
activeTool = OHIF.viewer.defaultTool;
}
return activeTool;
},
setDefaultTool: function(tool) {
OHIF.viewer.defaultTool = tool;
},
getDefaultTool: function() {
return OHIF.viewer.defaultTool;
}
};
@@ -0,0 +1,9 @@
uint8ArrayToString = function(data, offset, length) {
offset = offset || 0;
length = length || data.length - offset;
var str = "";
for(var i=offset; i < offset + length; i++) {
str += String.fromCharCode(data[i]);
}
return str;
};
@@ -0,0 +1,49 @@
updateOrientationMarkers = function(element, viewport) {
// Updates the orientation labels on the viewport
var enabledElement = cornerstone.getEnabledElement(element);
var imagePlane = cornerstoneTools.metaData.get('imagePlane', enabledElement.image.imageId);
if (!imagePlane || !imagePlane.rowCosines || !imagePlane.columnCosines) {
return;
}
var rowString = cornerstoneTools.orientation.getOrientationString(imagePlane.rowCosines);
var columnString = cornerstoneTools.orientation.getOrientationString(imagePlane.columnCosines);
var oppositeRowString = cornerstoneTools.orientation.invertOrientationString(rowString);
var oppositeColumnString = cornerstoneTools.orientation.invertOrientationString(columnString);
var markers = {
top: oppositeColumnString,
left: oppositeRowString,
};
// If any vertical or horizontal flips are applied, change the orientation strings ahead of
// the rotation applications
if (viewport.vflip) {
markers.top = cornerstoneTools.orientation.invertOrientationString(markers.top);
}
if (viewport.hflip) {
markers.left = cornerstoneTools.orientation.invertOrientationString(markers.left);
}
// Get the viewport orientation marker DOM elements
var viewportOrientationMarkers = $(element).siblings(".viewportOrientationMarkers");
var topMarker = viewportOrientationMarkers.find('.topMid');
var leftMarker = viewportOrientationMarkers.find('.leftMid');
// Swap the labels accordingly if the viewport has been rotated
// This could be done in a more complex way for intermediate rotation values (e.g. 45 degrees)
if (viewport.rotation === 90 || viewport.rotation === -270) {
topMarker.text(markers.left);
leftMarker.text(cornerstoneTools.orientation.invertOrientationString(markers.top));
} else if (viewport.rotation === -90 || viewport.rotation === 270) {
topMarker.text(cornerstoneTools.orientation.invertOrientationString(markers.left));
leftMarker.text(markers.top);
} else if (viewport.rotation === 180 || viewport.rotation === -180) {
topMarker.text(cornerstoneTools.orientation.invertOrientationString(markers.top));
leftMarker.text(cornerstoneTools.orientation.invertOrientationString(markers.left));
} else {
topMarker.text(markers.top);
leftMarker.text(markers.left);
}
};