Skip to content

fix CircleROI tool for wsi #1940

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

Open
wants to merge 26 commits into
base: main
Choose a base branch
from

Conversation

mbellehumeur
Copy link
Contributor

@mbellehumeur mbellehumeur commented Mar 26, 2025

Context

CircleROI and other ROI tools were not generating stats for wsi viewport.

Changes & Results

This PR fixes the CircleROI, RectangleROI, EllipseROI and FreehandROI tool for whole slide (wsi) viewports by skipping statistics calculation that require the voxel manager.
Key changes include:

Changing wsiViewport so that getScalarData returns an empty CanvasScalarData object instead of null.

Wrapping the voxel manager-based points collection of each tool in a condition that bypasses the process when the scalarData is empty.

Copy link

netlify bot commented Mar 26, 2025

Deploy Preview for cornerstone-3d-docs failed. Why did it fail? →

Name Link
🔨 Latest commit c6db595
🔍 Latest deploy log https://app.netlify.com/sites/cornerstone-3d-docs/deploys/67e56a3b2aff190007682d7a

@@ -919,7 +919,10 @@ class CircleROITool extends AnnotationTool {
// Check if one of the indexes are inside the volume, this then gives us
// Some area to do stats over.

if (this._isInsideVolume(pos1Index, pos2Index, dimensions)) {
if (
this._isInsideVolume(pos1Index, pos2Index, dimensions) ||
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you make the isInsideVolume work for WSI as well? I'd rather not have a viewport.type==='whileSlide' anywhere inside the tools.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It might be necessary to improve the WSIViewport with better mapping functions, or perhaps the transformWorldToIndex mapping functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, wsiviewport worldToCanvas returns negative values.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can make it work by changing wsiviewport 'getTransform' but I dont know what else it impacts.

Copy link
Collaborator

Choose a reason for hiding this comment

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

That impacts too much right now - @pedrokohler is working on a fix for that separately, so lets leave this as is for now.

@sedghi sedghi requested a review from Copilot March 26, 2025 17:48
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes the CircleROI tool so that it properly handles whole slide (wsi) viewports by skipping stats that require the voxel manager. Key changes include:

  • Extending the conditional check to include viewport type "wholeSlide" for stats calculation.
  • Wrapping the voxel manager-based points collection in a condition that bypasses the process for whole slide viewports.
Comments suppressed due to low confidence (1)

packages/tools/src/tools/annotation/CircleROITool.ts:924

  • [nitpick] Consider replacing the magic string 'wholeSlide' with a named constant to improve maintainability.
viewport.type === 'wholeSlide'

@wayfarer3130
Copy link
Collaborator

Can you ensure the tests pas[s - I think those are real issues

@@ -244,7 +254,7 @@ class WSIViewport extends Viewport {
getDirection: () => metadata.direction,
getDimensions: () => metadata.dimensions,
getRange: () => [0, 255],
getScalarData: () => this.getScalarData(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add a viewReference to the getter as an optional? I know we don't pass it in yet, but we need to start thinking about it as the getScalarData for whole slide imaging won't be able to return the entire scalar data.

// Return an empty CanvasScalarData object
const emptyData = new Uint8ClampedArray() as CanvasScalarData;
emptyData.getRange = () => [0, 255];
emptyData.frameNumber = -1;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add an emptyData.region that is a rectangle of the enclosing region, and a scale parameter? The region should be zero sized for now, and the scale can be 1, but again I'd like to start thinking about the future.

@@ -1028,6 +1030,14 @@ class CircleROITool extends AnnotationTool {
};

_isInsideVolume = (index1, index2, dimensions) => {
// for wsiViewport
if (index1[1] < 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you put this fix into the WSIViewport itself?

@@ -615,12 +627,16 @@ class WSIViewport extends Viewport {
protected canvasToIndex = (canvasPos: Point2): Point2 => {
const transform = this.getTransform();
transform.invert();
return transform.transformPoint(canvasPos);
return transform.transformPoint(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you use Math.abs on the result here - I think that will get us close to the right value, or on initial setup, figure out which attribute is negative and store a negative multiplier for it.

};

protected indexToCanvas = (indexPos: Point2): Point2 => {
const transform = this.getTransform();
return transform.transformPoint(indexPos);
return transform
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will need to convert back to negative values, so if you on initial setup store an index of which values are negative and multiple by that vector you should get the right sign.

Copy link
Collaborator

@wayfarer3130 wayfarer3130 left a comment

Choose a reason for hiding this comment

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

Couple of small remaining changes, hopefully merge this today.

* @param n - array of numbers or a simple number
* @returns True if n or the first element of n is finite and not NaN
*/
public static isNumber(n: number[] | number): boolean {
Copy link
Member

Choose a reason for hiding this comment

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

there is already a isNumber in packages/core/src/utilities/isEqual.ts , adapt that for your use

@@ -968,6 +968,14 @@ class RectangleROITool extends AnnotationTool {
};

_isInsideVolume = (index1, index2, dimensions) => {
// for wsiViewport
if (index1[1] < 0) {
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't look good. Could you please check if the viewport type is WSI before proceeding? I imagine this might disrupt other types of viewports.

Comment on lines +1033 to +1038
// for wsiViewport
if (index1[1] < 0) {
index1[1] = -index1[1];
}
if (index2[1] < 0) {
index2[1] = -index2[1];
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't look good. Could you please check if the viewport type is WSI before proceeding? I imagine this might disrupt other types of viewports.

Copy link
Member

@sedghi sedghi left a comment

Choose a reason for hiding this comment

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

please see my comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants