          self.k_2 = k_2
          self.max_value = max_value
          self.power_factors = power_factors
  
      def call(self, y_true, y_pred):
          """ Call the MS-SSIM Loss Function.
  
          Parameters
          ----------
          y_true: tensor or variable
 
</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         y_true: tensor or variable
            The ground truth value
        y_pred: tensor or variable
            The predicted value
 </s> add         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 </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         y_pred: tensor or variable
 </s> add         y_pred: :class:`tf.Tensor` </s> remove     def __init__(self, k_1=0.01, k_2=0.03, filter_size=11, filter_sigma=1.5, max_value=1.0):
        super().__init__(name="DSSIMObjective", reduction=tf.keras.losses.Reduction.NONE)
 </s> add     def __init__(self,
                 k_1: float = 0.01,
                 k_2: float = 0.03,
                 filter_size: int = 11,
                 filter_sigma: float = 1.5,
                 max_value: float = 1.0) -> None:
    def __call__(self, y_true: tf.Tensor, y_pred: tf.Tensor) -> tf.Tensor:
          self.k_2 = k_2
          self.max_value = max_value
          self.power_factors = power_factors
  
    def __call__(self, y_true: tf.Tensor, y_pred: tf.Tensor) -> tf.Tensor:
          """ Call the MS-SSIM Loss Function.
  
          Parameters
          ----------
          y_true: tensor or variable
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