1

Input image:
enter image description here

import cv2
import matplotlib.pyplot as plt
from skimage.morphology import skeletonize

im = cv2.imread('a.png',cv2.IMREAD_GRAYSCALE)
#conts ,_ = cv2.findContours(im, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#cont = conts[0]
sk = skeletonize(im >0)
plt.imshow(sk)

enter image description here

skeleton_yx = np.argwhere(sk > 0)
pts = np.flip(skeleton_yx, axis=None) #yx -> xy
print(pts.shape) #(774, 2)

I want to order these pts to reproduce the sk image above.

EDIT:
following a comment by Michael Szczesny that refers to this question , the first answer by Imanol Luengo works fine but it's very slow at step 4 (Find the path with smallest cost from all sources):

paths = [list(nx.dfs_preorder_nodes(T, i)) for i in range(len(points))]

some other solutions require knowing the first point to start ordering from, I don't know a way to find it, that generalizes to other problems where there's a single, continuous unsorted line (I guess finding extreme points is possible either by looping over all white pixels or by using filters, will try it).

what I've tried:

def rotational_sort(list_of_xy_coords):
  cx, cy = list_of_xy_coords.mean(0)
  x, y = list_of_xy_coords.T
  angles = np.arctan2(x-cx, y-cy)
  indices = np.argsort(angles)
  return list_of_xy_coords[indices]

im = np.zeros_like(im)
pts2= rotational_sort(pts)
cv2.polylines(im, [pts2], False, 255,2)
plt.imshow(im)

enter image description here

from scipy.signal import savgol_filter
from sklearn.decomposition import PCA
from scipy import interpolate
def XYclean(x,y): 
  xy = np.concatenate((x.reshape(-1,1), y.reshape(-1,1)), axis=1)     
  pca = PCA(2)
  pca.fit(xy)
  #transform into pca space   
  xypca = pca.transform(xy) 
  newx = xypca[:,0]
  newy = xypca[:,1]
  indexSort = np.argsort(x)
  newx = newx[indexSort]
  newy = newy[indexSort]
  #add some more points (optional)
  f = interpolate.interp1d(newx, newy, kind='linear')        
  newX=np.linspace(np.min(newx), np.max(newx), 100)
  newY = f(newX)            
  #return back to old coordinates
  xyclean = pca.inverse_transform(np.concatenate((newX.reshape(-1,1), newY.reshape(-1,1)), axis=1) )
  xc=xyclean[:,0]
  yc = xyclean[:,1]
  return np.hstack((xc.reshape(-1,1),yc.reshape(-1,1))).astype(int)

im = np.zeros_like(im)
X,Y=pts[:,0],pts[:,1]
pts2= XYclean(X,Y)
cv2.polylines(im, [pts2], False, 255,2)
plt.imshow(im)

enter image description here

yazan sayed
  • 777
  • 7
  • 24
  • @MichaelSzczesny thanks, it does but it's very slow if use the `NearestNeighbors` method at step 4 (smallest cost from all sources), other answers require knowing the first point, which I don't know if there's a possible way to find it that generalizes to other single, continuous unsorted lines, I'll edit the question with these notes. – yazan sayed Sep 07 '22 at 12:49
  • You could probably get a small speed-up by using Manhattan or Chebyshev distance, but that's just fiddling with details, not a fundamental speed-up – Jiří Baum Sep 07 '22 at 13:20

0 Answers0