(1)16384:points点数
(2)64/128:roi个数
(3)回归loss中bin数量的设置
(4)cfg中参数:RCNN:SIZE_RES_ON_ROI设置为True
(5)Kitti_rcnn_dataset中:pts_near_flag = pts_depth < 40.0 # 此处数值的设置
(6)尝试开启rgb,iou-branch等配置
不直接回归location,而是把location回归问题转成分类问题,x方向6个bin,z方向6个bin,y方向1个bin,yaw角9个bin,一个bin10°
rcnn_reg 输出为 batchsize*46,这46维 = 6_xbin_crossentropy_loss + 6_zbin_crossentropy_loss + 6_xbin_l1_loss + 6_zbin_l1_loss + 1_ybin_crossentropy_loss + 9_yaw_entropy_loss + 9_yaw_l1_loss + 3_size_l1_loss.
def evaluate(label_path,
result_path,
label_split_file,
current_class = 0,
coco = False,
score_thresh = -1):
val_image_ids = _read_imageset_file(label_split_file)
dt_annos = kitti.get_label_annos(result_path, val_image_ids)
MAX_DISTANCE = [60] #---add 60m filter---
for i in range(num_gt):
distance = gt_anno["location"][i][2] #---------
if ((gt_anno["occluded"][i] > MAX_OCCLUSION[difficulty])
or (gt_anno["truncated"][i] > MAX_TRUNCATION[difficulty])
or (height <= MIN_HEIGHT[difficulty]) or distance > MAX_DISTANCE[0]):
rvec 为3*1矩阵,则旋转矩阵RT为:
import cv2
RT, _ = cv2.Rodrigues(rvec)
设旋转矩阵为RT=3*3矩阵,则:
该矩阵中R[0][0]数值的正负决定:旋转的角度是否超过90度,+:表示不超过90度,-:表示超过90度
该矩阵中R[2][0]数值的正负决定:旋转的方向是顺时针(+),还是逆时针(-)
pi = 3.141592653589793
if RT[0, 0] > 0: #R[0,0] +/- :decide the rotation degree is larger than pi or not
theta = math.atan(RT[2, 0] / RT[0, 0])
elif RT[2, 0] > 0: #R[2,0] +/- :decide ratate towards left(-) or right(+)
theta = math.atan(RT[2, 0] / RT[0, 0]) + pi
else:
theta = math.atan(RT[2, 0] / RT[0, 0]) - pi
参考:
https://blog.csdn.net/reasonyuanrobot/article/details/89969676
https://blog.csdn.net/loongkingwhat/article/details/82765388
https://blog.csdn.net/sinolover/article/details/90671784
if yaw_wy + theta > pi:
yaw_new = -(yaw_wy + theta) + 2*pi
elif yaw_wy + theta < -pi:
yaw_new = -(yaw_wy + theta) - 2*pi
else:
yaw_new = -(yaw_wy + theta)
yaw = str(round(yaw_new, 2))
kitti_rcnn_dataset中generate_rpn_training_labels函数:
#center3d[1] -= gt_boxes3d[k][3] / 2 该位置注销
kitti_utils中boxes3d_to_corners3d函数:
#y_corners[:, 4:8] = -h.reshape(boxes_num, 1).repeat(4, axis = 1) # (N, 8) 注释掉改为以下
y_corners[:, 0:4] = (h/2).reshape(boxes_num, 1).repeat(4, axis = 1) # (N, 8)
y_corners[:, 4:8] = (-h/2).reshape(boxes_num, 1).repeat(4, axis = 1) # (N, 8)
proposal_layer中:
#proposals[:, 1] += proposals[:, 3] / 2 # set y as the center of bottom 注释掉
overlap = overlaps[gt_num: gt_num + gt_nums[i], dt_num:dt_num + dt_nums[i]] : gt_num 和 dt_num的顺序要跟前面overlaps的顺序对应起来,先gt后dt
RPN的分类部分:
(1)如果计算loss使用交叉熵crossentropy:
rpn_cls的输出维度为 [n, 3*2],然后reshape到[n, 3, 2](其中最后两维为背景和目标两类),和cls_label=[0,1,2,3....] (其中0代表背景,1,2,3代表类别)进行cross-entropy交叉熵计算分类损失cls-loss。
(2)如果计算loss使用BCE或者focal-loss:
rpn_cls的输出维度为[n,3](其中3为1,2,3目标类别,不包括背景),cls_label = [n,3] ,表示方式维度同rpn_cls,(其中 每一个类别都由0或者1表示,0背景,1目标),进行binary-cross-entropy的loss计算。其中rpn_cls的输出要先经过softmax,not sigmoid。如果是crossentropyloss则不用。
RPN的回归部分:
rpn_reg的输出维度为[n, 76*3],然后reshape到[n,3,76],reg_label为[n, 7],rpn_rep要先根据label的类别选择anchor,定好anchor之后,rpn_reg的维度就变为[n,76],然后进行decoder解析成[n,7]的维度,再和reg_label计算回归loss。回归loss通常为l1loss,由于point-rcnn的位置回归采用了bin的方式,将回归计算转为了分类,所以里面计算了分类+回归两次loss相加。x,y,z,yaw,都是如此,size只用了L1,iou不需要。
RCNN的分类和回归部分,同RPN一致。
class RCNNNet(nn.Module):
def __init__(self, num_classes, input_channels=0, use_xyz=True):
super().__init__()
self.SA_modules = nn.ModuleList()
channel_in = input_channels
if cfg.RCNN.USE_RPN_FEATURES: #True
self.rcnn_input_channel = 3 + int(cfg.RCNN.USE_INTENSITY) + int(cfg.RCNN.USE_MASK)+3 + int(cfg.RCNN.USE_DEPTH) #USE_INTENSITY False,USE_MASK:True, USE_DEPTH:True
self.xyz_up_layer = pt_utils.SharedMLP([self.rcnn_input_channel] + cfg.RCNN.XYZ_UP_LAYER,
bn=cfg.RCNN.USE_BN) #MLP共享权值网络提取特征
这里面的self.rcnn_input_channel的维度=3(x,y,z)+(use_intensity=1) + (use_mask= num_class目标类别个数) + (use_depth=0),要注意use_mask的地方。一个类别对应一个mask。
ProposalLayer中和train_functions中都设计anchor的生成,因为reg回归部分只是回归位置的偏移量,所以依靠anchor才能生成roi。
设anchor=[[h_car,w_car,l_car],[h_ped,w_ped,l_ped],[h_cyc,w_cyc,l_cyc]]
anchor要在所有点云的位置都要进行生成,三个anchor是生成 [n*car, n*ped,n*cyc]还是生成[n*(car,ped,cyc)]要根据rpn的输出来定。例如rpn的cls输出为 [n, 3*2]-->[n,3, 2]的维度,这个中间的3里面其实是car,ped和cyc,一个位置3个类别,所以anchor的生成就是torch.repeat_internal 而不用torch.repeat,因为前者是生成的[n*(car,ped,cyc)]。
之前出现能检测到车不能检测到人的原因是在于anchor生成的时候,会在每个点的位置生成anchor,然后按照这些anchor的score进行排序,通过NMS进行anchor过滤。车和人的anchor要分开进行生成,不然同一个位置生成三个类别的anchor,车anchor的score比较大,会通过NMS把其他类别anchor覆盖掉,因此,要按类别分别进行roi生成。