图像2D框
bounding box
目标框(bounding box)来描述目标的位置,目标框是矩形的。
由矩形左上角的坐标(x1,y1)以及右下角的坐标(x2,y2)进行表示。
另外,还可以采用边界框矩形的中心坐标(xc,yc)以及宽高(w,h)进行表示
(1)"左上-右下"转换为"中心-宽高"
def box_corner_to_center(boxes):
"""从(左上,右下)转换到(中间,宽度,高度)"""
x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
cx = (x1 + x2) / 2
cy = (y1 + y2) / 2
w = x2 - x1
h = y2 - y1
boxes = torch.stack((cx, cy, w, h), axis=-1)
return boxes
(2)"中心-宽高"转换为"左上-右下"
def box_center_to_corner(boxes):
"""从(中间,宽度,高度)转换到(左上,右下)"""
cx, cy, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
x1 = cx - 0.5 * w
y1 = cy - 0.5 * h
x2 = cx + 0.5 * w
y2 = cy + 0.5 * h
boxes = torch.stack((x1, y1, x2, y2), axis=-1)
return boxes
(3)画出目标框(dog_bbox采用绿色,cat_bbox采用红色)
def bbox_to_rect(bbox, color):
# 将边界框(左上x,左上y,右下x,右下y)格式转换成matplotlib格式:
# ((左上x,左上y),宽,高)
return plt.Rectangle(
xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1],
fill=False, edgecolor=color, linewidth=2)
img = plt.imread('1.jpg')
fig = plt.imshow(img)
fig.axes.add_patch(bbox_to_rect(dog_bbox, 'blue'))
fig.axes.add_patch(bbox_to_rect(cat_bbox, 'red'))
plt.show()
点云3D框
一个3D边界框可以由7个参数决定:位置(x,y,z)、尺寸(l w h)以及朝向角/偏航角/旋转角 。
将与物体朝向平行的棱的长度记为边界框长度l,竖直方向棱的长度记为边界框高度h,余下一组棱的长度记为边界框宽度w
box2corner
'''
bboxes: shape=(n, 7)
return: shape=(n, 8, 3)
^ z x 8 ------ 7
| / / | / |
| /-l/2,w/2,h/2)5 -|---- 6 |(-l/2,-w/2,h/2)
y | / | | | |
<------|o | 4 -----| 3(l/2,-w/2,-h/2)
|/ o |/
(-l/2,w/2,-h/2) 1 ------ 2 (-l/2,-w/2,-h/2)
x: front, y: left, z: top
'''
def box2corner(box):
x = box[0]
y = box[1]
z = box[2]
l = box[3] # dx
w = box[4] # dy
h = box[5] # dz
yaw = box[6]
Box = np.array(
[
[-l / 2, -l / 2, l / 2, l / 2, -l / 2, -l / 2, l / 2, l / 2],
[w / 2, -w / 2, -w / 2, w / 2, w / 2, -w / 2, -w / 2, w / 2],
[-h / 2, -h / 2, -h / 2, -h / 2, h / 2, h / 2, h / 2, h / 2],
]
)
# 先旋转再平移
R = rotz(yaw)
corners_3d = np.dot(R, Box) # corners_3d: (3, 8)
corners_3d[0, :] = corners_3d[0, :] + x
corners_3d[1, :] = corners_3d[1, :] + y
corners_3d[2, :] = corners_3d[2, :] + z
return np.transpose(corners_3d)
3dbox
center_to_corner_box3d
目的是将边界框的位置、尺寸、朝向角参数转换为边界框的8个角点坐标
def bbox3d_center2corners(bboxes):
'''
bboxes: shape=(n, 7)
return: shape=(n, 8, 3)
^ z x 6 ------ 5
| / / | / |
| / 2 -|---- 1 |
y | / | | | |
<------|o | 7 -----| 4
|/ o |/
3 ------ 0
x: front, y: left, z: top
'''
centers, dims, angles = bboxes[:, :3], bboxes[:, 3:6], bboxes[:, 6]
# # 1.generate bbox corner coordinates, clockwise from minimal point
bboxes_corners =np.array([[-0.5, 0.5, 0], [-0.5, -0.5, 0], [0.5, -0.5, 0], [0.5, 0.5, 0],
[-0.5, 0.5, 1.0],[-0.5, -0.5, 1.0], [0.5, -0.5, 1.0], [0.5, 0.5, 1.0]],
dtype=np.float32)
bboxes_corners = bboxes_corners[None, :, :] * dims[:, None, :] # (1, 8, 3) * (n, 1, 3) -> (n, 8, 3)
# # 2. rotate around z axis
rot_sin ,rot_cos = np.sin(angles),np.cos(angles)
ones ,zeros= np.ones_like(rot_cos), np.zeros_like(rot_cos)
rot_mat = np.array([[rot_cos, rot_sin, zeros],
[-rot_sin, rot_cos, zeros],
[zeros, zeros, ones]],
dtype=np.float32) # (3, 3, n)
rot_mat = np.transpose(rot_mat, (2, 0, 1)) # (n, 3, 3)
bboxes_corners = np.matmul(bboxes_corners,rot_mat) # (n, 8, 3)
# # 3. translate to centers
bboxes_corners += centers[:, None, :]
return bboxes_corners
corner_to_center_box3d
# -*- cooing:UTF-8 -*-
# File Name : utils.py
# Purpose :
# Creation Date : 09-12-2017
def angle_in_limit(angle):
# To limit the angle in -pi/2 - pi/2
limit_degree = 5
while angle >= np.pi / 2:
angle -= np.pi
while angle < -np.pi / 2:
angle += np.pi
if abs(angle + np.pi / 2) < limit_degree / 180 * np.pi:
angle = np.pi / 2
return angle
# TODO: 0/90 may be not correct
def corner_to_center_box3d(boxes_corner, coordinate='camera', calib_mat=None):
# (N, 8, 3) -> (N, 7) x,y,z,h,w,l,ry/z
if coordinate == 'lidar':
for idx in range(len(boxes_corner)):
boxes_corner[idx] = lidar_to_camera_point(boxes_corner[idx], calib_mat=calib_mat)
ret = []
for roi in boxes_corner:
if cfg.CORNER2CENTER_AVG: # average version
roi = np.array(roi)
h = abs(np.sum(roi[:4, 1] - roi[4:, 1]) / 4)
w = np.sum(
np.sqrt(np.sum((roi[0, [0, 2]] - roi[3, [0, 2]])**2)) +
np.sqrt(np.sum((roi[1, [0, 2]] - roi[2, [0, 2]])**2)) +
np.sqrt(np.sum((roi[4, [0, 2]] - roi[7, [0, 2]])**2)) +
np.sqrt(np.sum((roi[5, [0, 2]] - roi[6, [0, 2]])**2))
) / 4
l = np.sum(
np.sqrt(np.sum((roi[0, [0, 2]] - roi[1, [0, 2]])**2)) +
np.sqrt(np.sum((roi[2, [0, 2]] - roi[3, [0, 2]])**2)) +
np.sqrt(np.sum((roi[4, [0, 2]] - roi[5, [0, 2]])**2)) +
np.sqrt(np.sum((roi[6, [0, 2]] - roi[7, [0, 2]])**2))
) / 4
x, y, z = np.sum(roi, axis=0) / 8
y = y + h/2
ry = np.sum(
math.atan2(roi[2, 0] - roi[1, 0], roi[2, 2] - roi[1, 2]) +
math.atan2(roi[6, 0] - roi[5, 0], roi[6, 2] - roi[5, 2]) +
math.atan2(roi[3, 0] - roi[0, 0], roi[3, 2] - roi[0, 2]) +
math.atan2(roi[7, 0] - roi[4, 0], roi[7, 2] - roi[4, 2]) +
math.atan2(roi[0, 2] - roi[1, 2], roi[1, 0] - roi[0, 0]) +
math.atan2(roi[4, 2] - roi[5, 2], roi[5, 0] - roi[4, 0]) +
math.atan2(roi[3, 2] - roi[2, 2], roi[2, 0] - roi[3, 0]) +
math.atan2(roi[7, 2] - roi[6, 2], roi[6, 0] - roi[7, 0])
) / 8
if w > l:
w, l = l, w
ry = angle_in_limit(ry + np.pi / 2)
else: # max version
h = max(abs(roi[:4, 1] - roi[4:, 1]))
w = np.max(
np.sqrt(np.sum((roi[0, [0, 2]] - roi[3, [0, 2]])**2)) +
np.sqrt(np.sum((roi[1, [0, 2]] - roi[2, [0, 2]])**2)) +
np.sqrt(np.sum((roi[4, [0, 2]] - roi[7, [0, 2]])**2)) +
np.sqrt(np.sum((roi[5, [0, 2]] - roi[6, [0, 2]])**2))
)
l = np.max(
np.sqrt(np.sum((roi[0, [0, 2]] - roi[1, [0, 2]])**2)) +
np.sqrt(np.sum((roi[2, [0, 2]] - roi[3, [0, 2]])**2)) +
np.sqrt(np.sum((roi[4, [0, 2]] - roi[5, [0, 2]])**2)) +
np.sqrt(np.sum((roi[6, [0, 2]] - roi[7, [0, 2]])**2))
)
x, y, z = np.sum(roi, axis=0) / 8
y = y + h/2
ry = np.sum(
math.atan2(roi[2, 0] - roi[1, 0], roi[2, 2] - roi[1, 2]) +
math.atan2(roi[6, 0] - roi[5, 0], roi[6, 2] - roi[5, 2]) +
math.atan2(roi[3, 0] - roi[0, 0], roi[3, 2] - roi[0, 2]) +
math.atan2(roi[7, 0] - roi[4, 0], roi[7, 2] - roi[4, 2]) +
math.atan2(roi[0, 2] - roi[1, 2], roi[1, 0] - roi[0, 0]) +
math.atan2(roi[4, 2] - roi[5, 2], roi[5, 0] - roi[4, 0]) +
math.atan2(roi[3, 2] - roi[2, 2], roi[2, 0] - roi[3, 0]) +
math.atan2(roi[7, 2] - roi[6, 2], roi[6, 0] - roi[7, 0])
) / 8
if w > l:
w, l = l, w
ry = angle_in_limit(ry + np.pi / 2)
ret.append([x, y, z, h, w, l, ry])
if coordinate == 'lidar':
ret = camera_to_lidar_box(np.array(ret), calib_mat=calib_mat)
return np.array(ret)
if __name__ == '__main__':
pass
参考
目标检测(一):目标框和锚框 https://zhuanlan.zhihu.com/p/605626695
https://github.com/geaxgx/depthai_blazepose/blob/main/o3d_utils.py
https://stackoverflow.com/questions/59026581/create-arrows-in-open3d
MMDetection3D/3D目标检测中的边界框和坐标系介绍 https://blog.csdn.net/weixin_45657478/article/details/126860028
https://github.com/tianweiy/CenterPoint/blob/master/det3d/core/bbox/box_np_ops.py
https://github.com/Kiwoo/voxel_yolonet/blob/master/utils/utils.py
标签:roi,--,sum,角点,sqrt,np,boxes,点云,math
From: https://www.cnblogs.com/ytwang/p/18349419