diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..6773dced4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.pyc +*__pycache__* +*core.* +_ext +tmp +*.o* +*~ +*.idea +*.mp4 +*.h5 +*.pth +*.egg-info + +/build +/dist \ No newline at end of file diff --git a/README.md b/README.md index 1974b3640..86f32575b 100644 --- a/README.md +++ b/README.md @@ -20,14 +20,14 @@ We also provide a set of Face Detector for edge device in [here](https://github. | Pytorch (original image scale) | 90.70% | 88.16% | 73.82% | | Mxnet | 88.72% | 86.97% | 79.19% | | Mxnet(original image scale) | 89.58% | 87.11% | 69.12% | -

+

## FDDB Performance. | FDDB(pytorch) | performance | |:-|:-:| | Mobilenet0.25 | 98.64% | | Resnet50 | 99.22% | -

+

### Contents - [Installation](#installation) @@ -112,7 +112,7 @@ python test_fddb.py --trained_model weight_file --network mobile0.25 or resnet50 3. Download [eval_tool](https://bitbucket.org/marcopede/face-eval) to evaluate the performance. -

+

## TensorRT -[TensorRT](https://github.com/wang-xinyu/tensorrtx/tree/master/retinaface) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..2a84a692b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] +requires = [ + "setuptools>=64.0.0", + "wheel", +] +build-backend = "setuptools.build_meta" + diff --git a/retinaface/__init__.py b/retinaface/__init__.py new file mode 100644 index 000000000..162f22da6 --- /dev/null +++ b/retinaface/__init__.py @@ -0,0 +1 @@ +from .inference_framework import RetinaFaceDetector diff --git a/convert_to_onnx.py b/retinaface/convert_to_onnx.py similarity index 72% rename from convert_to_onnx.py rename to retinaface/convert_to_onnx.py index e7f32799a..c7f9121a7 100755 --- a/convert_to_onnx.py +++ b/retinaface/convert_to_onnx.py @@ -1,16 +1,10 @@ from __future__ import print_function -import os + import argparse import torch -import torch.backends.cudnn as cudnn -import numpy as np + from data import cfg_mnet, cfg_re50 -from layers.functions.prior_box import PriorBox -from utils.nms.py_cpu_nms import py_cpu_nms -import cv2 from models.retinaface import RetinaFace -from utils.box_utils import decode, decode_landm -from utils.timer import Timer parser = argparse.ArgumentParser(description='Test') @@ -18,7 +12,7 @@ type=str, help='Trained state_dict file path to open') parser.add_argument('--network', default='mobile0.25', help='Backbone network mobile0.25 or resnet50') parser.add_argument('--long_side', default=640, help='when origin_size is false, long_side is scaled size(320 or 640 for long side)') -parser.add_argument('--cpu', action="store_true", default=True, help='Use cpu inference') +parser.add_argument('--cpu', action="store_true", default=False, help='Use cpu inference') args = parser.parse_args() @@ -43,13 +37,17 @@ def remove_prefix(state_dict, prefix): return {f(key): value for key, value in state_dict.items()} -def load_model(model, pretrained_path, load_to_cpu): +def load_model(model, pretrained_path, device): print('Loading pretrained model from {}'.format(pretrained_path)) - if load_to_cpu: - pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage) - else: + if 'cuda' in device or device=='gpu': device = torch.cuda.current_device() pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage.cuda(device)) + elif device=='mps': + device = torch.device('mps') + pretrained_dict = torch.load(pretrained_path, map_location=device) + else: + pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage) + if "state_dict" in pretrained_dict.keys(): pretrained_dict = remove_prefix(pretrained_dict['state_dict'], 'module.') else: @@ -66,13 +64,31 @@ def load_model(model, pretrained_path, load_to_cpu): cfg = cfg_mnet elif args.network == "resnet50": cfg = cfg_re50 + + if args.cpu: + print('--> load model and config files to CPU') + device = "cpu" + elif torch.cuda.is_available(): + print('--> load model and config files to GPU') + device = "cuda" + elif torch.mps.is_available(): + print('--> load model and config files to MPS') + device = "mps" + else: + raise RuntimeError('No GPU or MPS found. Please use "--cpu"') + # net and model net = RetinaFace(cfg=cfg, phase = 'test') - net = load_model(net, args.trained_model, args.cpu) + net = load_model(net, args.trained_model, device=device) net.eval() - print('Finished loading model!') - print(net) - device = torch.device("cpu" if args.cpu else "cuda") + print('--> Finished loading model!') + # print(net) + + if device == "cuda" and torch.cuda.is_available: + cudnn.benchmark = True + + # device = torch.device("cpu" if args.cpu else "cuda") + device = torch.device(device) net = net.to(device) # ------------------------ export ----------------------------- @@ -82,7 +98,7 @@ def load_model(model, pretrained_path, load_to_cpu): output_names = ["output0"] inputs = torch.randn(1, 3, args.long_side, args.long_side).to(device) - torch_out = torch.onnx._export(net, inputs, output_onnx, export_params=True, verbose=False, + torch_out = torch.onnx.export(net, inputs, output_onnx, export_params=True, verbose=False, input_names=input_names, output_names=output_names) diff --git a/curve/1.jpg b/retinaface/curve/1.jpg similarity index 100% rename from curve/1.jpg rename to retinaface/curve/1.jpg diff --git a/curve/FDDB.png b/retinaface/curve/FDDB.png similarity index 100% rename from curve/FDDB.png rename to retinaface/curve/FDDB.png diff --git a/curve/Widerface.jpg b/retinaface/curve/Widerface.jpg similarity index 100% rename from curve/Widerface.jpg rename to retinaface/curve/Widerface.jpg diff --git a/curve/test.jpg b/retinaface/curve/test.jpg similarity index 100% rename from curve/test.jpg rename to retinaface/curve/test.jpg diff --git a/data/FDDB/img_list.txt b/retinaface/data/FDDB/img_list.txt similarity index 100% rename from data/FDDB/img_list.txt rename to retinaface/data/FDDB/img_list.txt diff --git a/data/__init__.py b/retinaface/data/__init__.py similarity index 100% rename from data/__init__.py rename to retinaface/data/__init__.py diff --git a/data/config.py b/retinaface/data/config.py similarity index 100% rename from data/config.py rename to retinaface/data/config.py diff --git a/data/data_augment.py b/retinaface/data/data_augment.py similarity index 99% rename from data/data_augment.py rename to retinaface/data/data_augment.py index c1b52ae19..8e0622150 100644 --- a/data/data_augment.py +++ b/retinaface/data/data_augment.py @@ -1,7 +1,7 @@ import cv2 import numpy as np import random -from utils.box_utils import matrix_iof +from retinaface.utils.box_utils import matrix_iof def _crop(image, boxes, labels, landm, img_dim): diff --git a/data/wider_face.py b/retinaface/data/wider_face.py similarity index 98% rename from data/wider_face.py rename to retinaface/data/wider_face.py index 22f56efdc..73ccfe8a5 100644 --- a/data/wider_face.py +++ b/retinaface/data/wider_face.py @@ -1,6 +1,3 @@ -import os -import os.path -import sys import torch import torch.utils.data as data import cv2 diff --git a/detect.py b/retinaface/detect.py similarity index 54% rename from detect.py rename to retinaface/detect.py index 2e822400e..a5238129d 100755 --- a/detect.py +++ b/retinaface/detect.py @@ -1,30 +1,18 @@ from __future__ import print_function -import os + import argparse import torch import torch.backends.cudnn as cudnn import numpy as np -from data import cfg_mnet, cfg_re50 -from layers.functions.prior_box import PriorBox -from utils.nms.py_cpu_nms import py_cpu_nms -import cv2 -from models.retinaface import RetinaFace -from utils.box_utils import decode, decode_landm import time +import cv2 -parser = argparse.ArgumentParser(description='Retinaface') +from retinaface.data import cfg_mnet, cfg_re50 +from retinaface.layers.functions.prior_box import PriorBox +from retinaface.utils.nms.py_cpu_nms import py_cpu_nms +from retinaface.models.retinaface import RetinaFace +from retinaface.utils.box_utils import decode, decode_landm -parser.add_argument('-m', '--trained_model', default='./weights/Resnet50_Final.pth', - type=str, help='Trained state_dict file path to open') -parser.add_argument('--network', default='resnet50', help='Backbone network mobile0.25 or resnet50') -parser.add_argument('--cpu', action="store_true", default=False, help='Use cpu inference') -parser.add_argument('--confidence_threshold', default=0.02, type=float, help='confidence_threshold') -parser.add_argument('--top_k', default=5000, type=int, help='top_k') -parser.add_argument('--nms_threshold', default=0.4, type=float, help='nms_threshold') -parser.add_argument('--keep_top_k', default=750, type=int, help='keep_top_k') -parser.add_argument('-s', '--save_image', action="store_true", default=True, help='show detection results') -parser.add_argument('--vis_thres', default=0.6, type=float, help='visualization_threshold') -args = parser.parse_args() def check_keys(model, pretrained_state_dict): @@ -33,58 +21,114 @@ def check_keys(model, pretrained_state_dict): used_pretrained_keys = model_keys & ckpt_keys unused_pretrained_keys = ckpt_keys - model_keys missing_keys = model_keys - ckpt_keys - print('Missing keys:{}'.format(len(missing_keys))) - print('Unused checkpoint keys:{}'.format(len(unused_pretrained_keys))) - print('Used keys:{}'.format(len(used_pretrained_keys))) assert len(used_pretrained_keys) > 0, 'load NONE from pretrained checkpoint' return True def remove_prefix(state_dict, prefix): ''' Old style model is stored with all names of parameters sharing common prefix 'module.' ''' - print('remove prefix \'{}\''.format(prefix)) f = lambda x: x.split(prefix, 1)[-1] if x.startswith(prefix) else x return {f(key): value for key, value in state_dict.items()} - -def load_model(model, pretrained_path, load_to_cpu): +def load_model(model, pretrained_path, device, url_file_name=None): print('Loading pretrained model from {}'.format(pretrained_path)) - if load_to_cpu: - pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage) - else: + + url_flag = False + if pretrained_path[:8] == 'https://': + url_flag = True + + if 'cuda' in device or device=='gpu': device = torch.cuda.current_device() - pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage.cuda(device)) + if url_flag: + pretrained_dict = torch.hub.load_state_dict_from_url(pretrained_path, + map_location=lambda storage, loc: storage.cuda(device), + file_name=url_file_name) + else: + pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage.cuda(device)) + elif device=='mps': + device = torch.device('mps') + if url_flag: + pretrained_dict = torch.hub.load_state_dict_from_url(pretrained_path, + map_location=device, + file_name=url_file_name) + else: + pretrained_dict = torch.load(pretrained_path, map_location=device) + else: + if url_flag: + pretrained_dict = torch.hub.load_state_dict_from_url(pretrained_path, + map_location=lambda storage, loc: storage, + file_name=url_file_name) + else: + pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage) + if "state_dict" in pretrained_dict.keys(): pretrained_dict = remove_prefix(pretrained_dict['state_dict'], 'module.') else: pretrained_dict = remove_prefix(pretrained_dict, 'module.') + check_keys(model, pretrained_dict) model.load_state_dict(pretrained_dict, strict=False) + return model if __name__ == '__main__': + + parser = argparse.ArgumentParser(description='Retinaface') + + parser.add_argument('-m', '--trained_model', default='./weights/Resnet50_Final.pth', + type=str, help='Trained state_dict file path to open') + parser.add_argument('--network', default='resnet50', help='Backbone network mobile0.25 or resnet50') + parser.add_argument('--cpu', action="store_true", default=False, help='Use cpu inference') + parser.add_argument('--confidence_threshold', default=0.02, type=float, help='confidence_threshold') + parser.add_argument('--top_k', default=5000, type=int, help='top_k') + parser.add_argument('--nms_threshold', default=0.4, type=float, help='nms_threshold') + parser.add_argument('--keep_top_k', default=750, type=int, help='keep_top_k') + parser.add_argument('-s', '--save_image', action="store_true", default=False, help='show detection results') + parser.add_argument('--vis_thres', default=0.6, type=float, help='visualization_threshold') + args = parser.parse_args() + torch.set_grad_enabled(False) cfg = None if args.network == "mobile0.25": cfg = cfg_mnet elif args.network == "resnet50": cfg = cfg_re50 + + if args.cpu: + print('--> load model and config files to CPU') + device = "cpu" + elif torch.cuda.is_available(): + print('--> load model and config files to GPU') + device = "cuda" + elif torch.mps.is_available(): + print('--> load model and config files to MPS') + device = "mps" + else: + raise RuntimeError('No GPU or MPS found. Please use "--cpu"') + # net and model net = RetinaFace(cfg=cfg, phase = 'test') - net = load_model(net, args.trained_model, args.cpu) + net = load_model(net, args.trained_model, device=device) net.eval() - print('Finished loading model!') - print(net) - cudnn.benchmark = True - device = torch.device("cpu" if args.cpu else "cuda") + print('--> Finished loading model!') + # print(net) + + if device == "cuda" and torch.cuda.is_available: + cudnn.benchmark = True + + # device = torch.device("cpu" if args.cpu else "cuda") + device = torch.device(device) net = net.to(device) resize = 1 + total_time = 0 + n_loops = 100 + # testing begin - for i in range(100): - image_path = "./curve/test.jpg" + for i in range(n_loops): + image_path = "curve/test.jpg" img_raw = cv2.imread(image_path, cv2.IMREAD_COLOR) img = np.float32(img_raw) @@ -99,7 +143,11 @@ def load_model(model, pretrained_path, load_to_cpu): tic = time.time() loc, conf, landms = net(img) # forward pass - print('net forward time: {:.4f}'.format(time.time() - tic)) + + time_det = time.time() - tic + # print('net forward time: {:.4f}'.format(time_det)) + + total_time += time_det priorbox = PriorBox(cfg, image_size=(im_height, im_width)) priors = priorbox.forward() @@ -166,3 +214,5 @@ def load_model(model, pretrained_path, load_to_cpu): name = "test.jpg" cv2.imwrite(name, img_raw) + avg_time = total_time / n_loops + print(f'--> Average time: {avg_time:.4f} for {n_loops} loops') \ No newline at end of file diff --git a/retinaface/inference_framework.py b/retinaface/inference_framework.py new file mode 100644 index 000000000..54806dba7 --- /dev/null +++ b/retinaface/inference_framework.py @@ -0,0 +1,140 @@ +import torch +import numpy as np + +torch.set_grad_enabled(False) + +# My libs +import retinaface.models.retinaface as rf_model +import retinaface.detect as rf_detect +import retinaface.data.config as rf_config +import retinaface.layers.functions.prior_box as rf_priors +import retinaface.utils.box_utils as rf_ubox +import retinaface.utils.nms.py_cpu_nms as rf_nms + + +# Default configs +cfg_postreat_dft = {'resize': 1., + 'score_thr': 0.75, + 'top_k': 5000, + 'nms_thr': 0.4, + 'keep_top_k': 50} + + +class RetinaFaceDetector: + + def __init__(self, + model='mobile0.25', + device='cuda', + extra_features=['landmarks'], + cfg_postreat=cfg_postreat_dft): + + # Set model configuration + cfg = None + trained_model = None + if model == "mobile0.25": + cfg = rf_config.cfg_mnet + trained_model = "https://drive.google.com/uc?export=download&confirm=yes&id=1nxhtpdVLbmheUTwyIb733MrL53X4SQgQ" + url_model_name = "retinaface_mobile025.pth" + elif model == "resnet50": + cfg = rf_config.cfg_re50 + trained_model = "https://drive.google.com/uc?export=download&confirm=yes&id=1a9SqFRkeTuJUwqerElCWJFrotZuDGVtT" + url_model_name = "retinaface_resnet50.pth" + else: + raise ValueError('Model configuration not found') + + # Load net and model + # cpu_flag = 'cpu' in device + net = rf_model.RetinaFace(cfg=cfg, phase='test') + net = rf_detect.load_model(net, trained_model, device=device, url_file_name=url_model_name) + net.eval() + print('RetinaFace loaded!') + + # Define detector variables + self.device = torch.device(device) + self.net = net.to(self.device) + self.cfg = cfg + self.features = ['bbox'] + extra_features + self.scale = {} + self.prior_data = None + + # Postreatment configuration + self.cfg['postreat'] = cfg_postreat + + def set_input_shape(self, im_height, im_width): + + # Scales + scale_bbox = torch.Tensor([im_width, im_height, im_width, im_height]) + self.scale['bbox'] = scale_bbox.to(self.device) + + if 'landmarks' in self.features: + scale_lnd = torch.Tensor([im_width, im_height, im_width, im_height, + im_width, im_height, im_width, im_height, + im_width, im_height]) + self.scale['landmarks'] = scale_lnd.to(self.device) + + # Load priors + priorbox = rf_priors.PriorBox(self.cfg, image_size=(im_height, im_width)) + priors = priorbox.forward() + priors = priors.to(self.device) + self.prior_data = priors.data + + def inference(self, image): + img = self._pretreatment(image) + loc, conf, lnd = self._net_forward(img) + features = self._postreatment(loc, conf, lnd) + return features + + def _pretreatment(self, img_raw): + img = np.float32(img_raw) + img -= (104, 117, 123) + img = img.transpose(2, 0, 1) + img = torch.from_numpy(img).unsqueeze(0) + img = img.to(self.device) + return img + + def _net_forward(self, img): + loc, conf, landms = self.net(img) + return loc, conf, landms + + def _postreatment(self, loc, conf, landms): + + cfg_post = self.cfg['postreat'] + boxes = rf_ubox.decode(loc.data.squeeze(0), self.prior_data, self.cfg['variance']) + boxes = boxes * self.scale['bbox'] / cfg_post['resize'] + boxes = boxes.cpu().numpy() + scores = conf.squeeze(0).data.cpu().numpy()[:, 1] + + landms = rf_ubox.decode_landm(landms.data.squeeze(0), self.prior_data, self.cfg['variance']) + landms = landms * self.scale['landmarks'] / cfg_post['resize'] + landms = landms.cpu().numpy() + + # Ignore low scores + inds = np.where(scores > cfg_post['score_thr'])[0] + boxes = boxes[inds] + scores = scores[inds] + + # Keep top-K before NMS + order = scores.argsort()[::-1][:cfg_post['top_k']] + boxes = boxes[order] + scores = scores[order] + + # NMS + dets = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=False) + keep = rf_nms.py_cpu_nms(dets, cfg_post['nms_thr']) + dets = dets[keep, :] + + # keep top-K faster NMS + dets = dets[:cfg_post['keep_top_k'], :] + + features = {'bbox': dets} + if 'landmarks' in self.features: + landms = landms[inds] + landms = landms[order] + landms = landms[keep] + landms = landms[:cfg_post['keep_top_k'], :] + landms = np.array(landms) + landms = np.expand_dims(landms, axis=-1) + landms = landms.reshape((-1, 5, 2)) + features['landmarks'] = landms + + return features diff --git a/layers/__init__.py b/retinaface/layers/__init__.py similarity index 100% rename from layers/__init__.py rename to retinaface/layers/__init__.py diff --git a/models/__init__.py b/retinaface/layers/functions/__init__.py similarity index 100% rename from models/__init__.py rename to retinaface/layers/functions/__init__.py diff --git a/layers/functions/prior_box.py b/retinaface/layers/functions/prior_box.py similarity index 98% rename from layers/functions/prior_box.py rename to retinaface/layers/functions/prior_box.py index 80c7f8583..a3a9723ab 100644 --- a/layers/functions/prior_box.py +++ b/retinaface/layers/functions/prior_box.py @@ -1,6 +1,5 @@ import torch from itertools import product as product -import numpy as np from math import ceil diff --git a/layers/modules/__init__.py b/retinaface/layers/modules/__init__.py similarity index 100% rename from layers/modules/__init__.py rename to retinaface/layers/modules/__init__.py diff --git a/layers/modules/multibox_loss.py b/retinaface/layers/modules/multibox_loss.py similarity index 97% rename from layers/modules/multibox_loss.py rename to retinaface/layers/modules/multibox_loss.py index 096620480..73158af70 100644 --- a/layers/modules/multibox_loss.py +++ b/retinaface/layers/modules/multibox_loss.py @@ -1,9 +1,8 @@ import torch import torch.nn as nn import torch.nn.functional as F -from torch.autograd import Variable -from utils.box_utils import match, log_sum_exp -from data import cfg_mnet +from retinaface.utils.box_utils import match, log_sum_exp +from retinaface.data import cfg_mnet GPU = cfg_mnet['gpu_train'] class MultiBoxLoss(nn.Module): diff --git a/utils/__init__.py b/retinaface/models/__init__.py similarity index 100% rename from utils/__init__.py rename to retinaface/models/__init__.py diff --git a/models/net.py b/retinaface/models/net.py similarity index 97% rename from models/net.py rename to retinaface/models/net.py index beb6040b2..e7a13d85d 100644 --- a/models/net.py +++ b/retinaface/models/net.py @@ -1,10 +1,7 @@ -import time import torch import torch.nn as nn -import torchvision.models._utils as _utils -import torchvision.models as models import torch.nn.functional as F -from torch.autograd import Variable + def conv_bn(inp, oup, stride = 1, leaky = 0): return nn.Sequential( diff --git a/models/retinaface.py b/retinaface/models/retinaface.py similarity index 85% rename from models/retinaface.py rename to retinaface/models/retinaface.py index d530bd839..eb7544240 100644 --- a/models/retinaface.py +++ b/retinaface/models/retinaface.py @@ -1,13 +1,11 @@ import torch import torch.nn as nn -import torchvision.models.detection.backbone_utils as backbone_utils import torchvision.models._utils as _utils import torch.nn.functional as F -from collections import OrderedDict -from models.net import MobileNetV1 as MobileNetV1 -from models.net import FPN as FPN -from models.net import SSH as SSH +from retinaface.models.net import MobileNetV1 as MobileNetV1 +from retinaface.models.net import FPN as FPN +from retinaface.models.net import SSH as SSH @@ -56,15 +54,6 @@ def __init__(self, cfg = None, phase = 'train'): backbone = None if cfg['name'] == 'mobilenet0.25': backbone = MobileNetV1() - if cfg['pretrain']: - checkpoint = torch.load("./weights/mobilenetV1X0.25_pretrain.tar", map_location=torch.device('cpu')) - from collections import OrderedDict - new_state_dict = OrderedDict() - for k, v in checkpoint['state_dict'].items(): - name = k[7:] # remove module. - new_state_dict[name] = v - # load params - backbone.load_state_dict(new_state_dict) elif cfg['name'] == 'Resnet50': import torchvision.models as models backbone = models.resnet50(pretrained=cfg['pretrain']) diff --git a/test_fddb.py b/retinaface/test_fddb.py similarity index 89% rename from test_fddb.py rename to retinaface/test_fddb.py index 98fef43b8..d466ea097 100755 --- a/test_fddb.py +++ b/retinaface/test_fddb.py @@ -49,13 +49,17 @@ def remove_prefix(state_dict, prefix): return {f(key): value for key, value in state_dict.items()} -def load_model(model, pretrained_path, load_to_cpu): +def load_model(model, pretrained_path, device): print('Loading pretrained model from {}'.format(pretrained_path)) - if load_to_cpu: - pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage) - else: + if 'cuda' in device or device=='gpu': device = torch.cuda.current_device() pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage.cuda(device)) + elif device=='mps': + device = torch.device('mps') + pretrained_dict = torch.load(pretrained_path, map_location=device) + else: + pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage) + if "state_dict" in pretrained_dict.keys(): pretrained_dict = remove_prefix(pretrained_dict['state_dict'], 'module.') else: @@ -72,16 +76,32 @@ def load_model(model, pretrained_path, load_to_cpu): cfg = cfg_mnet elif args.network == "resnet50": cfg = cfg_re50 + + if args.cpu: + print('--> load model and config files to CPU') + device = "cpu" + elif torch.cuda.is_available(): + print('--> load model and config files to GPU') + device = "cuda" + elif torch.mps.is_available(): + print('--> load model and config files to MPS') + device = "mps" + else: + raise RuntimeError('No GPU or MPS found. Please use "--cpu"') + # net and model net = RetinaFace(cfg=cfg, phase = 'test') - net = load_model(net, args.trained_model, args.cpu) + net = load_model(net, args.trained_model, device=device) net.eval() - print('Finished loading model!') - print(net) - cudnn.benchmark = True - device = torch.device("cpu" if args.cpu else "cuda") - net = net.to(device) + print('--> Finished loading model!') + # print(net) + if device == "cuda" and torch.cuda.is_available: + cudnn.benchmark = True + + # device = torch.device("cpu" if args.cpu else "cuda") + device = torch.device(device) + net = net.to(device) # save file if not os.path.exists(args.save_folder): diff --git a/test_widerface.py b/retinaface/test_widerface.py similarity index 90% rename from test_widerface.py rename to retinaface/test_widerface.py index baf7c42cd..df7323d39 100755 --- a/test_widerface.py +++ b/retinaface/test_widerface.py @@ -50,13 +50,17 @@ def remove_prefix(state_dict, prefix): return {f(key): value for key, value in state_dict.items()} -def load_model(model, pretrained_path, load_to_cpu): +def load_model(model, pretrained_path, device): print('Loading pretrained model from {}'.format(pretrained_path)) - if load_to_cpu: - pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage) - else: + if 'cuda' in device or device=='gpu': device = torch.cuda.current_device() pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage.cuda(device)) + elif device=='mps': + device = torch.device('mps') + pretrained_dict = torch.load(pretrained_path, map_location=device) + else: + pretrained_dict = torch.load(pretrained_path, map_location=lambda storage, loc: storage) + if "state_dict" in pretrained_dict.keys(): pretrained_dict = remove_prefix(pretrained_dict['state_dict'], 'module.') else: @@ -74,14 +78,31 @@ def load_model(model, pretrained_path, load_to_cpu): cfg = cfg_mnet elif args.network == "resnet50": cfg = cfg_re50 + + if args.cpu: + print('--> load model and config files to CPU') + device = "cpu" + elif torch.cuda.is_available(): + print('--> load model and config files to GPU') + device = "cuda" + elif torch.mps.is_available(): + print('--> load model and config files to MPS') + device = "mps" + else: + raise RuntimeError('No GPU or MPS found. Please use "--cpu"') + # net and model net = RetinaFace(cfg=cfg, phase = 'test') - net = load_model(net, args.trained_model, args.cpu) + net = load_model(net, args.trained_model, device=device) net.eval() - print('Finished loading model!') - print(net) - cudnn.benchmark = True - device = torch.device("cpu" if args.cpu else "cuda") + print('--> Finished loading model!') + # print(net) + + if device == "cuda" and torch.cuda.is_available: + cudnn.benchmark = True + + # device = torch.device("cpu" if args.cpu else "cuda") + device = torch.device(device) net = net.to(device) # testing dataset diff --git a/train.py b/retinaface/train.py similarity index 100% rename from train.py rename to retinaface/train.py diff --git a/utils/nms/__init__.py b/retinaface/utils/__init__.py similarity index 100% rename from utils/nms/__init__.py rename to retinaface/utils/__init__.py diff --git a/utils/box_utils.py b/retinaface/utils/box_utils.py similarity index 100% rename from utils/box_utils.py rename to retinaface/utils/box_utils.py diff --git a/retinaface/utils/nms/__init__.py b/retinaface/utils/nms/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/utils/nms/py_cpu_nms.py b/retinaface/utils/nms/py_cpu_nms.py similarity index 100% rename from utils/nms/py_cpu_nms.py rename to retinaface/utils/nms/py_cpu_nms.py diff --git a/utils/timer.py b/retinaface/utils/timer.py similarity index 100% rename from utils/timer.py rename to retinaface/utils/timer.py diff --git a/widerface_evaluate/README.md b/retinaface/widerface_evaluate/README.md similarity index 100% rename from widerface_evaluate/README.md rename to retinaface/widerface_evaluate/README.md diff --git a/retinaface/widerface_evaluate/__init__.py b/retinaface/widerface_evaluate/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/widerface_evaluate/box_overlaps.pyx b/retinaface/widerface_evaluate/box_overlaps.pyx similarity index 100% rename from widerface_evaluate/box_overlaps.pyx rename to retinaface/widerface_evaluate/box_overlaps.pyx diff --git a/widerface_evaluate/evaluation.py b/retinaface/widerface_evaluate/evaluation.py similarity index 99% rename from widerface_evaluate/evaluation.py rename to retinaface/widerface_evaluate/evaluation.py index a9b17448a..95f8a9855 100644 --- a/widerface_evaluate/evaluation.py +++ b/retinaface/widerface_evaluate/evaluation.py @@ -12,7 +12,6 @@ import numpy as np from scipy.io import loadmat from bbox import bbox_overlaps -from IPython import embed def get_gt_boxes(gt_dir): diff --git a/widerface_evaluate/ground_truth/wider_easy_val.mat b/retinaface/widerface_evaluate/ground_truth/wider_easy_val.mat similarity index 100% rename from widerface_evaluate/ground_truth/wider_easy_val.mat rename to retinaface/widerface_evaluate/ground_truth/wider_easy_val.mat diff --git a/widerface_evaluate/ground_truth/wider_face_val.mat b/retinaface/widerface_evaluate/ground_truth/wider_face_val.mat similarity index 100% rename from widerface_evaluate/ground_truth/wider_face_val.mat rename to retinaface/widerface_evaluate/ground_truth/wider_face_val.mat diff --git a/widerface_evaluate/ground_truth/wider_hard_val.mat b/retinaface/widerface_evaluate/ground_truth/wider_hard_val.mat similarity index 100% rename from widerface_evaluate/ground_truth/wider_hard_val.mat rename to retinaface/widerface_evaluate/ground_truth/wider_hard_val.mat diff --git a/widerface_evaluate/ground_truth/wider_medium_val.mat b/retinaface/widerface_evaluate/ground_truth/wider_medium_val.mat similarity index 100% rename from widerface_evaluate/ground_truth/wider_medium_val.mat rename to retinaface/widerface_evaluate/ground_truth/wider_medium_val.mat diff --git a/widerface_evaluate/setup.py b/retinaface/widerface_evaluate/setup.py similarity index 100% rename from widerface_evaluate/setup.py rename to retinaface/widerface_evaluate/setup.py diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000..f257c1996 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,39 @@ +# Configuration of the Python project + +# Configure setup.py +[metadata] +name = retinaface-py +version = 0.0.1 +author = Jiankang Deng +maintainer = Andres Prados Torreblanca +maintainer_email = andresprator@gmail.com +description = RetinaFace: Single-stage Dense Face Localisation in the Wild +long_description = file: README.md +long_description_content_type= text/markdown +license = MIT License +url = https://github.com/andresprados/Pytorch_Retinaface +classifiers = + License :: OSI Approved :: MIT License + Intended Audience :: Developers + Intended Audience :: Science/Research + Operating System :: OS Independent + Programming Language :: Python + Programming Language :: Python :: 3 + Topic :: Software Development :: Libraries + Topic :: Software Development :: Libraries :: Python Modules + +[options] +packages = find: +include_package_data = True +python_requires = >= 3.6 +install_requires = + numpy + torch>=1.1.0 + torchvision>=0.3.0 + opencv-python + +[options.packages.find] +exclude = + curve* + widerface_evaluate* +