# Get C-arm angles from 3D view orientation

**URL:** <https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435>\
**Category:** Support\
**Tags:** fluoroscopy\
**Created:** [July 21, 2022, 5:24pm UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435 "2022-07-21T17:24:45Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![donghmu](https://avatars.discourse-cdn.com/v4/letter/d/9f8e36/32.png) [@donghmu](https://discourse.slicer.org/u/donghmu)\
**Post date:** [July 21, 2022, 5:24pm UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/1 "2022-07-21T17:24:45Z")

</div>

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).

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/5/9/591ad2835fd5b293accbcd4a551d47e33ef73249.jpeg)  
Instead of only displaying the cube (for orientation), can we read the detail information about the related angles?  
 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/d/4/d42cf48f7a8e1b8903f00404595c6bfa3db3b24b.jpeg)  
Thanks in advanced!

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [July 21, 2022, 5:29pm UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/2 "2022-07-21T17:29:03Z")

</div>

There isn’t a dedicated planning tool that I’m aware of. You may want to just use [Markups Angles](https://slicer.readthedocs.io/en/latest/user_guide/modules/markups.html) to design your imaging plan and then match the device as best you can.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [July 21, 2022, 7:03pm UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/3 "2022-07-21T19:03:02Z")

</div>

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:

```python
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)

```

[https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/f/e/fe59764902aeb19599d558cd3b73a7ac4fbecf1a.mp4](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/f/e/fe59764902aeb19599d558cd3b73a7ac4fbecf1a.mp4)

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.

---

<div class="post-metadata">

**Author:** ![donghmu](https://avatars.discourse-cdn.com/v4/letter/d/9f8e36/32.png) [@donghmu](https://discourse.slicer.org/u/donghmu)\
**Post date:** [July 22, 2022, 1:26am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/4 "2022-07-22T01:26:20Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![cloudman](https://avatars.discourse-cdn.com/v4/letter/c/41988e/32.png) [@cloudman](https://discourse.slicer.org/u/cloudman)\
**Post date:** [April 13, 2023, 6:52am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/5 "2023-04-13T06:52:50Z")

</div>

Hi, my image is XA, I want to display the anatomical angles of the C-arm (LAO/RAO, CRA/CAU) in the corner of the red slice viewer, I try to modify your code, but an error is reported when running, how do I modify it?

```auto
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()
sliceViewName = layoutManager.sliceViewNames()[0]
view = layoutManager.sliceWidget(sliceViewName).sliceView()
sliceViewNode = view.mrmlViewNode()
cameraNode = slicer.modules.cameras.logic().GetViewActiveCameraNode(sliceViewNode)
cameraObservation = cameraNode.AddObserver(vtk.vtkCommand.ModifiedEvent, lambda caller, event, view=view: cameraUpdated(caller, view))

cameraUpdated(cameraNode, view)

```

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [April 19, 2023, 11:31am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/6 "2023-04-19T11:31:48Z")

</div>

> [@cloudman](#):
>
> but an error is reported

What is the error? What Slicer version do you use?

---

<div class="post-metadata">

**Author:** ![cloudman](https://avatars.discourse-cdn.com/v4/letter/c/41988e/32.png) [@cloudman](https://discourse.slicer.org/u/cloudman)\
**Post date:** [April 21, 2023, 7:52am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/7 "2023-04-21T07:52:46Z")

</div>

@lassoan The error is:

```auto
>>> sliceViewNode = view.mrmlViewNode()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: qMRMLSliceView has no attribute named 'mrmlViewNode'

```

I need to display angles in red slice viewer, the image is 2D, so I modified your 3D viewer code, but it is wrong, I don’t know how to modify it.

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [April 23, 2023, 12:26pm UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/8 "2023-04-23T12:26:28Z")

</div>

The script above solves a more complex task: computing C-arm angles from the camera viewpoint in a 3D view. You can use it to find a good angulation that you can just on your C-arm.

If you already have an X-ray angiography image sequence then the C-arm angles are already stored in the DICOM series for each frame, we just need to display it.

For a dynamic acquisition - for showing angles changing throughout the series - we would need to create a somewhat complex script. For showing angles for a static-gantry image we could easily just change the DICOM importer to include th C-arm angle in the node name.

What is your overall goal? How do you plan to use XA images in Slicer?

---

<div class="post-metadata">

**Author:** ![cloudman](https://avatars.discourse-cdn.com/v4/letter/c/41988e/32.png) [@cloudman](https://discourse.slicer.org/u/cloudman)\
**Post date:** [April 24, 2023, 2:14am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/9 "2023-04-24T02:14:52Z")

</div>

I just want to display the C-arm angles in red slice viewer. I found the angles are in the DICOM tags:(0018,1510) DS PositionerPrimaryAngle, (0018,1511) DS PositionerSecondaryAngle. How can I make it display as (LAO/RAO, CRA/CAU) in the red slice viewer?

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [April 24, 2023, 6:05pm UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/10 "2023-04-24T18:05:10Z")

</div>

Does the C-arm move during your acquisitions? Is it enough to show the angles of the for frame of the sequence?

---

<div class="post-metadata">

**Author:** ![cloudman](https://avatars.discourse-cdn.com/v4/letter/c/41988e/32.png) [@cloudman](https://discourse.slicer.org/u/cloudman)\
**Post date:** [April 25, 2023, 6:51am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/11 "2023-04-25T06:51:56Z")

</div>

The acquisition process does not move, and each sequence has an angle. I want to display the angle of the sequence after opening a sequence. The angle information is recorded in the Dicom tag, but I don’t know how to display it.  
 ![20230425145028](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/b/8/b87fb48487adcd8ada982482d442b304462d4f19.png)

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [April 25, 2023, 11:17am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/12 "2023-04-25T11:17:23Z")

</div>

Would it be sufficient to include the angles in the node name? You can enable showing the buffer name in the slice view corner annotation.

---

<div class="post-metadata">

**Author:** ![cloudman](https://avatars.discourse-cdn.com/v4/letter/c/41988e/32.png) [@cloudman](https://discourse.slicer.org/u/cloudman)\
**Post date:** [April 26, 2023, 3:10am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/13 "2023-04-26T03:10:48Z")

</div>

Yes, it’s enough, I just want the angle to be displayed. I don’t know how to get the value of PositionerPrimaryAngle and PositionerSecondaryAngle, and then display it on the red slice viewer. I only know that I can get the angle through the following function by getting those two values:

```auto
def formatPositionerAngle(primaryAngleDeg, secondaryAngleDeg):
    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

```

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [May 2, 2023, 8:12pm UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/14 "2023-05-02T20:12:53Z")

</div>

The angles could be added to the name of thr image at the time of DICOM import. The importer is [this](https://github.com/Slicer/Slicer/blob/main/Modules/Scripted/DICOMPlugins/DICOMImageSequencePlugin.py) Python script, you can modify it on your computer.

---

<div class="post-metadata">

**Author:** ![cloudman](https://avatars.discourse-cdn.com/v4/letter/c/41988e/32.png) [@cloudman](https://discourse.slicer.org/u/cloudman)\
**Post date:** [May 6, 2023, 7:40am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/15 "2023-05-06T07:40:23Z")

</div>

It works, Thank you！  
 ![QQ截图20230506153341](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/7/d/7d29b5df656f881f7ac62242bd7f1af2e02e1588.png)

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [March 26, 2024, 1:38am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/16 "2024-03-26T01:38:58Z")

</div>

A post was split to a new topic: [Angle measurement range](https://discourse.slicer.org/t/angle-measurement-range/35081)

---

<div class="post-metadata">

**Author:** ![davidneustadter](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/davidneustadter/32/78532_2.png) [@davidneustadter](https://discourse.slicer.org/u/davidneustadter)\
**Post date:** [October 27, 2024, 1:22pm UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/17 "2024-10-27T13:22:54Z")

</div>

Andras,

I was wondering if the fluoro simulator module in the SlicerHeart project has been released yet. I could really use it for my procedure planning! I was also wondering how accurate the simulated fluoro images will be; for instance, will they take into account the fanning of the x-ray beam and the distance from the x-ray tube focal spot to the anatomy?

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [October 27, 2024, 3:38pm UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/18 "2024-10-27T15:38:50Z")

</div>

The simulated fluoro image is fully accurate in term of geometry of the cone-beam projection.

The paper about the cathlab simulator is in a very good shape and it will be submitted soon, so it should not take too long to release the module.

---

<div class="post-metadata">

**Author:** ![davidneustadter](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/davidneustadter/32/78532_2.png) [@davidneustadter](https://discourse.slicer.org/u/davidneustadter)\
**Post date:** [October 30, 2024, 8:27am UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/19 "2024-10-30T08:27:14Z")

</div>

Andras,

Thank you very much for the quick reply. I look forward to trying the fluoro simulator, but if you haven’t submitted the article yet, then there is no telling how long it may take… I can’t wait that long, so I am working on a poor-man’s version on my own.

I found your code for extracting or defining the c-arm angles, and I figured out how to adjust the colors and opacity to get a reasonable fluoro simulated images, but I am stuck on one issue… the standard fluoro image display shows the projection as viewed from the detector (head up, left side of body on right side of image when at orientation 0,0). In slicer, the image is as viewed from the camera which mimics the x-ray tube in terms of the perspective. For this reason the images that I obtain are flipped L-R relative to the standard fluoro display.

Do you have any suggestions of how to solve this?

Thanks,

David

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [October 30, 2024, 9:29pm UTC](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435/20 "2024-10-30T21:29:23Z")

</div>

If you want to flip between showing the front or back of the mesh, you can use reverse perspective projection in the renderer camera. We have discussed this a couple of times on the VTK forum (for example, [here](https://discourse.vtk.org/t/inverted-perspective-divide-in-vtk/13061/16)).

In the simulator in SlicerHeart we do not support flipping between rendering from the front or back, as we could achieve all visualizations that we needed without implementing this (by rendering 3D content semitransparent). Also, there may be a few things that need to be fixed (one issue that we know about is that currently GPU volume rendering is not compatible with reverse perspective projection).

[Next page](https://discourse.slicer.org/t/get-c-arm-angles-from-3d-view-orientation/24435.md?page=2)
