Getting ROI bounds from vtkMRMLMarkupsROINode

I am looking into switching from vtkMRMLAnnotationROINode to vtkMRMLMarkupsROINode node for selecting ROI bounding box within a volume.

Previously I have been using GetBounds() function to quickly get bounding box of the ROI from vtkMRMLAnnotationROINode. The code looked like as follows:

roiNode = slicer.mrmlScene.GetFirstNodeByClass("vtkMRMLAnnotationROINode")
roi_points_ras = [0.0] * 6
roiNode.GetBounds(roi_points_ras)
selected_roi = convertRasToIJK(roi_points_ras)

Now with the vtkMRMLMarkupsROINode, I am trying to get the same functionality. But it seems like it does not have a GetBounds() function with similar output. How can I achieve the same with this node?

Here is my code until now:

roiNode = slicer.mrmlScene.GetFirstNodeByClass("vtkMRMLMarkupsROINode")
center = [0] * 3
roiNode.GetCenter(center)
roi_points_ras = [(x-s/2, x+s/2) for x, s in zip(center, roiNode.GetSize())]
roi_points_ras=[item for sublist in roi_points_ras for item in sublist]
selected_roi = convertRasToIJK(roi_points_ras)

Maybe I am missing something here and perhaps there is a starightforward way of getting roi bounding box in IJK coordinate space with vtkMRMLMarkupsROINode. If this is the case, kindly can someone guide me how to achieve the above.

Many thanks!
Muhammad

I believe you must be mistaken as the new ROI node type does have a GetBounds() method with results just like the previous.

old_roi = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLAnnotationROINode")
old_roi.SetRadiusXYZ(5,10,15)
old_roi_bounds = [0]*6
old_roi.GetBounds(old_roi_bounds)
print(old_roi_bounds)
# [-5.0, 5.0, -10.0, 10.0, -15.0, 15.0]

new_roi = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsROINode")
new_roi.SetRadiusXYZ(5,10,15)
new_roi_bounds = [0]*6
new_roi.GetBounds(new_roi_bounds)
print(new_roi_bounds)
# [-5.0, 5.0, -10.0, 10.0, -15.0, 15.0]
2 Likes

This works only in latest preview build (4.13). In stable release (4.11) it gives the following output:

new_roi = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsROINode")
new_roi.SetRadiusXYZ(5,10,15)
new_roi_bounds = [0]*6
new_roi.GetBounds(new_roi_bounds)
print(new_roi_bounds)
# [0, 0, 0, 0, 0, 0]

Any ideas how I can support both versions?

An option when there aren’t many differences is to do something like the following:

if slicer.app.majorVersion >= 5 or (slicer.app.majorVersion == 4 and slicer.app.minorVersion >= 13):
  # do code for Slicer 4.13 preview and later
  new_roi.GetBounds(new_roi_bounds)  # or new_roi.GetBoundsROI(new_roi_bounds)
  print(new_roi_bounds)
  # [-5.0, 5.0, -10.0, 10.0, -15.0, 15.0]
else:
  # do code for Slicer 4.11.20210226 and older
  new_roi.GetBoundsROI(new_roi_bounds)
  print(new_roi_bounds)
  # [-5.0, 5.0, -10.0, 10.0, -15.0, 15.0]

@Sunderlandkyl May be able to tell what is the preferred method for getting bounds for this object. Either GetBounds or GetBoundsROI.

Another option is this could be accomplished by setting up different branches with support for each version as indicated below.

https://slicer.readthedocs.io/en/latest/developer_guide/extensions.html#extensions-index

Extension developers have to make sure that the extension description in each branch of the Extensions index is compatible with the corresponding Slicer version. Extension developers often create the same branches ( master , 4.11 , 4.13 , …) in their repository and they specify this branch name in the extensions descriptor file.

1 Like

Because the new Markups ROI can be rotated freely, using a normal bounds with 6 values doesn’t always work. GetBounds()/GetRASBounds() returns the axis-aligned bounds, so it will be correct if the ROI is axis-aligned. In the image below, the magenta cube will be the bounds that are returned by GetBounds() if the ROI is rotated.

image

The way to get the OBB in any orientation would be to use the GetPlanes()/GetPlanesWorld() functions, which return the planes for the 6 faces of the ROI.

If you are checking whether or not a point is in the ROI, also consider the IsPointInROI()/IsPointInROIWorld() functions.

GetBoundsROI() returns the bounds in the axis-aligned ROI coordinate system, with the center of the ROI at (0,0,0). It is mostly used internally, and should probably be made into a protected function.

2 Likes

Yes, @Sunderlandkyl please make it protected to make the API simpler (we can later re-add, if we find that it is needed). Also add your explanation to the ROI node API documentation (developers may not think about the impact of that the ROI can be rotated).

I’ve made a PR to remove the function here: ENH: Remove vtkMRMLMarkupsROINode::GetBoundsROI() and add documentation by Sunderlandkyl · Pull Request #5905 · Slicer/Slicer · GitHub.

1 Like