3

I have a numpy array as the following:

my_array = np.float32([[[ 323. , 143.]], [[ 237. , 143.]], [[ 227. , 230.]], [[ 318. , 233.]]])

This 4 points represent the vertices of a rectangle that lies on a image, I need to reorder them clockwise and save it to a new np array, (top-left-> top-right -> bottom - right -> bottom - left). In my example it will be:

[237,  143] -> [323, 143] -> [318, 233] -> [227, 230]

I have read this but my skills on numpy aren't as good to implement it...

Thanks!

Community
  • 1
  • 1
Luis Enrique
  • 440
  • 1
  • 4
  • 10
  • Have you taken a look at this? http://stackoverflow.com/questions/2706605/sorting-a-2d-numpy-array-by-multiple-axes Maybe it helps – rafaelc May 06 '15 at 22:54

2 Answers2

4

You could do something like this -

import numpy as np
from scipy.spatial import distance

def sortpts_clockwise(A):
    # Sort A based on Y(col-2) coordinates
    sortedAc2 = A[np.argsort(A[:,1]),:]

    # Get top two and bottom two points
    top2 = sortedAc2[0:2,:]
    bottom2 = sortedAc2[2:,:]

    # Sort top2 points to have the first row as the top-left one
    sortedtop2c1 = top2[np.argsort(top2[:,0]),:]
    top_left = sortedtop2c1[0,:]

    # Use top left point as pivot & calculate sq-euclidean dist against
    # bottom2 points & thus get bottom-right, bottom-left sequentially
    sqdists = distance.cdist(top_left[None], bottom2, 'sqeuclidean')
    rest2 = bottom2[np.argsort(np.max(sqdists,0))[::-1],:]

    # Concatenate all these points for the final output
    return np.concatenate((sortedtop2c1,rest2),axis =0)

Sample input, output -

In [85]: A
Out[85]: 
array([[ 281.,  147.],
       [ 213.,  170.],
       [ 239.,  242.],
       [ 307.,  219.]], dtype=float32)

In [86]: sortpts_clockwise(A)
Out[86]: 
array([[ 213.,  170.],
       [ 281.,  147.],
       [ 307.,  219.],
       [ 239.,  242.]], dtype=float32)
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • Note to OP: If the input array is of shape `4 x 1 x 2`, it could be squeezed to a shape of `4 x 2` at the start before proceeding with the rest of the code. So, `A = np.squeeze(A)` at the start could be used in such a case. – Divakar May 07 '15 at 08:10
  • It works great but I need to change the tolerance for some matrices like A = np.float32([[281., 147.],[213. ,170.],[239. ,242.], [307. ,219.]]), so I made a variable tolerance, any advise how to pick this tolerance given the points? – Luis Enrique May 07 '15 at 12:20
  • @LuisEnrique Check out the edits please. Now, it uses eucl. dist., so must be more robust. – Divakar May 07 '15 at 15:00
0

If you need such you show in your example

new_array = my_array[[1,0,3,2]]

Or exactly clockwise (and in general, not only for 4 points)

n = len(my_array)
order = [i for i in range(0, n-2)]
order.insert(0, n-1)
new_array = my_array[order]
cosmoscalibur
  • 1,131
  • 1
  • 8
  • 17