コード例 #1
0
ファイル: target.py プロジェクト: zhuofalin/keras-faster-rcnn
def detect_targets_graph(gt_boxes, gt_class_ids, proposals,
                         train_rois_per_image, roi_positive_ratio):
    """
    每个图像生成检测网络的分类和回归目标
    IoU>=0.5的为正样本;IoU<0.5的为负样本
    :param gt_boxes: GT 边框坐标 [MAX_GT_BOXs, (y1,x1,y2,x2,tag)] ,tag=0 为padding
    :param gt_class_ids: GT 类别 [MAX_GT_BOXs, 1+1] ;最后一位为tag, tag=0 为padding
    :param proposals: [N,(y1,x1,y2,x2,tag)] ,tag=0 为padding
    :param train_rois_per_image: 每张图像训练的proposal数量
    :param roi_positive_ratio: proposal正负样本比
    :return:
    """
    # 去除padding
    gt_boxes = tf_utils.remove_pad(gt_boxes)
    gt_class_ids = tf_utils.remove_pad(gt_class_ids)[:, 0]  # 从[N,1]变为[N]
    proposals = tf_utils.remove_pad(proposals)
    proposals_num = tf.shape(proposals)[0]
    # 计算iou
    iou = compute_iou(gt_boxes, proposals)  # [gt_num,rois_num]

    # iou >=0.5为正
    proposals_iou_max = tf.reduce_max(iou, axis=0)  # [rois_num]
    positive_indices = tf.where(
        tf.logical_and(tf.equal(iou, proposals_iou_max),
                       tf.greater_equal(iou, 0.5)))
    gt_pos_idx = positive_indices[:, 0]  # 第一维gt索引
    proposal_pos_idx = positive_indices[:, 1]  # 第二位rois索引
    match_gt_num = tf.shape(tf.unique(gt_pos_idx)[0])[0]  # shuffle 前匹配的gt num

    gt_boxes_pos = tf.gather(gt_boxes, gt_pos_idx)
    class_ids = tf.gather(gt_class_ids, gt_pos_idx)
    proposal_pos = tf.gather(proposals, proposal_pos_idx)
    # 根据正负样本比确定最终的正样本
    positive_num = tf.minimum(
        tf.shape(proposal_pos)[0],
        int(train_rois_per_image * roi_positive_ratio))
    gt_boxes_pos, class_ids, proposal_pos, gt_pos_idx = shuffle_sample(
        [gt_boxes_pos, class_ids, proposal_pos, gt_pos_idx],
        tf.shape(proposal_pos)[0], positive_num)
    match_gt_num_after_shuffle = tf.shape(
        tf.unique(gt_pos_idx)[0])[0]  # shuffle 后匹配的gt num

    # 计算回归目标
    deltas = regress_target(proposal_pos, gt_boxes_pos)

    # 负样本:与所有GT的iou<0.5且iou>0.1
    proposal_iou_max = tf.reduce_max(iou, axis=0)
    proposal_neg_idx = tf.cond(  # 需要考虑GT个数为0的情况;全部都是负样本
        tf.greater(tf.shape(gt_boxes)[0], 0),
        true_fn=lambda: tf.where(
            tf.logical_and(proposal_iou_max < 0.5, proposal_iou_max > 0.1))[:,
                                                                            0],
        false_fn=lambda: tf.cast(tf.range(proposals_num), dtype=tf.int64))
    # 确定负样本数量
    negative_num = tf.minimum(train_rois_per_image - positive_num,
                              tf.shape(proposal_neg_idx)[0])
    proposal_neg_idx = tf.random_shuffle(proposal_neg_idx)[:negative_num]
    # 收集负样本
    proposal_neg = tf.gather(proposals, proposal_neg_idx)
    class_ids_neg = tf.zeros(shape=[negative_num])  # 背景类,类别id为0
    deltas_neg = tf.zeros(shape=[negative_num, 4])

    # 合并正负样本
    train_rois = tf.concat([proposal_pos, proposal_neg], axis=0)
    deltas = tf.concat([deltas, deltas_neg], axis=0)
    class_ids = tf.concat([class_ids, class_ids_neg], axis=0)

    # 计算padding
    class_ids, train_rois = tf_utils.pad_list_to_fixed_size(
        [tf.expand_dims(class_ids, axis=1), train_rois],
        train_rois_per_image)  # class_ids分类扩一维
    # 为后续处理方便负样本tag设置为-1
    deltas = tf_utils.pad_to_fixed_size_with_negative(
        deltas, train_rois_per_image, negative_num=negative_num)
    # 其它统计指标
    gt_num = tf.shape(gt_class_ids)[0]  # GT数
    miss_gt_num = gt_num - match_gt_num
    miss_gt_num_shuffle = gt_num - match_gt_num_after_shuffle  # shuffle后未分配roi的GT
    gt_min_max_iou = tf.reduce_min(tf.reduce_max(iou, axis=1))  # gt 匹配最小最大值
    train_rois.set_shape([train_rois_per_image, 5])
    return [
        deltas, class_ids, train_rois,
        tf_utils.scalar_to_1d_tensor(miss_gt_num),
        tf_utils.scalar_to_1d_tensor(miss_gt_num_shuffle),
        tf_utils.scalar_to_1d_tensor(gt_min_max_iou),
        tf_utils.scalar_to_1d_tensor(positive_num),
        tf_utils.scalar_to_1d_tensor(negative_num),
        tf_utils.scalar_to_1d_tensor(proposals_num)
    ]
