  # Gaussian Blur is here as it is only used for losses.
  # It was previously kept in lib/model/masks but the import of keras backend
  # breaks plaidml
  def gaussian_blur(radius=2.0):
      """ From https://github.com/iperov/DeepFaceLab
          Used for blurring mask in training """
      def gaussian(var_x, radius, sigma):
          return np.exp(-(float(var_x) - float(radius)) ** 2 / (2 * sigma ** 2))
  
      def make_kernel(sigma):
          kernel_size = max(3, int(2 * 2 * sigma + 1))
          mean = np.floor(0.5 * kernel_size)
 
</s> remove     def make_kernel(sigma):
 </s> add     def _make_kernel(sigma):
        """ Make the gaussian kernel. """ </s> remove         kernel_1d = np.array([gaussian(x, mean, sigma) for x in range(kernel_size)])
 </s> add         kernel_1d = np.array([_gaussian(x, mean, sigma) for x in range(kernel_size)]) </s> remove def ms_ssim_calc(img1, img2, max_val=1.0, power_factors=(0.0517, 0.3295, 0.3462, 0.2726)):
    """
    Computes the MS-SSIM between img1 and img2.
    This function assumes that `img1` and `img2` are image batches, i.e. the last
    three dimensions are [height, width, channels].
    Note: The true SSIM is only defined on grayscale.  This function does not
    perform any colorspace transform.  (If input is already YUV, then it will
    compute YUV SSIM average.)
    Original paper: Wang, Zhou, Eero P. Simoncelli, and Alan C. Bovik. "Multiscale
    structural similarity for image quality assessment." Signals, Systems and
    Computers, 2004.
    Arguments:
    img1: First image batch.
    img2: Second image batch. Must have the same rank as img1.
    max_val: The dynamic range of the images (i.e., the difference between the
      maximum the and minimum allowed values).
    power_factors: Iterable of weights for each of the scales. The number of
      scales used is the length of the list. Index 0 is the unscaled
      resolution's weight and each increasing scale corresponds to the image
      being downsampled by 2.  Defaults to (0.0448, 0.2856, 0.3001, 0.2363,
      0.1333), which are the values obtained in the original paper.
    Returns:
    A tensor containing an MS-SSIM value for each image in batch.  The values
    are in range [0, 1].  Returns a tensor with shape:
    broadcast(img1.shape[:-3], img2.shape[:-3]).
    """

    def _verify_compatible_image_shapes(img1, img2):
        """
        Checks if two image tensors are compatible for applying SSIM or PSNR.
        This function checks if two sets of images have ranks at least 3, and if the
        last three dimensions match.
        Args:
        img1: Tensor containing the first image batch.
        img2: Tensor containing the second image batch.
        Returns:
        A tuple containing: the first tensor shape, the second tensor shape, and a
        list of control_flow_ops.Assert() ops implementing the checks.
        Raises:
        ValueError: When static shape check fails.
        """
        shape1 = img1.get_shape().with_rank_at_least(3)
        shape2 = img2.get_shape().with_rank_at_least(3)
        shape1[-3:].assert_is_compatible_with(shape2[-3:])

        if shape1.ndims is not None and shape2.ndims is not None:
            for dim1, dim2 in zip(reversed(shape1[:-3]), reversed(shape2[:-3])):
                if not (dim1 == 1 or dim2 == 1 or dim1.is_compatible_with(dim2)):
                    raise ValueError('Two images are not compatible: %s and %s' % (shape1, shape2))

        # Now assign shape tensors.
        shape1, shape2 = tf.shape_n([img1, img2])

        # TODO(sjhwang): Check if shape1[:-3] and shape2[:-3] are broadcastable.
        checks = []
        checks.append(tf.Assert(tf.greater_equal(tf.size(shape1), 3),
                                [shape1, shape2], summarize=10))
        checks.append(tf.Assert(tf.reduce_all(tf.equal(shape1[-3:], shape2[-3:])),
                                [shape1, shape2], summarize=10))

        return shape1, shape2, checks

    def _ssim_per_channel(img1, img2, max_val=1.0):
        """
        Computes SSIM index between img1 and img2 per color channel.
        This function matches the standard SSIM implementation from:
        Wang, Z., Bovik, A. C., Sheikh, H. R., & Simoncelli, E. P. (2004). Image
        quality assessment: from error visibility to structural similarity. IEEE
        transactions on image processing.
        Details:
        - 11x11 Gaussian filter of width 1.5 is used.
        - k1 = 0.01, k2 = 0.03 as in the original paper.
        Args:
        img1: First image batch.
        img2: Second image batch.
        max_val: The dynamic range of the images (i.e., the difference between the
          maximum the and minimum allowed values).
        Returns:
        A pair of tensors containing and channel-wise SSIM and contrast-structure
        values. The shape is [..., channels].
        """

        def _fspecial_gauss(size, sigma):
            """ Function to mimic the 'fspecial' gaussian MATLAB function. """

            size = tf.convert_to_tensor(size, 'int32')
            sigma = tf.convert_to_tensor(sigma)
            coords = tf.cast(tf.range(size), sigma.dtype)
            coords -= tf.cast(size - 1, sigma.dtype) / 2.0

            gauss = tf.square(coords)
            gauss *= -0.5 / tf.square(sigma)
            gauss = tf.reshape(gauss, shape=[1, -1]) + tf.reshape(gauss, shape=[-1, 1])
            gauss = tf.reshape(gauss, shape=[1, -1])  # For tf.nn.softmax().
            gauss = tf.nn.softmax(gauss)
            return tf.reshape(gauss, shape=[size, size, 1, 1])

        def _ssim_helper(img1, img2, max_val, kernel, compensation=1.):
            """
            Helper function for computing SSIM.
            SSIM estimates covariances with weighted sums.  The default parameters
            use a biased estimate of the covariance:
            Suppose `reducer` is a weighted sum, then the mean estimators are
            mu_x = sum_i w_i x_i,
            mu_y = sum_i w_i y_i,
            where w_i's are the weighted-sum weights, and covariance estimator is
            cov_{xy} = sum_i w_i (x_i - mu_x) (y_i - mu_y)
            with assumption sum_i w_i = 1. This covariance estimator is biased, since
            E[cov_{xy}] = (1 - sum_i w_i ^ 2) Cov(X, Y).
            For SSIM measure with unbiased covariance estimators, pass as `compensation`
            argument (1 - sum_i w_i ^ 2).
            Arguments:
            img1: First set of images.
            img2: Second set of images.
            reducer: Function that computes 'local' averages from set of images.
              For non-covolutional version, this is usually tf.reduce_mean(img1, [1, 2]),
              and for convolutional version, this is usually tf.nn.avg_pool or
              tf.nn.conv2d with weighted-sum kernel.
            max_val: The dynamic range (i.e., the difference between the maximum
              possible allowed value and the minimum allowed value).
            compensation: Compensation factor. See above.
            Returns:
            A pair containing the luminance measure, and the contrast-structure measure.
            """

            def reducer(img1, kernel):
                shape = tf.shape(img1)
                img1 = tf.reshape(img1, shape=tf.concat([[-1], shape[-3:]], 0))
                img2 = tf.nn.depthwise_conv2d(img1, kernel, strides=[1, 1, 1, 1], padding='VALID')
                return tf.reshape(img2, tf.concat([shape[:-3], tf.shape(img2)[1:]], 0))

            c_one = (0.01 * max_val) ** 2
            c_two = ((0.03 * max_val)) ** 2 * compensation

            # SSIM luminance measure is
            # (2 * mu_x * mu_y + c_one) / (mu_x ** 2 + mu_y ** 2 + c_one).
            mean0 = reducer(img1, kernel)
            mean1 = reducer(img2, kernel)
            num0 = mean0 * mean1 * 2.
            den0 = tf.square(mean0) + tf.square(mean1)
            luminance = (num0 + c_one) / (den0 + c_one)

            # SSIM contrast-structure measure is
            #   (2 * cov_{xy} + c_two) / (cov_{xx} + cov_{yy} + c_two).
            # Note that `reducer` is a weighted sum with weight w_k, \sum_i w_i = 1, then
            #   cov_{xy} = \sum_i w_i (x_i - \mu_x) (y_i - \mu_y)
            #          = \sum_i w_i x_i y_i - (\sum_i w_i x_i) (\sum_j w_j y_j).
            num1 = reducer(img1 * img2, kernel) * 2.0
            den1 = reducer(tf.square(img1) + tf.square(img2), kernel)
            c_s = (num1 - num0 + c_two) / (den1 - den0 + c_two)

            # SSIM score is the product of the luminance and contrast-structure measures.
            return luminance, c_s

        filter_size = tf.constant(9, dtype='int32')  # changed from 11 to 9 due
        filter_sigma = tf.constant(1.5, dtype=img1.dtype)

        shape1, shape2 = tf.shape_n([img1, img2])
        checks = [tf.Assert(tf.reduce_all(tf.greater_equal(shape1[-3:-1], filter_size)),
                            [shape1, filter_size], summarize=8),
                  tf.Assert(tf.reduce_all(tf.greater_equal(shape2[-3:-1], filter_size)),
                            [shape2, filter_size], summarize=8)]

        # Enforce the check to run before computation.
        with tf.control_dependencies(checks):
            img1 = tf.identity(img1)

        # TODO(sjhwang): Try to cache kernels and compensation factor.
        kernel = _fspecial_gauss(filter_size, filter_sigma)
        kernel = tf.tile(kernel, multiples=[1, 1, shape1[-1], 1])

        # The correct compensation factor is `1.0 - tf.reduce_sum(tf.square(kernel))`,
        # but to match MATLAB implementation of MS-SSIM, we use 1.0 instead.
        compensation = 1.

        # TODO(sjhwang): Try FFT.
        # TODO(sjhwang): Gaussian kernel is separable in space. Consider applying
        #   1-by-n and n-by-1 Gaussain filters instead of an n-by-n filter.

        luminance, c_s = _ssim_helper(img1, img2, max_val, kernel, compensation)

        # Average over the second and the third from the last: height, width.
        axes = tf.constant([-3, -2], dtype='int32')
        ssim_val = tf.reduce_mean(luminance * c_s, axes)
        c_s = tf.reduce_mean(c_s, axes)
        return ssim_val, c_s

    def do_pad(images, remainder):
        padding = tf.expand_dims(remainder, -1)
        padding = tf.pad(padding, [[1, 0], [1, 0]])
        return [tf.pad(x, padding, mode='SYMMETRIC') for x in images]

    # Shape checking.
    shape1 = img1.get_shape().with_rank_at_least(3)
    shape2 = img2.get_shape().with_rank_at_least(3)
    shape1[-3:].merge_with(shape2[-3:])

    with tf.name_scope(None, 'MS-SSIM', [img1, img2]):
        shape1, shape2, checks = _verify_compatible_image_shapes(img1, img2)
    with tf.control_dependencies(checks):
        img1 = tf.identity(img1)

    # Need to convert the images to float32.  Scale max_val accordingly so that
    # SSIM is computed correctly.
    max_val = tf.cast(max_val, img1.dtype)
    max_val = tf.image.convert_image_dtype(max_val, 'float32')
    img1 = tf.image.convert_image_dtype(img1, 'float32')
    img2 = tf.image.convert_image_dtype(img2, 'float32')

    imgs = [img1, img2]
    shapes = [shape1, shape2]

    # img1 and img2 are assumed to be a (multi-dimensional) batch of
    # 3-dimensional images (height, width, channels). `heads` contain the batch
    # dimensions, and `tails` contain the image dimensions.
    heads = [s[:-3] for s in shapes]
    tails = [s[-3:] for s in shapes]

    divisor = [1, 2, 2, 1]
    divisor_tensor = tf.constant(divisor[1:], dtype='int32')

    mc_s = []
    for k in range(len(power_factors)):
        with tf.name_scope(None, 'Scale%d' % k, imgs):
            if k > 0:
                # Avg pool takes rank 4 tensors. Flatten leading dimensions.
                zipped = zip(imgs, tails)
                flat_imgs = [tf.reshape(x, tf.concat([[-1], t], 0)) for x, t in zipped]
                remainder = tails[0] % divisor_tensor
                need_padding = tf.reduce_any(tf.not_equal(remainder, 0))
                padded = tf.cond(need_padding,
                                 lambda: do_pad(flat_imgs, remainder), lambda: flat_imgs)

                downscaled = [tf.nn.avg_pool(x,
                                             ksize=divisor,
                                             strides=divisor,
                                             padding='VALID') for x in padded]
                tails = [x[1:] for x in tf.shape_n(downscaled)]
                zipper = zip(downscaled, heads, tails)
                imgs = [tf.reshape(x, tf.concat([h, t], 0)) for x, h, t in zipper]

            # Overwrite previous ssim value since we only need the last one.
            ssim_per_channel, c_s = _ssim_per_channel(*imgs, max_val=max_val)
            mc_s.append(tf.nn.relu(c_s))

    # Remove the c_s score for the last scale. In the MS-SSIM calculation,
    # we use the l(p) at the highest scale. l(p) * c_s(p) is ssim(p).
    mc_s.pop()  # Remove the c_s score for the last scale.
    mcs_and_ssim = tf.stack(mc_s + [tf.nn.relu(ssim_per_channel)], axis=-1)
    # Take weighted geometric mean across the scale axis.
    ms_ssim = tf.reduce_prod(tf.pow(mcs_and_ssim, power_factors), [-1])

    return tf.reduce_mean(ms_ssim, [-1])  # Avg over color channels.


