Skip to content

Commit 1f76d6d

Browse files
authored
feat: 🎸 Enable outline rendering of segmentations (#80)
1 parent 392e37a commit 1f76d6d

File tree

9 files changed

+1461
-1305
lines changed

9 files changed

+1461
-1305
lines changed

‎examples/VTKCornerstonePaintingSyncExample.js‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume';
1414
const { EVENTS } = cornerstoneTools;
1515
window.cornerstoneTools = cornerstoneTools;
1616

17+
const segmentationModule = cornerstoneTools.getModule('segmentation');
18+
1719
const voi = {
1820
windowCenter: 35,
1921
windowWidth: 80,
@@ -39,8 +41,6 @@ function setupSyncedBrush(imageDataObject) {
3941
throw new Error('Depth should match the number of imageIds');
4042
}
4143

42-
const segmentationModule = cornerstoneTools.getModule('segmentation');
43-
4444
segmentationModule.setters.labelmap3DByFirstImageId(
4545
imageIds[0],
4646
buffer,
@@ -51,10 +51,7 @@ function setupSyncedBrush(imageDataObject) {
5151
0
5252
);
5353

54-
segmentationModule.setters.colorLUT(0, [[255, 0, 0, 255]]);
55-
5654
// Create VTK Image Data with buffer as input
57-
5855
const labelMap = vtkImageData.newInstance();
5956

6057
// right now only support 256 labels
@@ -162,6 +159,9 @@ class VTKCornerstonePaintingSyncExample extends Component {
162159
volumes: [actor],
163160
cornerstoneViewportData,
164161
labelMapInputData,
162+
colorLUT: segmentationModule.getters.colorLUT(0),
163+
globalOpacity: segmentationModule.configuration.fillAlpha,
164+
outlineThickness: segmentationModule.configuration.outlineThickness,
165165
});
166166
});
167167
});
@@ -257,6 +257,8 @@ class VTKCornerstonePaintingSyncExample extends Component {
257257
};
258258

