I created a segmentation with some segments and want to get an axis-aligned bounding box for each segment. As far as i know, with the segment statistics module we can compute oriented bounding boxes, but i couldn’t find an option to get axis-aligned bounding boxes. I tried to get the segments as numpy arrays and use numpy functions to find the required values as suggested here. But since i rotated the segments before, the segments do not match with the original scalar volume anymore. Do I need to rotate and resample the volume as well? Or is there another way to get axis-aligned bounding boxes for segments?
There is the SegmentAxisAlignment
module in this extension:
The extension is not yet added to the Extension Manager so for now you can clone this repository and point to the module in Edit / Application Settings / Additional module directories.
Dear professor, I downloaded a zip file from GitHub to my local machine and then dragged the folder into “Additional module paths,” but the module still doesn’t appear. Did I understand “clone” correctly? Where should the folder be downloaded to?
The folder you need to drag&drop to Additional module paths is that of the module
Thank you very much! I successfully added the module. Can this module measure the length, width, and height of the axis-aligned bounding box (AABB)?
Sadly, no. But here is a function that does it using the above module
def computeAlignedROIForSegment(self, volumeNode, segmentationNode, segmentId, initialAngleLR = 0, initialAnglePA = 0):
# Use SegmentAxisAlignment module
saaw = slicer.modules.segmentaxisalignment.widgetRepresentation().self()
saaw.initializeParameterNode()
# Get segment name
segmentName = segmentationNode.GetSegmentation().GetSegment(segmentId).GetName()
# Create output ROI node
roi = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLMarkupsROINode')
roi.SetName(segmentName + '_ROI')
# Create output aligned volume node
if volumeNode:
alignedVolume = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLScalarVolumeNode')
alignedVolume.SetName(volumeNode.GetName() + '_' + segmentName)
else:
alignedVolume = None
# Create temporary nodes to store intermediate results
alignmentTransform = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLLinearTransformNode')
outputSegmentation = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode')
# Set parameters for segment alignment
saaw._parameterNode.SetNodeReferenceID("InputSegmentationNode", segmentationNode.GetID())
saaw._parameterNode.SetParameter("InitialAlignmentAngleLR", str(initialAngleLR))
saaw._parameterNode.SetParameter("InitialAlignmentAnglePA", str(initialAnglePA))
saaw._parameterNode.SetParameter("InputSegmentID", segmentId)
saaw._parameterNode.SetNodeReferenceID("OutputTransform", alignmentTransform.GetID())
saaw._parameterNode.SetNodeReferenceID("OutputSegmentationNode", outputSegmentation.GetID())
# Set parameters for volume alignment
if volumeNode:
saaw._parameterNode.SetNodeReferenceID("InputVolume", volumeNode.GetID())
saaw._parameterNode.SetNodeReferenceID("OutputVolume", alignedVolume.GetID())
# Calculate alignment
saaw.onCalculateButtonClicked()
# Get alignment angles
angleLR = float(saaw._parameterNode.GetParameter("OutputAlignmentAngleLR"))
anglePA = float(saaw._parameterNode.GetParameter("OutputAlignmentAnglePA"))
print('Computed alignment angles for segment = ', segmentId)
print(' - Alignment angle LR = ', angleLR)
print(' - Alignment angle PA = ', anglePA)
# Get aligned volume
if volumeNode:
saaw.onAlignVolumeButtonClicked()
# Get output segmentation polydata
outputSegmentation.HardenTransform() # harden alignment transform
outputSegmentation.CreateClosedSurfaceRepresentation()
outputSegmentIds = outputSegmentation.GetSegmentation().GetSegmentIDs()
outputSegmentPolyData = outputSegmentation.GetClosedSurfaceInternalRepresentation(outputSegmentIds[0])
# Get bounding box for segment
bounds = outputSegmentPolyData.GetBounds()
roiCenter = [(bounds[0]+bounds[1])/2, (bounds[2]+bounds[3])/2, (bounds[4]+bounds[5])/2]
roiSize = [abs(bounds[1]-bounds[0]), abs(bounds[3]-bounds[2]), abs(bounds[5]-bounds[4])]
roi.SetCenterWorld(roiCenter)
roi.SetSizeWorld(roiSize)
roi.SetLocked(True)
roi.GetDisplayNode().SetGlyphScale(0) # hide control point in the roi center
roi.GetDisplayNode().SetSelectedColor([1,1,0]) # yellow
roi.GetDisplayNode().SetFillOpacity(0.0)
roi.GetDisplayNode().SetHandlesInteractive(False)
roi.GetDisplayNode().SetPropertiesLabelVisibility(False)
# Apply inverse transform to align bounding boxes to original segments
alignmentTransform.Inverse()
roi.SetAndObserveTransformNodeID(alignmentTransform.GetID())
roi.HardenTransform()
# Delete temporary nodes
slicer.mrmlScene.RemoveNode(outputSegmentation)
slicer.mrmlScene.RemoveNode(alignmentTransform)
return [roi, alignedVolume, angleLR, anglePA]
You can decide if you run the module and then only some lines from this function to get the ROI, or try to use the function, which executes the module and creates the ROI node too. If you have trouble figuring out how to use this let me know.
I think I need to paste it into the Python Interactor in 3D Slicer? Do I need to open my file in Slicer first before pasting this code? However, it seems to not work.
You need to edit the code a bit to tailor to your needs and then copy-paste into the Python console.
To avoid the need for copy-pasting code, you could add computation of bounding boxes aligned to various coordinate system axes by enhancing any existing Segment Statistics plugins or adding a new plugin. Note that there are several coordinate system axes that you may want to align to:
- the world coordinate system (RAS)
- the segmentation’s parent coordinate system
- the binary labelmap’s image coordinate system (if the segmentation contains a binary labelmap representation)
You may need to clone the segmentation and harden the transform that moves the segmentation to the desired coordinate system; and then get RAS bounds (for closed surface representation) or IJK bounds for each segment (for labelmap representation). Depending on what you need exactly, you may be able to use the code that @cpinter referred to above with some small changes.