Angles measurement in 3D view

Hi. I am an interventional cardiologist. Currently I am interested in pre-planning for my intervention. In detail, I want to determine the optimal angles for setting the X-ray views.
Is there any extension, or Python codes for displaying these parameter (as I show in the following image).


Instead of only displaying the cube (for orientation), can we read the detail information about the related angles?

Thanks in advanced!

There isn’t a dedicated planning tool that I’m aware of. You may want to just use Markups Angles to design your imaging plan and then match the device as best you can.

You can copy-paste this code snippet to the Python console to show the anatomical angles (LAO/RAO, CRA/CAU) of the C-arm in the corner of a 3D view:

threeDViewIndex = 0  # change this to show angles in a different 3D view

def positionerAngleFromViewNormal(viewNormal):
    # According to https://www5.informatik.uni-erlangen.de/Forschung/Publikationen/2014/Koch14-OVA.pdf
    nx = -viewNormal[0]  # L
    ny = -viewNormal[1]  # P
    nz =  viewNormal[2]  # S
    import math
    if abs(ny) > 1e-6:
        primaryAngleDeg = math.atan(-nx/ny) * 180.0 / math.pi
    elif nx >= 0:
        primaryAngleDeg = 90.0
    else:
        primaryAngleDeg = -90.0
    secondaryAngleDeg = math.asin(nz) * 180.0 / math.pi
    return [primaryAngleDeg, secondaryAngleDeg]

def formatPositionerAngle(positionerAngles):
    primaryAngleDeg, secondaryAngleDeg = positionerAngles
    text =  f'{"RAO" if primaryAngleDeg < 0 else "LAO"} {abs(primaryAngleDeg):.1f}\n'
    text += f'{"CRA" if secondaryAngleDeg < 0 else "CAU"} {abs(secondaryAngleDeg):.1f}'
    return text

def cameraUpdated(cameraNode, view):
    viewNormal = cameraNode.GetCamera().GetDirectionOfProjection()
    positionerAngleText = formatPositionerAngle(positionerAngleFromViewNormal(viewNormal))
    view.cornerAnnotation().SetText(vtk.vtkCornerAnnotation.UpperRight, positionerAngleText)
    view.cornerAnnotation().GetTextProperty().SetColor(1,1,0)  # yellow
    view.scheduleRender()

layoutManager = slicer.app.layoutManager()
view = layoutManager.threeDWidget(threeDViewIndex).threeDView()
threeDViewNode = view.mrmlViewNode()
cameraNode = slicer.modules.cameras.logic().GetViewActiveCameraNode(threeDViewNode)
cameraObservation = cameraNode.AddObserver(vtk.vtkCommand.ModifiedEvent, lambda caller, event, view=view: cameraUpdated(caller, view))

cameraUpdated(cameraNode, view)

# Execute the next line to stop updating the positioner angles in the view corner
# cameraNode.RemoveObserver(cameraObservation)

We are developing a fluoro simulator module for interventional cardiology as part of the SlicerHeart project. The module will be able to display not just the angles but simulated fluoro images and compute optimal viewing angles, etc. We’ll release it when the results are first published - probably within a year.

1 Like

Kindly thanks for your super helpful reply :smiley:
Hope to see your module soon.
Thank you so much!