Link_tester
Link_tester

Reputation: 1081

How to find corners of several 3d points stored as numpy arrays

I am looking for an efficient way to extract four corners from several numpy arrays. I read this solution but could not solve my issue. My arrays are stored in a dictionary. These arrays and three column representing x,y and z coordinate. Each row is also a point and I have in some cases thousands of points. As my shows, I have here three sets with different colors and want to extract corners of each set. It should be noted that in reality I have tens of sets and each set I have thousands of point but still I want to extract four corners of each set. In the fig c stands for the corners, first number is set number and second numbers is corners number. I copied my dictionary including two data sets here:

my_di={'sub_arr1': array([[50., 22.5, 28.53083253],
       [50., 7.5, 28.53022337],
       [41.23412371, 7.5, 10.],
       [41.22613668, 22.5, 10.],
       [50.69447517, 7.5, 30.],
       [50.6946373 , 22.5, 30.],
       [60.12217999, 7.5, 50.],
       [60.12265205, 22.5, 50.],
       [69.37286377, 7.5, 70.],
       [69.35833931, 22.5, 70.],
       [70., 22.5, 71.41123295],
       [70., 7.5, 71.37528419],
       [78.46719742, 7.5, 90.],
       [78.42491627, 22.5, 90.]]), 'sub_arr2': array([[60.79483509, 7.5, 30.],
       [51.35614872,  7.5, 10.],
       [60.7971096 , 22.5, 30.],
       [51.34645939, 22.5, 10.],
       [70., 22.5, 49.47742224],
       [70., 7.5, 49.50087547],
       [70.2344656 , 7.5, 50.],
       [70.24563313, 22.5, 50.],
       [79.53233719, 7.5, 70.],
       [79.53927994, 22.5, 70.],
       [88.7122488, 7.5, 90.],
       [88.69922638, 22.5, 90.]])}

from these two sets, I want to have the following results as the corners:

corners=[[[41.23412371, 7.5, 10.],
          [41.22613668, 22.5, 10.],
          [78.42491627, 22.5, 90.],
          [78.46719742, 7.5, 90.]],
          [51.35614872,  7.5, 10.],
          [51.34645939, 22.5, 10.],
          [88.69922638, 22.5, 90.],
          [88.7122488, 7.5, 90.]]]
      

I tried two ways to do do that but my results are not that much useful. Firstly the following code that takes the 4 farthest points in comparison with the center of mass of all points, but for the case that repartition is not symmetrical it does not work:

from scipy.spatial import distance
four_c=np.array([])
for i in my_di.values():
    idx = np.argsort(distance.cdist([np.mean(i,axis=0)],i)).flatten()[-4:]
    f_c=i[idx,:]
    four_c=np.append (four_c, f_c)
four_c=four_c.reshape(int(len(four_c)/3),3)

when I print four_c, it give some other rows rather than four corners of each set. Then, I tried a much more longer code:

from scipy.spatial import distance
c1=np.array([])
c2=np.array([])
c3=np.array([])
c4=np.array([])
for i in dicti_ver_fault.values():
    corner1 = np.array(i.min (axis=0)) # this gives the row containing MIN values of x,y and z
    corner1 = corner1.reshape(1,3)
    c1=np.append(corner1, c1)
    corner2 = np.array([])
    corner3 = np.array([])
    corner4 = np.array(i.max (axis=0)) # this gives the row containing MAX values of x,y and z
    corner4 = corner4.reshape(1,3)
    c4=np.append(corner4, c4)
# the next block will find the corner in which x and y are minimum and z is maximum
    for j in i[:,0]:
        if j == max (i[:,0]):
            for h in i[:,1]: 
                if h == min (i[:,1]):
                    for k in i[:,2]:
                        if k == max (i[:,2]):
                            corner2 = np.append(corner2, np.array([j,h,k]))
                            corner2=corner2[0:3]
                            corner2 = corner2.reshape(1,3)
# the next block will find the corner in which x and z are minimum and y is maximum
    for m in i[:,0]:
        if m == min (i[:,0]):
            for n in i[:,1]: 
                if n == max (i[:,1]):
                    for o in i[:,2]:
                        if o == min (i[:,2]):
                            corner3 = np.append(corner3, np.array([m,n,o]))
                            corner3=corner3[0:3]
                            corner3 = corner3.reshape(1,3)
c1=c1.reshape(int(len(c1)/3),3)
c2=c2.reshape(int(len(c2)/3),3)
c3=c3.reshape(int(len(c3)/3),3)
c4=c4.reshape(int(len(c4)/3),3)

I cannot also find the absolute max and min of each column and use them, because in lots of cases corners are not absolute min or max values. I really appreciate any contribution.

enter image description here

Upvotes: 0

Views: 961

Answers (1)

Dalibor Cimr
Dalibor Cimr

Reputation: 58

You can find corners by the computing of vectors from point to both sides. After that, you compute the angle of these two vectors.

import itertools
import numpy as np
arr = [[50., 22.5, 28.53083253],
       [50., 7.5, 28.53022337],
       [41.23412371, 7.5, 10.],
       [41.22613668, 22.5, 10.],
       [50.69447517, 7.5, 30.],
       [50.6946373 , 22.5, 30.],
       [60.12217999, 7.5, 50.],
       [60.12265205, 22.5, 50.],
       [69.37286377, 7.5, 70.],
       [69.35833931, 22.5, 70.],
       [70., 22.5, 71.41123295],
       [70., 7.5, 71.37528419],
       [78.46719742, 7.5, 90.],
       [78.42491627, 22.5, 90.]]

comb = list(itertools.combinations(arr, 3))

line = []

#find the combination which have almost 0 or almost 180 degree angle
for points_arr in comb:
    points = np.array(points_arr)
    ba = points[0] - points[1]
    bc = points[2] - points[1]

    cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
    angle = np.arccos(cosine_angle)
    if(np.degrees(angle) < 1 or np.degrees(angle) > 179):
        print(np.degrees(angle))
        line.extend(points_arr)
        break


#remove points in line from arr          
for point1 in arr:
    for point2 in line:
        #print(sum(np.array(point1) - np.array(point2)), point1, point2)
        if sum(np.array(point1) - np.array(point2)) == 0:
            arr.remove(point1)

#add other points in line          
for point1 in arr:
    ba = np.array(line[0]) - np.array(line[1])
    bc = np.array(point1) - np.array(line[1])

    cosine_angle = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
    angle = np.arccos(cosine_angle)
    if(np.degrees(angle) < 1 or np.degrees(angle) > 179):
        line.append(point1)

#remove points in line from arr          
for point1 in arr:
    for point2 in line:
        #print(sum(np.array(point1) - np.array(point2)), point1, point2)
        if sum(np.array(point1) - np.array(point2)) == 0:
            arr.remove(point1)

I know that the code is not clear and many of it is duplication which can be modify to methods. This will move 7 points from arr to line and you can continue until arr will not be empty

Upvotes: 1

Related Questions