# How to extract both x-, y-, z-coordinates and normalvector at each point of centerline of airwaysegmentation

**URL:** <https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353>\
**Category:** Development\
**Tags:** centerlines\
**Created:** [April 8, 2024, 1:34pm UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353 "2024-04-08T13:34:02Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Johan\_Rubak](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/johan_rubak/32/69951_2.png) [@Johan\_Rubak](https://discourse.slicer.org/u/Johan_Rubak)\
**Post date:** [April 8, 2024, 1:34pm UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/1 "2024-04-08T13:34:02Z")

</div>

Hi. I want to extract a centerline of an airwaysegmentation. I have tried out different tools for this, among other SlicerVMTK extension, but always ending up in a situation with only the possibility of exporting x-, y-, z-coordinates but without corresponding normalvectors that indicate the actual curvature at each point.

It is possible to export the centerline with both the points and the corresponding normalvector at each point?

---

<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 8, 2024, 1:53pm UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/2 "2024-04-08T13:53:36Z")

</div>

Tangent, normal, and binormal vectors can be easily computed from curve points anywhere, anytime, using a coordinate system generator class, such as `vtkParallelTransportFrame`, so we don’t need to save these vectors.

You can conveniently access these vectors in the world coordinate system in Slicer’s Python console as numpy arrays:

```python
curveNode = getNode("OC")
curveNode.GetMeasurement("curvature mean").SetEnabled(True)
positions = slicer.util.arrayFromMarkupsCurvePoints(curveNode, True)
normals = slicer.util.arrayFromMarkupsCurveData(curveNode, "Normals", True)
curvatures = slicer.util.arrayFromMarkupsCurveData(curveNode, "Curvature", True)

```

If you prefer, you can access all these arrays as VTK polydata (to easily use in VTK filters or write to file):

```python
curveNode.GetCurveCoordinateSystemGeneratorWorld().Update()
curvePolyData = curveNode.GetCurveCoordinateSystemGeneratorWorld().GetOutput()

```

---

<div class="post-metadata">

