Skip to content

Commit

Permalink
fix: Stack/volume paired contour segmentations (#1078)
Browse files Browse the repository at this point in the history
* Fix volume/stack paired segmentations

* Fix video interpolation as well

* Tweak the interval for points
  • Loading branch information
wayfarer3130 authored Feb 13, 2024
1 parent 8e4cc96 commit ead38aa
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 81 deletions.
192 changes: 113 additions & 79 deletions packages/tools/examples/interpolationContourSegmentation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ const renderingEngineId = 'myRenderingEngine';
let renderingEngine;
const viewportIds = ['CT_STACK', 'CT_VOLUME_SAGITTAL'];
const viewports = [];
const segmentationId = `SEGMENTATION_ID`;
const segmentationIdStack = `SEGMENTATION_ID_STACK`;
const segmentationIdVolume = `SEGMENTATION_ID_VOLUME`;
const segmentationIds = [segmentationIdStack, segmentationIdVolume];
let frameOfReferenceUID;

const interpolationTools = new Map<string, any>();
Expand Down Expand Up @@ -155,56 +157,62 @@ addDropdownToToolbar({
options: { values: toolsNames, defaultValue: selectedToolName },
onSelectedValueChange: (newSelectedToolNameAsStringOrNumber) => {
const newSelectedToolName = String(newSelectedToolNameAsStringOrNumber);
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId);

// Set the new tool active
toolGroup.setToolActive(newSelectedToolName, {
bindings: [
{
mouseButton: MouseBindings.Primary, // Left Click
},
{
mouseButton: MouseBindings.Primary, // Shift + Left Click
modifierKey: KeyboardBindings.Shift,
},
],
});

// Set the old tool passive
toolGroup.setToolPassive(selectedToolName, { removeAllBindings: true });
for (const toolGroupId of toolGroupIds) {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId);

// Set the new tool active
toolGroup.setToolActive(newSelectedToolName, {
bindings: [
{
mouseButton: MouseBindings.Primary, // Left Click
},
{
mouseButton: MouseBindings.Primary, // Shift + Left Click
modifierKey: KeyboardBindings.Shift,
},
],
});

// Set the old tool passive
toolGroup.setToolPassive(selectedToolName, { removeAllBindings: true });
}
selectedToolName = <string>newSelectedToolName;
},
});

addButtonToToolbar({
title: 'Accept All',
onClick: () => {
cornerstoneTools.utilities.contours.acceptAutogeneratedInterpolations(
frameOfReferenceUID,
{
segmentIndex:
segmentation.segmentIndex.getActiveSegmentIndex(segmentationId),
segmentationId,
// Could also specify only for the active tool, but that doesn't seem useful here.
}
);
for (const segmentationId of segmentationIds) {
cornerstoneTools.utilities.contours.acceptAutogeneratedInterpolations(
frameOfReferenceUID,
{
segmentIndex:
segmentation.segmentIndex.getActiveSegmentIndex(segmentationId),
segmentationId,
// Could also specify only for the active tool, but that doesn't seem useful here.
}
);
}
renderingEngine.render();
},
});

function acceptCurrent() {
viewports.forEach((viewport) => {
cornerstoneTools.utilities.contours.acceptAutogeneratedInterpolations(
viewport.element,
{
segmentIndex:
segmentation.segmentIndex.getActiveSegmentIndex(segmentationId),
segmentationId,
sliceIndex: viewport.getCurrentImageIdIndex(),
}
);
for (const segmentationId of segmentationIds) {
cornerstoneTools.utilities.contours.acceptAutogeneratedInterpolations(
viewport.element,
{
segmentIndex:
segmentation.segmentIndex.getActiveSegmentIndex(segmentationId),
segmentationId: segmentationIdStack,
sliceIndex: viewport.getCurrentImageIdIndex(),
}
);
}
});

renderingEngine.render();
}

Expand Down Expand Up @@ -240,47 +248,34 @@ Deleting:
content.append(instructions);

