├── 3D-IoU-Python.py └── README.md /3D-IoU-Python.py: -------------------------------------------------------------------------------- 1 | # 3D IoU caculate code for 3D object detection 2 | # Kent 2018/12 3 | 4 | import numpy as np 5 | from scipy.spatial import ConvexHull 6 | from numpy import * 7 | 8 | def polygon_clip(subjectPolygon, clipPolygon): 9 | """ Clip a polygon with another polygon. 10 | 11 | Ref: https://rosettacode.org/wiki/Sutherland-Hodgman_polygon_clipping#Python 12 | 13 | Args: 14 | subjectPolygon: a list of (x,y) 2d points, any polygon. 15 | clipPolygon: a list of (x,y) 2d points, has to be *convex* 16 | Note: 17 | **points have to be counter-clockwise ordered** 18 | 19 | Return: 20 | a list of (x,y) vertex point for the intersection polygon. 21 | """ 22 | def inside(p): 23 | return(cp2[0]-cp1[0])*(p[1]-cp1[1]) > (cp2[1]-cp1[1])*(p[0]-cp1[0]) 24 | 25 | def computeIntersection(): 26 | dc = [ cp1[0] - cp2[0], cp1[1] - cp2[1] ] 27 | dp = [ s[0] - e[0], s[1] - e[1] ] 28 | n1 = cp1[0] * cp2[1] - cp1[1] * cp2[0] 29 | n2 = s[0] * e[1] - s[1] * e[0] 30 | n3 = 1.0 / (dc[0] * dp[1] - dc[1] * dp[0]) 31 | return [(n1*dp[0] - n2*dc[0]) * n3, (n1*dp[1] - n2*dc[1]) * n3] 32 | 33 | outputList = subjectPolygon 34 | cp1 = clipPolygon[-1] 35 | 36 | for clipVertex in clipPolygon: 37 | cp2 = clipVertex 38 | inputList = outputList 39 | outputList = [] 40 | s = inputList[-1] 41 | 42 | for subjectVertex in inputList: 43 | e = subjectVertex 44 | if inside(e): 45 | if not inside(s): 46 | outputList.append(computeIntersection()) 47 | outputList.append(e) 48 | elif inside(s): 49 | outputList.append(computeIntersection()) 50 | s = e 51 | cp1 = cp2 52 | if len(outputList) == 0: 53 | return None 54 | return(outputList) 55 | 56 | def poly_area(x,y): 57 | """ Ref: http://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates """ 58 | return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1))) 59 | 60 | def convex_hull_intersection(p1, p2): 61 | """ Compute area of two convex hull's intersection area. 62 | p1,p2 are a list of (x,y) tuples of hull vertices. 63 | return a list of (x,y) for the intersection and its volume 64 | """ 65 | inter_p = polygon_clip(p1,p2) 66 | if inter_p is not None: 67 | hull_inter = ConvexHull(inter_p) 68 | return inter_p, hull_inter.volume 69 | else: 70 | return None, 0.0 71 | 72 | def box3d_vol(corners): 73 | ''' corners: (8,3) no assumption on axis direction ''' 74 | a = np.sqrt(np.sum((corners[0,:] - corners[1,:])**2)) 75 | b = np.sqrt(np.sum((corners[1,:] - corners[2,:])**2)) 76 | c = np.sqrt(np.sum((corners[0,:] - corners[4,:])**2)) 77 | return a*b*c 78 | 79 | def is_clockwise(p): 80 | x = p[:,0] 81 | y = p[:,1] 82 | return np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)) > 0 83 | 84 | def box3d_iou(corners1, corners2): 85 | ''' Compute 3D bounding box IoU. 86 | 87 | Input: 88 | corners1: numpy array (8,3), assume up direction is negative Y 89 | corners2: numpy array (8,3), assume up direction is negative Y 90 | Output: 91 | iou: 3D bounding box IoU 92 | iou_2d: bird's eye view 2D bounding box IoU 93 | 94 | todo (kent): add more description on corner points' orders. 95 | ''' 96 | # corner points are in counter clockwise order 97 | rect1 = [(corners1[i,0], corners1[i,2]) for i in range(3,-1,-1)] 98 | rect2 = [(corners2[i,0], corners2[i,2]) for i in range(3,-1,-1)] 99 | 100 | area1 = poly_area(np.array(rect1)[:,0], np.array(rect1)[:,1]) 101 | area2 = poly_area(np.array(rect2)[:,0], np.array(rect2)[:,1]) 102 | 103 | inter, inter_area = convex_hull_intersection(rect1, rect2) 104 | iou_2d = inter_area/(area1+area2-inter_area) 105 | ymax = min(corners1[0,1], corners2[0,1]) 106 | ymin = max(corners1[4,1], corners2[4,1]) 107 | 108 | inter_vol = inter_area * max(0.0, ymax-ymin) 109 | 110 | vol1 = box3d_vol(corners1) 111 | vol2 = box3d_vol(corners2) 112 | iou = inter_vol / (vol1 + vol2 - inter_vol) 113 | return iou, iou_2d 114 | 115 | # ---------------------------------- 116 | # Helper functions for evaluation 117 | # ---------------------------------- 118 | 119 | def get_3d_box(box_size, heading_angle, center): 120 | ''' Calculate 3D bounding box corners from its parameterization. 121 | 122 | Input: 123 | box_size: tuple of (length,wide,height) 124 | heading_angle: rad scalar, clockwise from pos x axis 125 | center: tuple of (x,y,z) 126 | Output: 127 | corners_3d: numpy array of shape (8,3) for 3D box cornders 128 | ''' 129 | def roty(t): 130 | c = np.cos(t) 131 | s = np.sin(t) 132 | return np.array([[c, 0, s], 133 | [0, 1, 0], 134 | [-s, 0, c]]) 135 | 136 | R = roty(heading_angle) 137 | l,w,h = box_size 138 | x_corners = [l/2,l/2,-l/2,-l/2,l/2,l/2,-l/2,-l/2]; 139 | y_corners = [h/2,h/2,h/2,h/2,-h/2,-h/2,-h/2,-h/2]; 140 | z_corners = [w/2,-w/2,-w/2,w/2,w/2,-w/2,-w/2,w/2]; 141 | corners_3d = np.dot(R, np.vstack([x_corners,y_corners,z_corners])) 142 | corners_3d[0,:] = corners_3d[0,:] + center[0]; 143 | corners_3d[1,:] = corners_3d[1,:] + center[1]; 144 | corners_3d[2,:] = corners_3d[2,:] + center[2]; 145 | corners_3d = np.transpose(corners_3d) 146 | return corners_3d 147 | 148 | 149 | if __name__=='__main__': 150 | print('------------------') 151 | # get_3d_box(box_size, heading_angle, center) 152 | corners_3d_ground = get_3d_box((1.497255,1.644981, 3.628938), -1.531692, (2.882992 ,1.698800 ,20.785644)) 153 | corners_3d_predict = get_3d_box((1.458242, 1.604773, 3.707947), -1.549553, (2.756923, 1.661275, 20.943280 )) 154 | (IOU_3d,IOU_2d)=box3d_iou(corners_3d_predict,corners_3d_ground) 155 | print (IOU_3d,IOU_2d) #3d IoU/ 2d IoU of BEV(bird eye's view) 156 | 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 3D-IoU-Python 2 | Small code to caculate the 3D IOU score and BEV score for 3D object detection 3 | --------------------------------------------------------------------------------