diff --git a/examples/App.js b/examples/App.js
index 064c18b3..22515abe 100644
--- a/examples/App.js
+++ b/examples/App.js
@@ -5,6 +5,7 @@ import VTKBasicExample from './VTKBasicExample.js';
import VTKFusionExample from './VTKFusionExample.js';
import VTKMPRPaintingExample from './VTKMPRPaintingExample.js';
import VTKCornerstonePaintingSyncExample from './VTKCornerstonePaintingSyncExample.js';
+import VTKLoadImageDataExample from './VTKLoadImageDataExample.js';
import VTKCrosshairsExample from './VTKCrosshairsExample.js';
import VTKMPRRotateExample from './VTKMPRRotateExample.js';
@@ -69,6 +70,12 @@ function Index() {
url: '/rotate',
text: 'Demonstrates how to set up the MPR Rotate interactor style',
},
+ {
+ title: 'LoadImageData Example',
+ url: '/cornerstone-load-image-data',
+ text:
+ 'Generating vtkjs imagedata from cornerstone images and displaying them in a VTK viewport.',
+ },
];
const exampleComponents = examples.map(e => {
@@ -125,6 +132,7 @@ function AppRouter() {
const basic = () => Example({ children: });
const fusion = () => Example({ children: });
const painting = () => Example({ children: });
+ const loadImage = () => Example({ children: });
const synced = () =>
Example({ children: });
const crosshairs = () => Example({ children: });
@@ -140,6 +148,7 @@ function AppRouter() {
+
diff --git a/examples/VTKCornerstonePaintingSyncExample.js b/examples/VTKCornerstonePaintingSyncExample.js
index 619de2d6..c80b2836 100644
--- a/examples/VTKCornerstonePaintingSyncExample.js
+++ b/examples/VTKCornerstonePaintingSyncExample.js
@@ -14,7 +14,7 @@ import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume';
const { EVENTS } = cornerstoneTools;
window.cornerstoneTools = cornerstoneTools;
-function setupSyncedBrush(imageDataObject, element) {
+function setupSyncedBrush(imageDataObject) {
// Create buffer the size of the 3D volume
const dimensions = imageDataObject.dimensions;
const width = dimensions[0];
@@ -93,16 +93,6 @@ const imageIds = [
`dicomweb://${ROOT_URL}/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032221.5.dcm`,
];
-// Pre-retrieve the images for demo purposes
-// Note: In a real application you wouldn't need to do this
-// since you would probably have the image metadata ahead of time.
-// In this case, we preload the images so the WADO Image Loader can
-// read and store all of their metadata and subsequently the 'getImageData'
-// can run properly (it requires metadata).
-const promises = imageIds.map(imageId => {
- return cornerstone.loadAndCacheImage(imageId);
-});
-
class VTKCornerstonePaintingSyncExample extends Component {
state = {
volumes: null,
@@ -116,6 +106,16 @@ class VTKCornerstonePaintingSyncExample extends Component {
this.components = {};
this.cornerstoneElements = {};
+ // Pre-retrieve the images for demo purposes
+ // Note: In a real application you wouldn't need to do this
+ // since you would probably have the image metadata ahead of time.
+ // In this case, we preload the images so the WADO Image Loader can
+ // read and store all of their metadata and subsequently the 'getImageData'
+ // can run properly (it requires metadata).
+ const promises = imageIds.map(imageId => {
+ return cornerstone.loadAndCacheImage(imageId);
+ });
+
Promise.all(promises).then(
() => {
const displaySetInstanceUid = '12345';
@@ -128,10 +128,7 @@ class VTKCornerstonePaintingSyncExample extends Component {
};
const imageDataObject = getImageData(imageIds, displaySetInstanceUid);
- const labelMapInputData = setupSyncedBrush(
- imageDataObject,
- this.cornerstoneElements[0]
- );
+ const labelMapInputData = setupSyncedBrush(imageDataObject);
this.onMeasurementsChanged = event => {
if (event.type !== EVENTS.LABELMAP_MODIFIED) {
@@ -160,7 +157,7 @@ class VTKCornerstonePaintingSyncExample extends Component {
);
}
- onPaintEnd = () => {
+ onPaintEnd = strokeBuffer => {
const element = this.cornerstoneElements[0];
const enabledElement = cornerstone.getEnabledElement(element);
const { getters, setters } = cornerstoneTools.getModule('segmentation');
@@ -174,16 +171,35 @@ class VTKCornerstonePaintingSyncExample extends Component {
const stackData = stackState.data[0];
const numberOfFrames = stackData.imageIds.length;
+ const segmentIndex = labelmap3D.activeSegmentIndex;
- // TODO -> Can do more efficiently if we can grab the strokeBuffer from vtk-js.
for (let i = 0; i < numberOfFrames; i++) {
- const labelmap2D = getters.labelmap2DByImageIdIndex(
- labelmap3D,
- i,
- rows,
- columns
- );
- setters.updateSegmentsOnLabelmap2D(labelmap2D);
+ let labelmap2D = labelmap3D.labelmaps2D[i];
+
+ if (labelmap2D && labelmap2D.segmentsOnLabelmap.includes(segmentIndex)) {
+ continue;
+ }
+
+ const frameLength = rows * columns;
+ const byteOffset = frameLength * i;
+ const strokeArray = new Uint8Array(strokeBuffer, byteOffset, frameLength);
+
+ const strokeOnFrame = strokeArray.some(element => element === 1);
+
+ if (!strokeOnFrame) {
+ continue;
+ }
+
+ if (labelmap2D) {
+ labelmap2D.segmentsOnLabelmap.push(segmentIndex);
+ } else {
+ labelmap2D = getters.labelmap2DByImageIdIndex(
+ labelmap3D,
+ i,
+ rows,
+ columns
+ );
+ }
}
cornerstone.updateImage(element);
@@ -241,10 +257,11 @@ class VTKCornerstonePaintingSyncExample extends Component {
accessed in 2D.
- Both components are displaying the same labelmap UInt8Array. For
+ Both components are displaying the same labelmap UInt16Array. For
VTK, it has been encapsulated in a vtkDataArray and then a
- vtkImageData Object. For Cornerstone Tools, it is accessed by
- reference and index for each of the 2D slices.
+ vtkImageData Object. For Cornerstone Tools, the Uint16Array is
+ accessed through helpers based on the actively displayed image stack
+ and the index of the currently displayed image
Note: The PaintWidget (circle on hover) is not
diff --git a/examples/VTKLoadImageDataExample.js b/examples/VTKLoadImageDataExample.js
new file mode 100644
index 00000000..61630518
--- /dev/null
+++ b/examples/VTKLoadImageDataExample.js
@@ -0,0 +1,140 @@
+import React from 'react';
+import { Component } from 'react';
+
+import { View2D, getImageData, loadImageData } from '@vtk-viewport';
+import cornerstone from 'cornerstone-core';
+import cornerstoneTools from 'cornerstone-tools';
+import './initCornerstone.js';
+import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper';
+import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume';
+
+window.cornerstoneTools = cornerstoneTools;
+
+function createActorMapper(imageData) {
+ const mapper = vtkVolumeMapper.newInstance();
+ mapper.setInputData(imageData);
+
+ const actor = vtkVolume.newInstance();
+ actor.setMapper(mapper);
+
+ return {
+ actor,
+ mapper,
+ };
+}
+
+const ROOT_URL =
+ window.location.hostname === 'localhost'
+ ? window.location.host
+ : window.location.hostname;
+
+const imageIds = [
+ `dicomweb://${ROOT_URL}/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032221.1.dcm`,
+ `dicomweb://${ROOT_URL}/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032221.2.dcm`,
+ `dicomweb://${ROOT_URL}/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032221.3.dcm`,
+ `dicomweb://${ROOT_URL}/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032221.4.dcm`,
+ `dicomweb://${ROOT_URL}/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032221.5.dcm`,
+];
+
+class VTKLoadImageDataExample extends Component {
+ state = {
+ volumes: null,
+ vtkImageData: null,
+ cornerstoneViewportData: null,
+ focusedWidgetId: null,
+ isSetup: false,
+ };
+
+ componentDidMount() {
+ this.components = {};
+ this.cornerstoneElements = {};
+
+ // Pre-retrieve the images for demo purposes
+ // Note: In a real application you wouldn't need to do this
+ // since you would probably have the image metadata ahead of time.
+ // In this case, we preload the images so the WADO Image Loader can
+ // read and store all of their metadata and subsequently the 'getImageData'
+ // can run properly (it requires metadata).
+ const promises = imageIds.map(imageId => {
+ return cornerstone.loadAndCacheImage(imageId);
+ });
+
+ Promise.all(promises).then(
+ () => {
+ const displaySetInstanceUid = '12345';
+ const cornerstoneViewportData = {
+ stack: {
+ imageIds,
+ currentImageIdIndex: 0,
+ },
+ displaySetInstanceUid,
+ };
+
+ const imageDataObject = getImageData(imageIds, displaySetInstanceUid);
+
+ loadImageData(imageDataObject).then(() => {
+ const { actor } = createActorMapper(imageDataObject.vtkImageData);
+
+ this.setState({
+ vtkImageData: imageDataObject.vtkImageData,
+ volumes: [actor],
+ cornerstoneViewportData,
+ });
+ });
+ },
+ error => {
+ throw new Error(error);
+ }
+ );
+ }
+
+ saveCornerstoneElements = viewportIndex => {
+ return event => {
+ this.cornerstoneElements[viewportIndex] = event.detail.element;
+ };
+ };
+
+ setWidget = event => {
+ const widgetId = event.target.value;
+
+ if (widgetId === 'rotate') {
+ this.setState({
+ focusedWidgetId: null,
+ });
+ }
+ };
+
+ render() {
+ return (
+
+
+
Loading a cornerstone displayset into vtkjs
+
+ The example demonstrates loading cornerstone images already
+ available in the application into a vtkjs viewport.
+
+
+
+
+
+
+
+
+ {this.state.volumes && }
+
+
+
+ );
+ }
+}
+
+export default VTKLoadImageDataExample;
diff --git a/examples/VTKMPRRotateExample.js b/examples/VTKMPRRotateExample.js
index 20fade7f..1ccbda8f 100644
--- a/examples/VTKMPRRotateExample.js
+++ b/examples/VTKMPRRotateExample.js
@@ -434,34 +434,6 @@ class VTKMPRRotateExample extends Component {
for (let index = 0; index < volumeData.length; index++) {
columns.push(