def ms_ssim_loss(y_true, y_pred):
    """ Keras loss function for MS-SSIM """
    expanded = K.expand_dims(1.0 - ms_ssim_calc(y_true, y_pred), axis=-1)
    loss = K.expand_dims(expanded, axis=-1)
    # need to expand to [1,height,width] dimensions for Keras. modify to not be hard-coded
    return K.tile(loss, [1, 64, 64])


 </s> add  </s> remove         self.c_1 = (self.k1 * self.max_value) ** 2
        self.c_2 = (self.k2 * self.max_value) ** 2
 </s> add         self.c_1 = (self.k_1 * self.max_value) ** 2
        self.c_2 = (self.k_2 * self.max_value) ** 2 </s> remove         self.k1 = k1
        self.k2 = k2
 </s> add         self.k_1 = k_1
        self.k_2 = k_2
    """ Apply gaussian blur to an input.

    Used for blurring mask in training.

    Parameters
    ----------
    radius: float, optional
        The kernel radius for applying gaussian blur. Default: `2.0`

    Returns
    -------
    tensor
        The input tensor with gaussian blurring applied

    References
    ----------
    https://github.com/iperov/DeepFaceLab
    """
    def _gaussian(var_x, radius, sigma):
        """ Obtain the gaussian kernel. """
  # Gaussian Blur is here as it is only used for losses.
  # It was previously kept in lib/model/masks but the import of keras backend
  # breaks plaidml
  def gaussian_blur(radius=2.0):
    """ Apply gaussian blur to an input.

    Used for blurring mask in training.

    Parameters
    ----------
    radius: float, optional
        The kernel radius for applying gaussian blur. Default: `2.0`

    Returns
    -------
    tensor
        The input tensor with gaussian blurring applied

    References
    ----------
    https://github.com/iperov/DeepFaceLab
    """
    def _gaussian(var_x, radius, sigma):
        """ Obtain the gaussian kernel. """
    """ Apply gaussian blur to an input.

    Used for blurring mask in training.

    Parameters
    ----------
    radius: float, optional
        The kernel radius for applying gaussian blur. Default: `2.0`

    Returns
    -------
    tensor
        The input tensor with gaussian blurring applied

    References
    ----------
    https://github.com/iperov/DeepFaceLab
    """
    def _gaussian(var_x, radius, sigma):
        """ Obtain the gaussian kernel. """
    """ Apply gaussian blur to an input.

    Used for blurring mask in training.

    Parameters
    ----------
    radius: float, optional
        The kernel radius for applying gaussian blur. Default: `2.0`

    Returns
    -------
    tensor
        The input tensor with gaussian blurring applied

    References
    ----------
    https://github.com/iperov/DeepFaceLab
    """
    def _gaussian(var_x, radius, sigma):
        """ Obtain the gaussian kernel. """
          return np.exp(-(float(var_x) - float(radius)) ** 2 / (2 * sigma ** 2))
  
      def make_kernel(sigma):
          kernel_size = max(3, int(2 * 2 * sigma + 1))
          mean = np.floor(0.5 * kernel_size)
keep keep keep keep 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