259259
render() {
260+
const { globalOpacity, colorLUT, outlineThickness } = this.state;
261+
260262
return (
261263
<div className="row">
262264
<div className="col-xs-12">
@@ -312,6 +314,13 @@ class VTKCornerstonePaintingSyncExample extends Component {
312314
onPaintEnd={this.onPaintEnd}
313315
orientation={{ sliceNormal: [0, 0, 1], viewUp: [0, -1, 0] }}
314316
onCreated={this.saveApiReference}
317+
labelmapRenderingOptions={{
318+
colorLUT,
319+
globalOpacity,
320+
outlineThickness,
321+
visible: true,
322+
renderOutline: true,
323+
}}
315324
/>
316325
)}
317326
</div>

‎examples/VTKFusionExample.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function createCT2dPipeline(imageData) {
4141
const { actor } = createActorMapper(imageData);
4242
const cfun = vtkColorTransferFunction.newInstance();
4343
/*
44-
0: { description: 'Soft tissue', window: 400, level: 40 },
44+
0: { description: 'Soft tissue', window: 400, level: 40 },
4545
1: { description: 'Lung', window: 1500, level: -600 },
4646
2: { description: 'Liver', window: 150, level: 90 },
4747
3: { description: 'Bone', window: 2500, level: 480 },

‎examples/VTKMPRPaintingExample.js‎

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ function createLabelMapImageData(backgroundImageData) {
6464
labelMapData.setDimensions(backgroundImageData.getDimensions());
6565
labelMapData.computeTransforms();
6666

67-
const values = new Uint8Array(backgroundImageData.getNumberOfPoints());
67+
const values = new Uint16Array(backgroundImageData.getNumberOfPoints());
6868
const dataArray = vtkDataArray.newInstance({
6969
numberOfComponents: 1, // labelmap with single component
7070
values,
@@ -167,8 +167,8 @@ class VTKMPRPaintingExample extends Component {
167167
numberOfComponents: 1, // labelmap with single component
168168
values,
169169
});
170-
labelMapImageData.getPointData().setScalars(dataArray);
171170

171+
labelMapImageData.getPointData().setScalars(dataArray);
172172
labelMapImageData.modified();
173173

174174
this.rerenderAllViewports();
@@ -278,19 +278,21 @@ class VTKMPRPaintingExample extends Component {
278278
painting={this.state.focusedWidgetId === 'PaintWidget'}
279279
/>
280280
</div>
281-
<div className="col-xs-12 col-sm-6">
282-
<View3D
283-
volumes={this.state.volumeRenderingVolumes}
284-
paintFilterBackgroundImageData={
285-
this.state.paintFilterBackgroundImageData
286-
}
287-
paintFilterLabelMapImageData={
288-
this.state.paintFilterLabelMapImageData
289-
}
290-
onCreated={this.saveApiReference(1)}
291-
painting={this.state.focusedWidgetId === 'PaintWidget'}
292-
/>
293-
</div>
281+
{
282+
<div className="col-xs-12 col-sm-6">
283+
<View3D
284+
volumes={this.state.volumeRenderingVolumes}
285+
paintFilterBackgroundImageData={
286+
this.state.paintFilterBackgroundImageData
287+
}
288+
paintFilterLabelMapImageData={
289+
this.state.paintFilterLabelMapImageData
290+
}
291+
onCreated={this.saveApiReference(1)}
292+
painting={this.state.focusedWidgetId === 'PaintWidget'}
293+
/>
294+
</div>
295+
}
294296
</div>
295297
);
296298
}

‎package.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"peerDependencies": {
2525
"react": "^16.8.6",
2626
"react-dom": "^16.8.6",
27-
"vtk.js": "^11.7.2"
27+
"vtk.js": "^11.14.0"
2828
},
2929
"dependencies": {
3030
"date-fns": "^2.2.1",
@@ -82,7 +82,7 @@
8282
"style-loader": "^0.23.1",
8383
"stylelint": "^10.1.0",
8484
"stylelint-config-recommended": "^2.2.0",
85-
"vtk.js": "^11.11.1",
85+
"vtk.js": "^11.14.0",
8686
"webpack": "4.34.0",
8787
"webpack-cli": "^3.3.4",
8888
"webpack-dev-server": "^3.8.0",

‎src/VTKViewport/View2D.js‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export default class View2D extends Component {
3737
painting: false,
3838
labelmapRenderingOptions: {
3939
visible: true,
40+
renderOutline: true,
4041
},
4142
};
4243

@@ -389,9 +390,10 @@ export default class View2D extends Component {
389390
) {
390391
// re-render if data has updated
391392
this.subs.data.sub(
392-
this.props.paintFilterBackgroundImageData.onModified(() =>
393-
this.renderWindow.render()
394-
)
393+
this.props.paintFilterBackgroundImageData.onModified(() => {
394+
this.genericRenderWindow.resize();
395+
this.renderWindow.render();
396+
})
395397
);
396398
this.paintFilter.setBackgroundImage(
397399
this.props.paintFilterBackgroundImageData
@@ -431,6 +433,8 @@ export default class View2D extends Component {
431433
this.renderWindow.render();
432434
})
433435
);
436+
437+
this.genericRenderWindow.resize();
434438
}
435439

436440
if (
@@ -486,6 +490,8 @@ export default class View2D extends Component {
486490

487491
this.widgetManager.grabFocus(this.paintWidget);
488492
this.widgetManager.enablePicking();
493+
494+
this.genericRenderWindow.resize();
489495
} else if (this.viewWidget) {
490496
this.widgetManager.releaseFocus();
491497
this.widgetManager.removeWidget(this.paintWidget);
@@ -495,6 +501,8 @@ export default class View2D extends Component {
495501
this.subs.paint.unsubscribe();
496502
this.subs.paintEnd.unsubscribe();
497503
this.viewWidget = null;
504+
505+
this.genericRenderWindow.resize();
498506
}
499507
}
500508
}
@@ -530,7 +538,6 @@ export default class View2D extends Component {
530538
}
531539

532540
const style = { width: '100%', height: '100%', position: 'relative' };
533-
534541
const voi = this.state.voi;
535542

536543
return (

‎src/VTKViewport/View3D.js‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export default class View3D extends Component {
3232
sliceNormal: [0, 0, 1],
3333
labelmapRenderingOptions: {
3434
visible: true,
35+
renderOutline: false,
3536
},
3637
};
3738

@@ -194,7 +195,7 @@ export default class View3D extends Component {
194195
this.props.paintFilterBackgroundImageData,
195196
labelmapImageData,
196197
this.props.labelmapRenderingOptions,
197-
true
198+
false
198199
);
199200

200201
this.labelmap = labelmap;

‎src/VTKViewport/createLabelPipeline.js‎

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ export default function createLabelPipeline(
1212
useSampleDistance = false
1313
) {
1414
let labelMapData;
15-
16-
let { colorLUT, globalOpacity, visible } = options;
15+
let {
16+
colorLUT,
17+
globalOpacity,
18+
visible,
19+
renderOutline,
20+
outlineThickness,
21+
} = options;
1722

1823
if (visible === undefined) {
1924
visible = false;
@@ -23,6 +28,10 @@ export default function createLabelPipeline(
2328
globalOpacity = 1.0;
2429
}
2530

31+
if (outlineThickness === undefined) {
32+
outlineThickness = 3;
33+
}
34+
2635
if (paintFilterLabelMapImageData) {
2736
labelMapData = paintFilterLabelMapImageData;
2837
} else {
@@ -66,8 +75,9 @@ export default function createLabelPipeline(
6675
labelMap.actor.setMapper(labelMap.mapper);
6776
labelMap.actor.setVisibility(visible);
6877
labelMap.ofun.addPoint(0, 0);
78+
labelMap.ofun.addPoint(1, 1.0);
6979

70-
// set up labelMap color and opacity mapping
80+
// Set up labelMap color and opacity mapping
7181
if (colorLUT) {
7282
// TODO -> It seems to crash if you set it higher than 256??
7383
const numColors = Math.min(256, colorLUT.length);
@@ -82,8 +92,9 @@ export default function createLabelPipeline(
8292
color[2] / 255
8393
);
8494

95+
// Set the opacity per label.
8596
const segmentOpacity = (color[3] / 255) * globalOpacity;
86-
labelMap.ofun.addPointLong(i, segmentOpacity, 0.5, 1.0);
97+
labelMap.ofun.addPoint(1, segmentOpacity, 0.5, 1.0);
8798
}
8899
} else {
89100
// Some default.
@@ -95,8 +106,14 @@ export default function createLabelPipeline(
95106

96107
labelMap.actor.getProperty().setRGBTransferFunction(0, labelMap.cfun);
97108
labelMap.actor.getProperty().setScalarOpacity(0, labelMap.ofun);
98-
99109
labelMap.actor.getProperty().setInterpolationTypeToNearest();
110+
111+
if (renderOutline) {
112+
labelMap.actor.getProperty().setUseLabelOutline(true);
113+
labelMap.actor.getProperty().setLabelOutlineThickness(outlineThickness);
114+
}
115+
116+
labelMap.ofun.setClamping(false);
100117
labelMap.actor.getProperty().setScalarOpacityUnitDistance(0, 0.1);
101118
labelMap.actor.getProperty().setUseGradientOpacity(0, false);
102119

‎src/lib/getImageData.js‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default function getImageData(imageIds, displaySetInstanceUid) {
1818
const { rowCosines, columnCosines } = metaData0;
1919
const rowCosineVec = vec3.fromValues(...rowCosines);
2020
const colCosineVec = vec3.fromValues(...columnCosines);
21-
const scanAxisNormal = vec3.cross([], colCosineVec, rowCosineVec);
21+
const scanAxisNormal = vec3.cross([], rowCosineVec, colCosineVec);
2222

2323
const { spacing, origin, sortedDatasets } = sortDatasetsByImagePosition(
2424
scanAxisNormal,
@@ -65,7 +65,6 @@ export default function getImageData(imageIds, displaySetInstanceUid) {
6565
});
6666

6767
const imageData = vtkImageData.newInstance();
68-
6968
const direction = [...rowCosineVec, ...colCosineVec, ...scanAxisNormal];
7069

7170
imageData.setDimensions(xVoxels, yVoxels, zVoxels);

0 commit comments

Comments
 (0)