text: import numpy as np
  import os
  import imageio
  import math
  import torch
  import importlib
  import re
  import argparse
  from natsort import natsorted
  
  
  
  # convert a tensor into a numpy array
  def tensor2im(image_tensor, bytes=255.0, imtype=np.uint8):
      if image_tensor.dim() == 3:
          image_numpy = image_tensor.cpu().float().numpy()
      else:
          image_numpy = image_tensor[0].cpu().float().numpy()
      image_numpy = (np.transpose(image_numpy, (1, 2, 0)) + 1) / 2.0 * bytes
  
      return image_numpy.astype(imtype)
  
  
  # conver a tensor into a numpy array
  def tensor2array(value_tensor):
      if value_tensor.dim() == 3:
          numpy = value_tensor.view(-1).cpu().float().numpy()
      else:
          numpy = value_tensor[0].view(-1).cpu().float().numpy()
      return numpy
  
  # label color map
  def uint82bin(n, count=8):
      """returns the binary of integer n, count refers to amount of bits"""
      return ''.join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
      
  def labelcolormap(N):
      if N == 19: # CelebAMask-HQ
          cmap = np.array([(0,  0,  0), (204, 0,  0), (76, 153, 0),
                       (204, 204, 0), (51, 51, 255), (204, 0, 204), (0, 255, 255),
                       (51, 255, 255), (102, 51, 0), (255, 0, 0), (102, 204, 0),
                       (255, 255, 0), (0, 0, 153), (0, 0, 204), (255, 51, 153), 
                       (0, 204, 204), (0, 51, 0), (255, 153, 51), (0, 204, 0)], 
                       dtype=np.uint8) 
      else:
          cmap = np.zeros((N, 3), dtype=np.uint8)
          for i in range(N):
              r, g, b = 0, 0, 0
              id = i
              for j in range(7):
                  str_id = uint82bin(id)
                  r = r ^ (np.uint8(str_id[-1]) << (7-j))
                  g = g ^ (np.uint8(str_id[-2]) << (7-j))
                  b = b ^ (np.uint8(str_id[-3]) << (7-j))
                  id = id >> 3
              cmap[i, 0] = r
              cmap[i, 1] = g
              cmap[i, 2] = b
      return cmap
  
  class Colorize(object):
      def __init__(self, n):
          self.cmap = labelcolormap(n)
          self.cmap = torch.from_numpy(self.cmap[:n])
  
      def __call__(self, gray_image):
          if len(gray_image.size()) != 3:
              gray_image = gray_image[0]
              
          size = gray_image.size()
          color_image = torch.ByteTensor(3, size[1], size[2]).fill_(0)
  
          for label in range(0, len(self.cmap)):
              mask = (label == gray_image[0]).cpu()
              color_image[0][mask] = self.cmap[label][0]
              color_image[1][mask] = self.cmap[label][1]
              color_image[2][mask] = self.cmap[label][2]
          color_image = color_image.float()/255.0 * 2 - 1
          return color_image    
  
  
  def make_colorwheel():
          '''
          Generates a color wheel for optical flow visualization as presented in:
              Baker et al. "A Database and Evaluation Methodology for Optical Flow" (ICCV, 2007)
              URL: http://vision.middlebury.edu/flow/flowEval-iccv07.pdf
          According to the C++ source code of Daniel Scharstein
          According to the Matlab source code of Deqing Sun
          '''
          RY = 15
          YG = 6
          GC = 4
          CB = 11
          BM = 13
          MR = 6
  
          ncols = RY + YG + GC + CB + BM + MR
          colorwheel = np.zeros((ncols, 3))
          col = 0
  
          # RY
          colorwheel[0:RY, 0] = 255
          colorwheel[0:RY, 1] = np.floor(255*np.arange(0,RY)/RY)
          col = col+RY
          # YG
          colorwheel[col:col+YG, 0] = 255 - np.floor(255*np.arange(0,YG)/YG)
          colorwheel[col:col+YG, 1] = 255
          col = col+YG
          # GC
          colorwheel[col:col+GC, 1] = 255
          colorwheel[col:col+GC, 2] = np.floor(255*np.arange(0,GC)/GC)
          col = col+GC
          # CB
          colorwheel[col:col+CB, 1] = 255 - np.floor(255*np.arange(CB)/CB)
          colorwheel[col:col+CB, 2] = 255
          col = col+CB
          # BM
          colorwheel[col:col+BM, 2] = 255
          colorwheel[col:col+BM, 0] = np.floor(255*np.arange(0,BM)/BM)
          col = col+BM
          # MR
          colorwheel[col:col+MR, 2] = 255 - np.floor(255*np.arange(MR)/MR)
          colorwheel[col:col+MR, 0] = 255
          return colorwheel
  
  
  class flow2color():
  # code from: https://github.com/tomrunia/OpticalFlow_Visualization
  # MIT License
  #
  # Copyright (c) 2018 Tom Runia
  #
  # Permission is hereby granted, free of charge, to any person obtaining a copy
  # of this software and associated documentation files (the "Software"), to deal
  # in the Software without restriction, including without limitation the rights
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  # copies of the Software, and to permit persons to whom the Software is
  # furnished to do so, subject to conditions.
  #
  # Author: Tom Runia
  # Date Created: 2018-08-03
      def __init__(self):
          self.colorwheel = make_colorwheel()
  
  
      def flow_compute_color(self, u, v, convert_to_bgr=False):
          '''
          Applies the flow color wheel to (possibly clipped) flow components u and v.
          According to the C++ source code of Daniel Scharstein
          According to the Matlab source code of Deqing Sun
          :param u: np.ndarray, input horizontal flow
          :param v: np.ndarray, input vertical flow
          :param convert_to_bgr: bool, whether to change ordering and output BGR instead of RGB
          :return:
          '''
          flow_image = np.zeros((u.shape[0], u.shape[1], 3), np.uint8)
          ncols = self.colorwheel.shape[0]
  
          rad = np.sqrt(np.square(u) + np.square(v))
          a = np.arctan2(-v, -u)/np.pi
          fk = (a+1) / 2*(ncols-1)
          k0 = np.floor(fk).astype(np.int32)
          k1 = k0 + 1
          k1[k1 == ncols] = 0
          f = fk - k0
  
          for i in range(self.colorwheel.shape[1]):
  
              tmp = self.colorwheel[:,i]
              col0 = tmp[k0] / 255.0
              col1 = tmp[k1] / 255.0
              col = (1-f)*col0 + f*col1
  
              idx = (rad <= 1)
              col[idx]  = 1 - rad[idx] * (1-col[idx])
              col[~idx] = col[~idx] * 0.75   # out of range?
  
              # Note the 2-i => BGR instead of RGB
              ch_idx = 2-i if convert_to_bgr else i
              flow_image[:,:,ch_idx] = np.floor(255 * col)
  
          return flow_image
  
  
      def __call__(self, flow_uv, clip_flow=None, convert_to_bgr=False):
          '''
          Expects a two dimensional flow image of shape [H,W,2]
          According to the C++ source code of Daniel Scharstein
          According to the Matlab source code of Deqing Sun
          :param flow_uv: np.ndarray of shape [H,W,2]
          :param clip_flow: float, maximum clipping value for flow
          :return:
          '''
          if len(flow_uv.size()) != 3:
              flow_uv = flow_uv[0]
          flow_uv = flow_uv.permute(1,2,0).cpu().detach().numpy()    
  
          assert flow_uv.ndim == 3, 'input flow must have three dimensions'
          assert flow_uv.shape[2] == 2, 'input flow must have shape [H,W,2]'
  
          if clip_flow is not None:
              flow_uv = np.clip(flow_uv, 0, clip_flow)
  
          u = flow_uv[:,:,1]
          v = flow_uv[:,:,0]
  
  
          rad = np.sqrt(np.square(u) + np.square(v))
          rad_max = np.max(rad)
  
          epsilon = 1e-5
          u = u / (rad_max + epsilon)
          v = v / (rad_max + epsilon)
          image = self.flow_compute_color(u, v, convert_to_bgr) 
          image = torch.tensor(image).float().permute(2,0,1)/255.0 * 2 - 1
          return image
  
  
  def save_image(image_numpy, image_path):
      if image_numpy.shape[2] == 1:
          image_numpy = image_numpy.reshape(image_numpy.shape[0], image_numpy.shape[1])
  
      imageio.imwrite(image_path, image_numpy)
  
  
  def mkdirs(paths):
      if isinstance(paths, list) and not isinstance(paths, str):
          for path in paths:
              mkdir(path)
      else:
          mkdir(paths)
  
  
  def mkdir(path):
      if not os.path.exists(path):
          os.makedirs(path)
  
  
  def find_class_in_module(target_cls_name, module):
      target_cls_name = target_cls_name.replace('_', '').lower()
      clslib = importlib.import_module(module)
      cls = None
      for name, clsobj in clslib.__dict__.items():
          if name.lower() == target_cls_name:
              cls = clsobj
  
      if cls is None:
          print("In %s, there should be a class whose name matches %s in lowercase without underscore(_)" % (module, target_cls_name))
          exit(0)
  
      return cls        
  
  
  
  def atoi(text):
      return int(text) if text.isdigit() else text
  
  
  def natural_keys(text):
      '''
      alist.sort(key=natural_keys) sorts in human order
      http://nedbatchelder.com/blog/200712/human_sorting.html
      (See Toothy's implementation in the comments)
      '''
      return [atoi(c) for c in re.split('(\d+)', text)]
  
  
  def natural_sort(items):
      items.sort(key=natural_keys)    
  
  class StoreDictKeyPair(argparse.Action):
       def __call__(self, parser, namespace, values, option_string=None):
           my_dict = {}
           for kv in values.split(","):
               # print(kv)
               k,v = kv.split("=")
               my_dict[k] = int(v)
           setattr(namespace, self.dest, my_dict)   
  
  class StoreList(argparse.Action):
       def __call__(self, parser, namespace, values, option_string=None):
          my_list = [int(item) for item in values.split(',')]
          setattr(namespace, self.dest, my_list)   
  #           
  def get_iteration(dir_name, file_name, net_name):
      if os.path.exists(os.path.join(dir_name, file_name)) is False:
          return None
      if 'latest' in file_name:
          gen_models = [os.path.join(dir_name, f) for f in os.listdir(dir_name) if
                os.path.isfile(os.path.join(dir_name, f)) and (not 'latest' in f) and ('_net_'+net_name+'.pth' in f)]
          if gen_models == []:
              return 0
          model_name = os.path.basename(natsorted(gen_models)[-1])
      else:
          model_name = file_name
      iterations = int(model_name.replace('_net_'+net_name+'.pth', ''))
      return iterations