      return output
  
  
  def gmsd_loss(y_true, y_pred):
      """
      Improved image quality metric over MS-SSIM with easier calc
      http://www4.comp.polyu.edu.hk/~cslzhang/IQA/GMSD/GMSD.htm
      https://arxiv.org/ftp/arxiv/papers/1308/1308.3052.pdf
      """
  
      true_edge = scharr_edges(y_true, True)
 
</s> remove     """
 </s> add  </s> remove         output = tf.atan(K.squeeze(output[:, :, :, :, 0] / output[:, :, :, :, 1]))
    # magnitude of edges -- unified x & y edges don't work well with NN

 </s> add         output = tf.atan(K.squeeze(output[:, :, :, :, 0] / output[:, :, :, :, 1], axis=None))
    # magnitude of edges -- unified x & y edges don't work well with Neural Networks </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     def extract_image_patches(self, x, ksizes, ssizes, padding='same',
                              data_format='channels_last'):
        """
        Extract the patches from an image
        # Parameters
            x : The input image
            ksizes : 2-d tuple with the kernel size
            ssizes : 2-d tuple with the strides size
            padding : 'same' or 'valid'
            data_format : 'channels_last' or 'channels_first'
        # Returns
            The (k_w, k_h) patches extracted
            TF ==> (batch_size, w, h, k_w, k_h, c)
            TH ==> (batch_size, w, h, c, k_w, k_h)
 </s> add     def extract_image_patches(self, input_tensor, k_sizes, s_sizes,
                              padding='same', data_format='channels_last'):
        """ Extract the patches from an image.

        Parameters
        ----------
        input_tensor: tensor
            The input image
        k_sizes: tuple
            2-d tuple with the kernel size
        s_sizes: tuple
            2-d tuple with the strides size
        padding: str, optional
            `"same"` or `"valid"`. Default: `"same"`
        data_format: str, optional.
            `"channels_last"` or `"channels_first"`. Default: `"channels_last"`

        Returns
        -------
        The (k_w, k_h) patches extracted
            Tensorflow ==> (batch_size, w, h, k_w, k_h, c)
            Theano ==> (batch_size, w, h, c, k_w, k_h)
    """ Gradient Magnitude Similarity Deviation Loss.

    Improved image quality metric over MS-SSIM with easier calculations

    Parameters
    ----------
    y_true: tensor or variable
        The ground truth value
    y_pred: tensor or variable
        The predicted value

    Returns
    -------
    tensor
        The loss value

    References
    ----------
      return output
  
  
  def gmsd_loss(y_true, y_pred):
    """ Gradient Magnitude Similarity Deviation Loss.

    Improved image quality metric over MS-SSIM with easier calculations

    Parameters
    ----------
    y_true: tensor or variable
        The ground truth value
    y_pred: tensor or variable
        The predicted value

    Returns
    -------
    tensor
        The loss value

    References
    ----------
    """ Gradient Magnitude Similarity Deviation Loss.

    Improved image quality metric over MS-SSIM with easier calculations

    Parameters
    ----------
    y_true: tensor or variable
        The ground truth value
    y_pred: tensor or variable
        The predicted value

    Returns
    -------
    tensor
        The loss value

    References
    ----------
      http://www4.comp.polyu.edu.hk/~cslzhang/IQA/GMSD/GMSD.htm
      https://arxiv.org/ftp/arxiv/papers/1308/1308.3052.pdf
      """
  
      true_edge = scharr_edges(y_true, True)
keep keep keep keep 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