Files
insightface/detection/scrfd/tools/test_widerface.py

257 lines
9.7 KiB
Python
Raw Normal View History

2021-05-12 11:40:54 +08:00
import argparse
import os
import os.path as osp
import pickle
import numpy as np
import warnings
import mmcv
import torch
from mmcv import Config, DictAction
from mmcv.cnn import fuse_conv_bn
from mmcv.parallel import MMDataParallel, MMDistributedDataParallel
from mmcv.runner import (get_dist_info, init_dist, load_checkpoint,
wrap_fp16_model)
from mmdet.apis import multi_gpu_test, single_gpu_test
from mmdet.datasets import (build_dataloader, build_dataset,
replace_ImageToTensor)
from mmdet.models import build_detector
from mmdet.core.evaluation import wider_evaluation, get_widerface_gts
def parse_args():
parser = argparse.ArgumentParser(
description='MMDet test (and eval) a model')
parser.add_argument('config', help='test config file path')
parser.add_argument('checkpoint', help='checkpoint file')
parser.add_argument('--out', default='wout', help='output folder')
parser.add_argument(
'--eval',
type=str,
nargs='+',
help='evaluation metrics, which depends on the dataset, e.g., "bbox",'
' "segm", "proposal" for COCO, and "mAP", "recall" for PASCAL VOC')
parser.add_argument('--show', action='store_true', help='show results')
parser.add_argument('--save-preds', action='store_true', help='save results')
parser.add_argument('--show-assign', action='store_true', help='show bbox assign')
parser.add_argument('--debug', action='store_true', help='debug flag')
parser.add_argument(
'--show-dir', help='directory where painted images will be saved')
parser.add_argument(
'--show-score-thr',
type=float,
default=0.3,
help='score threshold (default: 0.3)')
parser.add_argument(
'--thr',
type=float,
default=0.02,
help='score threshold')
parser.add_argument('--local_rank', type=int, default=0)
parser.add_argument('--mode', type=int, default=0)
args = parser.parse_args()
if 'LOCAL_RANK' not in os.environ:
os.environ['LOCAL_RANK'] = str(args.local_rank)
return args
def main():
args = parse_args()
cfg = Config.fromfile(args.config)
if cfg.get('custom_imports', None):
from mmcv.utils import import_modules_from_strings
import_modules_from_strings(**cfg['custom_imports'])
# set cudnn_benchmark
if cfg.get('cudnn_benchmark', False):
torch.backends.cudnn.benchmark = True
cfg.model.pretrained = None
if cfg.model.get('neck'):
if isinstance(cfg.model.neck, list):
for neck_cfg in cfg.model.neck:
if neck_cfg.get('rfp_backbone'):
if neck_cfg.rfp_backbone.get('pretrained'):
neck_cfg.rfp_backbone.pretrained = None
elif cfg.model.neck.get('rfp_backbone'):
if cfg.model.neck.rfp_backbone.get('pretrained'):
cfg.model.neck.rfp_backbone.pretrained = None
# in case the test dataset is concatenated
if isinstance(cfg.data.test, dict):
cfg.data.test.test_mode = True
elif isinstance(cfg.data.test, list):
for ds_cfg in cfg.data.test:
ds_cfg.test_mode = True
gt_path = os.path.join(os.path.dirname(cfg.data.test.ann_file), 'gt')
pipelines = cfg.data.test.pipeline
for pipeline in pipelines:
if pipeline.type=='MultiScaleFlipAug':
if args.mode==0: #640 scale
pipeline.img_scale = (640, 640)
elif args.mode==1: #for single scale in other pages
pipeline.img_scale = (1100, 1650)
elif args.mode==2: #original scale
pipeline.img_scale = None
pipeline.scale_factor = 1.0
elif args.mode>30:
pipeline.img_scale = (args.mode, args.mode)
transforms = pipeline.transforms
for transform in transforms:
if transform.type=='Pad':
if args.mode!=2:
transform.size = pipeline.img_scale
else:
transform.size = None
transform.size_divisor = 32
print(cfg.data.test.pipeline)
distributed = False
# build the dataloader
samples_per_gpu = cfg.data.test.pop('samples_per_gpu', 1)
if samples_per_gpu > 1:
# Replace 'ImageToTensor' to 'DefaultFormatBundle'
cfg.data.test.pipeline = replace_ImageToTensor(cfg.data.test.pipeline)
dataset = build_dataset(cfg.data.test)
data_loader = build_dataloader(
dataset,
samples_per_gpu=samples_per_gpu,
workers_per_gpu=cfg.data.workers_per_gpu,
dist=distributed,
shuffle=False)
cfg.test_cfg.score_thr = args.thr
# build the model and load checkpoint
model = build_detector(cfg.model, train_cfg=None, test_cfg=cfg.test_cfg)
fp16_cfg = cfg.get('fp16', None)
checkpoint = load_checkpoint(model, args.checkpoint, map_location='cpu')
if 'CLASSES' in checkpoint['meta']:
model.CLASSES = checkpoint['meta']['CLASSES']
else:
model.CLASSES = dataset.CLASSES
if args.show_assign:
gts_easy, gts_medium, gts_hard = get_widerface_gts(gt_path)
assign_stat = [0, 0]
gts_size = []
model = MMDataParallel(model, device_ids=[0])
model.eval()
results = {}
output_folder = args.out
if not os.path.exists(output_folder):
os.makedirs(output_folder)
dataset = data_loader.dataset
prog_bar = mmcv.ProgressBar(len(dataset))
for i, data in enumerate(data_loader):
with torch.no_grad():
result = model(return_loss=False, rescale=True, **data)
assert len(result)==1
batch_size = 1
result = result[0][0]
img_metas = data['img_metas'][0].data[0][0]
filepath = img_metas['ori_filename']
det_scale = img_metas['scale_factor'][0]
#print(img_metas)
ori_shape = img_metas['ori_shape']
img_width = ori_shape[1]
img_height = ori_shape[0]
_vec = filepath.split('/')
pa, pb = _vec[-2], _vec[1]
if pa not in results:
results[pa] = {}
xywh = result.copy()
w = xywh[:,2] - xywh[:,0]
h = xywh[:,3] - xywh[:,1]
xywh[:,2] = w
xywh[:,3] = h
event_name = pa
img_name = pb.rstrip('.jpg')
results[event_name][img_name] = xywh
if args.save_preds:
out_dir = os.path.join(output_folder, pa)
if not os.path.exists(out_dir):
os.makedirs(out_dir)
out_file = os.path.join(out_dir, pb.replace('jpg', 'txt'))
boxes = result
with open(out_file, 'w') as f:
name = '/'.join([pa, pb])
f.write("%s\n"%(name))
f.write("%d\n"%(boxes.shape[0]))
for b in range(boxes.shape[0]):
box = boxes[b]
f.write("%.5f %.5f %.5f %.5f %g\n"%(box[0], box[1], box[2]-box[0], box[3]-box[1], box[4]))
if args.show_assign:
assert args.mode==0
input_height, input_width = 640, 640
gt_hard = gts_hard[event_name][img_name]
#print(event_name, img_name, gt_hard.shape)
gt_bboxes = gt_hard * det_scale
bbox_width = gt_bboxes[:,2] - gt_bboxes[:,0]
bbox_height = gt_bboxes[:,3] - gt_bboxes[:,1]
bbox_area = bbox_width * bbox_height
gt_size = np.sqrt(bbox_area+0.0001)
gts_size += list(gt_size)
anchor_cxs = []
anchor_cys = []
for idx, stride in enumerate([8,16,32,64,128]):
height = input_height // stride
width = input_width // stride
anchor_centers = np.stack(np.mgrid[:height, :width][::-1], axis=-1).astype(np.float32)
anchor_centers = (anchor_centers * stride).reshape( (-1, 2) )
anchor_cx = anchor_centers[:,0]
anchor_cy = anchor_centers[:,1]
anchor_cxs += list(anchor_cx)
anchor_cys += list(anchor_cy)
anchor_cx = np.array(anchor_cxs, dtype=np.float32)
anchor_cy = np.array(anchor_cys, dtype=np.float32)
num_gts = gt_bboxes.shape[0]
num_anchors = anchor_cx.shape[0]
anchor_cx = np.broadcast_to(anchor_cx.reshape((1,-1)), (num_gts, num_anchors)).reshape(num_anchors, num_gts)
anchor_cy = np.broadcast_to(anchor_cy.reshape((1,-1)), (num_gts, num_anchors)).reshape(num_anchors, num_gts)
gt_x1 = gt_bboxes[:,0]
gt_y1 = gt_bboxes[:,1]
gt_x2 = gt_bboxes[:,2]
gt_y2 = gt_bboxes[:,3]
gt_cover = np.zeros( (gt_bboxes.shape[0], ), dtype=np.float32)
l_ = anchor_cx - gt_x1
t_ = anchor_cy - gt_y1
r_ = gt_x2 - anchor_cx
b_ = gt_y2 - anchor_cy
dist = np.stack([l_, t_, r_, b_], axis=1).min(axis=1)
gt_dist = dist.max(axis=0)
gt_dist = gt_dist / gt_size
center_thres = 0.01
#center_thres = -0.25
gt_cover_inds = np.where(gt_dist>center_thres)[0]
num_assigned = len(gt_cover_inds)
assign_stat[0] += num_gts
assign_stat[1] += num_assigned
for _ in range(batch_size):
prog_bar.update()
aps = wider_evaluation(results, gt_path, 0.5, args.debug)
with open(os.path.join(output_folder, 'aps'), 'w') as f:
f.write("%f,%f,%f\n"%(aps[0],aps[1],aps[2]))
print('APS:', aps)
if args.show_assign:
print('ASSIGN:', assign_stat)
gts_size = np.array(gts_size, dtype=np.float32)
gts_size = np.sort(gts_size)
assert len(gts_size)==assign_stat[0]
print(gts_size[assign_stat[0]//2])
if __name__ == '__main__':
main()