I want to be able to plot a rectangle like the Rectangle ROI tool does by having a function with parameters that specify coordinates. How can I do this
Hi, I had the same desire and had quite a bit of struggle to achieve it. Being new to OHIF, Iām not sure if my approach is the right one, but I finally created a new tool that can display rectangles as overlays on StackViewports.
To understand how this works, it helped me a lot to look at extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx. If you add something like the following code, you can plot rectangles on the current viewport and toggle the visibility with the ImageOverlay Button.
private _renderRectangles(enabledElement, svgDrawingHelper, metaData ) {
const { viewport } = enabledElement;
const imageId = this.getReferencedImageId(viewport);
const svgns = 'http://www.w3.org/2000/svg';
const x = metaData.x; // center of rectangle x-coord
const y = metaData.y; // center of rectangle y-coord
const width = metaData.width; // width of rectangle
const height = metaData.height; // height of rectangle
const color = metaData.color; // color of the rectangle
// Calculate world and canvas coordinates for the rectangle
const overlayTopLeftWorldPos = utilities.imageToWorldCoords(imageId, [x - width / 2, y - height / 2]);
const overlayTopLeftOnCanvas = viewport.worldToCanvas(overlayTopLeftWorldPos);
const overlayBottomRightWorldPos = utilities.imageToWorldCoords(imageId, [x + width / 2, y + height / 2]);
const overlayBottomRightOnCanvas = viewport.worldToCanvas(overlayBottomRightWorldPos);
// Define rectangle attributes
const rectId = `rectangle-overlay-${metaData.id}`;
const rectAttributes = {
'data-id': rectId,
width: overlayBottomRightOnCanvas[0] - overlayTopLeftOnCanvas[0],
height: overlayBottomRightOnCanvas[1] - overlayTopLeftOnCanvas[1],
x: overlayTopLeftOnCanvas[0],
y: overlayTopLeftOnCanvas[1],
fill: `rgba(${color.join(',')})`, // Convert color array to CSS rgba string
'stroke-width': 2,
stroke: 'black',
};
// Create or update the rectangle element in the SVG
const rectElement = svgDrawingHelper.getSvgNode(rectId);
if (rectElement) {
drawing.setAttributesIfNecessary(rectAttributes, rectElement);
svgDrawingHelper.setNodeTouched(rectId);
} else {
const rectElement = document.createElementNS(svgns, 'rect');
drawing.setNewAttributesIfValid(rectAttributes, rectElement);
svgDrawingHelper.appendNode(rectElement, rectId);
}
}
Thanks so much! I was attempting to parse through the library but its so chaotic. After adding this function to extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx would I simply be able to import the function anywhere else in the directory? I created a button located in modes/longitudinal/src/ that runs a command in extensions/cornerstone/src/commandsModule.ts when pressed. How can I run it since enabledElement and svgDrawingHelper are local variables to extensions/cornerstone/src/tools/ImageOverlayViewerTool.tsx, right?
Hey, just following up
Hey, I donāt think you can just call this function from somewhere else, I think you are supposed to do the detour over the Tools. Letās say you run this function inside ā¦/ImageOverlayViewerTool.tsx :
this._renderRectangles(enabledElement, svgDrawingHelper, rectMetadata);
Then, when you toggle the ImageOverlay Button, the Rectangle is shown/not shown. From there I continued by creating a separate tool that is specifically for Rectangles, doing it exactly as they created the ImageOverlayViewerTool.
So you made a new button? Thatās essentially what I want to do as well. I am trying to add AI bounding box capabilities through a button. So far, I have a button that sends a request to a backend and gets coordinates for a bounding box. Can you elaborate on how you used the code provided in your first post and how you incorporated it into a button? Did you just copy paste the code from ImageOverlayViewerTool.tsx to make a new tool and modify it to only do rectangles? Thanks so much.
Did you just copy paste the code from ImageOverlayViewerTool.tsx to make a new tool and modify it to only do rectangles?
Basically yes, I followed the steps they did in this Commit: feat(ImageOverlayViewerTool): add ImageOverlayViewer tool that can reā¦ Ā· OHIF/Viewers@69115da Ā· GitHub
So I did:
- copy code from ImageOverlayViewerTool.tsx
- add new tool to Tool Group
- create Button
What should I do with toggleEnabledDisabledToolbar
? And where do I run
this._renderRectangles(enabledElement, svgDrawingHelper, rectMetadata);
(I duplicated ImageOverlayViewerTool.tsx
and added the code you provided earlier but Iām still unclear on how the code works and where I should insert this code to call it)
If you have one, do you mind sharing your repo?
Thanks so much for your help.
If you add the new Tool to a ToolGroup you can ārunā it when toggling the button.
My code is public: https://github.com/TomWartm/Viewers/blob/efb2c80d569c6fc4e612ebfa8ffb557667e41600/extensions/text-input-extension/src/tools/RectangleOverlayViewerTool.tsx
I hope this helps.
Thanks so much for your help, really appreciate it!
Currently, I am calling a function Iāve written from commandsModule.ts that jumps to a specific slice of a CT scan. Can I make the rectangle overlay work while still doing this (I want to put it inside the function)?
Also, how do I specify the parameters for the rectangle overlay? Thatās still unclear to me.
Good question. I also donāt know where the two elements: enabledElement
and svgDrawingHelper
are defined and why they can be used without an import.
It would be great if someone else could help with this, as this would help a lot to understand how these Tools work.
Take a look at out dynamic annotation addition https://www.cornerstonejs.org/live-examples/dynamicallyaddannotations
How do we implement this and do rectangles?
I found the code for the link you gave me but still unclear how to do for rectangles. cornerstone3D/packages/tools/examples/dynamicallyAddAnnotations/index.ts at 21f9869a43912ca4a1eb70391d439e9e11252f07 Ā· cornerstonejs/cornerstone3D (github.com)
Basically what I want to do is press a button in the toolbar, which gets the current dicom data and sends it to the backend, which runs a model and sends the results back (async). Then, I plot a rectangle on the viewport given the output.
@alireza I have added the same code to add dynamic measurements, but thatās not working for me. Please help.
@alireza Can I simply run cs3dTools.annotation.state.addAnnotation(annotationData); within a function activated by a button? Can I define the annotation (rectangle coordinates, etc.) within my code instead of fetching it from an existing one? How would I do this?
Thanks!
Yes, take a look at how you can assign commands to buttons to run in the toolbar
and
Thanks! My other question was how to define the annotation? In the faq you use a pre-existing annotation, but what if I want to create one with my own parameters?
What are your parameters for an instance? I assume you need something like image coordinates or world coordinates at the very least.
Yeah just image coordinates for where to put the rectangle, thatās it.