コード例 #2
0
ファイル: target.py プロジェクト: zhuofalin/keras-faster-rcnn
def rpn_targets_graph(gt_boxes,
                      gt_cls,
                      anchors,
                      anchors_tag,
                      rpn_train_anchors=None):
    """
    处理单个图像的rpn分类和回归目标
    a)正样本为 IoU>0.7的anchor;负样本为IoU<0.3的anchor; 居中的为中性样本,丢弃
    b)需要保证所有的GT都有anchor对应,即使IoU<0.3;
    c)正负样本比例保持1:1
    :param gt_boxes: GT 边框坐标 [MAX_GT_BOXs, (y1,x1,y2,x2,tag)] ,tag=0 为padding
    :param gt_cls: GT 类别 [MAX_GT_BOXs, 1+1] ;最后一位为tag, tag=0 为padding
    :param anchors: [anchor_num, (y1,x1,y2,x2)]
    :param anchors_tag:[anchor_num] bool类型
    :param rpn_train_anchors: 训练样本数(256)
    :return:
    deltas:[rpn_train_anchors,(dy,dx,dh,dw,tag)]:anchor边框回归目标,tag=1 为正样本,tag=0为padding,tag=-1为负样本
    class_ids:[rpn_train_anchors,1+1]: anchor边框分类,tag=1 为正样本,tag=0为padding,tag=-1为负样本
    indices:[rpn_train_anchors,(indices,tag)]: tag=1 为正样本,tag=0为padding,tag=-1为负样本
    """

    # 获取真正的GT,去除标签位
    gt_boxes = tf_utils.remove_pad(gt_boxes)
    gt_cls = tf_utils.remove_pad(gt_cls)[:, 0]  # [N,1]转[N]

    # 获取有效的anchors
    valid_anchor_indices = tf.where(anchors_tag)[:, 0]  # [valid_anchors_num]
    anchors = tf.gather(anchors, valid_anchor_indices)
    # 计算IoU
    iou = compute_iou(gt_boxes, anchors)
    # print("iou:{}".format(iou))

    # 每个GT对应的IoU最大的anchor是正样本
    gt_iou_argmax = tf.argmax(iou, axis=1)
    positive_gt_indices_1 = tf.range(tf.shape(gt_boxes)[0])  # 索引号就是1..n-1
    positive_anchor_indices_1 = gt_iou_argmax

    # 每个anchors最大iou ,且iou>0.7的为正样本
    anchors_iou_max = tf.reduce_max(iou, axis=0)
    # 正样本索引号(iou>0.7),
    positive_anchor_indices_2 = tf.where(
        anchors_iou_max > 0.7, name='rpn_target_positive_indices')  # [:, 0]
    # 找到正样本对应的GT boxes 索引
    # anchors_iou_argmax = tf.argmax(iou, axis=0)  # 每个anchor最大iou对应的GT 索引 [n]
    anchors_iou_argmax = tf.cond(  # 需要考虑GT个数为0的情况
        tf.greater(tf.shape(gt_boxes)[0], 0),
        true_fn=lambda: tf.argmax(iou, axis=0),
        false_fn=lambda: tf.cast(tf.constant([]), tf.int64))
    positive_gt_indices_2 = tf.gather_nd(anchors_iou_argmax,
                                         positive_anchor_indices_2)

    # 合并两部分正样本
    positive_gt_indices = tf.concat(
        [positive_gt_indices_1,
         tf.cast(positive_gt_indices_2, tf.int32)],
        axis=0,
        name='rpn_gt_boxes_concat')
    positive_anchor_indices = tf.concat(
        [positive_anchor_indices_1, positive_anchor_indices_2[:, 0]],
        axis=0,
        name='rpn_positive_anchors_concat')

    # 根据正负样本比1:1,确定最终的正样本
    positive_num = tf.minimum(
        tf.shape(positive_anchor_indices)[0], int(rpn_train_anchors * 0.9))
    positive_anchor_indices, positive_gt_indices = shuffle_sample(
        [positive_anchor_indices, positive_gt_indices],
        tf.shape(positive_anchor_indices)[0], positive_num)
    # 根据索引选择anchor和GT
    positive_anchors = tf.gather(anchors, positive_anchor_indices)
    positive_gt_boxes = tf.gather(gt_boxes, positive_gt_indices)
    positive_gt_cls = tf.gather(gt_cls, positive_gt_indices)
    # 回归目标计算
    deltas = regress_target(positive_anchors, positive_gt_boxes)

    # 处理负样本
    negative_indices = tf.where(anchors_iou_max < 0.3,
                                name='rpn_target_negative_indices')  # [:, 0]

    # 负样本,保证负样本不超过一半
    negative_num = tf.minimum(rpn_train_anchors - positive_num,
                              tf.shape(negative_indices)[0],
                              name='rpn_target_negative_num')
    # negative_num = tf.minimum(int(rpn_train_anchors * 0.5), negative_num, name='rpn_target_negative_num_2')
    negative_indices = tf.random_shuffle(negative_indices)[:negative_num]
    negative_gt_cls = tf.zeros([negative_num])  # 负样本类别id为0
    negative_deltas = tf.zeros([negative_num, 4])

    # 合并正负样本
    deltas = tf.concat([deltas, negative_deltas],
                       axis=0,
                       name='rpn_target_deltas')
    class_ids = tf.concat([positive_gt_cls, negative_gt_cls],
                          axis=0,
                          name='rpn_target_class_ids')
    indices = tf.concat([positive_anchor_indices, negative_indices[:, 0]],
                        axis=0,
                        name='rpn_train_anchor_indices')

    # indices转换会原始的anchors索引
    indices = tf.gather(valid_anchor_indices,
                        indices,
                        name='map_to_origin_anchor_indices')
    # 计算padding
    deltas, class_ids = tf_utils.pad_list_to_fixed_size(
        [deltas, tf.expand_dims(class_ids, 1)], rpn_train_anchors)
    # 将负样本tag标志改为-1;方便后续处理;
    indices = tf_utils.pad_to_fixed_size_with_negative(
        tf.expand_dims(indices, 1),
        rpn_train_anchors,
        negative_num=negative_num,
        data_type=tf.int64)
    # 其它统计指标
    gt_num = tf.shape(gt_cls)[0]  # GT数
    miss_match_gt_num = gt_num - tf.shape(
        tf.unique(positive_gt_indices)[0])[0]  # 未分配anchor的GT
    rpn_gt_min_max_iou = tf.reduce_min(tf.reduce_max(
        iou, axis=1))  # GT匹配anchor最小的IoU

    return [
        deltas, class_ids, indices,
        tf_utils.scalar_to_1d_tensor(gt_num),
        tf_utils.scalar_to_1d_tensor(positive_num),
        tf_utils.scalar_to_1d_tensor(negative_num),
        tf_utils.scalar_to_1d_tensor(miss_match_gt_num),
        tf_utils.scalar_to_1d_tensor(rpn_gt_min_max_iou)
    ]
