Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Stack/volume paired contour segmentations #1078

Merged
merged 3 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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