let shouldInterpolate = false;
function addToggleInterpolationButton(toolGroup) {
function addToggleInterpolationButton(toolGroupIds) {
addButtonToToolbar({
title: 'Toggle interpolation',
onClick: () => {
shouldInterpolate = !shouldInterpolate;

toolGroup.setToolConfiguration(interpolationToolName, {
interpolation: {
enabled: shouldInterpolate,
},
toolGroupIds.forEach((toolGroupId) => {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId);
toolGroup.setToolConfiguration(interpolationToolName, {
interpolation: {
enabled: shouldInterpolate,
},
});
});
},
});
}

const toolGroupId = 'STACK_TOOL_GROUP_ID';

/**
* Runs the demo
*/
async function run() {
// Init Cornerstone and related libraries
await initDemo();

// Add tools to Cornerstone3D
cornerstoneTools.addTool(PlanarFreehandContourSegmentationTool);
cornerstoneTools.addTool(PlanarFreehandROITool);
cornerstoneTools.addTool(SplineContourSegmentationTool);
cornerstoneTools.addTool(SplineROITool);
cornerstoneTools.addTool(LivewireContourSegmentationTool);
cornerstoneTools.addTool(LivewireContourTool);
const toolGroupIds = ['STACK_TOOL_GROUP_ID', 'VOLUME_TOOL_GROUP_ID'];

cornerstoneTools.addTool(SegmentationDisplayTool);

// Define a tool group, which defines how mouse events map to tool commands for
// Any viewport using the group
/** Adds the bindings for the tool group */
function addBindings(toolGroupId) {
const toolGroup = ToolGroupManager.createToolGroup(toolGroupId);
addManipulationBindings(toolGroup);

// Add the tools to the tool group
toolGroup.addTool(SegmentationDisplayTool.toolName);
toolGroup.setToolEnabled(SegmentationDisplayTool.toolName);

for (const [toolName, config] of interpolationTools.entries()) {
if (config.baseTool) {
Expand Down Expand Up @@ -315,9 +310,30 @@ async function run() {
},
],
});
}
/**
* Runs the demo
*/
async function run() {
// Init Cornerstone and related libraries
await initDemo();

// Add tools to Cornerstone3D
cornerstoneTools.addTool(PlanarFreehandContourSegmentationTool);
cornerstoneTools.addTool(PlanarFreehandROITool);
cornerstoneTools.addTool(SplineContourSegmentationTool);
cornerstoneTools.addTool(SplineROITool);
cornerstoneTools.addTool(LivewireContourSegmentationTool);
cornerstoneTools.addTool(LivewireContourTool);

cornerstoneTools.addTool(SegmentationDisplayTool);

// Define a tool group, which defines how mouse events map to tool commands for
// Any viewport using the group
addBindings(toolGroupIds[0]);
addBindings(toolGroupIds[1]);
// set up toggle interpolation tool button.
addToggleInterpolationButton(toolGroup);
addToggleInterpolationButton(toolGroupIds);

// Get Cornerstone imageIds and fetch metadata into RAM
const stackImageIds = await createImageIdsAndCacheMetaData({
Expand Down Expand Up @@ -363,9 +379,18 @@ async function run() {
renderingEngine.setViewports(viewportInputArray);

// Set the tool group on the viewport
viewportIds.forEach((viewportId) =>
toolGroup.addViewport(viewportId, renderingEngineId)
);
viewportIds.forEach((viewportId, index) => {
console.log(
'Setting tool group/viewport id',
index,
viewportId,
toolGroupIds[index]
);
ToolGroupManager.getToolGroup(toolGroupIds[index]).addViewport(
viewportId,
renderingEngineId
);
});

// Define a volume in memory
const volume = await volumeLoader.createAndCacheVolume(volumeId, {
Expand Down Expand Up @@ -400,27 +425,36 @@ async function run() {
renderingEngine.renderViewports(viewportIds);

// Add a segmentation that will contains the contour annotations
segmentation.addSegmentations([
{
segmentationId,
representation: {
let index = 0;
for (const segmentationId of segmentationIds) {
segmentation.addSegmentations([
{
segmentationId,
representation: {
type: csToolsEnums.SegmentationRepresentations.Contour,
},
},
]);
// Create a segmentation representation associated to the toolGroupId
// Add the segmentation representation to the toolgroup
await segmentation.addSegmentationRepresentations(toolGroupIds[index++], [
{
segmentationId,
type: csToolsEnums.SegmentationRepresentations.Contour,
},
},
]);
]);
}

// Create a segmentation representation associated to the toolGroupId
await segmentation.addSegmentationRepresentations(toolGroupId, [
{
segmentationId,
type: csToolsEnums.SegmentationRepresentations.Contour,
},
]);
frameOfReferenceUID = volumeViewport.getFrameOfReferenceUID();
}

function updateActiveSegmentIndex(segmentIndex: number): void {
segmentation.segmentIndex.setActiveSegmentIndex(segmentationId, segmentIndex);
for (const segmentationId of segmentationIds) {
segmentation.segmentIndex.setActiveSegmentIndex(
segmentationId,
segmentIndex
);
}
}

run();
9 changes: 9 additions & 0 deletions packages/tools/examples/videoContourSegmentation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ const segmentVisibilityMap = new Map();
const configuredTools = new Map<string, any>();
const interpolationConfiguration = {
interpolation: { enabled: true },
decimate: {
enabled: true,
/** A maximum given distance 'epsilon' to decide if a point should or
* shouldn't be added the resulting polyline which will have a lower
* number of points for higher `epsilon` values.
* Larger values work well for this video example
*/
epsilon: 0.5,
},
};

configuredTools.set('CatmullRomSplineROI', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,7 @@ function findMinimumRegions(dotValues, handleCount) {
const { length } = dotValues;
// Fallback for very uniform ojects.
if (deviation < 0.01 || length < handleCount * 3) {
// TODO - create handleCount evenly spaced handles
return [0, Math.floor(length / 3), Math.floor((length * 2) / 3)];
return [];
}

const inflection = [];
Expand Down

0 comments on commit ead38aa

Please sign in to comment.