想要融合多帧的时序信息,在目前的多种BEV方法中,使用Ego motion将前一帧Align到当前帧是效果较好的方法,本文将会对Align部分进行讲解。
获取nuscenes历史帧ego motion的方法:Nuscenes——环视相机数据准备:当前帧与前一帧所有数据的获取及操作细节
我们首先使用dataloader获取Nuscenes中的数据:
num_frame = 5 # 保留几帧时序信息
label_mask_list = []
ego_motion_list = []
for i, (gt_map, images_path, ego_motion) in enumerate(tqdm.tqdm(data_loader)):
'''
gt_map [list(Tensor)]: a list saves gt images of All batch
images_path list(list(str)): a list saves multi-view images dir of All batch
'''
for j, semantic_gt in enumerate(gt_map):
'''
j (int): batch size id.
semantic_gt (tensor): gt images # get gt [3, 400, 200]
'''
label_mask_list.append(semantic_gt)
if len(label_mask_list) > num_frame:
label_mask_list.pop(0)
ego_motion_list.append(ego_motion[j][-1])
if len(ego_motion_list) > num_frame:
ego_motion_list.pop(0)
获得的5帧时序mask如图所示:(车辆运动方向为向下)
然后使用motion_align
函数将其中任意两帧进行对齐:
if len(label_mask_list) is num_frame:
prev_idx=0; # 前一帧的idx
curr_idx=4 # 后一帧的idx
aligned_mask = motion_align(label_mask_list, ego_motion_list,prev_idx=prev_idx,curr_idx=curr_idx)
基于该函数即可实现使用ego_motion
的translation
和rotation
信息将两帧空间做对齐:
def motion_align(label_mask_list, ego_motion_list, x_scale=0.15, y_scale=0.15, prev_idx=0, curr_idx=1):
from pyquaternion import Quaternion
# 取出两帧mask
label_mask_prev, label_mask_curr = label_mask_list[prev_idx], label_mask_list[curr_idx]
# 获得全局坐标系下的translation和rotation_θ
translation_prev, translation_curr = ego_motion_list[prev_idx]['translation'][:2], ego_motion_list[curr_idx]['translation'][:2]
rotation_θ_prev, rotation_θ_curr = Quaternion(ego_motion_list[prev_idx]['rotation']).yaw_pitch_roll[0], Quaternion(ego_motion_list[curr_idx]['rotation']).yaw_pitch_roll[0]
# 在全局坐标系下,获得相对变换的平移矩阵translation和相对变换的旋转矩阵rotation
translation_shift = (np.array(translation_curr) - np.array(translation_prev)) / np.array([x_scale, y_scale])
rotation_θ_shift = np.array(rotation_θ_curr) - np.array(rotation_θ_prev) # yaw: rotation angle around the z-axis in radians, in the range `[-pi, pi]`
# 获取当前帧车相对于全局坐标系的旋转矩阵
rotation_θ_ego = np.array(-rotation_θ_curr)
ego_transform_matrix = np.array([[np.cos(rotation_θ_ego),-np.sin(rotation_θ_ego)],
[np.sin(rotation_θ_ego), np.cos(rotation_θ_ego)]])
# 将基于全局坐标系的相对平移量转换到基于ego坐标系的相对平移量
ego_translation_shift = ego_transform_matrix @ translation_shift.T
print("ego_translation_shift:", ego_translation_shift)
# 由于我在生成mask时,进行了顺时针旋转,因此需要将x,y调换位置
translation = ego_translation_shift[::-1]
# 给ego坐标系的x变换为正
translation[0] * -1
# 不同时序帧做空间对齐
aligned_mask = align(label_mask_prev, -rotation_θ_shift, translation)
return aligned_mask
def align(src, θ, xy):
transform_matrix=np.array([[np.cos(θ),-np.sin(θ), -xy[0] ],
[np.sin(θ), np.cos(θ), xy[1] ],
[ 0 , 0 , 1 ]])
# 因为这里我的mask时H,W形式,因此需要转换成一般的W,H
src = np.swapaxes(src, 1, 0)
rows, cols = src.shape
center = (int(rows/2),int(cols/2))
dst=np.zeros((rows,cols),dtype=np.uint8)
for i in range(rows):
for j in range(cols):
src_pos=np.array([i-center[0],j-center[1],1])
[x,y,_]=np.dot(transform_matrix,src_pos)
x=int(x)+center[0]
y=int(y)+center[1]
if x>=rows or y>=cols or x<0 or y<0:
dst[i][j]=255
else:
dst[i][j]=src[x][y]
# 最终转换回H,W形式
return np.swapaxes(dst, 1, 0)
参考文章: