How do I generate a curved tube from 2D slices by shifting center coordinates?

Question:

I am trying to generate a 3D matrix with a tube structure running through it. I can make the tube straight by copying a 2D numpy array with a circle centered at (x,y) inside, and I can make the tube slanted by adding an int to either the x or y axis for each slice I generate. My question is, how can I move the (x,y) coordinates so that they can form a curve? I can’t add step sizes of curved functions like sine and cosine to the coordinates since to index the numpy array it must be an integer. What is a smart way to generate a curved tube from 2D slices by shifting the center coordinates?

Here is the code I am using to generate a straight tube as a 3D matrix:

import numpy as np 
import cv2
import matplotlib.pyplot as plt

slice_2d = np.zeros((128,128))
circle_center = (50,50)
radius=10

slice_2d = cv2.circle(slice_2d, circle_center, radius, color=1, thickness=-1)
plt.imshow(slice_2d)

# then we repeat the slice 128 times to create a straight tube in a 3D matrix of 128,128,128
tube_matrix = []
for i in range(0,128):
    tube_matrix.append(slice_2d)

tube_matrix = np.array(tube_matrix)
Asked By: accAscrub

||

Answers:

You may use any curve, scale and add offset as needed, and round the center coordinates to integer.

I used the curve for this post.

Here is the loop that adds the slices:

tube_matrix = []
for i in range(128):    
    circle_center = np.round(curve[i]*12 + 15).astype(int)
    slice_2d = cv2.circle(np.zeros((128,128)), tuple(circle_center), radius, color=1, thickness=-1)
    tube_matrix.append(slice_2d)

Each iteration the circle center changes according to the value of curve[i].
Note that curve[i] is scaled and rounded (and converted to int).


Here is the complete code (with some testing code):

import numpy as np
import cv2
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt

# https://stackoverflow.com/questions/52014197/how-to-interpolate-a-2d-curve-in-python
# Define some points:
points = np.array([[0, 1, 8, 2, 2],
                   [1, 0, 6, 7, 2]]).T  # a (nbre_points x nbre_dim) array

# Linear length along the line:
distance = np.cumsum( np.sqrt(np.sum( np.diff(points, axis=0)**2, axis=1 )) )
distance = np.insert(distance, 0, 0)/distance[-1]

alpha = np.linspace(0, 1, 128)

method = 'cubic'   
interpolator =  interp1d(distance, points, kind=method, axis=0)
curve = interpolator(alpha)

#slice_2d = np.zeros((128,128))
#circle_center = (30, 30)
img = np.zeros((128, 128, 3), np.uint8) + 255
radius = 10

tube_matrix = []
for i in range(128):    
    circle_center = np.round(curve[i]*12 + 15).astype(int)
    slice_2d = cv2.circle(np.zeros((128,128)), tuple(circle_center), radius, color=1, thickness=-1)
    tube_matrix.append(slice_2d)

    #Draw cicle on image - for testing
    img = cv2.circle(img, tuple(circle_center), radius, color=(i*10 % 255, i*20 % 255, i*30 % 255), thickness=2)

# Graph:
plt.figure(figsize=(7,7))
plt.plot(*curve.T, 'o')
plt.axis('equal'); plt.legend(); plt.xlabel('x'); plt.ylabel('y')

plt.figure(figsize=(7,7))
plt.imshow(img)
plt.show()

Testing image (img):
enter image description here

Answered By: Rotem
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.