      return inner_loss
  # <<< END: from Dfaker >>> #
  
  
  # <<< START: from DFL >>> #
  def style_loss(gaussian_blur_radius=0.0, loss_weight=1.0, wnd_size=0, step_size=1):
      """ Style Loss from DeepFaceLab
          https://github.com/iperov/DeepFaceLab """
  
      if gaussian_blur_radius > 0.0:
          gblur = gaussian_blur(gaussian_blur_radius)
  
      def std(content, style, loss_weight):
          content_nc = K.int_shape(content)[-1]
          style_nc = K.int_shape(style)[-1]
          if content_nc != style_nc:
              raise Exception("style_loss() content_nc != style_nc")
  
          axes = [1, 2]
          c_mean, c_var = K.mean(content, axis=axes, keepdims=True), K.var(content,
                                                                           axis=axes,
                                                                           keepdims=True)
          s_mean, s_var = K.mean(style, axis=axes, keepdims=True), K.var(style,
                                                                         axis=axes,
                                                                         keepdims=True)
          c_std, s_std = K.sqrt(c_var + 1e-5), K.sqrt(s_var + 1e-5)
  
          mean_loss = K.sum(K.square(c_mean-s_mean))
          std_loss = K.sum(K.square(c_std-s_std))
  
          return (mean_loss + std_loss) * (loss_weight / float(content_nc))
  
      def func(target, style):
          if wnd_size == 0:
              if gaussian_blur_radius > 0.0:
                  return std(gblur(target), gblur(style), loss_weight=loss_weight)
              return std(target, style, loss_weight=loss_weight)
  
          # currently unused
          if K.backend() == "plaidml.keras.backend":
              logger.warning("plaidML backend does not support style_loss. Disabling")
              return 0
          shp = K.int_shape(target)[1]
          k = (shp - wnd_size) // step_size + 1
          if gaussian_blur_radius > 0.0:
              target, style = gblur(target), gblur(style)
          target = tf.image.extract_image_patches(target,
                                                  [1, k, k, 1],
                                                  [1, 1, 1, 1],
                                                  [1, step_size, step_size, 1],
                                                  "VALID")
          style = tf.image.extract_image_patches(style,
                                                 [1, k, k, 1],
                                                 [1, 1, 1, 1],
                                                 [1, step_size, step_size, 1],
                                                 "VALID")
          return std(target, style, loss_weight)
  
      return func
  # <<< END: from DFL >>> #
  
  
  # <<< START: from Shoanlu GAN >>> #
  def first_order(var_x, axis=1):
      """ First Order Function from Shoanlu GAN """
      img_nrows = var_x.shape[1]
      img_ncols = var_x.shape[2]
      if axis == 1:
          return K.abs(var_x[:, :img_nrows - 1, :img_ncols - 1, :] - var_x[:, 1:, :img_ncols - 1, :])
      if axis == 2:
          return K.abs(var_x[:, :img_nrows - 1, :img_ncols - 1, :] - var_x[:, :img_nrows - 1, 1:, :])
      return None
  
  
  def calc_loss(pred, target, loss='l2'):
      """ Calculate Loss from Shoanlu GAN """
      if loss.lower() == "l2":
          return K.mean(K.square(pred - target))
      if loss.lower() == "l1":
          return K.mean(K.abs(pred - target))
      if loss.lower() == "cross_entropy":
          return -K.mean(K.log(pred + K.epsilon()) * target +
                         K.log(1 - pred + K.epsilon()) * (1 - target))
      raise ValueError('Recieve an unknown loss type: {}.'.format(loss))
  
  
  def cyclic_loss(net_g1, net_g2, real1):
      """ Cyclic Loss Function from Shoanlu GAN """
      fake2 = net_g2(real1)[-1]  # fake2 ABGR
      fake2 = Lambda(lambda x: x[:, :, :, 1:])(fake2)  # fake2 BGR
      cyclic1 = net_g1(fake2)[-1]  # cyclic1 ABGR
      cyclic1 = Lambda(lambda x: x[:, :, :, 1:])(cyclic1)  # cyclic1 BGR
      loss = calc_loss(cyclic1, real1, loss='l1')
      return loss
  
  
  def adversarial_loss(net_d, real, fake_abgr, distorted, gan_training="mixup_LSGAN", **weights):
      """ Adversarial Loss Function from Shoanlu GAN """
      alpha = Lambda(lambda x: x[:, :, :, :1])(fake_abgr)
      fake_bgr = Lambda(lambda x: x[:, :, :, 1:])(fake_abgr)
      fake = alpha * fake_bgr + (1-alpha) * distorted
  
      if gan_training == "mixup_LSGAN":
          dist = Beta(0.2, 0.2)
          lam = dist.sample()
          mixup = lam * concatenate([real, distorted]) + (1 - lam) * concatenate([fake, distorted])
          pred_fake = net_d(concatenate([fake, distorted]))
          pred_mixup = net_d(mixup)
          loss_d = calc_loss(pred_mixup, lam * K.ones_like(pred_mixup), "l2")
          loss_g = weights['w_D'] * calc_loss(pred_fake, K.ones_like(pred_fake), "l2")
          mixup2 = lam * concatenate([real,
                                      distorted]) + (1 - lam) * concatenate([fake_bgr,
                                                                             distorted])
          pred_fake_bgr = net_d(concatenate([fake_bgr, distorted]))
          pred_mixup2 = net_d(mixup2)
          loss_d += calc_loss(pred_mixup2, lam * K.ones_like(pred_mixup2), "l2")
          loss_g += weights['w_D'] * calc_loss(pred_fake_bgr, K.ones_like(pred_fake_bgr), "l2")
      elif gan_training == "relativistic_avg_LSGAN":
          real_pred = net_d(concatenate([real, distorted]))
          fake_pred = net_d(concatenate([fake, distorted]))
          loss_d = K.mean(K.square(real_pred - K.ones_like(fake_pred)))/2
          loss_d += K.mean(K.square(fake_pred - K.zeros_like(fake_pred)))/2
          loss_g = weights['w_D'] * K.mean(K.square(fake_pred - K.ones_like(fake_pred)))
  
          fake_pred2 = net_d(concatenate([fake_bgr, distorted]))
          loss_d += K.mean(K.square(real_pred - K.mean(fake_pred2, axis=0) -
                                    K.ones_like(fake_pred2)))/2
          loss_d += K.mean(K.square(fake_pred2 - K.mean(real_pred, axis=0) -
                                    K.zeros_like(fake_pred2)))/2
          loss_g += weights['w_D'] * K.mean(K.square(real_pred - K.mean(fake_pred2, axis=0) -
                                                     K.zeros_like(fake_pred2)))/2
          loss_g += weights['w_D'] * K.mean(K.square(fake_pred2 - K.mean(real_pred, axis=0) -
                                                     K.ones_like(fake_pred2)))/2
      else:
          raise ValueError("Receive an unknown GAN training method: {gan_training}")
      return loss_d, loss_g
  
  
  def reconstruction_loss(real, fake_abgr, mask_eyes, model_outputs, **weights):
      """ Reconstruction Loss Function from Shoanlu GAN """
      alpha = Lambda(lambda x: x[:, :, :, :1])(fake_abgr)
      fake_bgr = Lambda(lambda x: x[:, :, :, 1:])(fake_abgr)
  
      loss_g = weights['w_recon'] * calc_loss(fake_bgr, real, "l1")
      loss_g += weights['w_eyes'] * K.mean(K.abs(mask_eyes*(fake_bgr - real)))
  
      for out in model_outputs[:-1]:
          out_size = out.get_shape().as_list()
          resized_real = tf.image.resize_images(real, out_size[1:3])
          loss_g += weights['w_recon'] * calc_loss(out, resized_real, "l1")
      return loss_g
  
  
  def edge_loss(real, fake_abgr, mask_eyes, **weights):
      """ Edge Loss Function from Shoanlu GAN """
      alpha = Lambda(lambda x: x[:, :, :, :1])(fake_abgr)
      fake_bgr = Lambda(lambda x: x[:, :, :, 1:])(fake_abgr)
  
      loss_g = weights['w_edge'] * calc_loss(first_order(fake_bgr, axis=1),
                                             first_order(real, axis=1), "l1")
      loss_g += weights['w_edge'] * calc_loss(first_order(fake_bgr, axis=2),
                                              first_order(real, axis=2), "l1")
      shape_mask_eyes = mask_eyes.get_shape().as_list()
      resized_mask_eyes = tf.image.resize_images(mask_eyes,
                                                 [shape_mask_eyes[1]-1, shape_mask_eyes[2]-1])
      loss_g += weights['w_eyes'] * K.mean(K.abs(resized_mask_eyes *
                                                 (first_order(fake_bgr, axis=1) -
                                                  first_order(real, axis=1))))
      loss_g += weights['w_eyes'] * K.mean(K.abs(resized_mask_eyes *
                                                 (first_order(fake_bgr, axis=2) -
                                                  first_order(real, axis=2))))
      return loss_g
  
  
  def perceptual_loss(real, fake_abgr, distorted, vggface_feats, **weights):
      """ Perceptual Loss Function from Shoanlu GAN """
      alpha = Lambda(lambda x: x[:, :, :, :1])(fake_abgr)
      fake_bgr = Lambda(lambda x: x[:, :, :, 1:])(fake_abgr)
      fake = alpha * fake_bgr + (1-alpha) * distorted
  
      def preprocess_vggface(var_x):
          var_x = (var_x + 1.) / 2. * 255.  # channel order: BGR
          var_x -= [91.4953, 103.8827, 131.0912]
          return var_x
  
      real_sz224 = tf.image.resize_images(real, [224, 224])
      real_sz224 = Lambda(preprocess_vggface)(real_sz224)
      dist = Beta(0.2, 0.2)
      lam = dist.sample()  # use mixup trick here to reduce foward pass from 2 times to 1.
      mixup = lam*fake_bgr + (1-lam)*fake
      fake_sz224 = tf.image.resize_images(mixup, [224, 224])
      fake_sz224 = Lambda(preprocess_vggface)(fake_sz224)
      real_feat112, real_feat55, real_feat28, real_feat7 = vggface_feats(real_sz224)
      fake_feat112, fake_feat55, fake_feat28, fake_feat7 = vggface_feats(fake_sz224)
  
      # Apply instance norm on VGG(ResNet) features
      # From MUNIT https://github.com/NVlabs/MUNIT
      loss_g = 0
  
      def instnorm():
          return InstanceNormalization()
  
      loss_g += weights['w_pl'][0] * calc_loss(instnorm()(fake_feat7),
                                               instnorm()(real_feat7), "l2")
      loss_g += weights['w_pl'][1] * calc_loss(instnorm()(fake_feat28),
                                               instnorm()(real_feat28), "l2")
      loss_g += weights['w_pl'][2] * calc_loss(instnorm()(fake_feat55),
                                               instnorm()(real_feat55), "l2")
      loss_g += weights['w_pl'][3] * calc_loss(instnorm()(fake_feat112),
                                               instnorm()(real_feat112), "l2")
      return loss_g
  # <<< END: from Shoanlu GAN >>> #
  
  
  def generalized_loss(y_true, y_pred, alpha=1.0, beta=1.0/255.0):
      """
      generalized function used to return a large variety of mathematical loss functions
      primary benefit is smooth, differentiable version of L1 loss
  
 
</s> remove     def diff_xx(img):
 </s> add     def _diff_xx(img):
        """ X-X Difference """ </s> remove     def diff_yy(img):
 </s> add     def _diff_yy(img):
        """ Y-Y Difference """ </s> remove     def diff_y(img):
 </s> add     def _diff_y(img):
        """ Y Difference """ </s> remove     def diff_x(img):
 </s> add     def _diff_x(img):
        """ X Difference """ </s> remove     """
    generalized function used to return a large variety of mathematical loss functions
    primary benefit is smooth, differentiable version of L1 loss

    Barron, J. A More General Robust Loss Function
    https://arxiv.org/pdf/1701.03077.pdf
    Parameters:
        alpha: penalty factor. larger number give larger weight to large deviations
        beta: scale factor used to adjust to the input scale (i.e. inputs of mean 1e-4 or 256 )
    Return:
        a loss value from the results of function(y_pred - y_true)
    Example:
        a=1.0, x>>c , c=1.0/255.0 will give a smoothly differentiable version of L1 / MAE loss
        a=1.999999 (lim as a->2), beta=1.0/255.0 will give L2 / RMSE loss
 </s> add     """ Generalized function used to return a large variety of mathematical loss functions.

    The primary benefit is a smooth, differentiable version of L1 loss.

    Parameters
    ----------
    y_true: tensor or variable
        The ground truth value
    y_pred: tensor or variable
        The predicted value
    alpha: float, optional
        Penalty factor. Larger number give larger weight to large deviations. Default: `1.0`
    beta: float, optional
        Scale factor used to adjust to the input scale (i.e. inputs of mean `1e-4` or `256`).
        Default: `1.0/255.0`

    Returns
    -------
    tensor
        The loss value from the results of function(y_pred - y_true)

    References
    ----------
    Barron, J. A More General Robust Loss Function - https://arxiv.org/pdf/1701.03077.pdf

    Example
    -------
    >>> a=1.0, x>>c , c=1.0/255.0  # will give a smoothly differentiable version of L1 / MAE loss
    >>> a=1.999999 (limit as a->2), beta=1.0/255.0 # will give L2 / RMSE loss
      return inner_loss
  # <<< END: from Dfaker >>> #
  
  


















































































































































































































  def generalized_loss(y_true, y_pred, alpha=1.0, beta=1.0/255.0):
      """
      generalized function used to return a large variety of mathematical loss functions
      primary benefit is smooth, differentiable version of L1 loss
  
keep keep keep keep replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace replace keep keep keep keep keep
 Simple backend unit tests (#1020)

* Add simple backend tests for lib.model
* Document lib.model
* Fix GMSD Loss for AMD
* Remove obsolete code from lib.model 
lib/model/losses.py