Files
insightface/detection/scrfd/tools/test_widerface.py
2021-05-12 11:40:54 +08:00

257 lines
9.7 KiB
Python
Executable File

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()