**Author:** ![Johan\_Rubak](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/johan_rubak/32/69951_2.png) [@Johan\_Rubak](https://discourse.slicer.org/u/Johan_Rubak)\
**Post date:** [April 8, 2024, 7:05pm UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/3 "2024-04-08T19:05:42Z")

</div>

Ahh thanks, that helped a lot, seems like the whole medical imaging industry can always rely on you lassoan 🙂

---

<div class="post-metadata">

**Author:** ![Johan\_Rubak](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/johan_rubak/32/69951_2.png) [@Johan\_Rubak](https://discourse.slicer.org/u/Johan_Rubak)\
**Post date:** [April 11, 2024, 5:42pm UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/4 "2024-04-11T17:42:00Z")

</div>

I got the normals exported and was just about using it for a program im developing, when i noticed that the normals is not exactly what i expected them to be. I want them to indicate the normal to the plane to each point facing towards the next point, instead they seem to indicate the normal from the side if that makes any sense. I have tried to extract the centerline in Simpleware SCANIP, where they indicate what i want them to, but i would rather use 3D slicer, since it is open-source! 😄 Here are some pictures (first 3d slicer, second scanip):

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/7/2/72f7233b0874080652d0f3203e44bff3f67c3ed3.jpeg)

Do you think there is any way i can calculate and export them in way so they indicate what i want them to?

---

<div class="post-metadata">

**Author:** ![Johan\_Rubak](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/johan_rubak/32/69951_2.png) [@Johan\_Rubak](https://discourse.slicer.org/u/Johan_Rubak)\
**Post date:** [April 11, 2024, 7:38pm UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/5 "2024-04-11T19:38:31Z")

</div>

Okay figured out myself that extracting the tangents would achieve what i wanted. Now I have a new problem… 🙂

When using the “Extract Centerline” module, i set the curve sampling distance to 0.1269mm (Image 1), this gives 517 controlpoints spread along the centerline (Image 2). But when I want to save the positions and corresponding tangents (using the commands you showed me) to a csv file, I end up with way more points, 5375 points (Image 3).

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/a/d/adad12ef04978777734aa1f46cea05b70fed2422.jpeg)

I want to extract points with the specific distance 0.1269mm and then the corresponding tangents. Is there a way to access the tangents at each controlpoint instead?

---

<div class="post-metadata">

**Author:** ![Johan\_Rubak](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/johan_rubak/32/69951_2.png) [@Johan\_Rubak](https://discourse.slicer.org/u/Johan_Rubak)\
**Post date:** [April 11, 2024, 9:02pm UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/6 "2024-04-11T21:02:59Z")

</div>

So far I have made a way to calculate the tangents between each controlpoint, but do think there is a way this should be done more precise or is it as good as it gets?

---

<div class="post-metadata">

**Author:** ![mssm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/mssm/32/76622_2.png) [@mssm](https://discourse.slicer.org/u/mssm)\
**Post date:** [August 16, 2024, 1:37am UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/7 "2024-08-16T01:37:14Z")

</div>

Hi, Johan.

Your thread is also very interesting.

I might have the answer for the “many points (5375 points)”.

The Slicer program seems to interpolate the curve when calculating measurements such as tangents, normals, binormals, curvatures, etc. It seems to create nine points between each control point.

So, if the number of control points is N, the total number of measurement points is 10(N-1)+1 = 10N-9.

By the way, I am very interested in the visualization of normal vectors posted on April 12th. Actually, you needed the figure on the right, but I would like the one on the left.

Could you tell me how to draw normal vectors at each control point in 3D slicer with a Python script?

I appreciate any help you can provide.

Masa Shojima, Japan.

---

<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:** [August 19, 2024, 1:21pm UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/8 "2024-08-19T13:21:59Z")

</div>

> [@mssm](#):
>
> So, if the number of control points is N, the total number of measurement points is 10(N-1)+1 = 10N-9.

If your curve already have many points very close to each other then you can [set the number of points per interpolating segment](https://apidocs.slicer.org/master/classvtkMRMLMarkupsCurveNode.html#ab7e78f2538dae5ea876ae7d0963e376c) to 0.

> [@mssm](#):
>
> By the way, I am very interested in the visualization of normal vectors

This question was also posted and was answered here:

> [@How can I draw normal vectors of a centerline in the 3D slicer?](https://discourse.slicer.org/t/how-can-i-draw-normal-vectors-of-a-centerline-in-the-3d-slicer/37933/5):
>
> Dear Andras Lasso sensei, Thanks a lot. I tried the magic code right away. However, I got the error message as shown in the attached image. Could you please help me again? I also looked into “addCoordinateSystemUpdator”. For some reason, I couldn’t use it in my environment. MacStudio, M2, MacOS Sonoma 14.6.1 Slicer 5.6.2 r32448 / f10cd8c Regards, Masaaki By the way, when application is starting, such messages appears in the python console: Python 3.9.10 (main, Apr 5 2024, 00:33:09) […

---

<div class="post-metadata">

**Author:** ![Johan\_Rubak](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/johan_rubak/32/69951_2.png) [@Johan\_Rubak](https://discourse.slicer.org/u/Johan_Rubak)\
**Post date:** [August 19, 2024, 6:56pm UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/9 "2024-08-19T18:56:39Z")

</div>

Thankyou for the respons!

For the normalvectors I ended up using this script to just calculate the normals i needed from point to point manually. Ignore the variable referencepoints that is for something specific in my case. This creates a csv. file with the points and normals.

```auto
import slicer
import csv
import qt
import numpy as np

refpoints_name = qt.QInputDialog.getText(None, "String Input", "Enter name of referencepoints:")
centerline_name = qt.QInputDialog.getText(None, "String Input", "Enter name of centerline:")

curveNode = slicer.util.getNode(centerline_name)
curveNode.GetMeasurement("curvature mean").SetEnabled(True)
#normals = slicer.util.arrayFromMarkupsCurveData(curveNode, "Tangents", True)
#positions = slicer.util.arrayFromMarkupsCurvePoints(curveNode, True)

positions = slicer.util.arrayFromMarkupsControlPoints(curveNode)
normals = []
for i in range(len(positions)-1):
    normal = positions[i+1] - positions[i]
    normal = normal / np.linalg.norm(normal)
    normals.append(normal)

ref_points = slicer.util.getNode(refpoints_name)
ref_points_only = slicer.util.arrayFromMarkupsControlPoints(ref_points)

directory_dialog = qt.QFileDialog()
directory_dialog.setFileMode(qt.QFileDialog.Directory)
directory_dialog.setWindowTitle("Select folder to save the csv file")

directory_dialog.exec_()
selected_directory = directory_dialog.selectedFiles()[0]
print("Selected directory:", selected_directory)

file_name = qt.QInputDialog.getText(None, "File Name Input", "Enter a file name:")
file_path = selected_directory + "/" + file_name + ".csv"
print("Selected file path:", file_path)

with open(file_path, mode='w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(['X', 'Y', 'Z'])

    for pos in ref_points_only:
        writer.writerow([pos[0], pos[1], pos[2]])
    
    writer.writerow(['X', 'Y', 'Z', 'Normal_X', 'Normal_Y', 'Normal_Z'])
    for i in range(len(positions)-1):
        writer.writerow([positions[i][0], positions[i][1], positions[i][2], normals[i][0], normals[i][1], normals[i][2]])

print("Data saved to:", file_path)

```

For the plotting i was using matplotlib, but have also good succes with open3d, but it is not applicable with newer versions of python.

```auto
 import matplotlib.pyplot as plt

        # Create a 3D figure
        fig = plt.figure()
        ax = fig.add_subplot(111, projection='3d')

        # Plot the centerline points
        ax.scatter(centerline_points[:, 0], centerline_points[:, 1], centerline_points[:, 2])
        # Plot the normal vectors
        ax.quiver(centerline_points[:, 0], centerline_points[:, 1], centerline_points[:, 2],
                  centerline_points[:, 3], centerline_points[:, 4], centerline_points[:, 5],
                  length=0.1, normalize=True, color='r')

        ax.set_box_aspect([np.ptp(centerline_points[:, 0]), np.ptp(centerline_points[:, 1]), np.ptp(centerline_points[:, 2])])

        # Set labels for the axes
        ax.set_xlabel('X')
        ax.set_ylabel('Y')
        ax.set_zlabel('Z')

        # Show the plot
        plt.show()

```

---

<div class="post-metadata">

**Author:** ![mssm](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/mssm/32/76622_2.png) [@mssm](https://discourse.slicer.org/u/mssm)\
**Post date:** [August 19, 2024, 10:57pm UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/10 "2024-08-19T22:57:13Z")

</div>

How kind you are!  
Thanks a lot.

---

<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:** [August 24, 2024, 7:44am UTC](https://discourse.slicer.org/t/how-to-extract-both-x-y-z-coordinates-and-normalvector-at-each-point-of-centerline-of-airwaysegmentation/35353/11 "2024-08-24T07:44:13Z")

</div>

> [@Johan\_Rubak](#):
>
> For the plotting i was using matplotlib, but have also good succes with open3d, but it is not applicable with newer versions of python.

You can get really good, interactive plots right in Slicer’s 3D viewer, by simple Python scripting. For example, you can plot the tangent, normal, and binormal for each curve control point with a [short Python script](https://discourse.slicer.org/t/how-can-i-draw-normal-vectors-of-a-centerline-in-the-3d-slicer/37933/4):

[![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/3/7/3794275dd32e10a919d845740a17950db4e1ce8c.jpeg)](https://discourse.slicer.org/t/how-can-i-draw-normal-vectors-of-a-centerline-in-the-3d-slicer/37933/4)
