text: import torch
  import torchvision.transforms as transforms
  import torch.nn.functional as F
  import torchvision.transforms.functional as tf
  from .verification import evaluate
  
  from datetime import datetime
  import matplotlib.pyplot as plt
  plt.switch_backend('agg')
  import numpy as np
  from PIL import Image
  import bcolz
  import io
  import os
  import math
  import numbers
  import random
  
  
  # Support: ['get_time', 'l2_norm', 'make_weights_for_balanced_classes', 'get_val_pair', 'get_val_data', 'separate_irse_bn_paras', 'separate_resnet_bn_paras', 'warm_up_lr', 'schedule_lr', 'de_preprocess', 'hflip_batch', 'ccrop_batch', 'gen_plot', 'perform_val', 'buffer_val', 'AverageMeter', 'accuracy']
  
  
  def get_time():
      return (str(datetime.now())[:-10]).replace(' ', '-').replace(':', '-')
  
  
  def l2_norm(input, axis = 1):
      norm = torch.norm(input, 2, axis, True)
      output = torch.div(input, norm)
  
      return output
  
  
  def make_weights_for_balanced_classes(images, nclasses):
      '''
          Make a vector of weights for each image in the dataset, based
          on class frequency. The returned vector of weights can be used
          to create a WeightedRandomSampler for a DataLoader to have
          class balancing when sampling for a training batch.
              images - torchvisionDataset.imgs
              nclasses - len(torchvisionDataset.classes)
          https://discuss.pytorch.org/t/balanced-sampling-between-classes-with-torchvision-dataloader/2703/3
      '''
      count = [0] * nclasses
      for item in images:
          count[item[1]] += 1  # item is (img-data, label-id)
      weight_per_class = [0.] * nclasses
      N = float(sum(count))  # total number of images
      for i in range(nclasses):
          weight_per_class[i] = N / float(count[i])
      weight = [0] * len(images)
      for idx, val in enumerate(images):
          weight[idx] = weight_per_class[val[1]]
  
      return weight
  
  
  def get_val_pair(path, name):
      carray = bcolz.carray(rootdir = os.path.join(path, name), mode = 'r')
      issame = np.load('{}/{}_list.npy'.format(path, name))
  
      return carray, issame
  
  
  def get_val_data(data_path):
      lfw, lfw_issame = get_val_pair(data_path, 'lfw')
      cfp_ff, cfp_ff_issame = get_val_pair(data_path, 'cfp_ff')
      cfp_fp, cfp_fp_issame = get_val_pair(data_path, 'cfp_fp')
      agedb_30, agedb_30_issame = get_val_pair(data_path, 'agedb_30')
      calfw, calfw_issame = get_val_pair(data_path, 'calfw')
      cplfw, cplfw_issame = get_val_pair(data_path, 'cplfw')
      vgg2_fp, vgg2_fp_issame = get_val_pair(data_path, 'vgg2_fp')
  
      return lfw, cfp_ff, cfp_fp, agedb_30, calfw, cplfw, vgg2_fp, lfw_issame, cfp_ff_issame, cfp_fp_issame, agedb_30_issame, calfw_issame, cplfw_issame, vgg2_fp_issame
  
  
  def separate_irse_bn_paras(modules):
      if not isinstance(modules, list):
          modules = [*modules.modules()]
      paras_only_bn = []
      paras_wo_bn = []
      for layer in modules:
          if 'distributed' in str(layer.__class__):
              continue
          if 'model' in str(layer.__class__):
              continue
          if 'cbam' in str(layer.__class__):
              continue
          if 'container' in str(layer.__class__):
              continue
          else:
              if 'batchnorm' in str(layer.__class__):
                  paras_only_bn.extend([*layer.parameters()])
              else:
                  paras_wo_bn.extend([*layer.parameters()])
  
      return paras_only_bn, paras_wo_bn
  
  
  def separate_resnet_bn_paras(modules):
      all_parameters = modules.parameters()
      paras_only_bn = []
  
      for pname, p in modules.named_parameters():
          if pname.find('bn') >= 0:
              paras_only_bn.append(p)
  
      paras_only_bn_id = list(map(id, paras_only_bn))
      paras_wo_bn = list(filter(lambda p: id(p) not in paras_only_bn_id, all_parameters))
  
      return paras_only_bn, paras_wo_bn
  
  
  def warm_up_lr(batch, num_batch_warm_up, init_lr, optimizer):
      for params in optimizer.param_groups:
          params['lr'] = batch * init_lr / num_batch_warm_up
      #print(optimizer)
  
  
  def schedule_lr(optimizer):
      for params in optimizer.param_groups:
          params['lr'] /= 10.
  
      print(optimizer)
  
  
  def de_preprocess(tensor):
  
      return tensor * 0.5 + 0.5
  
  
  hflip = transforms.Compose([
              de_preprocess,
              transforms.ToPILImage(),
              transforms.functional.hflip,
              transforms.ToTensor(),
              transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
          ])
  
  
  def hflip_batch(imgs_tensor):
      hfliped_imgs = torch.empty_like(imgs_tensor)
      for i, img_ten in enumerate(imgs_tensor):
          hfliped_imgs[i] = hflip(img_ten)
  
      return hfliped_imgs
  
  
  ccrop = transforms.Compose([
              de_preprocess,
              transforms.ToPILImage(),
              transforms.Resize([128, 128]),  # smaller side resized
              transforms.CenterCrop([112, 112]),
              transforms.ToTensor(),
              transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])
          ])
  
  
  def ccrop_batch(imgs_tensor):
      ccropped_imgs = torch.empty_like(imgs_tensor)
      for i, img_ten in enumerate(imgs_tensor):
          ccropped_imgs[i] = ccrop(img_ten)
  
      return ccropped_imgs
  
  
  def gen_plot(fpr, tpr):
      """Create a pyplot plot and save to buffer."""
      plt.figure()
      plt.xlabel("FPR", fontsize = 14)
      plt.ylabel("TPR", fontsize = 14)
      plt.title("ROC Curve", fontsize = 14)
      plot = plt.plot(fpr, tpr, linewidth = 2)
      buf = io.BytesIO()
      plt.savefig(buf, format = 'jpeg')
      buf.seek(0)
      plt.close()
  
      return buf
  
  
  def perform_val(device, embedding_size, batch_size, backbone, quality, carray, issame, nrof_folds = 10, dataset='', tta = True):
      backbone.eval() # switch to evaluation mode
      quality.eval()
  
      to_image = transforms.ToPILImage()
      if dataset != '':
          os.makedirs(dataset)
      idx = 0
      embeddings = np.zeros([len(carray), embedding_size])
      qualities = np.zeros([len(carray)])
      with torch.no_grad():
          while idx + batch_size <= len(carray):
              batch = torch.tensor(carray[idx:idx + batch_size][:, [2, 1, 0], :, :])
              if tta:
                  ccropped = ccrop_batch(batch)
                  fliped = hflip_batch(ccropped)
                  feature_cropped, fc_cropped = backbone(ccropped.to(device), True)
                  quality_cropped = quality(fc_cropped)
                  feature_fliped, fc_fliped = backbone(fliped.to(device), True)
                  quality_fliped = quality(fc_fliped)
                  emb_batch = feature_cropped.cpu() + feature_fliped.cpu()
                  quality_final = (quality_cropped.cpu() + quality_fliped.cpu()) / 2
                  embeddings[idx:idx + batch_size] = l2_norm(emb_batch)
                  qualities[idx:idx + batch_size] = quality_final[:, 0]
              else:
                  ccropped = ccrop_batch(batch)
                  feature_cropped, fc_cropped = backbone(ccropped.to(device), True)
                  quality_cropped = quality(fc_cropped)
                  embeddings[idx:idx + batch_size] = l2_norm(feature_cropped).cpu()
                  qualities[idx:idx + batch_size] = quality_cropped[:, 0].cpu()
              for i in range(len(qualities[idx:idx + batch_size])):
                  if qualities[idx+i] < 0.3:
                      img = to_image(batch[i] * 0.5 + 0.5)
                      if dataset != '':
                          img.save('{}/{:.4f}.jpg'.format(dataset, qualities[idx+i]))
              idx += batch_size
          if idx < len(carray):
              batch = torch.tensor(carray[idx:])
              if tta:
                  ccropped = ccrop_batch(batch)
                  fliped = hflip_batch(ccropped)
                  feature_cropped, fc_cropped = backbone(ccropped.to(device), True)
                  quality_cropped = quality(fc_cropped)
                  feature_fliped, fc_fliped = backbone(fliped.to(device), True)
                  quality_fliped = quality(fc_fliped)
                  emb_batch = feature_cropped.cpu() + feature_fliped.cpu()
                  quality_final = (quality_cropped.cpu() + quality_fliped.cpu()) / 2
                  embeddings[idx:] = l2_norm(emb_batch)
                  qualities[idx:] = quality_final[:, 0]
              else:
                  ccropped = ccrop_batch(batch)
                  feature_cropped, fc_cropped = backbone(ccropped.to(device), True)
                  quality_cropped = quality(fc_cropped)
                  embeddings[idx:] = l2_norm(feature_cropped).cpu()
                  qualities[idx:] = quality_cropped[:, 0].cpu()
              for i in range(len(qualities[idx:])):
                  if qualities[idx+i] < 0.3:
                      img = to_image(batch[i] * 0.5 + 0.5)
                      if dataset != '':
                          img.save('{}/{:.4f}.jpg'.format(dataset, qualities[idx+i]))
      for quality_thresold in [0.0, 0.1, 0.2, 0.3]:
          tpr, fpr, accuracy, best_thresholds = evaluate(embeddings, qualities, issame, nrof_folds, 0, quality_thresold)
          print(dataset, ":",  quality_thresold, accuracy.mean(), best_thresholds.mean())
      buf = gen_plot(fpr, tpr)
      roc_curve = Image.open(buf)
      roc_curve_tensor = transforms.ToTensor()(roc_curve)
  
      return accuracy.mean(), best_thresholds.mean(), roc_curve_tensor
  
  
  def buffer_val(writer, db_name, acc, best_threshold, roc_curve_tensor, epoch):
      writer.add_scalar('{}_Accuracy'.format(db_name), acc, epoch)
      writer.add_scalar('{}_Best_Threshold'.format(db_name), best_threshold, epoch)
      writer.add_image('{}_ROC_Curve'.format(db_name), roc_curve_tensor, epoch)
  
  
  class AverageMeter(object):
      """Computes and stores the average and current value"""
      def __init__(self):
          self.reset()
  
      def reset(self):
          self.val   = 0
          self.avg   = 0
          self.sum   = 0
          self.count = 0
  
      def update(self, val, n = 1):
          self.val   = val
          self.sum   += val * n
          self.count += n
          self.avg   = self.sum / self.count
  
  
  def accuracy(output, target, topk=(1,)):
      """Computes the precision@k for the specified values of k"""
      maxk = max(topk)
      batch_size = target.size(0)
  
      _, pred = output.topk(maxk, 1, True, True)
      pred    = pred.t()
      correct = pred.eq(target.view(1, -1).expand_as(pred))
  
      res = []
      for k in topk:
          correct_k = correct[:k].reshape(-1).float().sum(0)
          res.append(correct_k.mul_(100.0 / batch_size))
  
      return res
  
  class BottomRandomErasing(object):
      def __init__(self, p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0, inplace=False):
          assert isinstance(value, (numbers.Number, str, tuple, list))
          if (scale[0] > scale[1]) or (ratio[0] > ratio[1]):
              warnings.warn("range should be of kind (min, max)")
          if scale[0] < 0 or scale[1] > 1:
              raise ValueError("range of scale should be between 0 and 1")
          if p < 0 or p > 1:
              raise ValueError("range of random erasing probability should be between 0 and 1")
  
          self.p = p
          self.scale = scale
          self.ratio = ratio
          self.value = value
          self.inplace = inplace
  
      @staticmethod
      def get_params(img, scale, ratio, value=0):
          img_c, img_h, img_w = img.shape
          area = img_h * img_w / 2
  
          for attempt in range(10):
              erase_area = random.uniform(scale[0], scale[1]) * area
              aspect_ratio = random.uniform(ratio[0], ratio[1])
  
              h = int(round(math.sqrt(erase_area * aspect_ratio)))
              w = int(round(math.sqrt(erase_area / aspect_ratio)))
  
              if h < img_h // 2 and w < img_w:
                  i = random.randint(0, img_h // 2 - h) + img_h // 2
                  j = random.randint(0, img_w - w)
                  if isinstance(value, numbers.Number):
                      v = value
                  elif isinstance(value, torch._six.string_classes):
                      v = torch.empty([img_c, h, w], dtype=torch.float32).normal_()
                  elif isinstance(value, (list, tuple)):
                      v = torch.tensor(value, dtype=torch.float32).view(-1, 1, 1).expand(-1, h, w)
                  return i, j, h, w, v
  
          # Return original image
          return 0, 0, img_h, img_w, img
  
      def __call__(self, img):
          if random.uniform(0, 1) < self.p:
              x, y, h, w, v = self.get_params(img, scale=self.scale, ratio=self.ratio, value=self.value)
              return tf.erase(img, x, y, h, w, v, self.inplace)
          return img