コード例 #3
0
def detect_targets_graph(gt_boxes, gt_class_ids, proposals,
                         train_rois_per_image, roi_positive_ratio):
    """
    每个图像生成检测网络的分类和回归目标
    IoU>=0.5的为正样本;IoU<0.5的为负样本
    :param gt_boxes: GT 边框坐标 [MAX_GT_BOXs, (y1,x1,y2,x2,tag)] ,tag=0 为padding
    :param gt_class_ids: GT 类别 [MAX_GT_BOXs, 1+1] ;最后一位为tag, tag=0 为padding
    :param proposals: [N,(y1,x1,y2,x2,tag)] ,tag=0 为padding
    :param train_rois_per_image: 每张图像训练的proposal数量
    :param roi_positive_ratio: proposal正负样本比
    :return:
    """
    # 去除padding
    gt_boxes = tf_utils.remove_pad(gt_boxes)
    gt_class_ids = tf_utils.remove_pad(gt_class_ids)[:, 0]  # 从[N,1]变为[N]
    proposals = tf_utils.remove_pad(proposals)
    # 计算iou
    iou = compute_iou(gt_boxes, proposals)

    # 每个GT边框IoU最大的proposal为正
    # gt_iou_argmax = tf.argmax(iou, axis=1)
    gt_iou_argmax = tf.cond(  # 需要考虑proposal个数为0的情况
        tf.greater(tf.shape(proposals)[0], 0),
        true_fn=lambda: tf.argmax(iou, axis=1),
        false_fn=lambda: tf.cast(tf.constant([]), tf.int64))

    # GT和对应的proposal
    # gt_boxes_pos_1 = tf.identity(gt_boxes)
    # gt_class_ids_pos_1 = tf.identity(gt_class_ids)
    # proposal_pos_1 = tf.gather(proposals, gt_iou_argmax)

    # 在接下来的操作之前提出已经被选中的proposal
    indices = tf.unique(gt_iou_argmax)[0]  # 被选中的索引
    all_indices = tf.range(tf.shape(proposals)[0])  # 所有的索引
    remainder_indices = tf.setdiff1d(all_indices,
                                     tf.cast(indices, tf.int32))[0]  # 剩余的索引
    # 剩余的proposals和iou
    proposals = tf.gather(proposals, remainder_indices)
    iou = tf.gather(iou, remainder_indices, axis=1)

    # 正样本每个proposal 最大的iou,且iou>=0.5
    proposal_iou_max = tf.reduce_max(iou, axis=0)
    proposal_pos_idx = tf.where(
        proposal_iou_max >= 0.5)  # 正样本proposal对应的索引号,二维

    # proposal_iou_argmax = tf.argmax(iou, axis=0)
    proposal_iou_argmax = tf.cond(  # 需要考虑GT个数为0的情况
        tf.greater(tf.shape(gt_boxes)[0], 0),
        true_fn=lambda: tf.argmax(iou, axis=0),
        false_fn=lambda: tf.cast(tf.constant([]), tf.int64))
    gt_pos_idx = tf.gather_nd(proposal_iou_argmax,
                              proposal_pos_idx)  # 对应的GT 索引号,一维的

    gt_boxes_pos_2 = tf.gather(gt_boxes, gt_pos_idx)
    gt_class_ids_pos_2 = tf.gather(gt_class_ids, gt_pos_idx)
    proposal_pos_2 = tf.gather_nd(proposals, proposal_pos_idx)

    # 合并两部分正样本
    # gt_boxes_pos = tf.concat([gt_boxes_pos_1, gt_boxes_pos_2], axis=0)
    # class_ids = tf.concat([gt_class_ids_pos_1, gt_class_ids_pos_2], axis=0)
    # proposal_pos = tf.concat([proposal_pos_1, proposal_pos_2], axis=0)
    gt_boxes_pos = gt_boxes_pos_2
    class_ids = gt_class_ids_pos_2
    proposal_pos = proposal_pos_2

    # 根据正负样本比确定最终的正样本
    positive_num = tf.minimum(
        tf.shape(proposal_pos)[0],
        int(train_rois_per_image * roi_positive_ratio))
    gt_boxes_pos, class_ids, proposal_pos = shuffle_sample(
        [gt_boxes_pos, class_ids, proposal_pos],
        tf.shape(proposal_pos)[0], positive_num)

    # 计算回归目标
    deltas = regress_target(proposal_pos, gt_boxes_pos)

    # 负样本:与所有GT的iou<0.5
    proposal_neg_idx = tf.where(proposal_iou_max < 0.5)
    # 确定负样本数量
    negative_num = tf.minimum(train_rois_per_image - positive_num,
                              tf.shape(proposal_neg_idx)[0])
    proposal_neg_idx = tf.random_shuffle(proposal_neg_idx)[:negative_num]
    # 收集负样本
    proposal_neg = tf.gather_nd(proposals, proposal_neg_idx)
    class_ids_neg = tf.zeros(shape=[negative_num])  # 背景类,类别id为0
    deltas_neg = tf.zeros(shape=[negative_num, 4])

    # 合并正负样本
    train_rois = tf.concat([proposal_pos, proposal_neg], axis=0)
    deltas = tf.concat([deltas, deltas_neg], axis=0)
    class_ids = tf.concat([class_ids, class_ids_neg], axis=0)

    # 计算padding
    class_ids, train_rois = tf_utils.pad_list_to_fixed_size(
        [tf.expand_dims(class_ids, axis=1), train_rois],
        train_rois_per_image)  # class_ids分类扩一维
    # 为后续处理方便负样本tag设置为-1
    deltas = tf_utils.pad_to_fixed_size_with_negative(
        deltas, train_rois_per_image, negative_num=negative_num)
    # 其它统计指标
    gt_num = tf.shape(gt_class_ids)[0]  # GT数
    miss_match_gt_num = gt_num - tf.shape(
        tf.unique(gt_pos_idx)[0])[0]  # 未分配anchor的GT
    return [
        deltas, class_ids, train_rois,
        tf_utils.scalar_to_1d_tensor(miss_match_gt_num)
    ]