Integrate Study Prefetcher and Study Loading Listener

This commit is contained in:
Erik Ziegler 2019-01-09 00:13:44 +01:00
parent 8962f957d8
commit 3954c32441
22 changed files with 149 additions and 784 deletions

View File

@ -0,0 +1,16 @@
{
"extends": [
"react-app",
"eslint:recommended",
"plugin:react/recommended"
],
"parser": "babel-eslint",
"env": {
"jest": true
},
"settings": {
"react": {
"version": "detect",
},
},
}

View File

@ -13,12 +13,12 @@
"hammerjs": "^2.0.8",
"lodash.debounce": "^4.0.8",
"moment": "^2.22.2",
"ohif-core": "^0.1.3",
"ohif-core": "^0.1.4",
"oidc-client": "^1.6.1",
"prop-types": "^15.6.2",
"query-string": "^6.2.0",
"react": "^16.4.1",
"react-cornerstone-viewport": "^0.1.5",
"react-cornerstone-viewport": "^0.1.6",
"react-dom": "^16.4.1",
"react-redux": "^6.0.0",
"react-router": "^4.3.1",

View File

@ -4,13 +4,17 @@ import OHIF from 'ohif-core'
const { setViewportActive } = OHIF.redux.actions;
const mapStateToProps = state => {
const mapStateToProps = (state, ownProps) => {
const activeButton = state.tools.buttons.find(tool => tool.active === true);
// If this is the active viewport, enable prefetching.
const enableStackPrefetch = ownProps.viewportData.viewportIndex === state.viewports.activeViewportIndex;
return {
layout: state.viewports.layout,
activeViewportIndex: state.viewports.activeViewportIndex,
activeTool: activeButton && activeButton.command
activeTool: activeButton && activeButton.command,
enableStackPrefetch
};
};

View File

@ -4,7 +4,8 @@ import FlexboxLayout from './FlexboxLayout';
const mapStateToProps = state => {
return {
leftSidebarOpen: state.ui.leftSidebarOpen,
rightSidebarOpen: state.ui.rightSidebarOpen
rightSidebarOpen: state.ui.rightSidebarOpen,
stackLoadingData: state.loading
};
};

View File

@ -63,23 +63,60 @@ const loadError = function(error, studyInstanceUid, displaySetInstanceUid) {
};
class FlexboxLayout extends Component {
constructor(props) {
super(props);
static propTypes = {
studies: PropTypes.array.isRequired,
leftSidebarOpen: PropTypes.bool.isRequired,
rightSidebarOpen: PropTypes.bool.isRequired,
stackLoadingData: PropTypes.object// { lastUpdated, progress }
};
this.state = {
studiesForBrowser: this.getStudiesForBrowser(),
};
this.getStudiesForBrowser = this.getStudiesForBrowser.bind(this);
this.getThumbnailsFromImageIds = this.getThumbnailsFromImageIds.bind(this);
}
state = {
studiesForBrowser: []
};
componentDidMount() {
this.getThumbnailsFromImageIds();
const studiesForBrowser = this.getStudiesForBrowser();
this.setState({
studiesForBrowser
});
this.getThumbnailsFromImageIds(studiesForBrowser);
}
getThumbnailsFromImageIds() {
this.state.studiesForBrowser.forEach(function (study) {
componentDidUpdate(prevProps) {
const { stackLoadingData } = this.props;
// If we know that the stack loading progress details have changed,
// we can try to update the component state so that the thumbnail
// progress bar is updated
if (stackLoadingData.lastUpdated !== prevProps.stackLoadingData.lastUpdated) {
const stackLoadingProgressMap = stackLoadingData.progress;
const studiesForBrowser = this.state.studiesForBrowser;
studiesForBrowser.forEach(study => {
study.thumbnails.forEach(data => {
const { displaySetInstanceUid } = data
const stackId = `StackProgress:${displaySetInstanceUid}`;
const stackProgressData = stackLoadingProgressMap[stackId];
let stackPercentComplete = 0;
if (stackProgressData) {
stackPercentComplete = stackProgressData.percentComplete;
}
data.stackPercentComplete = stackPercentComplete;
})
})
this.setState({
studiesForBrowser
});
}
}
getThumbnailsFromImageIds = (studiesForBrowser) => {
studiesForBrowser.forEach(function (study) {
const { studyInstanceUid } = study;
study.thumbnails.forEach(function (displaySet) {
@ -96,8 +133,9 @@ class FlexboxLayout extends Component {
}
getStudiesForBrowser() {
const { studies } = this.props;
getStudiesForBrowser = () => {
const { studies, stackLoadingData } = this.props;
const stackLoadingProgressMap = stackLoadingData.progress;
// TODO[react]:
// - Add sorting of display sets
@ -109,10 +147,18 @@ class FlexboxLayout extends Component {
const { studyInstanceUid } = study;
const thumbnails = study.displaySets.map((displaySet) => {
const active = false;
const stackPercentComplete = 0;
const { displaySetInstanceUid, seriesDescription, seriesNumber, instanceNumber, numImageFrames } = displaySet;
const active = false;
const stackId = `StackProgress:${displaySetInstanceUid}`;
const stackProgressData = stackLoadingProgressMap[stackId];
let stackPercentComplete = 0;
if (stackProgressData) {
stackPercentComplete = stackProgressData.percentComplete;
}
const imageId = displaySet.images[0].getImageId();
return {
imageSrc: '',
imageId,
@ -160,10 +206,4 @@ class FlexboxLayout extends Component {
}
}
FlexboxLayout.propTypes = {
studies: PropTypes.array.isRequired,
leftSidebarOpen: PropTypes.bool.isRequired,
rightSidebarOpen: PropTypes.bool.isRequired,
};
export default FlexboxLayout;

View File

@ -7,16 +7,13 @@ import cornerstoneMath from "cornerstone-math";
import { OHIF } from 'ohif-core';
import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport.js';
import ConnectedLayoutManager from './ConnectedLayoutManager.js';
import StackManager from '../lib/StackManager.js';
import './ViewerMain.css';
// Attempt to fix weird undefined dep issue
cornerstoneTools.external.cornerstoneMath = cornerstoneMath;
//const { StudyLoadingListener, StudyPrefetcher, ResizeViewportManager } = OHIF.classes;
//window.ResizeViewportManager = window.ResizeViewportManager || new ResizeViewportManager();
const { StudyLoadingListener, StudyPrefetcher } = OHIF.classes;
const { StackManager } = OHIF.utils;
function getCornerstoneStack(studies, viewportData) {
const {
@ -36,35 +33,21 @@ function getCornerstoneStack(studies, viewportData) {
}
class ViewerMain extends Component {
constructor(props) {
super(props);
this.state = {
displaySets: [],
viewportData: [],
};
this.getCornerstoneViewport = this.getCornerstoneViewport.bind(this);
}
state = {
displaySets: [],
viewportData: [],
};
componentDidMount() {
// Attach the Window resize listener
// Don't use jQuery here. "window.onresize" will always be null
// If its necessary, check all the code for window.onresize getter
// and change it to jQuery._data(window, 'events')['resize'].
// Otherwise this function will be probably overrided.
// See cineDialog instance.setResizeHandler function
//window.addEventListener('resize', window.ResizeViewportManager.getResizeHandler());
// Add beforeUnload event handler to check for unsaved changes
//window.addEventListener('beforeunload', unloadHandlers.beforeUnload);
const { studies } = this.props;
//this.studyPrefetcher = StudyPrefetcher.getInstance();
//this.studyPrefetcher.setStudies(studies);
//this.studyLoadingListener = StudyLoadingListener.getInstance();
//this.studyLoadingListener.clear();
//this.studyLoadingListener.addStudies(studies);
this.studyPrefetcher = StudyPrefetcher.getInstance(cornerstone, cornerstoneTools);
this.studyPrefetcher.setStudies(studies);
this.studyLoadingListener = StudyLoadingListener.getInstance(cornerstone);
this.studyLoadingListener.clear();
this.studyLoadingListener.addStudies(studies);
// Get all the display sets for the viewer studies
const displaySets = [];
@ -84,7 +67,7 @@ class ViewerMain extends Component {
});
}
getCornerstoneViewport(data, index) {
getCornerstoneViewport = (data, index) => {
// Clone the stack here so we don't mutate it later
const stack = Object.assign({}, getCornerstoneStack(this.props.studies, data));
stack.currentImageIdIndex = 0;
@ -130,9 +113,6 @@ class ViewerMain extends Component {
}
componentWillUnmount() {
// Remove the Window resize listener
//window.removeEventListener('resize', window.ResizeViewportManager.getResizeHandler());
// Remove beforeUnload event handler...
//window.removeEventListener('beforeunload', unloadHandlers.beforeUnload);
@ -140,13 +120,13 @@ class ViewerMain extends Component {
OHIF.viewer.updateImageSynchronizer.destroy();
// Stop prefetching when we close the viewer
//this.studyPrefetcher.destroy();
this.studyPrefetcher.destroy();
// Destroy stack loading listeners when we close the viewer
//this.studyLoadingListener.clear();
this.studyLoadingListener.clear();
// Clear references to all stacks in the StackManager
//OHIF.viewerbase.stackManager.clearStacks();
StackManager.clearStacks();
// @TypeSafeStudies
// Clears OHIF.viewer.Studies collection

View File

@ -3,7 +3,8 @@ import PropTypes from "prop-types";
import qs from 'query-string'
import Viewer from "./Viewer/Viewer";
import OHIF from 'ohif-core';
import createDisplaySets from "./lib/createDisplaySets";
const { createDisplaySets } = OHIF.utils;
function parseQueryAndFetchStudies(query) {
return new Promise((resolve, reject) => {

View File

@ -10,6 +10,8 @@ import ConnectedFlexboxLayout from '../FlexboxLayout/ConnectedFlexboxLayout.js';
import ConnectedToolbarRow from "../FlexboxLayout/ConnectedToolbarRow";
import './Viewer.css';
const { StackManager } = OHIF.utils;
/**
* Inits OHIF Hanging Protocol's onReady.
* It waits for OHIF Hanging Protocol to be ready to instantiate the ProtocolEngine
@ -65,13 +67,17 @@ OHIF.viewer.functionList = {
// Create the synchronizer used to update reference lines
OHIF.viewer.updateImageSynchronizer = new cornerstoneTools.Synchronizer('cornerstonenewimage', cornerstoneTools.updateImageSynchronizer);
OHIF.viewer.metadataProvider = new OHIF.cornerstone.MetadataProvider();
// Metadata configuration
const metadataProvider = OHIF.viewer.metadataProvider;
const metadataProvider = new OHIF.cornerstone.MetadataProvider();
cornerstone.metaData.addProvider(metadataProvider.provider.bind(metadataProvider));
StackManager.setMetadataProvider(metadataProvider);
class Viewer extends Component {
static propTypes = {
studies: PropTypes.array
};
constructor(props) {
super(props);
@ -97,8 +103,5 @@ class Viewer extends Component {
}
}
Viewer.propTypes = {
studies: PropTypes.array
};
export default Viewer;

View File

@ -2,7 +2,8 @@ import React, {Component} from "react";
import PropTypes from "prop-types";
import OHIF from 'ohif-core';
import Viewer from "./Viewer/Viewer.js";
import createDisplaySets from './lib/createDisplaySets.js';
const { createDisplaySets } = OHIF.utils;
class ViewerFromStudyData extends Component {
static propTypes = {

View File

@ -9,8 +9,6 @@ import sha from './sha.js';
import version from './version.js';
import { homepage } from '../package.json';
console.warn('cornerstoneMath');
console.warn(cornerstoneMath)
window.info = {
sha,
version,
@ -20,6 +18,7 @@ window.info = {
// For debugging
window.cornerstone = cornerstone;
window.cornerstoneTools = cornerstoneTools;
window.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
cornerstoneTools.external.cornerstone = cornerstone;
cornerstoneTools.external.Hammer = Hammer;
@ -29,6 +28,10 @@ cornerstoneTools.init();
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
OHIF.external.cornerstone = cornerstone;
OHIF.external.cornerstoneTools = cornerstoneTools;
OHIF.external.cornerstoneWADOImageLoader = cornerstoneWADOImageLoader;
// TODO: Is there a better way to guess ROOT_URL?
let ROOT_URL = homepage;

View File

@ -99,12 +99,12 @@ const servers = {
//"wadoUriRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/wado",
//"qidoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs",
//"wadoRoot": "http://localhost:8080/dcm4chee-arc/aets/DCM4CHEE/rs",
"wadoUriRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/wado",
"qidoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs",
"wadoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs",
//"wadoUriRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/wado",
//"qidoRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/rs",
//"wadoRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/rs",
//"wadoUriRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/wado",
//"qidoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs",
//"wadoRoot": "https://dcm4che.ohif.club/dcm4chee-arc/aets/DCM4CHEE/rs",
"wadoUriRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/wado",
"qidoRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/rs",
"wadoRoot": "https://cancer.crowds-cure.org/dcm4chee-arc/aets/DCM4CHEE/rs",
"qidoSupportsIncludeField": true,
"imageRendering": "wadors",
"thumbnailRendering": "wadors",
@ -114,7 +114,7 @@ const servers = {
"logResponses": false,
"logTiming": true,
//"auth": "admin:admin"
"auth": "cloud:healthcare"
//"auth": "cloud:healthcare"
}
}
]

View File

@ -1,151 +0,0 @@
import OHIF from 'ohif-core';
import { getImageId } from './getImageId';
let stackMap = {};
let configuration = {};
const stackUpdatedCallbacks = [];
const { OHIFError } = OHIF.classes;
/**
* Loop through the current series and add metadata to the
* Cornerstone meta data provider. This will be used to fill information
* into the viewport overlays, and to calculate reference lines and orientation markers
* @param {Object} stackMap stackMap object
* @param {Object} study Study object
* @param {Object} displaySet The set of images to make the stack from
* @return {Array} Array with image IDs
*/
function createAndAddStack(stackMap, study, displaySet) {
const metadataProvider = OHIF.viewer.metadataProvider;
const numImages = displaySet.images.length;
const imageIds = [];
let imageId;
displaySet.images.forEach((instance, imageIndex) => {
const image = instance.getData();
const metaData = {
instance: image, // in this context, instance will be the data of the InstanceMetadata object...
series: displaySet, // TODO: Check this
study,
numImages,
imageIndex: imageIndex + 1
};
const numberOfFrames = image.numberOfFrames;
if (numberOfFrames > 1) {
OHIF.log.info('Multiframe image detected');
for (let i = 0; i < numberOfFrames; i++) {
metaData.frameNumber = i;
imageId = getImageId(image, i);
imageIds.push(imageId);
metadataProvider.addMetadata(imageId, metaData);
}
} else {
metaData.frameNumber = 1;
imageId = getImageId(image);
imageIds.push(imageId);
metadataProvider.addMetadata(imageId, metaData);
}
});
const stack = {
displaySetInstanceUid: displaySet.displaySetInstanceUid,
imageIds,
frameRate: displaySet.frameRate,
isClip: displaySet.isClip
};
stackMap[displaySet.displaySetInstanceUid] = stack;
return stack;
}
configuration = {
createAndAddStack
};
/**
* This object contains all the functions needed for interacting with the stack manager.
* Generally, findStack is the only function used. If you want to know when new stacks
* come in, you can register a callback with addStackUpdatedCallback.
*/
const StackManager = {
/**
* Removes all current stacks
*/
clearStacks() {
stackMap = {};
},
/**
* Create a stack from an image set, as well as add in the metadata on a per image bases.
* @param study The study who's metadata will be added
* @param displaySet The set of images to make the stack from
* @return {Array} Array with image IDs
*/
makeAndAddStack(study, displaySet) {
return configuration.createAndAddStack(stackMap, study, displaySet, stackUpdatedCallbacks);
},
/**
* Find a stack from the currently created stacks.
* @param displaySetInstanceUid The UID of the stack to find.
* @returns {*} undefined if not found, otherwise the stack object is returned.
*/
findStack(displaySetInstanceUid) {
return stackMap[displaySetInstanceUid];
},
/**
* Find a stack or reate one if it has not been created yet
* @param study The study who's metadata will be added
* @param displaySet The set of images to make the stack from
* @return {Array} Array with image IDs
*/
findOrCreateStack(study, displaySet) {
let stack = this.findStack(displaySet.displaySetInstanceUid);
if (!stack || !stack.imageIds) {
stack = this.makeAndAddStack(study, displaySet);
}
return stack;
},
/**
* Gets the underlying map of displaySetInstanceUid to stack object.
* WARNING: Do not change this object. It directly affects the manager.
* @returns {{}} map of displaySetInstanceUid -> stack.
*/
getAllStacks() {
return stackMap;
},
/**
* Adds in a callback to be called on a stack being added / updated.
* @param callback must accept at minimum one argument,
* which is the stack that was added / updated.
*/
addStackUpdatedCallback(callback) {
if (typeof callback !== 'function') {
throw new OHIFError('callback must be provided as a function');
}
stackUpdatedCallbacks.push(callback);
},
/**
* Return configuration
*/
getConfiguration() {
return configuration;
},
/**
* Set configuration, in order to provide compatibility
* with other systems by overriding this functions
* @param {Object} config object with functions to be overrided
*
* For now, only makeAndAddStack can be overrided
*/
setConfiguration(config) {
configuration = config;
}
};
export { StackManager };
export default StackManager;

View File

@ -1,44 +0,0 @@
import OHIF from 'ohif-core';
import { sortingManager } from './sortingManager.js';
import { updateMetaDataManager } from './updateMetaDataManager.js';
// TODO: Move to react-viewerbase
export default function createDisplaySets(studies) {
// Define the OHIF.viewer.data global object
// TODO: Save all data that is currently in OHIF.viewer in redux instead
//OHIF.viewer.data = OHIF.viewer.data || {};
// @TypeSafeStudies
// Clears OHIF.viewer.Studies collection
//OHIF.viewer.Studies.removeAll();
// @TypeSafeStudies
// Clears OHIF.viewer.StudyMetadataList collection
//OHIF.viewer.StudyMetadataList.removeAll();
//OHIF.viewer.data.studyInstanceUids = [];
const updatedStudies = studies.map(study => {
const studyMetadata = new OHIF.metadata.OHIFStudyMetadata(study, study.studyInstanceUid);
let displaySets = study.displaySets;
if (!study.displaySets) {
displaySets = sortingManager.getDisplaySets(studyMetadata);
study.displaySets = displaySets;
}
studyMetadata.setDisplaySets(displaySets);
study.selected = true;
//OHIF.viewer.Studies.insert(study);
//OHIF.viewer.StudyMetadataList.insert(studyMetadata);
//OHIF.viewer.data.studyInstanceUids.push(study.studyInstanceUid);
// Updates WADO-RS metaDataManager
updateMetaDataManager(study);
return study;
});
return updatedStudies;
}

View File

@ -1,128 +0,0 @@
import OHIF from 'ohif-core';
import { isImage } from './isImage';
const { ImageSet } = OHIF.classes;
const isMultiFrame = instance => {
// NumberOfFrames (0028,0008)
return instance.getRawValue('x00280008') > 1;
};
const makeDisplaySet = (series, instances) => {
const instance = instances[0];
const imageSet = new ImageSet(instances);
const seriesData = series.getData();
// set appropriate attributes to image set...
imageSet.setAttributes({
displaySetInstanceUid: imageSet.uid, // create a local alias for the imageSet UID
seriesDate: seriesData.seriesDate,
seriesTime: seriesData.seriesTime,
seriesInstanceUid: series.getSeriesInstanceUID(),
seriesNumber: instance.getRawValue('x00200011'),
seriesDescription: instance.getRawValue('x0008103e'),
numImageFrames: instances.length,
frameRate: instance.getRawValue('x00181063'),
modality: instance.getRawValue('x00080060'),
isMultiFrame: isMultiFrame(instance)
});
// Sort the images in this series if needed
const shallSort = true//!OHIF.utils.ObjectPath.get(Meteor, 'settings.public.ui.sortSeriesByIncomingOrder');
if (shallSort) {
imageSet.sortBy((a, b) => {
// Sort by InstanceNumber (0020,0013)
return (parseInt(a.getRawValue('x00200013', 0)) || 0) - (parseInt(b.getRawValue('x00200013', 0)) || 0);
});
}
// Include the first image instance number (after sorted)
imageSet.setAttribute('instanceNumber', imageSet.getImage(0).getRawValue('x00200013'));
return imageSet;
};
const isSingleImageModality = modality => {
return (modality === 'CR' ||
modality === 'MG' ||
modality === 'DX');
};
/**
* Creates a set of series to be placed in the Study Metadata
* The series that appear in the Study Metadata must represent
* imaging modalities.
*
* Furthermore, for drag/drop functionality,
* it is easiest if the stack objects also contain information about
* which study they are linked to.
*
* @param study The study instance metadata to be used
* @returns {Array} An array of series to be placed in the Study Metadata
*/
const createStacks = study => {
// Define an empty array of display sets
const displaySets = [];
if (!study || !study.getSeriesCount()) {
return displaySets;
}
// Loop through the series (SeriesMetadata)
study.forEachSeries(series => {
// If the series has no instances, skip it
if (!series.getInstanceCount()) {
return;
}
// Search through the instances (InstanceMedatada object) of this series
// Split Multi-frame instances and Single-image modalities
// into their own specific display sets. Place the rest of each
// series into another display set.
const stackableInstances = [];
series.forEachInstance(instance => {
// All imaging modalities must have a valid value for sopClassUid (x00080016) or rows (x00280010)
if (!isImage(instance.getRawValue('x00080016')) && !instance.getRawValue('x00280010')) {
return;
}
let displaySet;
if (isMultiFrame(instance)) {
displaySet = makeDisplaySet(series, [ instance ]);
displaySet.setAttributes({
isClip: true,
studyInstanceUid: study.getStudyInstanceUID(), // Include the study instance Uid for drag/drop purposes
numImageFrames: instance.getRawValue('x00280008'), // Override the default value of instances.length
instanceNumber: instance.getRawValue('x00200013'), // Include the instance number
acquisitionDatetime: instance.getRawValue('x0008002a') // Include the acquisition datetime
});
displaySets.push(displaySet);
} else if (isSingleImageModality(instance.modality)) {
displaySet = makeDisplaySet(series, [ instance ]);
displaySet.setAttributes({
studyInstanceUid: study.getStudyInstanceUID(), // Include the study instance Uid
instanceNumber: instance.getRawValue('x00200013'), // Include the instance number
acquisitionDatetime: instance.getRawValue('x0008002a') // Include the acquisition datetime
});
displaySets.push(displaySet);
} else {
stackableInstances.push(instance);
}
});
if (stackableInstances.length) {
const displaySet = makeDisplaySet(series, stackableInstances);
displaySet.setAttribute('studyInstanceUid', study.getStudyInstanceUID());
displaySets.push(displaySet);
}
});
return displaySets;
};
/**
* Expose "createStacks"...
*/
export { createStacks };

View File

@ -1,51 +0,0 @@
import { getWADORSImageId } from './getWADORSImageId';
// https://stackoverflow.com/a/6021027/3895126
function updateQueryStringParameter(uri, key, value) {
const regex = new RegExp('([?&])' + key + '=.*?(&|$)', 'i');
const separator = uri.indexOf('?') !== -1 ? '&' : '?';
if (uri.match(regex)) {
return uri.replace(regex, '$1' + key + '=' + value + '$2');
} else {
return uri + separator + key + '=' + value;
}
}
/**
* Obtain an imageId for Cornerstone from an image instance
*
* @param instance
* @param frame
* @param thumbnail
* @returns {string} The imageId to be used by Cornerstone
*/
export function getImageId(instance, frame, thumbnail=false) {
if (!instance) {
return;
}
if (typeof instance.getImageId === 'function') {
return instance.getImageId();
}
if (instance.url) {
if (frame !== undefined) {
instance.url = updateQueryStringParameter(instance.url, 'frame', frame);
}
return instance.url;
}
const renderingAttr = thumbnail ? 'thumbnailRendering' : 'imageRendering';
if (!instance[renderingAttr] || instance[renderingAttr] === 'wadouri' || !instance.wadorsuri) {
let imageId = 'dicomweb:' + instance.wadouri;
if (frame !== undefined) {
imageId += '&frame=' + frame;
}
return imageId;
} else {
return getWADORSImageId(instance, frame, thumbnail); // WADO-RS Retrieve Frame
}
}

View File

@ -1,31 +0,0 @@
function getWADORSImageUrl(instance, frame) {
let wadorsuri = instance.wadorsuri;
if (!wadorsuri) {
return;
}
// We need to sum 1 because WADO-RS frame number is 1-based
frame = (frame || 0) + 1;
// Replaces /frame/1 by /frame/{frame}
wadorsuri = wadorsuri.replace(/(%2Fframes%2F)(\d+)/, `$1${frame}`);
return wadorsuri;
}
/**
* Obtain an imageId for Cornerstone based on the WADO-RS scheme
*
* @param {object} instanceMetada metadata object (InstanceMetadata)
* @returns {string} The imageId to be used by Cornerstone
*/
export function getWADORSImageId(instance, frame) {
const uri = getWADORSImageUrl(instance, frame);
if (!uri) {
return;
}
return `wadors:${uri}`;
}

View File

@ -1,61 +0,0 @@
import { sopClassDictionary } from './sopClassDictionary';
/**
* Checks whether dicom files with specified SOP Class UID have image data
* @param {string} sopClassUid - SOP Class UID to be checked
* @returns {boolean} - true if it has image data
*/
export function isImage(sopClassUid) {
if (sopClassUid === sopClassDictionary.ComputedRadiographyImageStorage
|| sopClassUid === sopClassDictionary.DigitalXRayImageStorageForPresentation
|| sopClassUid === sopClassDictionary.DigitalXRayImageStorageForProcessing
|| sopClassUid === sopClassDictionary.DigitalMammographyXRayImageStorageForPresentation
|| sopClassUid === sopClassDictionary.DigitalMammographyXRayImageStorageForProcessing
|| sopClassUid === sopClassDictionary.DigitalIntraOralXRayImageStorageForPresentation
|| sopClassUid === sopClassDictionary.DigitalIntraOralXRayImageStorageForProcessing
|| sopClassUid === sopClassDictionary.CTImageStorage
|| sopClassUid === sopClassDictionary.EnhancedCTImageStorage
|| sopClassUid === sopClassDictionary.LegacyConvertedEnhancedCTImageStorage
|| sopClassUid === sopClassDictionary.UltrasoundMultiframeImageStorage
|| sopClassUid === sopClassDictionary.MRImageStorage
|| sopClassUid === sopClassDictionary.EnhancedMRImageStorage
|| sopClassUid === sopClassDictionary.EnhancedMRColorImageStorage
|| sopClassUid === sopClassDictionary.LegacyConvertedEnhancedMRImageStorage
|| sopClassUid === sopClassDictionary.UltrasoundImageStorage
|| sopClassUid === sopClassDictionary.SecondaryCaptureImageStorage
|| sopClassUid === sopClassDictionary.MultiframeSingleBitSecondaryCaptureImageStorage
|| sopClassUid === sopClassDictionary.MultiframeGrayscaleByteSecondaryCaptureImageStorage
|| sopClassUid === sopClassDictionary.MultiframeGrayscaleWordSecondaryCaptureImageStorage
|| sopClassUid === sopClassDictionary.MultiframeTrueColorSecondaryCaptureImageStorage
|| sopClassUid === sopClassDictionary.XRayAngiographicImageStorage
|| sopClassUid === sopClassDictionary.EnhancedXAImageStorage
|| sopClassUid === sopClassDictionary.XRayRadiofluoroscopicImageStorage
|| sopClassUid === sopClassDictionary.EnhancedXRFImageStorage
|| sopClassUid === sopClassDictionary.XRay3DAngiographicImageStorage
|| sopClassUid === sopClassDictionary.XRay3DCraniofacialImageStorage
|| sopClassUid === sopClassDictionary.BreastTomosynthesisImageStorage
|| sopClassUid === sopClassDictionary.BreastProjectionXRayImageStorageForPresentation
|| sopClassUid === sopClassDictionary.BreastProjectionXRayImageStorageForProcessing
|| sopClassUid === sopClassDictionary.IntravascularOpticalCoherenceTomographyImageStorageForPresentation
|| sopClassUid === sopClassDictionary.IntravascularOpticalCoherenceTomographyImageStorageForProcessing
|| sopClassUid === sopClassDictionary.NuclearMedicineImageStorage
|| sopClassUid === sopClassDictionary.VLEndoscopicImageStorage
|| sopClassUid === sopClassDictionary.VideoEndoscopicImageStorage
|| sopClassUid === sopClassDictionary.VLMicroscopicImageStorage
|| sopClassUid === sopClassDictionary.VideoMicroscopicImageStorage
|| sopClassUid === sopClassDictionary.VLSlideCoordinatesMicroscopicImageStorage
|| sopClassUid === sopClassDictionary.VLPhotographicImageStorage
|| sopClassUid === sopClassDictionary.VideoPhotographicImageStorage
|| sopClassUid === sopClassDictionary.OphthalmicPhotography8BitImageStorage
|| sopClassUid === sopClassDictionary.OphthalmicPhotography16BitImageStorage
|| sopClassUid === sopClassDictionary.OphthalmicTomographyImageStorage
|| sopClassUid === sopClassDictionary.VLWholeSlideMicroscopyImageStorage
|| sopClassUid === sopClassDictionary.PositronEmissionTomographyImageStorage
|| sopClassUid === sopClassDictionary.EnhancedPETImageStorage
|| sopClassUid === sopClassDictionary.LegacyConvertedEnhancedPETImageStorage
|| sopClassUid === sopClassDictionary.RTImageStorage) {
return true;
}
return false;
}

View File

@ -1,116 +0,0 @@
// TODO: Deprecate since we have the same thing in dcmjs?
export const sopClassDictionary = {
ComputedRadiographyImageStorage: "1.2.840.10008.5.1.4.1.1.1",
DigitalXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.1.1",
DigitalXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.1.1.1",
DigitalMammographyXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.1.2",
DigitalMammographyXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.1.2.1",
DigitalIntraOralXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.1.3",
DigitalIntraOralXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.1.3.1",
CTImageStorage: "1.2.840.10008.5.1.4.1.1.2",
EnhancedCTImageStorage: "1.2.840.10008.5.1.4.1.1.2.1",
LegacyConvertedEnhancedCTImageStorage: "1.2.840.10008.5.1.4.1.1.2.2",
UltrasoundMultiframeImageStorage: "1.2.840.10008.5.1.4.1.1.3.1",
MRImageStorage: "1.2.840.10008.5.1.4.1.1.4",
EnhancedMRImageStorage: "1.2.840.10008.5.1.4.1.1.4.1",
MRSpectroscopyStorage: "1.2.840.10008.5.1.4.1.1.4.2",
EnhancedMRColorImageStorage: "1.2.840.10008.5.1.4.1.1.4.3",
LegacyConvertedEnhancedMRImageStorage: "1.2.840.10008.5.1.4.1.1.4.4",
UltrasoundImageStorage: "1.2.840.10008.5.1.4.1.1.6.1",
EnhancedUSVolumeStorage: "1.2.840.10008.5.1.4.1.1.6.2",
SecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7",
MultiframeSingleBitSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.1",
MultiframeGrayscaleByteSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.2",
MultiframeGrayscaleWordSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.3",
MultiframeTrueColorSecondaryCaptureImageStorage: "1.2.840.10008.5.1.4.1.1.7.4",
Sop12LeadECGWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.1.1",
GeneralECGWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.1.2",
AmbulatoryECGWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.1.3",
HemodynamicWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.2.1",
CardiacElectrophysiologyWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.3.1",
BasicVoiceAudioWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.4.1",
GeneralAudioWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.4.2",
ArterialPulseWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.5.1",
RespiratoryWaveformStorage: "1.2.840.10008.5.1.4.1.1.9.6.1",
GrayscaleSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.1",
ColorSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.2",
PseudoColorSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.3",
BlendingSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.4",
XAXRFGrayscaleSoftcopyPresentationStateStorage: "1.2.840.10008.5.1.4.1.1.11.5",
XRayAngiographicImageStorage: "1.2.840.10008.5.1.4.1.1.12.1",
EnhancedXAImageStorage: "1.2.840.10008.5.1.4.1.1.12.1.1",
XRayRadiofluoroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.12.2",
EnhancedXRFImageStorage: "1.2.840.10008.5.1.4.1.1.12.2.1",
XRay3DAngiographicImageStorage: "1.2.840.10008.5.1.4.1.1.13.1.1",
XRay3DCraniofacialImageStorage: "1.2.840.10008.5.1.4.1.1.13.1.2",
BreastTomosynthesisImageStorage: "1.2.840.10008.5.1.4.1.1.13.1.3",
BreastProjectionXRayImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.13.1.4",
BreastProjectionXRayImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.13.1.5",
IntravascularOpticalCoherenceTomographyImageStorageForPresentation: "1.2.840.10008.5.1.4.1.1.14.1",
IntravascularOpticalCoherenceTomographyImageStorageForProcessing: "1.2.840.10008.5.1.4.1.1.14.2",
NuclearMedicineImageStorage: "1.2.840.10008.5.1.4.1.1.20",
RawDataStorage: "1.2.840.10008.5.1.4.1.1.66",
SpatialRegistrationStorage: "1.2.840.10008.5.1.4.1.1.66.1",
SpatialFiducialsStorage: "1.2.840.10008.5.1.4.1.1.66.2",
DeformableSpatialRegistrationStorage: "1.2.840.10008.5.1.4.1.1.66.3",
SegmentationStorage: "1.2.840.10008.5.1.4.1.1.66.4",
SurfaceSegmentationStorage: "1.2.840.10008.5.1.4.1.1.66.5",
RealWorldValueMappingStorage: "1.2.840.10008.5.1.4.1.1.67",
SurfaceScanMeshStorage: "1.2.840.10008.5.1.4.1.1.68.1",
SurfaceScanPointCloudStorage: "1.2.840.10008.5.1.4.1.1.68.2",
VLEndoscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.1",
VideoEndoscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.1.1",
VLMicroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.2",
VideoMicroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.2.1",
VLSlideCoordinatesMicroscopicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.3",
VLPhotographicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.4",
VideoPhotographicImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.4.1",
OphthalmicPhotography8BitImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.1",
OphthalmicPhotography16BitImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.2",
StereometricRelationshipStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.3",
OphthalmicTomographyImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.5.4",
VLWholeSlideMicroscopyImageStorage: "1.2.840.10008.5.1.4.1.1.77.1.6",
LensometryMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.1",
AutorefractionMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.2",
KeratometryMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.3",
SubjectiveRefractionMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.4",
VisualAcuityMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.5",
SpectaclePrescriptionReportStorage: "1.2.840.10008.5.1.4.1.1.78.6",
OphthalmicAxialMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.78.7",
IntraocularLensCalculationsStorage: "1.2.840.10008.5.1.4.1.1.78.8",
MacularGridThicknessandVolumeReport: "1.2.840.10008.5.1.4.1.1.79.1",
OphthalmicVisualFieldStaticPerimetryMeasurementsStorage: "1.2.840.10008.5.1.4.1.1.80.1",
OphthalmicThicknessMapStorage: "1.2.840.10008.5.1.4.1.1.81.1",
CornealTopographyMapStorage: "1.2.840.10008.5.1.4.1.1.82.1",
BasicTextSR: "1.2.840.10008.5.1.4.1.1.88.11",
EnhancedSR: "1.2.840.10008.5.1.4.1.1.88.22",
ComprehensiveSR: "1.2.840.10008.5.1.4.1.1.88.33",
Comprehensive3DSR: "1.2.840.10008.5.1.4.1.1.88.34",
ProcedureLog: "1.2.840.10008.5.1.4.1.1.88.40",
MammographyCADSR: "1.2.840.10008.5.1.4.1.1.88.50",
KeyObjectSelection: "1.2.840.10008.5.1.4.1.1.88.59",
ChestCADSR: "1.2.840.10008.5.1.4.1.1.88.65",
XRayRadiationDoseSR: "1.2.840.10008.5.1.4.1.1.88.67",
RadiopharmaceuticalRadiationDoseSR: "1.2.840.10008.5.1.4.1.1.88.68",
ColonCADSR: "1.2.840.10008.5.1.4.1.1.88.69",
ImplantationPlanSRDocumentStorage: "1.2.840.10008.5.1.4.1.1.88.70",
EncapsulatedPDFStorage: "1.2.840.10008.5.1.4.1.1.104.1",
EncapsulatedCDAStorage: "1.2.840.10008.5.1.4.1.1.104.2",
PositronEmissionTomographyImageStorage: "1.2.840.10008.5.1.4.1.1.128",
EnhancedPETImageStorage: "1.2.840.10008.5.1.4.1.1.130",
LegacyConvertedEnhancedPETImageStorage: "1.2.840.10008.5.1.4.1.1.128.1",
BasicStructuredDisplayStorage: "1.2.840.10008.5.1.4.1.1.131",
RTImageStorage: "1.2.840.10008.5.1.4.1.1.481.1",
RTDoseStorage: "1.2.840.10008.5.1.4.1.1.481.2",
RTStructureSetStorage: "1.2.840.10008.5.1.4.1.1.481.3",
RTBeamsTreatmentRecordStorage: "1.2.840.10008.5.1.4.1.1.481.4",
RTPlanStorage: "1.2.840.10008.5.1.4.1.1.481.5",
RTBrachyTreatmentRecordStorage: "1.2.840.10008.5.1.4.1.1.481.6",
RTTreatmentSummaryRecordStorage: "1.2.840.10008.5.1.4.1.1.481.7",
RTIonPlanStorage: "1.2.840.10008.5.1.4.1.1.481.8",
RTIonBeamsTreatmentRecordStorage: "1.2.840.10008.5.1.4.1.1.481.9",
RTBeamsDeliveryInstructionStorage: "1.2.840.10008.5.1.4.34.7",
GenericImplantTemplateStorage: "1.2.840.10008.5.1.4.43.1",
ImplantAssemblyTemplateStorage: "1.2.840.10008.5.1.4.44.1",
ImplantTemplateGroupStorage: "1.2.840.10008.5.1.4.45.1"
};

View File

@ -1,13 +0,0 @@
import { createStacks } from './createStacks';
const getDisplaySets = (studyMetadata, seriesNumber, iteratorFunction) => {
const iteratorFn = typeof iteratorFunction !== 'function' ? createStacks : iteratorFunction;
return iteratorFn(studyMetadata, seriesNumber);
};
const sortingManager = {
getDisplaySets
};
export { sortingManager };

View File

@ -1,88 +0,0 @@
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
import OHIF from 'ohif-core';
import { getWADORSImageId } from './getWADORSImageId';
function getRadiopharmaceuticalInfoMetaData(instance) {
const radiopharmaceuticalInfo = instance.radiopharmaceuticalInfo;
if ((instance.modality !== 'PT') || !radiopharmaceuticalInfo) {
return;
}
const { WadoRsMetaDataBuilder } = OHIF.metadata;
return new WadoRsMetaDataBuilder()
.addTag('00181072', radiopharmaceuticalInfo.radiopharmaceuticalStartTime)
.addTag('00181074', radiopharmaceuticalInfo.radionuclideTotalDose)
.addTag('00181075', radiopharmaceuticalInfo.radionuclideHalfLife)
.toJSON();
}
const getWadoRsInstanceMetaData = (study, series, instance) => {
const { WadoRsMetaDataBuilder } = OHIF.metadata;
return new WadoRsMetaDataBuilder()
.addTag('00080016', instance.sopClassUid)
.addTag('00080018', instance.sopInstanceUid)
.addTag('00080021', series.seriesDate)
.addTag('00080031', series.seriesTime)
.addTag('00080060', instance.modality)
.addTag('00101010', study.patientAge)
.addTag('00101020', study.patientSize)
.addTag('00101030', study.patientWeight)
.addTag('00180050', instance.sliceThickness)
.addTag('0020000e', series.seriesInstanceUid)
.addTag('00200011', series.seriesNumber)
.addTag('0020000d', study.studyInstanceUid)
.addTag('00200013', instance.instanceNumber)
.addTag('00200032', instance.imagePositionPatient, true)
.addTag('00200037', instance.imageOrientationPatient, true)
.addTag('00200052', instance.frameOfReferenceUID)
.addTag('00201041', instance.sliceLocation)
.addTag('00280002', instance.samplesPerPixel)
.addTag('00280004', instance.photometricInterpretation)
.addTag('00280006', instance.planarConfiguration)
.addTag('00280010', instance.rows)
.addTag('00280011', instance.columns)
.addTag('00280030', instance.pixelSpacing, true)
.addTag('00280034', instance.pixelAspectRatio, true)
.addTag('00280100', instance.bitsAllocated)
.addTag('00280101', instance.bitsStored)
.addTag('00280102', instance.highBit)
.addTag('00280103', instance.pixelRepresentation)
.addTag('00280106', instance.smallestPixelValue)
.addTag('00280107', instance.largestPixelValue)
.addTag('00281050', instance.windowCenter, true)
.addTag('00281051', instance.windowWidth, true)
.addTag('00281052', instance.rescaleIntercept)
.addTag('00281053', instance.rescaleSlope)
.addTag('00281054', instance.rescaleType)
.addTag('00281101', instance.redPaletteColorLookupTableDescriptor)
.addTag('00281102', instance.greenPaletteColorLookupTableDescriptor)
.addTag('00281103', instance.bluePaletteColorLookupTableDescriptor)
.addTag('00281201', instance.redPaletteColorLookupTableData)
.addTag('00281202', instance.greenPaletteColorLookupTableData)
.addTag('00281203', instance.bluePaletteColorLookupTableData)
.addTag('00540016', getRadiopharmaceuticalInfoMetaData(instance))
.toJSON();
};
export function updateMetaDataManager(study) {
study.seriesList.forEach(series => {
series.instances.forEach(instance => {
// Cache just images that are going to be loaded via WADO-RS
if ((instance.imageRendering !== 'wadors') && (instance.thumbnailRendering !== 'wadors')) {
return;
}
const metaData = getWadoRsInstanceMetaData(study, series, instance);
const numberOfFrames = instance.numberOfFrames || 1;
// We can share the same metaData with all frames because it doesn't have
// any frame specific data, such as frameNumber, pixelData, offset, etc.
// WADO-RS frame number is 1-based
for (let frameNumber = 0; frameNumber < numberOfFrames; frameNumber++) {
const imageId = getWADORSImageId(instance, frameNumber);
cornerstoneWADOImageLoader.wadors.metaDataManager.add(imageId, metaData);
}
});
});
}

View File

@ -36,4 +36,17 @@
--text-secondary-color: #91b9cd;
--large-numbers-color: #6fbde2;
--text-disabled-color: #878787;
--input-background-color: #2c363f;
--input-placeholder-color: #d3d3d3;
--table-hover-color: #2c363f;
--table-text-primary-color: #ffffff;
--table-text-secondary-color: #91b9cd;
--large-numbers-color:#6FBDE2;
--state-error: #FFCCCC;
--state-error-border: #FFCCCC;
--state-error-text: #FFCCCC;
}

View File

@ -2519,14 +2519,6 @@ cornerstone-math@0.1.7, cornerstone-math@^0.1.7:
resolved "https://registry.yarnpkg.com/cornerstone-math/-/cornerstone-math-0.1.7.tgz#7c55536f02e7221b05fc49a4c780bb82c234ce39"
integrity sha512-svsDSoqLNFM9niCV2AtV2DNmRkYztT1v80lVdbA5hkgdGdpWg5/h2On794zbqBK+aI3hNOHufERDsI4wdXGknA==
cornerstone-tools@3.0.0-b.1471:
version "3.0.0-b.1471"
resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-3.0.0-b.1471.tgz#9bbfd6e21ec6acfbc7b497ff16fe897a03cac899"
integrity sha512-XyNOH0ptfZBAW0xghnhyVVc9PqteBRH9OUiLcBoVQSh1GvY0lsmGZURUF/5a4VbnfmXaFuV1niAkODP1zP7mbA==
dependencies:
"@babel/runtime" "7.1.2"
cornerstone-math "^0.1.7"
cornerstone-tools@3.0.0-b.1512:
version "3.0.0-b.1512"
resolved "https://registry.yarnpkg.com/cornerstone-tools/-/cornerstone-tools-3.0.0-b.1512.tgz#64e60709b7b638c08d201313a5748aa6f5145337"
@ -8317,17 +8309,11 @@ react-bootstrap-modal@^4.2.0:
react-overlays "^0.8.0"
react-transition-group "^2.0.0"
react-cornerstone-viewport@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.4.tgz#6e1e1e31cbfca28617d4aebddd591f3a13206382"
integrity sha512-bowfkjKR/hdg+2nEQy2BJejjKIsudiedJrMKehygK2f0SI17w2FC8r8QJPysdykfwz1WuBouOlA0JJtfHzk+CQ==
react-cornerstone-viewport@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/react-cornerstone-viewport/-/react-cornerstone-viewport-0.1.5.tgz#64809bcba4942710c1cd7dddd63ab14cb682b31f"
integrity sha512-ANSjQvYOc533k055j4/sV+SrOgmLoEI7ssQ6MiHn5j3E3po30ooUYMWc56fsDxEH/lxFt5VtcFyHYgJdI4RHig==
dependencies:
cornerstone-core "2.2.8"
cornerstone-math "0.1.7"
cornerstone-tools "3.0.0-b.1471"
cornerstone-wado-image-loader "2.2.3"
dicom-parser "1.8.3"
hammerjs "^2.0.8"
lodash.debounce "^4.0.8"
moment "^2.23.0"
stylus "^0.54.5"
@ -8521,10 +8507,10 @@ react-transition-group@^2.0.0, react-transition-group@^2.2.0:
prop-types "^15.6.2"
react-lifecycles-compat "^3.0.4"
react-viewerbase@^0.1.9:
version "0.1.9"
resolved "https://registry.yarnpkg.com/react-viewerbase/-/react-viewerbase-0.1.9.tgz#9d6930cc22149dd34bbf5d088218f86a804f0260"
integrity sha512-0wpEATJxdZAUT8dqUJa4Q0FSv7Zo+HLh969uDXx+x9ZtlZi2JgsU8Hb97D4ump16LHfg6OG5uQqJPcQzrDGWYA==
react-viewerbase@^0.1.10:
version "0.1.10"
resolved "https://registry.yarnpkg.com/react-viewerbase/-/react-viewerbase-0.1.10.tgz#7b5d2d9adcea20174a863c0291eca6a728d8da6a"
integrity sha512-I6f6n3wgGD9aHyMTta9CIae3BXWk6TTmeAmqaYrfR+uSTfnjBsARmaj2FKtnusGLc2XCZlsSKUQRHSVL6NH3Kg==
dependencies:
classnames "^2.2.6"
react-bootstrap-modal "^4.2.0"