def detect(self, im): """ Detecting texts from an image :return: the bounding boxes of the detected texts """ text_proposals, scores = self.text_proposal_detector.detect( im, cfg.MEAN) keep_inds = np.where(scores > cfg.TEXT_PROPOSALS_MIN_SCORE)[0] text_proposals, scores = text_proposals[keep_inds], scores[keep_inds] sorted_indices = np.argsort(scores.ravel())[::-1] text_proposals, scores = text_proposals[sorted_indices], scores[ sorted_indices] # nms for text proposals keep_inds = nms(np.hstack((text_proposals, scores)), cfg.TEXT_PROPOSALS_NMS_THRESH) text_proposals, scores = text_proposals[keep_inds], scores[keep_inds] scores = normalize(scores) text_lines = self.text_proposal_connector.get_text_lines( text_proposals, scores, im.shape[:2]) keep_inds = self.filter_boxes(text_lines) text_lines = text_lines[keep_inds] # nms for text lines if text_lines.shape[0] != 0: keep_inds = nms(text_lines, cfg.TEXT_LINE_NMS_THRESH) text_lines = text_lines[keep_inds] text_lines = sorted(text_lines, key=lambda x: -float(x[1]), reverse=True) return text_lines
def detect(self, im): """ Detecting texts from an image :return: the bounding boxes of the detected texts """ text_proposals, scores=self.text_proposal_detector.detect(im, cfg.MEAN) keep_inds=np.where(scores>cfg.TEXT_PROPOSALS_MIN_SCORE)[0] text_proposals, scores=text_proposals[keep_inds], scores[keep_inds] sorted_indices=np.argsort(scores.ravel())[::-1] text_proposals, scores=text_proposals[sorted_indices], scores[sorted_indices] # nms for text proposals keep_inds=nms(np.hstack((text_proposals, scores)), cfg.TEXT_PROPOSALS_NMS_THRESH) text_proposals, scores=text_proposals[keep_inds], scores[keep_inds] scores=normalize(scores) text_lines=self.text_proposal_connector.get_text_lines(text_proposals, scores, im.shape[:2]) keep_inds=self.filter_boxes(text_lines) text_lines=text_lines[keep_inds] # nms for text lines if text_lines.shape[0]!=0: keep_inds=nms(text_lines, cfg.TEXT_LINE_NMS_THRESH) text_lines=text_lines[keep_inds] return text_lines
def detect(self, im): """ Detecting text boxes from an image :return: the bounding boxes of the detected texts """ text_proposals, scores = self.text_proposal_detector.detect( im, cfg.MEAN) keep_inds = np.where(scores > cfg.TEXT_PROPOSALS_MIN_SCORE)[0] text_proposals = text_proposals[keep_inds] scores = scores[keep_inds] sorted_indices = np.argsort(scores.ravel())[::-1] text_proposals, scores = text_proposals[sorted_indices], scores[ keep_inds] # nms for text propoasls keep_inds = nms(np.hstack((text_proposals, scores)), cfg.TEXT_PROPOSALS_NMS_THRESH) text_proposals, scores = text_proposals[keep_inds], scores[keep_inds] # 得分归一化统一为[0,1]之间 scores = normalize(scores) text_lines = self.text_proposal_connector.get_text_lines( text_proposals, scores, im.shape[:2]) # text_lines:(x1, x2, y1, y2, score) # 筛选text boxes keep_inds = self.filter_boxes(text_lines) text_lines = text_lines[keep_inds] if text_lines.shape[0] != 0: keep_inds = nms(text_lines, cfg.TETEXT_LINE_NMS_THRESHXL) text_lines = text_lines[keep_inds] return text_lines
def show(): test_dataset = TDETDataset(['voc07_test'], args.data_dir, args.prop_method, num_classes=20, prop_min_scale=args.prop_min_scale, prop_topk=args.num_prop) if args.multiscale: save_name = os.path.join(args.save_dir, 'detection_result', '{}_multiscale.pkl'.format(args.model_name)) else: save_name = os.path.join(args.save_dir, 'detection_result', '{}.pkl'.format(args.model_name)) saved_data = pickle.load(open(save_name, 'rb'), encoding='latin1') all_boxes = saved_data['all_boxes'] cls_scores = saved_data['cls'] det_scores = saved_data['det'] for cls in range(15, 20): for index in range(len(all_boxes[0])): dets = all_boxes[cls][index] if dets == [] or len(dets) == 0: continue keep = nms(dets, 0.3) all_boxes[cls][index] = dets[keep, :].copy() c_s = cls_scores[cls][index][keep] d_s = det_scores[cls][index][keep] print(cls) print(all_boxes[cls][index][:10, 4]) print(c_s[:10]) print(d_s[:10]) if all_boxes[cls][index][0, 4] > 10: plt.imshow(test_dataset.get_raw_img(index)) draw_box(all_boxes[cls][index][0:1, :4], 'black') draw_box(all_boxes[cls][index][1:2, :4], 'red') draw_box(all_boxes[cls][index][2:3, :4], 'green') draw_box(all_boxes[cls][index][3:4, :4], 'blue') draw_box(all_boxes[cls][index][4:5, :4], 'yellow') plt.show()
def eval_saved_result(): eval_kit = voc_eval_kit('test', '2007', os.path.join(args.data_dir, 'VOCdevkit2007')) save_name = os.path.join(args.save_dir, 'detection_result', '{}.pkl'.format(args.model_name)) all_boxes = pickle.load(open(save_name, 'rb'), encoding='latin1') #all_boxes = pickle.load(open('../repo/oicr_result/test_detections.pkl', 'rb'), encoding='latin1') for cls in range(20): for index in range(len(all_boxes[0])): dets = all_boxes[cls][index] if dets == []: continue keep = nms(dets, 0.4) all_boxes[cls][index] = dets[keep, :].copy() if index % 500 == 499: print(index) print('nms: cls %d complete' % cls) eval_kit.evaluate_detections(all_boxes)
def eval_saved_result(): eval_kit = voc_eval_kit('test', '2007', os.path.join(args.data_dir, 'VOCdevkit2007')) if args.multiscale: save_name = os.path.join(args.save_dir, 'detection_result', '{}_multiscale.pkl'.format(args.model_name)) else: save_name = os.path.join(args.save_dir, 'detection_result', '{}.pkl'.format(args.model_name)) saved_data = pickle.load(open(save_name, 'rb'), encoding='latin1') all_boxes = saved_data['all_boxes'] for cls in range(20): for index in range(len(all_boxes[0])): dets = all_boxes[cls][index] if dets == [] or len(dets) == 0: continue keep = nms(dets, 0.3) all_boxes[cls][index] = dets[keep, :].copy() eval_kit.evaluate_detections(all_boxes)
def apply_nms(all_boxes, thresh): """Apply non-maximum suppression to all predicted boxes output by 솓 test_net method. """ num_classes = len(all_boxes) num_image = len(all_boxes[0]) nms_boxes = [[[] for _ in range(num_image)] for _ in range(num_classes)] for cls_ind in range(num_classes): for im_ind in range(num_image): dets = all_boxes[cls_ind][im_ind] if dets == []: continue keep = nms(dets, thresh, force_cpu=True) if len(keep) == 0: continue nms_boxes[cls_ind][im_ind] = dets[keep, :].copy() return nms_boxes
def eval(): print('Called with args:') print(args) np.random.seed(3) torch.manual_seed(4) if torch.cuda.is_available(): torch.cuda.manual_seed(5) device = torch.device('cuda') else: device = torch.device('cpu') eval_kit = voc_eval_kit('test', '2007', os.path.join(args.data_dir, 'VOCdevkit2007')) test_dataset = TDETDataset(['voc07_test'], args.data_dir, args.prop_method, num_classes=20, prop_min_scale=args.prop_min_scale, prop_topk=args.num_prop) load_name = os.path.join(args.save_dir, 'tdet', '{}.pth'.format(args.model_name)) print("loading checkpoint %s" % (load_name)) checkpoint = torch.load(load_name) if checkpoint['net'] == 'TDET_VGG16': model = TDET_VGG16(None, 20, pooling_method=checkpoint['pooling_method'], cls_specific_det=checkpoint['cls_specific'] if checkpoint['cls_specific'] is not False else 'no', share_level=checkpoint['share_level'], det_softmax=checkpoint['det_softmax'] if 'det_softmax' in checkpoint else 'no', det_choice=checkpoint['det_choice'] if 'det_choice' in checkpoint else 1) else: raise Exception('network is not defined') model.load_state_dict(checkpoint['model']) print("loaded checkpoint %s" % (load_name)) model.to(device) model.eval() start = time.time() num_images = len(test_dataset) # heuristic: keep an average of 40 detections per class per images prior # to NMS max_per_set = 40 * num_images # heuristic: keep at most 100 detection per class per image prior to NMS max_per_image = 100 # detection thresold for each class (this is adaptively set based on the # max_per_set constraint) thresh = -np.inf * np.ones(20) # thresh = 0.1 * np.ones(imdb.num_classes) # top_scores will hold one minheap of scores per class (used to enforce # the max_per_set constraint) top_scores = [[] for _ in range(20)] # all detections are collected into: # all_boxes[cls][image] = N x 5 array of detections in # (x1, y1, x2, y2, score) all_boxes = [[[] for _ in range(num_images)] for _ in range(20)] cls_scores_set = [[[] for _ in range(num_images)] for _ in range(20)] det_scores_set = [[[] for _ in range(num_images)] for _ in range(20)] for index in range(len(test_dataset)): scores = 0 c_scores = 0 d_scores = 0 N = 0 if args.multiscale: comb = itertools.product([False, True], [480, 576, 688, 864, 1200]) else: comb = itertools.product([False], [688]) for h_flip, im_size in comb: test_batch = test_dataset.get_data(index, h_flip, im_size) im_data = test_batch['im_data'].unsqueeze(0).to(device) proposals = test_batch['proposals'].to(device) local_scores, local_cls_scores, local_det_scores = model( im_data, proposals) local_scores = local_scores.detach().cpu().numpy() local_cls_scores = local_cls_scores.detach().cpu().numpy() local_det_scores = local_det_scores.detach().cpu().numpy() scores = scores + local_scores c_scores = c_scores + local_cls_scores d_scores = d_scores + local_det_scores N += 1 scores = 100 * scores / N c_scores = 10 * c_scores / N d_scores = 10 * d_scores / N boxes = test_dataset.get_raw_proposal(index) for cls in range(20): inds = np.where((scores[:, cls] > thresh[cls]))[0] cls_scores = scores[inds, cls] cls_c_scores = c_scores[inds, cls] if checkpoint['cls_specific']: cls_d_scores = d_scores[inds, cls] else: cls_d_scores = d_scores[inds, 0] cls_boxes = boxes[inds].copy() top_inds = np.argsort(-cls_scores)[:max_per_image] cls_scores = cls_scores[top_inds] cls_c_scores = cls_c_scores[top_inds] cls_d_scores = cls_d_scores[top_inds] cls_boxes = cls_boxes[top_inds, :] # if cls_scores[0] > 10: # print(cls) # plt.imshow(test_batch['raw_img']) # draw_box(cls_boxes[0:10, :]) # draw_box(test_batch['gt_boxes'] / test_batch['im_scale'], 'black') # plt.show() # push new scores onto the minheap for val in cls_scores: heapq.heappush(top_scores[cls], val) # if we've collected more than the max number of detection, # then pop items off the minheap and update the class threshold if len(top_scores[cls]) > max_per_set: while len(top_scores[cls]) > max_per_set: heapq.heappop(top_scores[cls]) thresh[cls] = top_scores[cls][0] all_boxes[cls][index] = np.hstack( (cls_boxes, cls_scores[:, np.newaxis])).astype(np.float32, copy=False) cls_scores_set[cls][index] = cls_c_scores det_scores_set[cls][index] = cls_d_scores if index % 100 == 99: print('%d images complete, elapsed time:%.1f' % (index + 1, time.time() - start)) for j in range(20): for i in range(len(test_dataset)): inds = np.where(all_boxes[j][i][:, -1] > thresh[j])[0] all_boxes[j][i] = all_boxes[j][i][inds, :] cls_scores_set[j][i] = cls_scores_set[j][i][inds] det_scores_set[j][i] = det_scores_set[j][i][inds] if args.multiscale: save_name = os.path.join(args.save_dir, 'detection_result', '{}_multiscale.pkl'.format(args.model_name)) else: save_name = os.path.join(args.save_dir, 'detection_result', '{}.pkl'.format(args.model_name)) pickle.dump( { 'all_boxes': all_boxes, 'cls': cls_scores_set, 'det': det_scores_set }, open(save_name, 'wb')) print('Detection Complete, elapsed time: %.1f', time.time() - start) for cls in range(20): for index in range(len(test_dataset)): dets = all_boxes[cls][index] if dets == []: continue keep = nms(dets, 0.3) all_boxes[cls][index] = dets[keep, :].copy() print('NMS complete, elapsed time: %.1f', time.time() - start) eval_kit.evaluate_detections(all_boxes)
def eval(): print('Called with args:') print(args) np.random.seed(3) torch.manual_seed(4) if torch.cuda.is_available(): torch.cuda.manual_seed(5) device = torch.device('cuda') else: device = torch.device('cpu') eval_kit = voc_eval_kit('test', '2007', os.path.join(args.data_dir, 'VOCdevkit2007')) test_dataset = WSDDNDataset(dataset_names=['voc07_test'], data_dir=args.data_dir, prop_method=args.prop_method, num_classes=20, min_prop_scale=args.min_prop) load_name = os.path.join(args.save_dir, 'wsddn', '{}.pth'.format(args.model_name)) print("loading checkpoint %s" % (load_name)) checkpoint = torch.load(load_name) if checkpoint['net'] == 'WSDDN_VGG16': model = WSDDN_VGG16(None, 20) else: raise Exception('network is not defined') model.load_state_dict(checkpoint['model']) print("loaded checkpoint %s" % (load_name)) model.to(device) model.eval() start = time.time() num_images = len(test_dataset) # heuristic: keep an average of 40 detections per class per images prior # to NMS max_per_set = 40 * num_images # heuristic: keep at most 100 detection per class per image prior to NMS max_per_image = 100 # detection thresold for each class (this is adaptively set based on the # max_per_set constraint) thresh = -np.inf * np.ones(20) # thresh = 0.1 * np.ones(imdb.num_classes) # top_scores will hold one minheap of scores per class (used to enforce # the max_per_set constraint) top_scores = [[] for _ in range(20)] # all detections are collected into: # all_boxes[cls][image] = N x 5 array of detections in # (x1, y1, x2, y2, score) all_boxes = [[[] for _ in range(num_images)] for _ in range(20)] for index in range(len(test_dataset)): scores = 0 if args.multiscale: comb = itertools.product([False, True], [480, 576, 688, 864, 1200]) else: comb = itertools.product([False], [688]) for h_flip, im_size in comb: im_data, gt_boxes, box_labels, proposals, prop_scores, image_level_label, im_scale_ratio, raw_img, im_id = test_dataset.get_data(index, h_flip, im_size, args.min_resize) im_data = im_data.unsqueeze(0).to(device) rois = proposals.to(device) if args.use_prop_score: prop_scores = prop_scores.to(device) else: prop_scores = None local_scores = model(im_data, rois, prop_scores, None).detach().cpu().numpy() scores = scores + local_scores scores = scores * 1000 boxes = test_dataset.get_raw_proposal(index) for cls in range(20): inds = np.where((scores[:, cls] > thresh[cls]))[0] cls_scores = scores[inds, cls] cls_boxes = boxes[inds].copy() top_inds = np.argsort(-cls_scores)[:max_per_image] cls_scores = cls_scores[top_inds] cls_boxes = cls_boxes[top_inds, :] # if cls_scores[0] > 0.001: # #print(cls) # plt.imshow(raw_img) # draw_box(cls_boxes[0:10, :]) # draw_box(gt_boxes / im_scale, 'black') # plt.show() # push new scores onto the minheap for val in cls_scores: heapq.heappush(top_scores[cls], val) # if we've collected more than the max number of detection, # then pop items off the minheap and update the class threshold if len(top_scores[cls]) > max_per_set: while len(top_scores[cls]) > max_per_set: heapq.heappop(top_scores[cls]) thresh[cls] = top_scores[cls][0] all_boxes[cls][index] = np.hstack((cls_boxes, cls_scores[:, np.newaxis])).astype(np.float32, copy=False) # sorted_scores, sorted_indices = torch.sort(scores.detach(), dim=0, descending=True) # sorted_boxes = rois[sorted_indices.permute(1, 0)] # # for cls in range(20): # here = torch.cat((sorted_boxes[cls], sorted_scores[:, cls:cls + 1]), 1).cpu() # print(here) # all_boxes[cls][index] = here.numpy() if index % 100 == 99: print('%d images complete, elapsed time:%.1f' % (index + 1, time.time() - start)) for j in range(20): for i in range(len(test_dataset)): inds = np.where(all_boxes[j][i][:, -1] > thresh[j])[0] all_boxes[j][i] = all_boxes[j][i][inds, :] save_name = os.path.join(args.save_dir, 'detection_result', '{}.pkl'.format(args.model_name)) pickle.dump(all_boxes, open(save_name, 'wb')) print('Detection Complete, elapsed time: %.1f', time.time() - start) for cls in range(20): for index in range(len(test_dataset)): dets = all_boxes[cls][index] if dets == []: continue keep = nms(dets, 0.4) all_boxes[cls][index] = dets[keep, :].copy() print('NMS complete, elapsed time: %.1f', time.time() - start) eval_kit.evaluate_detections(all_boxes)
def my_eval(): print('Called with args:') print(args) np.random.seed(3) torch.manual_seed(4) if torch.cuda.is_available(): torch.cuda.manual_seed(5) device = torch.device('cuda') else: device = torch.device('cpu') eval_kit = voc_eval_kit('test', '2007', os.path.join(args.data_dir, 'VOCdevkit2007')) test_dataset = WSDDNDataset(dataset_names=['voc07_test'], data_dir=args.data_dir, prop_method=args.prop_method, num_classes=20, min_prop_scale=args.min_prop) load_name = os.path.join(args.save_dir, 'wsddn', '{}.pth'.format(args.model_name)) print("loading checkpoint %s" % (load_name)) checkpoint = torch.load(load_name) if checkpoint['net'] == 'WSDDN_VGG16': model = WSDDN_VGG16(None, 20) else: raise Exception('network is not defined') model.load_state_dict(checkpoint['model']) print("loaded checkpoint %s" % (load_name)) model.to(device) model.eval() start = time.time() all_boxes = [[[] for _ in range(len(test_dataset))] for _ in range(20)] for index in range(len(test_dataset)): im_data, gt_boxes, box_labels, proposals, prop_scores, image_level_label, im_scale_ratio, raw_img, im_id = test_dataset.get_data( index, False, 688) im_data = im_data.unsqueeze(0).to(device) rois = proposals.to(device) if args.use_prop_score: prop_scores = prop_scores.to(device) else: prop_scores = None scores = model(im_data, rois, prop_scores, None) sorted_scores, sorted_indices = torch.sort(scores.detach(), dim=0, descending=True) sorted_boxes = rois[sorted_indices.permute(1, 0)] / im_scale_ratio for cls in range(20): here = torch.cat((sorted_boxes[cls], sorted_scores[:, cls:cls + 1]), 1).cpu() all_boxes[cls][index] = here.numpy() if index % 500 == 499: print('%d images complete, elapsed time:%.1f' % (index + 1, time.time() - start)) save_name = os.path.join(args.save_dir, 'detection_result', '{}.pkl'.format(args.model_name)) pickle.dump(all_boxes, open(save_name, 'wb')) print('Detection Complete, elapsed time: %.1f', time.time() - start) for cls in range(20): for index in range(len(test_dataset)): dets = all_boxes[cls][index] if dets == []: continue keep = nms(dets, 0.4) all_boxes[cls][index] = dets[keep, :].copy() print('NMS complete, elapsed time: %.1f', time.time() - start) eval_kit.evaluate_detections(all_boxes)
def test(args, model, test_dataset): np.random.seed(3) torch.manual_seed(3) if torch.cuda.is_available(): torch.cuda.manual_seed(5) model.cuda() model.eval() eval_kit = voc_eval_kit('test', '2007', os.path.join(args.dataroot, 'VOCdevkit')) result_dir = os.path.join(args.result_path, str(args.session)) start = time.time() # DataLoader test_loader = torch.utils.data.DataLoader(dataset=test_dataset, num_workers=0) all_boxes = [[[] for _ in range(len(test_dataset))] for _ in range(args.N_classes)] """ print('all_boxes', len(all_boxes)) print('type', type(all_boxes)) print('all_boxes[0] length', len(all_boxes[0])) #print('all_boxes[0]', all_boxes[0]) print('all_boxes[0][0]', all_boxes[0][0][0]) print('x', all_boxes[0][k]) """ # Set to update threshold max_per_set = args.max_per_set * len(test_dataset) max_per_prop = args.max_per_prop print('max_per_set', max_per_set) print('max_per_prop', max_per_prop) thresh = -np.inf * np.ones(20) minheap_cls_scores = [[] for _ in range(20)] for step, (file_name, image, proposals, labels) in tqdm.tqdm(enumerate(test_loader)): start_time = time.time() image = image.cuda() proposals = proposals.cuda() labels = labels.cuda() # scores, (# of proposals, classes) scores = model(image, proposals, image_level_label=labels).detach().cpu().numpy() scores = scores * 1000 image = tensor_to_image(image.squeeze(0)) #print('scores', scores.shape) #print('thresh', thresh) """ for c in range(args.N_classes): indices = np.where() """ for c in range(args.N_classes): # Indexing, (# of proposals) indices = np.where((scores[:, c] > thresh[c]))[0] #print('indices.shape', indices.shape) #print('indices', indices) # Scores per proposals in each class, (proposals,) cls_scores = scores[indices, c] #print('cls_scores.shape', cls_scores.shape) #print('cls_scores', cls_scores) # Boxes per proposals, (proposals, 4) cls_boxes = proposals[0][indices] #print('cls_boxes.type', type(cls_boxes)) #print('cls_boxes.shape', cls_boxes.shape) #print('cls_boxes', cls_boxes) # Sorting highscore top_indices = np.argsort(-cls_scores)[:args.max_per_prop] #print("top 100 proposal's indices score", top_indices) """ if cls_scores.shape[0] < 100: print('proposal from model', cls_scores.shape) """ # Top 100 scores & proposals try: top100_scores = cls_scores[top_indices] top100_boxes = cls_boxes[top_indices, :] except Exception as e: pass """ print(e) print(top100_scores.shape) print(top100_boxes.shape) """ # Push new scores in minheap for val in cls_scores: heapq.heappush(minheap_cls_scores[c], val) #print('minheap_cls_scores[c].len', len(minheap_cls_scores[c])) # Score Threshold Update if len(minheap_cls_scores[c]) > max_per_set: #print(print('minheap', len(minheap_cls_scores[c]))) while len(minheap_cls_scores[c]) > max_per_set: heapq.heappop(minheap_cls_scores[c]) thresh[c] = minheap_cls_scores[c][0] try: all_boxes[c][step] = np.hstack( (top100_boxes, top100_scores[:, np.newaxis])).astype(np.float32, copy=False) except Exception as e: pass """ print(e) print(top100_scores.shape) print(top100_boxes.shape) """ """ if cls_scores[0] > 0.001: print(cls_boxes.shape) keep = nms(cls_boxes.cpu().numpy(), 0.4) draw_box(image, cls_boxes[keep, :], result_dir, file_name[0]) """ if step % 100 == 99: print('thresh', thresh) print('cls_scores', cls_scores.shape, type(cls_scores)) print('cls_boxes', cls_boxes.shape, type(cls_boxes)) print('top_dincies', top_indices.shape, type(top_indices)) logger.info('%d images complete, time: %.3f' % (step + 1, time.time() - start)) print('indice', indices.shape, type(indices)) print('thresh', thresh.shape, type(thresh)) print('all_bex', len(all_boxes), type(all_boxes)) print('all_bex[0]', len(all_boxes[0]), type(all_boxes[0])) print('score', scores.shape, type(scores)) """ for j in range(args.N_classes): for i in range(len(test_dataset)): inds = np.where(all_boxes[j][i][:, -1] > thresh[j])[0] all_boxes[j][i] = all_boxes[j][i][inds, :] #print(all_boxes) print('inds', inds.shape, type(inds)) #print(all_boxes.shape) print(len(all_boxes)) """ save_boxes = os.path.join(result_dir, '{}.pkl'.format(args.pretrained_name)) pkl.dump(all_boxes, open(save_boxes, 'wb')) logger.info('Detection complete, elapsed time: %.3f', time.time() - start) # Calculate NMS for c in range(args.N_classes): for index in range(len(test_dataset)): dets = all_boxes[c][index] if dets == []: continue keep = nms(dets, 0.4) all_boxes[c][index] = dets[keep, :].copy() print('NMS complete, time: %.3f', time.time() - start) eval_kit.evaluate_detections(all_boxes)