Integrate Study Prefetcher and Study Loading Listener
This commit is contained in:
parent
8962f957d8
commit
3954c32441
16
OHIFViewer-react/.eslintrc
Normal file
16
OHIFViewer-react/.eslintrc
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": [
|
||||
"react-app",
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended"
|
||||
],
|
||||
"parser": "babel-eslint",
|
||||
"env": {
|
||||
"jest": true
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect",
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
@ -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 };
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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}`;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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"
|
||||
};
|
||||
@ -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 };
|
||||
@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user