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

feat: ROI threshold to consider two volumes for thresholding #325

Merged
merged 21 commits into from
Jan 12, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
eadae94
Adding ROI Threshold based on multiple volumes
rodrigobasilio2022 Nov 30, 2022
0b50ac9
Add sliders for petVolume
rodrigobasilio2022 Nov 30, 2022
b84d084
Refactoring threshold function parameters
rodrigobasilio2022 Dec 1, 2022
cb369d8
Working with LPS coords
rodrigobasilio2022 Dec 1, 2022
79bd35c
Running required checks
rodrigobasilio2022 Dec 3, 2022
c91ba23
Adding perfusion colormap
rodrigobasilio2022 Dec 7, 2022
b907ec9
Fix CI error in PR
rodrigobasilio2022 Dec 8, 2022
488485b
Fixing CI function parameter checking error
rodrigobasilio2022 Dec 8, 2022
2aec974
Fixing CI error related to api change
rodrigobasilio2022 Dec 9, 2022
48eb735
Removing unnecessary extra parameter
rodrigobasilio2022 Dec 9, 2022
17aed50
Minor parameter adjustment
rodrigobasilio2022 Dec 12, 2022
9524fd9
Add test coverType option
rodrigobasilio2022 Dec 13, 2022
84f35f6
Add some comments
rodrigobasilio2022 Dec 13, 2022
5e47114
Convert parameter in optional and set defaul value
rodrigobasilio2022 Dec 14, 2022
5e46d31
Convert parameter in optional and set defaul value
rodrigobasilio2022 Dec 14, 2022
f0585a6
Fixing CI errors
rodrigobasilio2022 Dec 14, 2022
e8c2a88
Refactoring and comment threshold functions
rodrigobasilio2022 Dec 15, 2022
168ed3f
Refactoring code based on PR reviews
rodrigobasilio2022 Jan 12, 2023
35753f7
Add spacing check between volumes in roi threshold
rodrigobasilio2022 Jan 12, 2023
8fee29c
Merge branch 'main' into feat/ROIThreshold_new
sedghi Jan 12, 2023
47d4b22
fix build
sedghi Jan 12, 2023
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
1 change: 1 addition & 0 deletions common/reviews/api/core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2181,6 +2181,7 @@ type VolumeCacheVolumeRemovedEventDetail = {
type VolumeInputCallback = (params: {
volumeActor: VolumeActor;
volumeId: string;
preset?: any;
}) => unknown;

// @public (undocumented)
Expand Down
1 change: 1 addition & 0 deletions common/reviews/api/streaming-image-volume-loader.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,7 @@ type VolumeCacheVolumeRemovedEventDetail = {
type VolumeInputCallback = (params: {
volumeActor: VolumeActor;
volumeId: string;
preset?;
}) => unknown;

// @public
Expand Down
5 changes: 3 additions & 2 deletions common/reviews/api/tools.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3501,7 +3501,7 @@ export class RectangleROIThresholdTool extends RectangleROITool {
}

// @public (undocumented)
function rectangleROIThresholdVolumeByRange(annotationUIDs: string[], segmentationVolume: Types_2.IImageVolume, referenceVolumes: Types_2.IImageVolume[], options: ThresholdRangeOptions_2): Types_2.IImageVolume;
function rectangleROIThresholdVolumeByRange(annotationUIDs: string[], segmentationVolume: Types_2.IImageVolume, thresholdVolumeInformation: ThresholdInformation_2[], options: ThresholdOptions): Types_2.IImageVolume;

// @public (undocumented)
export class RectangleROITool extends AnnotationTool {
Expand Down Expand Up @@ -4310,7 +4310,7 @@ type TextBoxHandle = {
};

// @public (undocumented)
function thresholdVolumeByRange(segmentationVolume: Types_2.IImageVolume, referenceVolume: Types_2.IImageVolume, options: ThresholdRangeOptions): Types_2.IImageVolume;
function thresholdVolumeByRange(segmentationVolume: Types_2.IImageVolume, thresholdVolumeInformation: ThresholdInformation[], options: ThresholdRangeOptions): Types_2.IImageVolume;

// @public (undocumented)
function throttle(func: Function, wait?: number, options?: {
Expand Down Expand Up @@ -4670,6 +4670,7 @@ type VolumeCacheVolumeRemovedEventDetail = {
type VolumeInputCallback = (params: {
volumeActor: VolumeActor;
volumeId: string;
preset?;
}) => unknown;

// @public
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/types/IVolumeInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type VolumeInputCallback = (params: {
volumeActor: VolumeActor;
/** unique volume Id in the cache */
volumeId: string;
preset?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to put this here, the callback isn't getting a preset, it is using the preset from the surrounding

}) => unknown;

/**
Expand Down
141 changes: 103 additions & 38 deletions packages/tools/examples/rectangleROIThreshold/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import {
addButtonToToolbar,
addSliderToToolbar,
setCtTransferFunctionForVolumeActor,
setPetColorMapTransferFunctionForVolumeActor,
} from '../../../../utils/demo/helpers';
import * as cornerstoneTools from '@cornerstonejs/tools';
import perfusionColorMap from './preset';

// This is for debugging purposes
console.warn(
Expand Down Expand Up @@ -42,6 +44,12 @@ const { ViewportType } = Enums;
const volumeName = 'CT_VOLUME_ID'; // Id of the volume less loader prefix
const volumeLoaderScheme = 'cornerstoneStreamingImageVolume'; // Loader id which defines which volume loader to use
const volumeId = `${volumeLoaderScheme}:${volumeName}`; // VolumeId with loader id + volume id

const ctVolumeName = 'CT_VOLUME_ID'; // Id of the volume less loader prefix
const ctVolumeId = `${volumeLoaderScheme}:${ctVolumeName}`; // VolumeId with loader id + volume id
const ptVolumeName = 'PT_VOLUME_ID';
const ptVolumeId = `${volumeLoaderScheme}:${ptVolumeName}`;

const segmentationId = 'MY_SEGMENTATION_ID';
const toolGroupId = 'MY_TOOLGROUP_ID';

Expand Down Expand Up @@ -98,8 +106,12 @@ content.append(instructions);
// ============================= //

let numSlicesToProject = 3;
let lowerThreshold = 100;
let upperThreshold = 500;
let ctLowerThreshold = -900;
let ctUpperThreshold = -700;
const overwrite = true;

let ptLowerThreshold = 0;
let ptUpperThreshold = 5;

addButtonToToolbar({
title: 'Execute threshold',
Expand All @@ -121,65 +133,83 @@ addButtonToToolbar({
return;
}

const { metadata } = annotation; // assuming they are all overlayed on the same toolGroup
const viewport = metadata.enabledElement.viewport as Types.IVolumeViewport;

const volumeActorInfo = viewport.getDefaultActor();

// Todo: this only works for volumeViewport
const { uid } = volumeActorInfo;
const referenceVolume = cache.getVolume(uid);
const ctVolume = cache.getVolume(ctVolumeId);
const ptVolume = cache.getVolume(ptVolumeId);
const segmentationVolume = cache.getVolume(segmentationId);

csToolsUtils.segmentation.rectangleROIThresholdVolumeByRange(
selectedAnnotationUIDs,
segmentationVolume,
[referenceVolume],
[
{ volume: ctVolume, lower: ctLowerThreshold, upper: ctUpperThreshold },
{ volume: ptVolume, lower: ptLowerThreshold, upper: ptUpperThreshold },
],
{
numSlicesToProject,
lower: lowerThreshold,
upper: upperThreshold,
overwrite: false,
overwrite,
}
);
},
});

addSliderToToolbar({
title: `Number of Slices to Segment: ${numSlicesToProject
.toString()
.padStart(4)}`,
range: [1, 5],
title: `#Slices to Segment: ${numSlicesToProject}`,
range: [1, 10],
defaultValue: numSlicesToProject,
onSelectedValueChange: (value) => {
numSlicesToProject = Number(value);
},
updateLabelOnChange: (value, label) => {
label.innerText = `Number of Slices to Segment: ${value}`;
label.innerText = `#Slices to Segment: ${value}`;
},
});

addSliderToToolbar({
title: `PT Lower Thresh: ${ptLowerThreshold}`,
range: [0, 10],
defaultValue: ptLowerThreshold,
onSelectedValueChange: (value) => {
ptLowerThreshold = Number(value);
},
updateLabelOnChange: (value, label) => {
label.innerText = `PT Lower Thresh: ${value}`;
},
});

addSliderToToolbar({
title: `PT Upper Thresh: ${ptUpperThreshold}`,
range: [0, 10],
defaultValue: ptUpperThreshold,
onSelectedValueChange: (value) => {
ptUpperThreshold = Number(value);
},
updateLabelOnChange: (value, label) => {
label.innerText = `PT Upper Thresh: ${value}`;
},
});

addSliderToToolbar({
title: `Lower Threshold: ${lowerThreshold}`,
range: [100, 400],
defaultValue: lowerThreshold,
title: `CT Lower Thresh: ${ctLowerThreshold}`,
range: [-1000, 1000],
defaultValue: ctLowerThreshold,
onSelectedValueChange: (value) => {
lowerThreshold = Number(value);
ctLowerThreshold = Number(value);
},
updateLabelOnChange: (value, label) => {
label.innerText = `Lower Threshold: ${value}`;
label.innerText = `CT Lower Thresh: ${value}`;
},
});

addSliderToToolbar({
title: `Upper Threshold: ${upperThreshold.toString().padStart(4)}`,
range: [500, 1000],
defaultValue: upperThreshold,
title: `CT Upper Thresh: ${ctUpperThreshold}`,
range: [-1000, 1000],
defaultValue: ctUpperThreshold,
onSelectedValueChange: (value) => {
upperThreshold = Number(value);
ctUpperThreshold = Number(value);
},
updateLabelOnChange: (value, label) => {
label.innerText = `Upper Threshold: ${value}`;
label.innerText = `CT Upper Thresh: ${value}`;
},
});

Expand Down Expand Up @@ -258,19 +288,34 @@ async function run() {
// hook instead of mouse buttons, it does not need to assign any mouse button.
toolGroup.setToolActive(StackScrollMouseWheelTool.toolName);

// Get Cornerstone imageIds for the source data and fetch metadata into RAM
const imageIds = await createImageIdsAndCacheMetaData({
StudyInstanceUID:
'1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463',
const wadoRsRoot = 'https://domvja9iplmyu.cloudfront.net/dicomweb';
const StudyInstanceUID =
'1.3.6.1.4.1.14519.5.2.1.7009.2403.871108593056125491804754960339';

// Get Cornerstone imageIds and fetch metadata into RAM
const ctImageIds = await createImageIdsAndCacheMetaData({
StudyInstanceUID,
SeriesInstanceUID:
'1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561',
wadoRsRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb',
'1.3.6.1.4.1.14519.5.2.1.7009.2403.367700692008930469189923116409',
wadoRsRoot,
type: 'VOLUME',
});

const ptImageIds = await createImageIdsAndCacheMetaData({
StudyInstanceUID,
SeriesInstanceUID:
'1.3.6.1.4.1.14519.5.2.1.7009.2403.780462962868572737240023906400',
wadoRsRoot,
type: 'VOLUME',
});

// Define a volume in memory
const ctVolume = await volumeLoader.createAndCacheVolume(ctVolumeId, {
imageIds: ctImageIds,
});
// Define a volume in memory
const volume = await volumeLoader.createAndCacheVolume(volumeId, {
imageIds,
const ptVolume = await volumeLoader.createAndCacheVolume(ptVolumeId, {
imageIds: ptImageIds,
});

// Add some segmentations based on the source data volume
Expand Down Expand Up @@ -321,8 +366,9 @@ async function run() {
toolGroup.addViewport(viewportId2, renderingEngineId);
toolGroup.addViewport(viewportId3, renderingEngineId);

// Set the volume to load
volume.load();
// Set the volumes to load
ptVolume.load();
ctVolume.load();

// Set volumes on the viewports
await setVolumesForViewports(
Expand All @@ -331,6 +377,25 @@ async function run() {
[viewportId1, viewportId2, viewportId3]
);

await setVolumesForViewports(
renderingEngine,
[
{
volumeId: ctVolumeId,
callback: setCtTransferFunctionForVolumeActor,
},
{
volumeId: ptVolumeId,
callback: ({ volumeActor }) =>
setPetColorMapTransferFunctionForVolumeActor({
volumeActor,
preset: perfusionColorMap,
}),
},
],
[viewportId1, viewportId2, viewportId3]
);

// // Add the segmentation representation to the toolgroup
const segmentationRepresentationByUIDs =
await segmentation.addSegmentationRepresentations(toolGroupId, [
Expand Down
Loading