code: from easydict import EasyDict as edict
  # from pathlib import Path
  import torch
  import os
  from torchvision import transforms as trans
  from utils.constants import *
  list_model = ['wget https://www.dropbox.com/s/akktsgxp0n8cwn2/model_mobilefacenet.pth?dl=0 -O model_mobilefacenet.pth',
  'wget https://www.dropbox.com/s/kzo52d9neybjxsb/model_ir_se50.pth?dl=0 -O model_ir_se50.pth',
  'wget https://www.dropbox.com/s/rxavczg9dlxy3a8/model_ir50.pth?dl=0 -O model_ir50.pth']
  def get_config(mode = 'app', net_size = 'large', net_mode = 'ir_se', use_mtcnn = 1, threshold = 1.25):
      conf = edict()
      conf.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
      conf.input_size = [112, 112]
      conf.face_limit = 5 
      conf.min_face_size = 30
      conf.mode = mode
      conf.net_size = net_size
      if mode =='app':
          assert net_size in ['mobi', 'large', None], 'net_size should be mobi or large, please change in cogfig.py'
          conf.use_tensor = True
          conf.work_path = WORK_PATH
          conf.model_path = '%s/models'%WORK_PATH
          conf.log_path = '%s/log'%WORK_PATH
          conf.save_path = '%s/save'%WORK_PATH
          conf.facebank_path = '%s/Face_bank'%WORK_PATH
          conf.threshold = threshold
          if use_mtcnn:
              conf.use_mtcnn = True
          else:
              conf.use_mtcnn = False
          #when inference, at maximum detect 10 faces in one image, my laptop is slow
          conf.test_transform = trans.Compose([
                          trans.ToTensor(),
                          trans.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
                      ])
          if net_size == 'large':
              conf.use_mobilfacenet = False
              if net_mode == 'ir_se':
                  conf.net_mode = 'ir_se' # or 'ir'
                  conf.weight_path = '%s/weights/model_ir_se50.pth'%WORK_PATH
                  conf.url = list_model[1]
              else:
                  conf.net_mode = 'ir' # or 'ir'
                  conf.weight_path = '%s/weights/model_ir50.pth'%WORK_PATH
                  conf.url = list_model[2]
          if net_size =='mobi':
              conf.use_mobilfacenet = True
              conf.weight_path = '%s/weights/model_mobilefacenet.pth'%WORK_PATH
              conf.url = list_model[0]
          conf.video_source = 0
  
      if mode =='training_eval':
          conf.lr = 1e-3
          conf.milestones = [18,30,42]
          conf.momentum = 0.9
          conf.pin_memory = True
  #         conf.num_workers = 4 # when batchsize is 200
          conf.num_workers = 3
          conf.train_root = "/mnt/01D4A1D481139570/Dataset/Face/casia"
          conf.file_list =  '/mnt/01D4A1D481139570/Dataset/Face/casia_train.txt' 
          conf.batch_size = 4
          conf.lfw_root = '/mnt/01D4A1D481139570/Dataset/Face/data/LFW/lfw_align_112'
          conf.lfw_file_list = '/mnt/01D4A1D481139570/Dataset/Face/data/LFW/pairs.txt'
          conf.agedb_root = '/mnt/01D4A1D481139570/Dataset/Face/data/AgeDB-30/agedb30_align_112'
          conf.agedb_file_list = '/mnt/01D4A1D481139570/Dataset/Face/data/AgeDB-30/agedb_30_pair.txt'
          conf.cfp_root = '/mnt/01D4A1D481139570/Dataset/Face/data/CFP-FP/CFP_FP_aligned_112'
          conf.cfp_file_list = '/mnt/01D4A1D481139570/Dataset/Face/data/CFP-FP/cfp_fp_pair.txt'
      return conf from network import PNet,ONet
  import torch,cv2,itertools
  from torch.autograd import Variable
  import torch.nn.functional as F
  import numpy as np
  import time
  from matlab_cp2tform import get_similarity_transform_for_cv2
  
  import math
  def alignment(src_img,src_pts, crop_size = (112, 112)):
      ref_pts = np.array([ [30.2946, 51.6963],
        [65.5318, 51.5014],
        [48.0252, 71.7366],
        [33.5493, 92.3655],
        [62.7299, 92.2041] ])
      
      if crop_size[1]==112:
          ref_pts[:,0] += 8.0
      src_pts = np.array(src_pts).reshape(5,2)
      
      s = np.array(src_pts).astype(np.float32)
      r = np.array(ref_pts).astype(np.float32)
  
      tfm = get_similarity_transform_for_cv2(s, r)
      face_img = cv2.warpAffine(src_img, tfm, crop_size)
      return face_img
  def resize_square(img, height=128, color=(0, 0, 0)):  # resize a rectangular image to a padded square
      shape = img.shape[:2]  # shape = [height, width]
      ratio = float(height) / max(shape)  # ratio  = old / new
      new_shape = [round(shape[0] * ratio), round(shape[1] * ratio)]
      dw = height - new_shape[1]  # width padding
      dh = height - new_shape[0]  # height padding
      top, bottom = dh // 2, dh - (dh // 2)
      left, right = dw // 2, dw - (dw // 2)
      img = cv2.resize(img, (new_shape[1], new_shape[0]), interpolation=cv2.INTER_AREA)  # resized, no border
      return cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color), ratio, dw // 2, dh // 2
  
  def dotproduct(v1, v2):
    return sum((a*b) for a, b in zip(v1, v2))
  
  def length(v):
    return math.sqrt(dotproduct(v, v))
  
  def angle(v1, v2):
    return math.acos(dotproduct(v1, v2) / (length(v1) * length(v2)))
  def get_anchors(scale=64):
      '''
      compute anchors
      return:
          u_boxes:tensor([anchor_num,4]) (cx,cy,w,h): real anchors
          boxes:tensor([anchor_num,4]) (x1,y1,x2,y2): crop box for ONet,each with size 80
      '''
      sizes = [float(s) / scale for s in [32]]
      
      aspect_ratios = [(1.,)]
      feature_map_sizes = [int(scale/16)]
      
      num_layers = len(feature_map_sizes)
      u_boxes,boxes = [],[]
      for i in range(num_layers):
          fmsize = feature_map_sizes[i]
          for h,w in itertools.product(range(fmsize),repeat=2):
              cx = float(w)/feature_map_sizes[i]
              cy = float(h)/feature_map_sizes[i]
              
              s = sizes[i]
              for j,ar in enumerate(aspect_ratios[i]):
                  u_boxes.append((cx,cy,float(s)*ar,float(s)*ar))
                  boxes.append((w*16-32,h*16-32,w*16+32,h*16+32))       
      return torch.Tensor(u_boxes),torch.Tensor(boxes).long()
  
  def nms(bboxes,scores,threshold=0.35):
      '''
          bboxes(tensor) [N,4]
          scores(tensor) [N,]
      '''
      x1 = bboxes[:,0]
      y1 = bboxes[:,1]
      x2 = bboxes[:,2]
      y2 = bboxes[:,3]
      areas = (x2-x1) * (y2-y1)
  
      _,order = scores.sort(0,descending=True)
      keep = []
      while order.numel() > 0:
          if order.numel() == 1:
              i = order.item()
          else:
              i = order[0].item()
          keep.append(i) 
  
          if order.numel() == 1:
              break 
  
          xx1 = x1[order[1:]].clamp(min=x1[i]) 
          yy1 = y1[order[1:]].clamp(min=y1[i])
          xx2 = x2[order[1:]].clamp(max=x2[i])
          yy2 = y2[order[1:]].clamp(max=y2[i])
  
          w = (xx2-xx1).clamp(min=0)
          h = (yy2-yy1).clamp(min=0)
          inter = w*h
  
          ovr = inter / (areas[i] + areas[order[1:]] - inter) 
          ids = (ovr<=threshold).nonzero().squeeze()
          if ids.numel() == 0:
              break
          order = order[ids+1] 
      return torch.LongTensor(keep)
      
  def decode_box(loc, size=64):
      variances = [0.1,0.2]
      anchor,crop = get_anchors(scale=size)
      cxcy = loc[:,:2] * variances[0] * anchor[:,2:] + anchor[:,:2]
      wh = torch.exp(loc[:,2:] * variances[1]) * anchor[:,2:]
      boxes = torch.cat([cxcy-wh/2,cxcy+wh/2],1)
      
      return boxes,anchor,crop
      
  def decode_ldmk(ldmk,anchor):
      variances = [0.1,0.2]
      index_x = torch.Tensor([0,2,4,6,8]).long()
      index_y = torch.Tensor([1,3,5,7,9]).long()
      ldmk[:,index_x] = ldmk[:,index_x] * variances[0] * anchor[:,2].view(-1,1) + anchor[:,0].view(-1,1)
      ldmk[:,index_y] = ldmk[:,index_y] * variances[0] * anchor[:,3].view(-1,1) + anchor[:,1].view(-1,1)
      return ldmk
      
  import os
  # list_per = []
  def detect(file, pic = None):
      def change(boxes,ldmks, h, w, pad1):
          index_x = torch.LongTensor([0,2,4,6,8])
          index_y = torch.LongTensor([1,3,5,7,9])
          if h <= w:
              boxes[:,1] = boxes[:,1]*w-pad1
              boxes[:,3] = boxes[:,3]*w-pad1
              boxes[:,0] = boxes[:,0]*w
              boxes[:,2] = boxes[:,2]*w  
              ldmks[:,index_x] = ldmks[:,index_x] * w
              ldmks[:,index_y] = ldmks[:,index_y] * w - torch.Tensor([pad1])
          else:
              boxes[:,1] = boxes[:,1]*h
              boxes[:,3] = boxes[:,3]*h
              boxes[:,0] = boxes[:,0]*h-pad1
              boxes[:,2] = boxes[:,2]*h-pad1
              ldmks[:,index_x] = ldmks[:,index_x] * h - torch.Tensor([pad1])
              ldmks[:,index_y] = ldmks[:,index_y] * h 
          return boxes,ldmks
      if not isinstance(file, np.ndarray):
          im = cv2.imread(file)
      else:
          im = file
      if im is None:
          print("can not open image:", file)
          return
  
      # pad img to square
      h, w,_ = im.shape
  
      dim_diff = np.abs(h - w)
      pad1, pad2 = dim_diff //2, dim_diff - dim_diff // 2
      pad = ((pad1,pad2),(0,0),(0,0)) if h<=w else ((0,0),(pad1, pad2),(0,0))
      img = np.pad(im, pad,'constant', constant_values=128)
      
      #get img_pyramid
      img_scale, img_size = 0,int((img.shape[0]-1)/32)
      while img_size > 0:
          img_scale += 1
          img_size /= 2
          if img_scale == 6:
              break
      img_size = 64
      img_pyramid = []
      t_boxes,t_probs, t_anchors, t_crops, t_which = None, None, None, None, None
      
      for scale in range(4):
          # print('scale:{0} img_size:{1}'.format(scale, img_size))
          input_img = cv2.resize(img,(img_size, img_size))
          img_pyramid.append(input_img.transpose(2,0,1))
          im_tensor = torch.from_numpy(input_img.transpose(2,0,1)).float()
          if use_gpu:
              im_tensor = im_tensor.cuda()
          #get conf and loc(box)
          if use_gpu:
              torch.cuda.synchronize()
          loc,conf = pnet(torch.unsqueeze(im_tensor,0))
          if use_gpu:
              torch.cuda.synchronize()
      
          # print('forward time:{}s'.format(e_t-s_t))        
          loc, conf = loc.detach().cpu(),conf.detach().cpu()
          loc, conf = loc.data.squeeze(0),F.softmax(conf.squeeze(0))
          boxes, anchor, crop = decode_box(loc,size=img_size)
          which_img = torch.tensor([scale]).long().expand((crop.shape[0],))
          
          #add box into stack
          if scale == 0:
              t_boxes, t_confs, t_anchors, t_crops, t_which = boxes, conf, anchor, crop, which_img
          else:
              t_boxes = torch.cat((t_boxes, boxes),0)
              t_confs = torch.cat((t_confs, conf),0)
              t_anchors = torch.cat((t_anchors, anchor),0)
              t_crops = torch.cat((t_crops, crop),0)
              t_which = torch.cat((t_which, which_img),0)
          img_size *= 2
  
      #get right boxes and nms
      t_confs[:,0] = 0.6
      max_conf, labels = t_confs.max(1)
      if labels.long().sum().item() is 0:
          return None
      ids = labels.nonzero().squeeze(1)
      t_boxes, t_confs, t_anchors, t_crops, t_which = t_boxes[ids], t_confs[ids], t_anchors[ids], t_crops[ids], t_which[ids]
      max_conf = max_conf[ids]
      
      keep = nms(t_boxes, max_conf)
      t_boxes, max_conf, t_anchors, t_crops, t_which = t_boxes[keep], max_conf[keep], t_anchors[keep], t_crops[keep], t_which[keep]
  
      t_boxes = t_boxes.detach().numpy()
      max_conf = max_conf.detach().numpy()
      
      #get crop and ldmks
      crop_imgs = []
      for i in range(t_boxes.shape[0]):
          img = img_pyramid[t_which[i]]
          crop = t_crops[i].numpy()
          _,h_,w_ = img.shape
          o_x1,o_y1,o_x2,o_y2 = max(crop[0],0),max(crop[1],0),min(crop[2],w_),min(crop[3],h_)
          c_x1 = 0 if crop[0] >=0 else -crop[0]
          c_y1 = 0 if crop[1] >=0 else -crop[1]
          c_x2 = 64 if crop[2] <= w_ else 64 - (crop[2] - w_)
          c_y2 = 64 if crop[3] <= h_ else 64 - (crop[3] - h_)
          crop_img = np.ones((3,64,64))*128
          np.copyto(crop_img[:,c_y1:c_y2,c_x1:c_x2],img[:,o_y1:o_y2,o_x1:o_x2])
          crop_imgs.append(crop_img)
      crop_imgs = torch.from_numpy(np.array(crop_imgs)).float()
      if use_gpu:
          crop_imgs = crop_imgs.cuda()
      t_ldmks = onet(crop_imgs).detach().cpu()[:,10,:].squeeze(1)
      t_ldmks = decode_ldmk(t_ldmks, t_anchors)
      t_boxes, t_ldmks = change(t_boxes,t_ldmks, h, w, pad1)
      t_faces = []
      for i in range(len(t_boxes)):
          box, prob, ldmk = t_boxes[i], max_conf[i], t_ldmks[i]
          if prob <= 0.7:
              continue
          ldmk_fn = ldmk.reshape(5,2)
          x1 = min(int(box[0])-5, 0)
          x2 = min(int(box[2]) -5, 0)
          y1 = max(int(box[1])+5, im.shape[1])
          y2 = max(int(box[3])+5, im.shape[2])
          face = alignment(im, ldmk_fn)
          cv2.rectangle(im, (x1,y1),(x2,y2), (255,0,0), 1)
          cv2.imwrite('a.png',im)  
          t_faces.append(face)
      return t_boxes, t_faces
  import glob, tqdm
  class Face_Alignt():
      def __init__(self, use_gpu = True):
          pnet,onet = PNet(),ONet() 
          pnet.load_state_dict(torch.load('weight/msos_pnet_rotate.pt',map_location=lambda storage, loc:storage), strict=False) 
          onet.load_state_dict(torch.load('weight/msos_onet_rotate.pt',map_location=lambda storage, loc:storage), strict=False)
          onet.float()
          pnet.eval()
          onet.eval()
          if use_gpu:
              torch.cuda.set_device(0)
              pnet.cuda()
              onet.cuda()
          else:
              torch.set_num_threads(1)
      def align_multi(img, limit=None, min_face_size=30.0):
          return detect(img)
apis: torch.cuda.is_available
apis: torch.unsqueeze numpy.ones torch.load numpy.abs torch.cuda.synchronize torch.exp numpy.copyto torch.set_num_threads torch.tensor numpy.array numpy.pad torch.LongTensor torch.cat torch.Tensor torch.cuda.set_device