                                             filter_sigma=self.filter_sigma,
                                             k1=self.k_1,
                                             k2=self.k_2)
          ms_ssim_loss = 1. - ms_ssim
          return ms_ssim_loss
  
      def _get_smallest_size(self, size, idx):
          """ Recursive function to obtain the smallest size that the image will be scaled to.
  
          Parameters
 
</s> remove     def _get_smallest_size(self, size, idx):
 </s> add     def _get_smallest_size(self, size: int, idx: int) -> int: </s> remove                  k_1=0.01,
                 k_2=0.03,
                 filter_size=4,
                 filter_sigma=1.5,
                 max_value=1.0,
                 power_factors=(0.0448, 0.2856, 0.3001, 0.2363, 0.1333)):
        self.filter_size = filter_size
        self.filter_sigma = filter_sigma
        self.k_1 = k_1
        self.k_2 = k_2
        self.max_value = max_value
        self.power_factors = power_factors
 </s> add                  k_1: float = 0.01,
                 k_2: float = 0.03,
                 filter_size: int = 11,
                 filter_sigma: float = 1.5,
                 max_value: float = 1.0,
                 power_factors: Tuple[float, ...] = (0.0448, 0.2856, 0.3001, 0.2363, 0.1333)
                 ) -> None:
        super().__init__(k_1=k_1,
                         k_2=k_2,
                         filter_size=filter_size,
                         filter_sigma=filter_sigma,
                         max_value=max_value)
        self._power_factors = K.constant(power_factors)

    def _get_smallest_size(self, size: int, idx: int) -> int:
        """ Recursive function to obtain the smallest size that the image will be scaled to.
        for MS-SSIM

        Parameters
        ----------
        size: int
            The current scaled size to iterate through
        idx: int
            The current iteration to be performed. When iteration hits zero the value will
            be returned

        Returns
        -------
        int
            The smallest size the image will be scaled to based on the original image size and
            the amount of scaling factors that will occur
        """
        logger.debug("scale id: %s, size: %s", idx, size)
        if idx > 0:
            size = self._get_smallest_size(size // 2, idx - 1)
        return size

    @classmethod
    def _shrink_images(cls, images: List[plaidml.tile.Value]) -> List[plaidml.tile.Value]:
        """ Reduce the dimensional space of a batch of images in half. If the images are an odd
        number of pixels then pad them to an even dimension prior to shrinking

        All incoming images are assumed square.

        Parameters
        ----------
        images: list
            The y_true, y_pred batch of images to be shrunk

        Returns
        -------
        list
            The y_true, y_pred batch shrunk by half
        """
        if any(x % 2 != 0 for x in K.int_shape(images[1])[1:2]):
            images = [pad(img,
                          [[0, 0], [0, 1], [0, 1], [0, 0]],
                          mode="REFLECT")
                      for img in images]

        images = [K.pool2d(img, (2, 2), strides=(2, 2), padding="valid", pool_mode="avg")
                  for img in images]

        return images

    def _get_ms_ssim(self,
                     y_true: plaidml.tile.Value,
                     y_pred: plaidml.tile.Value) -> plaidml.tile.Value:
        """ Obtain the Multiscale Stuctural Similarity metric.

        Parameters
        ----------
        y_true: :class:`plaidml.tile.Value`
            The input batch of ground truth images
        y_pred: :class:`plaidml.tile.Value`
            The input batch of predicted images

        Returns
        -------
        :class:`plaidml.tile.Value`
            The MS-SSIM for the given images
        """
        im_size = K.int_shape(y_pred)[1]
        # filter size cannot be larger than the smallest scale
        recursions = K.int_shape(self._power_factors)[0]
        smallest_scale = self._get_smallest_size(im_size, recursions - 1)
        if smallest_scale < self._filter_size:
            self._filter_size = smallest_scale
            self._kernel = self._get_kernel()

        images = [y_true, y_pred]
        contrasts = []

        for idx in range(recursions):
            images = self._shrink_images(images) if idx > 0 else images
            ssim, contrast = self._get_ssim(*images)

            if idx < recursions - 1:
                contrasts.append(K.relu(K.expand_dims(contrast, axis=-1)))

        contrasts.append(K.relu(K.expand_dims(ssim, axis=-1)))
        mcs_and_ssim = K.concatenate(contrasts, axis=-1)
        ms_ssim = K.pow(mcs_and_ssim, self._power_factors)

        # K.prod does not work in plaidml so slow recursion it is
        out = ms_ssim[..., 0]
        for idx in range(1, recursions):
            out *= ms_ssim[..., idx]
        return out </s> remove         additional channels at the end of :param:`y_true`, so that they can be split off and
 </s> add         additional channels at the end of :attr:`y_true`, so that they can be split off and </s> remove         tensor
            The DSSIM Loss value
 </s> add         :class:`tf.Tensor`
            The output from the convolution
        """
        return K.depthwise_conv2d(image, kernel, strides=(1, 1), padding="valid")

    def _get_ssim(self, y_true: tf.Tensor, y_pred: tf.Tensor) -> Tuple[tf.Tensor, tf.Tensor]:
        """ Obtain the structural similarity between a batch of true and predicted images.

        Parameters
        ----------
        y_true: :class:`tf.Tensor`
            The input batch of ground truth images
        y_pred: :class:`tf.Tensor`
            The input batch of predicted images

        Returns
        -------
        :class:`tf.Tensor`
            The SSIM for the given images
        :class:`tf.Tensor`
            The Contrast for the given images
        """
        channels = K.int_shape(y_true)[-1]
        kernel = K.tile(self._kernel, (1, 1, channels, 1))

        # SSIM luminance measure is (2 * mu_x * mu_y + c1) / (mu_x ** 2 + mu_y ** 2 + c1)
        mean_true = self._depthwise_conv2d(y_true, kernel)
        mean_pred = self._depthwise_conv2d(y_pred, kernel)
        num_lum = mean_true * mean_pred * 2.0
        den_lum = K.square(mean_true) + K.square(mean_pred)
        luminance = (num_lum + self._c1) / (den_lum + self._c1)

        # SSIM contrast-structure measure is (2 * cov_{xy} + c2) / (cov_{xx} + cov_{yy} + c2)
        num_con = self._depthwise_conv2d(y_true * y_pred, kernel) * 2.0
        den_con = self._depthwise_conv2d(K.square(y_true) + K.square(y_pred), kernel)

        contrast = (num_con - num_lum + self._c2) / (den_con - den_lum + self._c2)

        # Average over the height x width dimensions
        axes = (-3, -2)
        ssim = K.mean(luminance * contrast, axis=axes)
        contrast = K.mean(contrast, axis=axes)

        return ssim, contrast

    def __call__(self, y_true: tf.Tensor, y_pred: tf.Tensor) -> tf.Tensor:
        """ Call the DSSIM  or MS-DSSIM Loss Function.

        Parameters
        ----------
        y_true: :class:`tf.Tensor`
            The input batch of ground truth images
        y_pred: :class:`tf.Tensor`
            The input batch of predicted images

        Returns
        -------
        :class:`tf.Tensor`
            The DSSIM or MS-DSSIM for the given images </s> remove         kernel = [self.kernel_size, self.kernel_size]
        y_true = K.reshape(y_true, [-1] + list(self._int_shape(y_pred)[1:]))
        y_pred = K.reshape(y_pred, [-1] + list(self._int_shape(y_pred)[1:]))
        patches_pred = self.extract_image_patches(y_pred,
                                                  kernel,
                                                  kernel,
                                                  'valid',
                                                  self.dim_ordering)
        patches_true = self.extract_image_patches(y_true,
                                                  kernel,
                                                  kernel,
                                                  'valid',
                                                  self.dim_ordering)

        # Get mean
        u_true = K.mean(patches_true, axis=-1)
        u_pred = K.mean(patches_pred, axis=-1)
        # Get variance
        var_true = K.var(patches_true, axis=-1)
        var_pred = K.var(patches_pred, axis=-1)
        # Get standard deviation
        covar_true_pred = K.mean(
            patches_true * patches_pred, axis=-1) - u_true * u_pred

        ssim = (2 * u_true * u_pred + self.c_1) * (
            2 * covar_true_pred + self.c_2)
        denom = (K.square(u_true) + K.square(u_pred) + self.c_1) * (
            var_pred + var_true + self.c_2)
        ssim /= denom  # no need for clipping, c_1 + c_2 make the denorm non-zero
        return (1.0 - ssim) / 2.0

    @staticmethod
    def _preprocess_padding(padding):
        """Convert keras padding to tensorflow padding.
 </s> add         # SSIM luminance measure is (2 * mu_x * mu_y + c1) / (mu_x ** 2 + mu_y ** 2 + c1)
        mean_true = self._depthwise_conv2d(y_true, kernel)
        mean_pred = self._depthwise_conv2d(y_pred, kernel)
        num_lum = mean_true * mean_pred * 2.0
        den_lum = K.square(mean_true) + K.square(mean_pred)
        luminance = (num_lum + self._c1) / (den_lum + self._c1)
        return K.mean(ms_ssim_loss)
                                             filter_sigma=self.filter_sigma,
                                             k1=self.k_1,
                                             k2=self.k_2)
          ms_ssim_loss = 1. - ms_ssim
        return K.mean(ms_ssim_loss)
  
      def _get_smallest_size(self, size, idx):
          """ Recursive function to obtain the smallest size that the image will be scaled to.
  
          Parameters
keep keep keep keep replace keep keep keep keep keep
 SSIM Updates
  - Standardize DSSIM Function
  - Implement MSSIM function for AMD 
lib/model/losses_tf.py