WIP SR viewport.
This commit is contained in:
1 parent
8674e676b7
commit
558ab00c23
22 files changed
+904
-8
No files matched your search
@@ -0,0 +1,8 @@
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
return webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge');
|
||||
const path = require('path');
|
||||
const webpackCommon = require('./../../../.webpack/webpack.commonjs.js');
|
||||
const pkg = require('./../package.json');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, './..');
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
const commonConfig = webpackCommon(env, argv, { SRC_DIR, DIST_DIR });
|
||||
|
||||
return merge(commonConfig, {
|
||||
devtool: 'source-map',
|
||||
stats: {
|
||||
colors: true,
|
||||
hash: true,
|
||||
timings: true,
|
||||
assets: true,
|
||||
chunks: false,
|
||||
chunkModules: false,
|
||||
modules: false,
|
||||
children: false,
|
||||
warnings: true,
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
sideEffects: true,
|
||||
},
|
||||
output: {
|
||||
path: ROOT_DIR,
|
||||
library: 'OHIFExtCornerstone',
|
||||
libraryTarget: 'umd',
|
||||
libraryExport: 'default',
|
||||
filename: pkg.main,
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.LimitChunkCountPlugin({
|
||||
maxChunks: 1,
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Open Health Imaging Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,121 @@
|
||||
# @ohif/extension-cornerstone
|
||||
|
||||

|
||||
|
||||
This extension adds support for viewing and manipulating 2D medical images via a
|
||||
viewport. The underlying implementation wraps the
|
||||
`cornerstonejs/react-cornerstone-viewport`, and provides basic commands and
|
||||
toolbar buttons for common actions.
|
||||
|
||||
<!-- TODO: Simple image or GIF? -->
|
||||
|
||||
#### Index
|
||||
|
||||
Extension Id: `cornerstone`
|
||||
|
||||
- [Commands Module](#commands-module)
|
||||
- [Toolbar Module](#toolbar-module)
|
||||
- [Viewport Module](#viewport-module)
|
||||
|
||||
## Commands Module
|
||||
|
||||
This extensions includes the following `Commands` and `Command Definitions`.
|
||||
These can be registered with `@ohif/core`'s `CommandManager`. After registering
|
||||
the commands, they can be bound to `hotkeys` using the `HotkeysManager` and
|
||||
listed in the `UserPreferences` modal.
|
||||
|
||||
You can read more about [`Commands`][docs-commands], [`Hotkeys`][docs-hotkeys],
|
||||
and the [`UserPreferences` Modal][docs-userprefs] in their respective locations
|
||||
in the OHIF Viewer's documentation.
|
||||
|
||||
| Command Name | Description | Store Contexts |
|
||||
| ---------------------------- | --------------------------------------- | -------------- |
|
||||
| `rotateViewportCW` | | viewports |
|
||||
| `rotateViewportCCW` | | viewports |
|
||||
| `invertViewport` | | viewports |
|
||||
| `flipViewportVertical` | | viewports |
|
||||
| `flipViewportHorizontal` | | viewports |
|
||||
| `scaleUpViewport` | | viewports |
|
||||
| `scaleDownViewport` | | viewports |
|
||||
| `fitViewportToWindow` | | viewports |
|
||||
| `resetViewport` | | viewports |
|
||||
| clearAnnotations | TODO | |
|
||||
| next/previous Image | TODO | |
|
||||
| first/last Image | TODO | |
|
||||
| `nextViewportDisplaySet` | | |
|
||||
| `previousViewportDisplaySet` | | |
|
||||
| `setToolActive` | Activates tool for primary button/touch | |
|
||||
|
||||
## Toolbar Module
|
||||
|
||||
Our toolbar module contains definitions for:
|
||||
|
||||
- `StackScroll`
|
||||
- `Zoom`
|
||||
- `Wwwc`
|
||||
- `Pan`
|
||||
- `Length`
|
||||
- `Angle`
|
||||
- `Reset`
|
||||
- `Cine`
|
||||
|
||||
All use the `ACTIVE_VIEWPORT::CORNERSTONE` context.
|
||||
|
||||
## Viewport Module
|
||||
|
||||
Our Viewport wraps [cornerstonejs/react-cornerstone-viewport][react-viewport]
|
||||
and is connected the redux store. This module is the most prone to change as we
|
||||
hammer out our Viewport interface.
|
||||
|
||||
## Tool Configuration
|
||||
|
||||
Tools can be configured through extension configuration using the tools key:
|
||||
|
||||
```js
|
||||
...
|
||||
cornerstoneExtensionConfig: {
|
||||
tools: {
|
||||
ArrowAnnotate: {
|
||||
configuration: {
|
||||
getTextCallback: (callback, eventDetails) => callback(prompt('Enter your custom annotation')),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
## Annotate Tools Configuration
|
||||
|
||||
*We currently support one property for annotation tools.*
|
||||
|
||||
### Hide handles
|
||||
This extension configuration allows you to toggle on/off handle rendering for all annotate tools:
|
||||
|
||||
```js
|
||||
...
|
||||
cornerstoneExtensionConfig: {
|
||||
hideHandles: true,
|
||||
},
|
||||
...
|
||||
|
||||
## Resources
|
||||
|
||||
### Repositories
|
||||
|
||||
- [cornerstonejs/react-cornerstone-viewport][react-viewport]
|
||||
- [cornerstonejs/cornerstoneTools][cornerstone-tools]
|
||||
- [cornerstonejs/cornerstone][cornerstone]
|
||||
|
||||
<!--
|
||||
Links
|
||||
-->
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[docs-commands]: https://www.com
|
||||
[docs-hotkeys]: https://www.com
|
||||
[docs-userprefs]: htt
|
||||
[react-viewport]: https://github.com/cornerstonejs/react-cornerstone-viewport
|
||||
[cornerstone-tools]: https://github.com/cornerstonejs/cornerstoneTools
|
||||
[cornerstone]: https://github.com/cornerstonejs/cornerstone
|
||||
<!-- prettier-ignore-end -->
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('../../babel.config.js');
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "@ohif/extension-dicom-sr",
|
||||
"version": "0.0.1",
|
||||
"description": "OHIF extension for an SR Cornerstone Viewport",
|
||||
"author": "OHIF",
|
||||
"license": "MIT",
|
||||
"repository": "OHIF/Viewers",
|
||||
"main": "dist/index.umd.js",
|
||||
"module": "src/index.js",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1.16.0"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"README.md"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development webpack --config .webpack/webpack.dev.js --watch --debug --output-pathinfo",
|
||||
"dev:cornerstone": "yarn run dev",
|
||||
"build": "cross-env NODE_ENV=production webpack --config .webpack/webpack.prod.js",
|
||||
"build:package": "yarn run build",
|
||||
"start": "yarn run dev",
|
||||
"test:unit": "jest --watchAll",
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "^0.50.0",
|
||||
"@ohif/ui": "^0.50.0",
|
||||
"cornerstone-core": "^2.3.0",
|
||||
"cornerstone-math": "^0.1.8",
|
||||
"cornerstone-tools": "4.15.1",
|
||||
"dcmjs": "^0.12.3",
|
||||
"cornerstone-wado-image-loader": "^3.1.2",
|
||||
"dicom-parser": "^1.8.3",
|
||||
"hammerjs": "^2.0.8",
|
||||
"prop-types": "^15.6.2",
|
||||
"react": "^16.11.0",
|
||||
"react-dom": "^16.11.0",
|
||||
"react-redux": "^6.0.0",
|
||||
"react-resize-detector": "^3.4.0",
|
||||
"redux": "^4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.7.6",
|
||||
"classnames": "^2.2.6",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"react-cornerstone-viewport": "2.3.8"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
import React, { Component } from 'react';
|
||||
import CornerstoneViewport from 'react-cornerstone-viewport';
|
||||
//import ConnectedCornerstoneViewport from './ConnectedCornerstoneViewport';
|
||||
import OHIF from '@ohif/core';
|
||||
import PropTypes from 'prop-types';
|
||||
import cornerstone from 'cornerstone-core';
|
||||
import debounce from 'lodash.debounce';
|
||||
import throttle from 'lodash.throttle';
|
||||
|
||||
// const {
|
||||
// onAdded,
|
||||
// onRemoved,
|
||||
// onModified,
|
||||
// } = OHIF.measurements.MeasurementHandlers;
|
||||
|
||||
// // TODO: Transition to enums for the action names so that we can ensure they stay up to date
|
||||
// // everywhere they're used.
|
||||
// const MEASUREMENT_ACTION_MAP = {
|
||||
// added: onAdded,
|
||||
// removed: onRemoved,
|
||||
// modified: throttle(event => {
|
||||
// return onModified(event);
|
||||
// }, 300),
|
||||
// };
|
||||
|
||||
// const cine = viewportSpecificData.cine;
|
||||
|
||||
// isPlaying = cine.isPlaying === true;
|
||||
// frameRate = cine.cineFrameRate || frameRate;
|
||||
|
||||
const { StackManager } = OHIF.utils;
|
||||
|
||||
class OHIFCornerstoneViewport extends Component {
|
||||
state = {
|
||||
viewportData: null,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
customProps: {},
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
displaySet: PropTypes.object,
|
||||
viewportIndex: PropTypes.number,
|
||||
dataSource: PropTypes.object,
|
||||
children: PropTypes.node,
|
||||
customProps: PropTypes.object,
|
||||
};
|
||||
|
||||
static name = 'OHIFCornerstoneViewport';
|
||||
|
||||
static init() {
|
||||
console.log('OHIFCornerstoneViewport init()');
|
||||
}
|
||||
|
||||
static destroy() {
|
||||
console.log('OHIFCornerstoneViewport destroy()');
|
||||
StackManager.clearStacks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the CornerstoneTools Stack for the specified display set.
|
||||
*
|
||||
* @param {Object} displaySet
|
||||
* @param {Object} dataSource
|
||||
* @return {Object} CornerstoneTools Stack
|
||||
*/
|
||||
static getCornerstoneStack(displaySet, dataSource) {
|
||||
const { frameIndex } = displaySet;
|
||||
|
||||
// Get stack from Stack Manager
|
||||
const storedStack = StackManager.findOrCreateStack(displaySet, dataSource);
|
||||
|
||||
// Clone the stack here so we don't mutate it
|
||||
const stack = Object.assign({}, storedStack);
|
||||
|
||||
stack.currentImageIdIndex = frameIndex;
|
||||
|
||||
// TODO -> Do we ever use this like this?
|
||||
// if (SOPInstanceUID) {
|
||||
// const index = stack.imageIds.findIndex(imageId => {
|
||||
// const imageIdSOPInstanceUID = cornerstone.metaData.get(
|
||||
// 'SOPInstanceUID',
|
||||
// imageId
|
||||
// );
|
||||
|
||||
// return imageIdSOPInstanceUID === SOPInstanceUID;
|
||||
// });
|
||||
|
||||
// if (index > -1) {
|
||||
// stack.currentImageIdIndex = index;
|
||||
// } else {
|
||||
// console.warn(
|
||||
// 'SOPInstanceUID provided was not found in specified DisplaySet'
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
getViewportData = async displaySet => {
|
||||
let viewportData;
|
||||
|
||||
const { dataSource } = this.props;
|
||||
|
||||
const stack = OHIFCornerstoneViewport.getCornerstoneStack(
|
||||
displaySet,
|
||||
dataSource
|
||||
);
|
||||
|
||||
viewportData = {
|
||||
StudyInstanceUID: displaySet.StudyInstanceUID,
|
||||
displaySetInstanceUID: displaySet.displaySetInstanceUID,
|
||||
stack,
|
||||
};
|
||||
|
||||
return viewportData;
|
||||
};
|
||||
|
||||
setStateFromProps() {
|
||||
const { displaySet } = this.props;
|
||||
const {
|
||||
StudyInstanceUID,
|
||||
displaySetInstanceUID,
|
||||
sopClassUids,
|
||||
} = displaySet;
|
||||
|
||||
if (!StudyInstanceUID || !displaySetInstanceUID) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sopClassUids && sopClassUids.length > 1) {
|
||||
console.warn(
|
||||
'More than one SOPClassUID in the same series is not yet supported.'
|
||||
);
|
||||
}
|
||||
|
||||
this.getViewportData(displaySet).then(viewportData => {
|
||||
this.setState({
|
||||
viewportData,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setStateFromProps();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { displaySet } = this.props;
|
||||
const prevDisplaySet = prevProps.displaySet;
|
||||
|
||||
if (
|
||||
displaySet.displaySetInstanceUID !==
|
||||
prevDisplaySet.displaySetInstanceUID ||
|
||||
displaySet.SOPInstanceUID !== prevDisplaySet.SOPInstanceUID ||
|
||||
displaySet.frameIndex !== prevDisplaySet.frameIndex
|
||||
) {
|
||||
this.setStateFromProps();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let childrenWithProps = null;
|
||||
|
||||
if (!this.state.viewportData) {
|
||||
return null;
|
||||
}
|
||||
const { viewportIndex } = this.props;
|
||||
const {
|
||||
imageIds,
|
||||
currentImageIdIndex,
|
||||
// If this comes from the instance, would be a better default
|
||||
// `FrameTime` in the instance
|
||||
// frameRate = 0,
|
||||
} = this.state.viewportData.stack;
|
||||
|
||||
// TODO: Does it make more sense to use Context?
|
||||
if (this.props.children && this.props.children.length) {
|
||||
childrenWithProps = this.props.children.map((child, index) => {
|
||||
return (
|
||||
child &&
|
||||
React.cloneElement(child, {
|
||||
viewportIndex: this.props.viewportIndex,
|
||||
key: index,
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const debouncedNewImageHandler = debounce(
|
||||
({ currentImageIdIndex, sopInstanceUid }) => {
|
||||
const { displaySet } = this.props;
|
||||
const { StudyInstanceUID } = displaySet;
|
||||
if (currentImageIdIndex > 0) {
|
||||
this.props.onNewImage({
|
||||
StudyInstanceUID,
|
||||
SOPInstanceUID: sopInstanceUid,
|
||||
frameIndex: currentImageIdIndex,
|
||||
activeViewportIndex: viewportIndex,
|
||||
});
|
||||
}
|
||||
},
|
||||
700
|
||||
);
|
||||
|
||||
// TODO -> We may still want a wrapped component to define all the measurement api stuff.
|
||||
|
||||
return (
|
||||
<>
|
||||
<CornerstoneViewport
|
||||
viewportIndex={viewportIndex}
|
||||
imageIds={imageIds}
|
||||
imageIdIndex={currentImageIdIndex}
|
||||
onNewImage={debouncedNewImageHandler}
|
||||
// TODO: ViewportGrid Context?
|
||||
isActive={true} // todo
|
||||
isStackPrefetchEnabled={true} // todo
|
||||
isPlaying={false}
|
||||
frameRate={24}
|
||||
/>
|
||||
{childrenWithProps}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const temp = () => <div>Hello SR Viewport!</div>;
|
||||
|
||||
//export default OHIFCornerstoneViewport;
|
||||
export default temp;
|
||||
@@ -0,0 +1,146 @@
|
||||
import id from './id';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const sopClassHandlerName = 'dicom-sr';
|
||||
|
||||
const sopClassUids = [
|
||||
'1.2.840.10008.5.1.4.1.1.88.11', //BASIC_TEXT_SR:
|
||||
'1.2.840.10008.5.1.4.1.1.88.22', //ENHANCED_SR:
|
||||
'1.2.840.10008.5.1.4.1.1.88.33', //COMPREHENSIVE_SR:
|
||||
];
|
||||
|
||||
const scoordTypes = ['POINT', 'MULTIPOINT', 'POLYLINE', 'CIRCLE', 'ELLIPSE'];
|
||||
|
||||
const CodeNameCodeSequenceValues = {
|
||||
ImagingMeasurementReport: '126000',
|
||||
ImageLibrary: '111028',
|
||||
ImagingMeasurements: '126010',
|
||||
MeasurementGroup: '125007',
|
||||
ImageLibraryGroup: '126200',
|
||||
};
|
||||
|
||||
/**
|
||||
* Basic SOPClassHandler:
|
||||
* - For all Image types that are stackable, create
|
||||
* a displaySet with a stack of images
|
||||
*
|
||||
* @param {Array} sopClassHandlerModules List of SOP Class Modules
|
||||
* @param {SeriesMetadata} series The series metadata object from which the display sets will be created
|
||||
* @returns {Array} The list of display sets created for the given series object
|
||||
*/
|
||||
function getDisplaySetsFromSeries(instances) {
|
||||
// If the series has no instances, stop here
|
||||
if (!instances || !instances.length) {
|
||||
throw new Error('No instances were provided');
|
||||
}
|
||||
|
||||
const instance = instances[0];
|
||||
|
||||
const { StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID } = instance;
|
||||
|
||||
const { ConceptNameCodeSequence, ContentSequence } = instance;
|
||||
|
||||
if (
|
||||
ConceptNameCodeSequence.CodeValue !==
|
||||
CodeNameCodeSequenceValues.ImagingMeasurementReport
|
||||
) {
|
||||
console.warn(
|
||||
'Only support Imaging Measurement Report SRs (TID1500) for now'
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const referencedImages = _getReferencedImagesList(ContentSequence);
|
||||
|
||||
const measurements = _getMeasurements(ContentSequence, SOPInstanceUID);
|
||||
|
||||
const displaySet = {
|
||||
plugin: id,
|
||||
Modality: 'SR',
|
||||
displaySetInstanceUID: utils.guid(),
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
SOPClassHandlerId: `${id}.sopClassHandlerModule.${sopClassHandlerName}`,
|
||||
referencedImages,
|
||||
};
|
||||
|
||||
return [displaySet];
|
||||
}
|
||||
|
||||
function getSopClassHandlerModule() {
|
||||
return [
|
||||
{
|
||||
name: sopClassHandlerName,
|
||||
sopClassUids,
|
||||
getDisplaySetsFromSeries,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function _getMeasurements(
|
||||
ImagingMeasurementReportContentSequence,
|
||||
SOPInstanceUID
|
||||
) {
|
||||
const ImagingMeasurements = ImagingMeasurementReportContentSequence.find(
|
||||
item =>
|
||||
item.ConceptNameCodeSequence.CodeValue ===
|
||||
CodeNameCodeSequenceValues.ImagingMeasurements
|
||||
);
|
||||
|
||||
if (SOPInstanceUID === '2.25.435452399240481307327287169369305113868') {
|
||||
debugger;
|
||||
}
|
||||
|
||||
const MeasurementGroup = _getSequenceAsArray(
|
||||
ImagingMeasurements.ContentSequence
|
||||
).find(
|
||||
item =>
|
||||
item.ConceptNameCodeSequence.CodeValue ===
|
||||
CodeNameCodeSequenceValues.MeasurementGroup
|
||||
);
|
||||
|
||||
const Measurements = _getSequenceAsArray(
|
||||
MeasurementGroup.ContentSequence
|
||||
).filter(group => group.ValueType === 'NUM');
|
||||
|
||||
debugger;
|
||||
|
||||
_getSequenceAsArray(MeasurementGroup.ContentSequence).forEach(item => {});
|
||||
}
|
||||
|
||||
function _getReferencedImagesList(ImagingMeasurementReportContentSequence) {
|
||||
const ImageLibrary = ImagingMeasurementReportContentSequence.find(
|
||||
item =>
|
||||
item.ConceptNameCodeSequence.CodeValue ===
|
||||
CodeNameCodeSequenceValues.ImageLibrary
|
||||
);
|
||||
|
||||
const ImageLibraryGroup = _getSequenceAsArray(
|
||||
ImageLibrary.ContentSequence
|
||||
).find(
|
||||
item =>
|
||||
item.ConceptNameCodeSequence.CodeValue ===
|
||||
CodeNameCodeSequenceValues.ImageLibraryGroup
|
||||
);
|
||||
|
||||
const referencedImages = [];
|
||||
|
||||
_getSequenceAsArray(ImageLibraryGroup.ContentSequence).forEach(item => {
|
||||
const { ReferencedSOPSequence } = item;
|
||||
const {
|
||||
ReferencedSOPClassUID,
|
||||
ReferencedSOPInstanceUID,
|
||||
} = ReferencedSOPSequence;
|
||||
|
||||
referencedImages.push({ ReferencedSOPClassUID, ReferencedSOPInstanceUID });
|
||||
});
|
||||
|
||||
return referencedImages;
|
||||
}
|
||||
|
||||
function _getSequenceAsArray(sequence) {
|
||||
return Array.isArray(sequence) ? sequence : [sequence];
|
||||
}
|
||||
|
||||
export default getSopClassHandlerModule;
|
||||
@@ -0,0 +1 @@
|
||||
export default 'org.ohif.dicom-sr';
|
||||
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import getSopClassHandlerModule from './getSopClassHandlerModule';
|
||||
import id from './id.js';
|
||||
|
||||
const Component = React.lazy(() => {
|
||||
return import('./OHIFCornerstoneSRViewport');
|
||||
});
|
||||
|
||||
const OHIFCornerstoneSRViewport = props => {
|
||||
return (
|
||||
<React.Suspense fallback={<div>Loading...</div>}>
|
||||
<Component {...props} />
|
||||
</React.Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export default {
|
||||
/**
|
||||
* Only required property. Should be a unique value across all extensions.
|
||||
*/
|
||||
id,
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {object} [configuration={}]
|
||||
* @param {object|array} [configuration.csToolsConfig] - Passed directly to `initCornerstoneTools`
|
||||
*/
|
||||
getViewportModule({ commandsManager }) {
|
||||
const ExtendedOHIFCornerstoneSRViewport = props => {
|
||||
const onNewImageHandler = jumpData => {
|
||||
commandsManager.runCommand('jumpToImage', jumpData);
|
||||
};
|
||||
return (
|
||||
<OHIFCornerstoneSRViewport {...props} onNewImage={onNewImageHandler} />
|
||||
);
|
||||
};
|
||||
|
||||
return [{ name: 'dicom-sr', component: ExtendedOHIFCornerstoneSRViewport }];
|
||||
},
|
||||
getSopClassHandlerModule,
|
||||
};
|
||||
Reference in new issue
Block a user