Skip to content

Commit 9be5a56

Browse files
JamesAPettsswederik
authored andcommitted
feat: 🎸 CornerstoneTools 4.0 + vtk-js brush sync example (OHIF#33)
* feat: 🎸 CorerstoneTools 4.0 + vtk-js brush sync example Sync example for cornerstoneTools 4.0 and brush sync. * Test remove .npmrc
1 parent f49d6d3 commit 9be5a56

File tree

2 files changed

+59
-50
lines changed

2 files changed

+59
-50
lines changed

‎examples/VTKCornerstonePaintingSyncExample.js

+55-46
Original file line numberDiff line numberDiff line change
@@ -24,46 +24,32 @@ function setupSyncedBrush(imageDataObject, element) {
2424

2525
// If you want to load a segmentation labelmap, you would want to load
2626
// it into this array at this point.
27-
const threeDimensionalPixelData = new Uint8ClampedArray(numVolumePixels);
27+
const threeDimensionalPixelData = new Uint16Array(numVolumePixels);
2828

2929
const buffer = threeDimensionalPixelData.buffer;
30-
31-
// Slice buffer into 2d-sized pieces, which are added to Cornerstone ToolData
32-
const toolType = 'brush';
33-
const segmentationIndex = 0;
3430
const imageIds = imageDataObject.imageIds;
35-
if (imageIds.length !== depth) {
31+
const numberOfFrames = imageIds.length;
32+
33+
if (numberOfFrames !== depth) {
3634
throw new Error('Depth should match the number of imageIds');
3735
}
3836

39-
const { globalImageIdSpecificToolStateManager } = cornerstoneTools;
40-
41-
for (let i = 0; i < imageIds.length; i++) {
42-
const imageId = imageIds[i];
43-
const byteOffset = width * height * i;
44-
const length = width * height;
45-
const slicePixelData = new Uint8ClampedArray(buffer, byteOffset, length);
46-
47-
const toolData = [];
48-
toolData[segmentationIndex] = {
49-
pixelData: slicePixelData,
50-
invalidated: true,
51-
};
52-
53-
const toolState =
54-
globalImageIdSpecificToolStateManager.saveImageIdToolState(imageId) || {};
37+
const segmentationModule = cornerstoneTools.getModule('segmentation');
5538

56-
toolState[toolType] = {
57-
data: toolData,
58-
};
39+
segmentationModule.setters.labelmap3DByFirstImageId(
40+
imageIds[0],
41+
buffer,
42+
0,
43+
[],
44+
numberOfFrames,
45+
undefined,
46+
0
47+
);
5948

60-
globalImageIdSpecificToolStateManager.restoreImageIdToolState(
61-
imageId,
62-
toolState
63-
);
64-
}
49+
segmentationModule.setters.colorLUT(0, [[255, 0, 0, 255]]);
6550

6651
// Create VTK Image Data with buffer as input
52+
6753
const labelMap = vtkImageData.newInstance();
6854

6955
// right now only support 256 labels
@@ -100,9 +86,6 @@ const ROOT_URL =
10086
: window.location.hostname;
10187

10288
const imageIds = [
103-
//'dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.10.dcm',
104-
//'dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.11.dcm',
105-
//'dicomweb://s3.amazonaws.com/lury/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032220.12.dcm',
10689
`dicomweb://${ROOT_URL}/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032221.1.dcm`,
10790
`dicomweb://${ROOT_URL}/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032221.2.dcm`,
10891
`dicomweb://${ROOT_URL}/PTCTStudy/1.3.6.1.4.1.25403.52237031786.3872.20100510032221.3.dcm`,
@@ -120,8 +103,6 @@ const promises = imageIds.map(imageId => {
120103
return cornerstone.loadAndCacheImage(imageId);
121104
});
122105

123-
const BaseBrushTool = cornerstoneTools.import('base/BaseBrushTool');
124-
125106
class VTKCornerstonePaintingSyncExample extends Component {
126107
state = {
127108
volumes: null,
@@ -147,10 +128,13 @@ class VTKCornerstonePaintingSyncExample extends Component {
147128
};
148129

149130
const imageDataObject = getImageData(imageIds, displaySetInstanceUid);
150-
const labelMapInputData = setupSyncedBrush(imageDataObject);
131+
const labelMapInputData = setupSyncedBrush(
132+
imageDataObject,
133+
this.cornerstoneElements[0]
134+
);
151135

152-
this.onMeasurementModified = event => {
153-
if (event.type !== EVENTS.MEASUREMENT_MODIFIED) {
136+
this.onMeasurementsChanged = event => {
137+
if (event.type !== EVENTS.LABELMAP_MODIFIED) {
154138
return;
155139
}
156140

@@ -161,6 +145,7 @@ class VTKCornerstonePaintingSyncExample extends Component {
161145

162146
loadImageData(imageDataObject).then(() => {
163147
const { actor } = createActorMapper(imageDataObject.vtkImageData);
148+
164149
this.setState({
165150
vtkImageData: imageDataObject.vtkImageData,
166151
volumes: [actor],
@@ -175,14 +160,33 @@ class VTKCornerstonePaintingSyncExample extends Component {
175160
);
176161
}
177162

178-
invalidateBrush = () => {
163+
onPaintEnd = () => {
179164
const element = this.cornerstoneElements[0];
180165
const enabledElement = cornerstone.getEnabledElement(element);
181-
const enabledElementUid = enabledElement.uuid;
166+
const { getters, setters } = cornerstoneTools.getModule('segmentation');
167+
const labelmap3D = getters.labelmap3D(element);
168+
const stackState = cornerstoneTools.getToolState(element, 'stack');
169+
const { rows, columns } = enabledElement.image;
170+
171+
if (!stackState || !labelmap3D) {
172+
return;
173+
}
174+
175+
const stackData = stackState.data[0];
176+
const numberOfFrames = stackData.imageIds.length;
177+
178+
// TODO -> Can do more efficiently if we can grab the strokeBuffer from vtk-js.
179+
for (let i = 0; i < numberOfFrames; i++) {
180+
const labelmap2D = getters.labelmap2DByImageIdIndex(
181+
labelmap3D,
182+
i,
183+
rows,
184+
columns
185+
);
186+
setters.updateSegmentsOnLabelmap2D(labelmap2D);
187+
}
182188

183-
// Note: This calls updateImage internally
184-
// TODO: Find out why it's not very quick to update...
185-
BaseBrushTool.invalidateBrushOnEnabledElement(enabledElementUid);
189+
cornerstone.updateImage(element);
186190
};
187191

188192
rerenderAllVTKViewports = () => {
@@ -233,7 +237,7 @@ class VTKCornerstonePaintingSyncExample extends Component {
233237
<h1>Syncing VTK Labelmap with Cornerstone Brush Tool Data</h1>
234238
<p>
235239
This example demonstrates how to keep painting in VTK, which is
236-
performed in 3D, in sync with Cornerstone&apos;s tool data, which is
240+
performed in 3D, in sync with Cornerstone's tool data, which is
237241
accessed in 2D.
238242
</p>
239243
<p>
@@ -278,7 +282,7 @@ class VTKCornerstonePaintingSyncExample extends Component {
278282
paintFilterBackgroundImageData={this.state.vtkImageData}
279283
paintFilterLabelMapImageData={this.state.labelMapInputData}
280284
painting={this.state.focusedWidgetId === 'PaintWidget'}
281-
onPaint={this.invalidateBrush}
285+
onPaintEnd={this.onPaintEnd}
282286
onCreated={this.saveComponentReference(0)}
283287
/>
284288
)}
@@ -287,8 +291,13 @@ class VTKCornerstonePaintingSyncExample extends Component {
287291
{this.state.cornerstoneViewportData && (
288292
<CornerstoneViewport
289293
activeTool={'Brush'}
294+
availableTools={[
295+
{ name: 'Brush', mouseButtonMasks: [1] },
296+
{ name: 'StackScrollMouseWheel' },
297+
{ name: 'StackScrollMultiTouch' },
298+
]}
290299
viewportData={this.state.cornerstoneViewportData}
291-
onMeasurementModified={this.onMeasurementModified}
300+
onMeasurementsChanged={this.onMeasurementsChanged}
292301
onElementEnabled={this.saveCornerstoneElements(0)}
293302
/>
294303
)}

‎src/VTKViewport/createLabelPipeline.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ export default function createLabelPipeline(
5353
labelMap.actor.setMapper(labelMap.mapper);
5454

5555
// set up labelMap color and opacity mapping
56-
labelMap.cfun.addRGBPoint(1, 0, 0, 1); // label '1' will be blue
57-
labelMap.cfun.addRGBPoint(2, 1, 0, 0); // label '2' will be red
58-
labelMap.cfun.addRGBPoint(3, 0, 1, 0); // label '3' will be green
56+
labelMap.cfun.addRGBPoint(1, 1, 0, 0); // label '1' will be red
57+
labelMap.cfun.addRGBPoint(2, 0, 1, 0); // label '2' will be green
58+
labelMap.cfun.addRGBPoint(3, 0, 1, 1); // label '3' will be blue
5959
labelMap.ofun.addPoint(0, 0);
60-
labelMap.ofun.addPoint(1, 0.5);
60+
labelMap.ofun.addPoint(1, 0.9);
6161

6262
labelMap.actor.getProperty().setRGBTransferFunction(0, labelMap.cfun);
6363
labelMap.actor.getProperty().setScalarOpacity(0, labelMap.ofun);

0 commit comments

Comments
 (0)