def compute_detection(Outputs, Anchors, ih, iw, num_classes, score_threshold=0.5, overlap_threshold=0.7, only_anchor=False, max_det_num=100): """Post processing outputs of single shot detection Params: Outpus: a list of (rpn_cls, rpn_boxes) Outpus: a list of anchors """ _, rpn_box, rpn_prob = Outputs n = cfg.batch_size assert Anchors.shape[0] * n == rpn_prob.shape[0] == rpn_box.shape[0], \ 'shape dont match' rpn_box_ = rpn_box.copy() if only_anchor: rpn_box_[...] = 0.0 out_dim = rpn_box.shape[0] / n Dets = [] for i in range(n): s, e = i * out_dim, (i + 1) * out_dim bboxes = rpn_box_[s:e] probs = rpn_prob[s:e] final_boxes, _, _ = anchor.decode(bboxes, probs, Anchors, ih, iw, num_classes) dets = [] for j in xrange(1, num_classes): inds = np.where(probs[:, j] > score_threshold)[0] probs_selected = probs[inds, j] boxes_selected = final_boxes[inds, :] cls_dets = np.hstack( (boxes_selected, probs_selected[:, np.newaxis])).astype(np.float32, copy=False) if False: keep, clusters = nms_multianchor(cls_dets, overlap_threshold) if only_anchor: cls_dets = cls_dets[keep, :] else: cls_dets = dets_cluster(cls_dets, keep, clusters) if True: keep = nms(cls_dets, overlap_threshold) cls_dets = cls_dets[keep, :] tmp_det = np.zeros([cls_dets.shape[0], 6]) tmp_det[:, 0:5] = cls_dets tmp_det[:, 5] = j # class-label dets.append(tmp_det) dets = np.vstack(dets) order = dets[:, 4].argsort()[::-1] dets = dets[order, :] Dets.append(dets[:max_det_num, :]) return Dets
def sample_rpn_outputs(boxes, scores, is_training=False, only_positive=False): """Sample boxes according to scores and some learning strategies assuming the first class is background Params: boxes: of shape (..., Ax4), each entry is [x1, y1, x2, y2], the last axis has k*4 dims scores: of shape (..., A), probs of fg, in [0, 1] """ min_size = cfg.FLAGS.min_size rpn_nms_threshold = cfg.FLAGS.rpn_nms_threshold pre_nms_top_n = cfg.FLAGS.pre_nms_top_n post_nms_top_n = cfg.FLAGS.post_nms_top_n if not is_training: pre_nms_top_n = int(pre_nms_top_n / 2) post_nms_top_n = int(post_nms_top_n / 2) boxes = boxes.reshape((-1, 4)) scores = scores.reshape((-1, 1)) # filter backgrounds # Hope this will filter most of background anchors, since a argsort is too slow.. if only_positive: keeps = np.where(scores > 0.5)[0] boxes = boxes[keeps, :] scores = scores[keeps] # filter minimum size keeps = _filter_boxes(boxes, min_size=min_size) boxes = boxes[keeps, :] scores = scores[keeps] # filter with scores order = scores.ravel().argsort()[::-1] if pre_nms_top_n > 0: order = order[:pre_nms_top_n] boxes = boxes[order, :] scores = scores[order] # filter with nms det = np.hstack((boxes, scores)).astype(np.float32) keeps = nms_wrapper.nms(det, rpn_nms_threshold) if post_nms_top_n > 0: keeps = keeps[:post_nms_top_n] boxes = boxes[keeps, :] scores = scores[keeps] batch_inds = np.zeros([boxes.shape[0]], dtype=np.int32) if _DEBUG: LOG('SAMPLE: %d rois has been choosen' % len(keeps)) LOG('SAMPLE: a positive box: %d %d %d %d %.4f' % (boxes[0, 0], boxes[0, 1], boxes[0, 2], boxes[0, 3], scores[0])) hs = boxes[:, 3] - boxes[:, 1] ws = boxes[:, 2] - boxes[:, 0] assert min(np.min(hs), np.min(ws)) > 0, 'invalid boxes' return boxes, scores, batch_inds
def sample_rpn_outputs(boxes, scores, is_training=False, only_positive=False): """Sample boxes according to scores and some learning strategies assuming the first class is background Params: boxes: of shape (..., Ax4), each entry is [x1, y1, x2, y2], the last axis has k*4 dims scores: of shape (..., A), foreground prob """ min_size = cfg.FLAGS.min_size rpn_nms_threshold = cfg.FLAGS.rpn_nms_threshold pre_nms_top_n = cfg.FLAGS.pre_nms_top_n post_nms_top_n = cfg.FLAGS.post_nms_top_n if not is_training: pre_nms_top_n = int(pre_nms_top_n / 2) post_nms_top_n = int(post_nms_top_n / 2) boxes = boxes.reshape((-1, 4)) scores = scores.reshape((-1, 1)) # filter backgrounds # Hope this will filter most of background anchors, since a argsort is too slow.. if only_positive: keeps = np.where(scores > 0.5)[0] boxes = boxes[keeps, :] scores = scores[keeps] # filter minimum size keeps = _filter_boxes(boxes, min_size=min_size) boxes = boxes[keeps, :] scores = scores[keeps] # filter with scores order = scores.ravel().argsort() if cfg.FLAGS.pre_nms_top_n > 0: order = order[:pre_nms_top_n] boxes = boxes[order, :] scores = scores[order] # filter with nms det = np.hstack((boxes, scores)).astype(np.float32) keeps = nms_wrapper.nms(det, rpn_nms_threshold) if cfg.FLAGS.post_nms_top_n > 0: keeps = keeps[:post_nms_top_n] boxes = boxes[keeps, :] scores = scores[keeps] LOG('%d rois has been choosen' % len(keeps)) return boxes, scores
def _apply_nms(boxes, scores, threshold = 0.5): """After this only positive boxes are left Applying this class-wise """ num_class = scores.shape[1] assert boxes.shape[0] == scores.shape[0], \ 'Shape dismatch {} vs {}'.format(boxes.shape, scores.shape) final_boxes = [] final_scores = [] for cls in np.arange(1, num_class): cls_boxes = boxes[:, 4*cls: 4*cls+4] cls_scores = scores[:, cls] dets = np.hstack((cls_boxes, cls_scores[:, np.newaxis])) keep = nms_wrapper.nms(dets, thresh=0.3) dets = dets[keep, :] dets = dets[np.where(dets[:, 4] > threshold)] final_boxes.append(dets[:, :4]) final_scores.append(dets[:, 4]) final_boxes = np.vstack(final_boxes) final_scores = np.vstack(final_scores) return final_boxes, final_scores
def _apply_nms(boxes, scores, threshold=0.5): """After this only positive boxes are left Applying this class-wise """ num_class = scores.shape[1] assert boxes.shape[0] == scores.shape[0], \ 'Shape dismatch {} vs {}'.format(boxes.shape, scores.shape) final_boxes = [] final_scores = [] for cls in np.arange(1, num_class): cls_boxes = boxes[:, 4 * cls:4 * cls + 4] cls_scores = scores[:, cls] dets = np.hstack((cls_boxes, cls_scores[:, np.newaxis])) keep = nms_wrapper.nms(dets, thresh=0.3) dets = dets[keep, :] dets = dets[np.where(dets[:, 4] > threshold)] final_boxes.append(dets[:, :4]) final_scores.append(dets[:, 4]) final_boxes = np.vstack(final_boxes) final_scores = np.vstack(final_scores) return final_boxes, final_scores
def sample_rpn_outputs(boxes, scores, is_training=False, only_positive=False): """Sample boxes according to scores and some learning strategies assuming the first class is background Params: boxes: of shape (..., Ax4), each entry is [x1, y1, x2, y2], the last axis has k*4 dims scores: of shape (..., A), probs of fg, in [0, 1] """ min_size = cfg.FLAGS.min_size rpn_nms_threshold = cfg.FLAGS.rpn_nms_threshold pre_nms_top_n = cfg.FLAGS.pre_nms_top_n post_nms_top_n = cfg.FLAGS.post_nms_top_n # training: 12000, 2000 # testing: 6000, 400 if not is_training: pre_nms_top_n = int(pre_nms_top_n / 2) post_nms_top_n = int(post_nms_top_n / 5) boxes = boxes.reshape((-1, 4)) scores = scores.reshape((-1, 1)) assert scores.shape[0] == boxes.shape[0], 'scores and boxes dont match' # filter backgrounds # Hope this will filter most of background anchors, since a argsort is too slow.. if only_positive: keeps = np.where(scores > 0.5)[0] boxes = boxes[keeps, :] scores = scores[keeps] # filter minimum size keeps = _filter_boxes(boxes, min_size=min_size) boxes = boxes[keeps, :] scores = scores[keeps] # filter with scores order = scores.ravel().argsort()[::-1] if pre_nms_top_n > 0: order = order[:pre_nms_top_n] boxes = boxes[order, :] scores = scores[order] # filter with nms det = np.hstack((boxes, scores)).astype(np.float32) keeps = nms_wrapper.nms(det, rpn_nms_threshold) if post_nms_top_n > 0: keeps = keeps[:post_nms_top_n] boxes = boxes[keeps, :] scores = scores[keeps] batch_inds = np.zeros([boxes.shape[0]], dtype=np.int32) # # random sample boxes ## try early sample later # fg_inds = np.where(scores > 0.5)[0] # num_fgs = min(len(fg_inds.size), int(rois_per_image * fg_roi_fraction)) if _DEBUG: LOG('SAMPLE: %d rois has been choosen' % len(scores)) LOG('SAMPLE: a positive box: %d %d %d %d %.4f' % (boxes[0, 0], boxes[0, 1], boxes[0, 2], boxes[0, 3], scores[0])) LOG('SAMPLE: a negative box: %d %d %d %d %.4f' % (boxes[-1, 0], boxes[-1, 1], boxes[-1, 2], boxes[-1, 3], scores[-1])) hs = boxes[:, 3] - boxes[:, 1] ws = boxes[:, 2] - boxes[:, 0] assert min(np.min(hs), np.min(ws)) > 0, 'invalid boxes' return boxes, scores.astype(np.float32), batch_inds
def sample_rpn_outputs(boxes, scores, is_training=False, only_positive=False): """Sample boxes according to scores and some learning strategies assuming the first class is background Params: boxes: of shape (..., Ax4), each entry is [x1, y1, x2, y2], the last axis has k*4 dims scores: of shape (..., A), probs of fg, in [0, 1] #but the boxex are allready in form [-1,4] also scores is in shape [-1,1] """ min_size = cfg.FLAGS.min_size rpn_nms_threshold = cfg.FLAGS.rpn_nms_threshold pre_nms_top_n = cfg.FLAGS.pre_nms_top_n post_nms_top_n = cfg.FLAGS.post_nms_top_n # training: 12000, 2000 # testing: 6000, 400 if not is_training: pre_nms_top_n = int(pre_nms_top_n / 2) post_nms_top_n = int(post_nms_top_n / 5) boxes = boxes.reshape((-1, 4)) scores = scores.reshape((-1, 1)) assert scores.shape[0] == boxes.shape[0], 'scores and boxes dont match' # filter backgrounds # Hope this will filter most of background anchors, since a argsort is too slow.. if only_positive: keeps = np.where(scores > 0.5)[0] boxes = boxes[keeps, :] scores = scores[keeps] # filter minimum size keeps = _filter_boxes(boxes, min_size=min_size) boxes = boxes[keeps, :] scores = scores[keeps] # filter with scores order = scores.ravel().argsort()[::-1] if pre_nms_top_n > 0: order = order[:pre_nms_top_n] boxes = boxes[order, :] scores = scores[order] # filter with nms det = np.hstack((boxes, scores)).astype(np.float32) keeps = nms_wrapper.nms(det, rpn_nms_threshold) if post_nms_top_n > 0: keeps = keeps[:post_nms_top_n] boxes = boxes[keeps, :] scores = scores[keeps] batch_inds = np.zeros([boxes.shape[0]], dtype=np.int32) # # random sample boxes ## try early sample later # fg_inds = np.where(scores > 0.5)[0] # num_fgs = min(len(fg_inds.size), int(rois_per_image * fg_roi_fraction)) if _DEBUG: LOG('SAMPLE: %d rois has been choosen' % len(scores)) LOG('SAMPLE: a positive box: %d %d %d %d %.4f' % (boxes[0, 0], boxes[0, 1], boxes[0, 2], boxes[0, 3], scores[0])) LOG('SAMPLE: a negative box: %d %d %d %d %.4f' % (boxes[-1, 0], boxes[-1, 1], boxes[-1, 2], boxes[-1, 3], scores[-1])) hs = boxes[:, 3] - boxes[:, 1] ws = boxes[:, 2] - boxes[:, 0] assert min(np.min(hs), np.min(ws)) > 0, 'invalid boxes' return boxes, scores.astype(np.float32), batch_inds
def sample_rcnn_outputs(boxes, classes, prob, indexs, class_agnostic=True): min_size = cfg.FLAGS.min_size mask_nms_threshold = cfg.FLAGS.mask_nms_threshold post_nms_inst_n = cfg.FLAGS.post_nms_inst_n if class_agnostic is True: scores = prob[range(prob.shape[0]), classes] boxes = boxes.reshape((-1, 4)) scores = scores.reshape((-1, 1)) indexs = indexs.reshape((-1, 1)) assert scores.shape[0] == boxes.shape[0], 'scores and boxes dont match' # filter background keeps = np.where(classes != 0)[0] scores = scores[keeps] indexs = indexs[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] # filter minimum size keeps = _filter_boxes(boxes, min_size=min_size) scores = scores[keeps] indexs = indexs[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] #filter with scores keeps = np.where(scores > 0.5)[0] scores = scores[keeps] indexs = indexs[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] # filter with nms order = scores.ravel().argsort()[::-1] scores = scores[order] indexs = indexs[order] boxes = boxes[order, :] classes = classes[order] prob = prob[order, :] det = np.hstack((boxes, scores)).astype(np.float32) keeps = nms_wrapper.nms(det, mask_nms_threshold) # filter low score if post_nms_inst_n > 0: keeps = keeps[:post_nms_inst_n] scores = scores[keeps] indexs = indexs[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] # quick fix for tensorflow error when no bbox presents #@TODO if len(classes) is 0: scores = np.zeros((1, 1)) indexs = np.zeros((1, 1)) boxes = np.array([[0.0, 0.0, 2.0, 2.0]]) classes = np.array([[0]]) prob = np.zeros((1, 81)) else: #@TODO raise "inference nms type error" batch_inds = np.zeros([boxes.shape[0]]) return boxes.astype(np.float32), classes.astype(np.int32), prob.astype( np.float32), batch_inds.astype(np.int32), indexs.astype(np.int32)
def compute_detection_new(batch_rpn_box, batch_rpn_prob, batch_anchors, image_inds, ih, iw, num_classes, score_threshold=0.5, overlap_threshold=0.7, only_anchor=False, max_dets=100): """Post processing outputs of single shot detection, this decode boxes only once """ assert image_inds.shape[0] == batch_rpn_box.shape[0] == batch_rpn_prob.shape[0] == batch_anchors.shape[0], \ 'unmatch output: {} vs {} vs {} vs {}'.format(image_inds.shape[0], batch_rpn_box.shape[0], batch_rpn_prob.shape[0], batch_anchors.shape[0]) n = cfg.batch_size rpn_box_ = batch_rpn_box.copy() if only_anchor: rpn_box_[...] = 0.0 Dets = [] for i in range(n): inds = np.where(image_inds == i)[0] bboxes = rpn_box_[inds] probs = batch_rpn_prob[inds] anc = batch_anchors[inds] probs_positive = 1 - probs[:, 0] # the objectness score = 1 - background final_boxes, _, _ = anchor.decode(bboxes, probs, anc, ih, iw, num_classes) object_dets = np.hstack( (final_boxes, probs_positive[:, np.newaxis])).astype(np.float32, copy=False) if False: keep, clusters = nms_multianchor(object_dets, overlap_threshold) keep = np.asarray(keep, dtype=np.int32) if only_anchor: object_dets = object_dets[keep, :] else: object_dets = dets_cluster(object_dets, keep, clusters) if True: keep = nms(object_dets, overlap_threshold) keep = np.asarray(keep, dtype=np.int32) object_dets = object_dets[keep, :] if keep.size > max_dets: keep = keep[:max_dets] probs = probs[keep] probs_objectness = probs[:, 1:] # if True: # cls_dets = np.zeros([object_dets.shape[0], 6]) # cls_dets[:, 0:4] = object_dets[:, 0:4] # cls_argmax = probs_objectness.argmax(axis=1) # cls_maxprobs = probs_objectness[np.arange(cls_argmax.shape[0]), cls_argmax] # # else: d0, d1 = np.where(probs_objectness > score_threshold) cls_maxprobs = probs_objectness[d0, d1] cls_argmax = d1 cls_dets = np.zeros([d0.size, 6]) cls_dets[:, 0:4] = object_dets[d0, 0:4] cls_dets[:, 4] = cls_maxprobs cls_dets[:, 5] = cls_argmax + 1 order = cls_dets[:, 4].argsort()[::-1] cls_dets = cls_dets[order, :] Dets.append(cls_dets) return Dets
def sample_rcnn_outputs(boxes, classes, prob, class_agnostic=False): min_size = cfg.FLAGS.min_size mask_nms_threshold = cfg.FLAGS.mask_nms_threshold post_nms_inst_n = cfg.FLAGS.post_nms_inst_n if class_agnostic is True: scores = prob.max(axis=1) boxes = boxes.reshape((-1, 4)) classes = classes.reshape((-1, 1)) scores = scores.reshape((-1, 1)) probs = probs.reshape((-1, 81)) assert scores.shape[0] == boxes.shape[0], 'scores and boxes dont match' # filter background keeps = np.where(classes != 0)[0] scores = scores[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] # filter minimum size keeps = _filter_boxes(boxes, min_size=min_size) scores = scores[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] #filter with scores keeps = np.where(scores > 0.5)[0] scores = scores[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] # filter with nms order = scores.ravel().argsort()[::-1] scores = scores[order] boxes = boxes[order, :] classes = classes[order] prob = prob[order, :] det = np.hstack((boxes, scores)).astype(np.float32) keeps = nms_wrapper.nms(det, mask_nms_threshold) scores = scores[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] # filter low score if post_nms_inst_n > 0: scores = scores[:post_nms_inst_n] boxes = boxes[:post_nms_inst_n, :] classes = classes[:post_nms_inst_n] prob = prob[:post_nms_inst_n, :] # quick fix for tensorflow error when no bbox presents #@TODO if len(classes) is 0: scores = np.zeros((1, 1)) boxes = np.array([[0.0, 0.0, 2.0, 2.0]]) classes = np.array([0]).reshape(-1) prob = np.zeros((1, 81)) else: scores = prob.max(axis=1) boxes = boxes.reshape((-1, 4)) classes = classes.reshape((-1, 1)) scores = scores.reshape((-1, 1)) prob = prob.reshape((-1, 81)) assert scores.shape[0] == boxes.shape[0], 'scores and boxes dont match' # filter background keeps = np.where(classes != 0)[0] scores = scores[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] # filter minimum size keeps = _filter_boxes(boxes, min_size=min_size) scores = scores[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] #filter with scores keeps = np.where(scores > 0.5)[0] scores = scores[keeps] boxes = boxes[keeps, :] classes = classes[keeps] prob = prob[keeps, :] all_scores = [] all_boxes = [] all_classes = [] all_prob = [] for c in range(1, (prob.shape[1])): keeps = (classes == c).reshape(-1) per_class_scores = scores[keeps] per_class_boxes = boxes[keeps, :] per_class_classes = classes[keeps] per_class_prob = prob[keeps, :] # filter with nms order = per_class_scores.ravel().argsort()[::-1] per_class_scores = per_class_scores[order] per_class_boxes = per_class_boxes[order, :] per_class_classes = per_class_classes[order] per_class_prob = per_class_prob[order, :] det = np.hstack( (per_class_boxes, per_class_scores)).astype(np.float32) keeps = nms_wrapper.nms(det, mask_nms_threshold) # filter low score if post_nms_inst_n > 0: keeps = keeps[:post_nms_inst_n] all_scores.append(per_class_scores[keeps]) all_boxes.append(per_class_boxes[keeps, :]) all_classes.append(per_class_classes[keeps]) all_prob.append(per_class_prob[keeps, :]) scores = np.vstack(all_scores) boxes = np.vstack(all_boxes) classes = np.vstack(all_classes).reshape(-1) prob = np.vstack(all_prob) if len(classes) is 0: scores = np.zeros((1, 1)) boxes = np.array([[0.0, 0.0, 2.0, 2.0]]) classes = np.array([0]).reshape(-1) prob = np.zeros((1, 81)) batch_inds = np.zeros([boxes.shape[0]]) return boxes.astype(np.float32), classes.astype(np.int32), prob.astype( np.float32), batch_inds.astype(np.int32)
def sample_rpn_outputs(boxes, scores, is_training=False, only_positive=False, with_nms=False, random=False): """Sample boxes according to scores and some learning strategies assuming the first class is background Params: boxes: of shape (..., Ax4), each entry is [x1, y1, x2, y2], the last axis has k*4 dims scores: of shape (..., A), probs of fg, in [0, 1] """ min_size = cfg.FLAGS.min_size rpn_nms_threshold = cfg.FLAGS.rpn_nms_threshold pre_nms_top_n = cfg.FLAGS.pre_nms_top_n post_nms_top_n = cfg.FLAGS.post_nms_top_n # training: 12000, 2000 # testing: 6000, 400 # if not is_training: # pre_nms_top_n = int(pre_nms_top_n / 2) # post_nms_top_n = int(post_nms_top_n / 5) boxes = boxes.reshape((-1, 4)) scores = scores.reshape((-1, 1)) assert scores.shape[0] == boxes.shape[0], 'scores and boxes dont match' ## filter backgrounds ## Hope this will filter most of background anchors, since a argsort is too slow.. if only_positive: keeps = np.where(scores > 0.5)[0] boxes = boxes[keeps, :] scores = scores[keeps] ## filter minimum size keeps = _filter_boxes(boxes, min_size=min_size) boxes = boxes[keeps, :] scores = scores[keeps] ## filter before nms if random is True: keeps = np.random.choice(np.arange(boxes.shape[0]), size=pre_nms_top_n, replace=False) boxes = boxes[keeps, :] scores = scores[keeps] else: if len(scores) > pre_nms_top_n: partial_order = scores.ravel() partial_order = np.argpartition(-partial_order, pre_nms_top_n)[:pre_nms_top_n] boxes = boxes[partial_order, :] scores = scores[partial_order] ## sort order = scores.ravel().argsort()[::-1] boxes = boxes[order, :] scores = scores[order] ## filter by nms if with_nms is True: det = np.hstack((boxes, scores)).astype(np.float32) keeps = nms_wrapper.nms(det, rpn_nms_threshold) boxes = boxes[keeps, :] scores = scores[keeps].astype(np.float32) ## filter after nms if post_nms_top_n > 0: boxes = boxes[:post_nms_top_n, :] scores = scores[:post_nms_top_n] #create dummpy box in case of no box remains if boxes.size is 0: boxes = np.array([[0, 0, 16, 16]], dtype=np.float32) scores = np.array([ 0, ], dtype=np.float32) batch_inds = np.zeros([boxes.shape[0]], dtype=np.int32) ## random sample boxes ## try early sample later # fg_inds = np.where(scores > 0.5)[0] # num_fgs = min(len(fg_inds.size), int(rois_per_image * fg_roi_fraction)) # if _DEBUG: # LOG('SAMPLE: %d rois has been choosen' % len(scores)) # LOG('SAMPLE: a positive box: %d %d %d %d %.4f' % (boxes[0, 0], boxes[0, 1], boxes[0, 2], boxes[0, 3], scores[0])) # LOG('SAMPLE: a negative box: %d %d %d %d %.4f' % (boxes[-1, 0], boxes[-1, 1], boxes[-1, 2], boxes[-1, 3], scores[-1])) # hs = boxes[:, 3] - boxes[:, 1] # ws = boxes[:, 2] - boxes[:, 0] # assert min(np.min(hs), np.min(ws)) > 0, 'invalid boxes' # print(boxes.shape) return boxes, scores, batch_inds