title: Using SSIM , trying compare images and trying to get Brightness , Contrast and Structure separately
body: <p>I am using the SSIM in python and trying to compare the image . </p>
  
  <p>When we use inbuilt <code>skimage.metrics import structural_similarity</code> we will get the SSIM Score. 
  But I wanted the Brightness, Contrast and Structure score separately so as to deep-dive into the image </p>
  
  <p>I have changed the inbuild lib in below way :</p>
  
  <p>In Below code: Orginal- Is the inbuild Lib code. Added, is the modified code </p>
  
  <pre><code>from warnings import warn
  import numpy as np
  from scipy.ndimage import uniform_filter, gaussian_filter
  
  from ..util.dtype import dtype_range
  from ..util.arraycrop import crop
  from .._shared.utils import warn, check_shape_equality
  
  __all__ = ['structural_similarity']
  
  
  def structural_similarity(im1, im2,
                            *,
                            win_size=None, gradient=False, data_range=None,
                            multichannel=False, gaussian_weights=False,
                            full=False, **kwargs):
      """
      Compute the mean structural similarity index between two images.
  
      Parameters
      ----------
      im1, im2 : ndarray
          Images. Any dimensionality with same shape.
      win_size : int or None, optional
          The side-length of the sliding window used in comparison. Must be an
          odd value. If `gaussian_weights` is True, this is ignored and the
          window size will depend on `sigma`.
      gradient : bool, optional
          If True, also return the gradient with respect to im2.
      data_range : float, optional
          The data range of the input image (distance between minimum and
          maximum possible values). By default, this is estimated from the image
          data-type.
      multichannel : bool, optional
          If True, treat the last dimension of the array as channels. Similarity
          calculations are done independently for each channel then averaged.
      gaussian_weights : bool, optional
          If True, each patch has its mean and variance spatially weighted by a
          normalized Gaussian kernel of width sigma=1.5.
      full : bool, optional
          If True, also return the full structural similarity image.
  
      Other Parameters
      ----------------
      use_sample_covariance : bool
          If True, normalize covariances by N-1 rather than, N where N is the
          number of pixels within the sliding window.
      K1 : float
          Algorithm parameter, K1 (small constant, see [1]_).
      K2 : float
          Algorithm parameter, K2 (small constant, see [1]_).
      sigma : float
          Standard deviation for the Gaussian when `gaussian_weights` is True.
  
      Returns
      -------
      mssim : float
          The mean structural similarity index over the image.
      grad : ndarray
          The gradient of the structural similarity between im1 and im2 [2]_.
          This is only returned if `gradient` is set to True.
      S : ndarray
          The full SSIM image.  This is only returned if `full` is set to True.
  
      Notes
      -----
      To match the implementation of Wang et. al. [1]_, set `gaussian_weights`
      to True, `sigma` to 1.5, and `use_sample_covariance` to False.
  
      .. versionchanged:: 0.16
          This function was renamed from ``skimage.measure.compare_ssim`` to
          ``skimage.metrics.structural_similarity``.
  
      References
      ----------
      .. [1] Wang, Z., Bovik, A. C., Sheikh, H. R., &amp; Simoncelli, E. P.
         (2004). Image quality assessment: From error visibility to
         structural similarity. IEEE Transactions on Image Processing,
         13, 600-612.
         https://ece.uwaterloo.ca/~z70wang/publications/ssim.pdf,
         :DOI:`10.1109/TIP.2003.819861`
  
      .. [2] Avanaki, A. N. (2009). Exact global histogram specification
         optimized for structural similarity. Optical Review, 16, 613-621.
         :arxiv:`0901.0065`
         :DOI:`10.1007/s10043-009-0119-z`
  
      """
      check_shape_equality(im1, im2)
  
      if multichannel:
          # loop over channels
          args = dict(win_size=win_size,
                      gradient=gradient,
                      data_range=data_range,
                      multichannel=False,
                      gaussian_weights=gaussian_weights,
                      full=full)
          args.update(kwargs)
          nch = im1.shape[-1]
          mssim = np.empty(nch)
          if gradient:
              G = np.empty(im1.shape)
          if full:
              S = np.empty(im1.shape)
              #Added-----------------------------
              luminance=np.empty(im1.shape)
              contrast=np.empty(im1.shape)
              structural=np.empty(im1.shape)
              #--------------------------
          for ch in range(nch):
              ch_result = structural_similarity(im1[..., ch],
                                                im2[..., ch], **args)
              if gradient and full:
                  mssim[..., ch], G[..., ch], S[..., ch] = ch_result
              elif gradient:
                  mssim[..., ch], G[..., ch] = ch_result
              elif full:
                  #mssim[..., ch], S[..., ch] = ch_result #orginal
                  #--------Added
                  mssim[..., ch], S[..., ch],luminance[...,ch],contrast[...,ch],structural[...,ch] = ch_result
                  #-------------
              else:
                  mssim[..., ch] = ch_result
          mssim = mssim.mean()
          if gradient and full:
              return mssim, G, S
          elif gradient:
              return mssim, G
          elif full:
              #return mssim, S -- Orginal
              #--------------------------
              return mssim, S , luminance, contrast, structural
              #--------------------------
          else:
              return mssim
  
      K1 = kwargs.pop('K1', 0.01)
      K2 = kwargs.pop('K2', 0.03)
      sigma = kwargs.pop('sigma', 1.5)
      if K1 &lt; 0:
          raise ValueError("K1 must be positive")
      if K2 &lt; 0:
          raise ValueError("K2 must be positive")
      if sigma &lt; 0:
          raise ValueError("sigma must be positive")
      use_sample_covariance = kwargs.pop('use_sample_covariance', True)
  
      if gaussian_weights:
          # Set to give an 11-tap filter with the default sigma of 1.5 to match
          # Wang et. al. 2004.
          truncate = 3.5
  
      if win_size is None:
          if gaussian_weights:
              # set win_size used by crop to match the filter size
              r = int(truncate * sigma + 0.5)  # radius as in ndimage
              win_size = 2 * r + 1
          else:
              win_size = 7   # backwards compatibility
  
      if np.any((np.asarray(im1.shape) - win_size) &lt; 0):
          raise ValueError(
              "win_size exceeds image extent.  If the input is a multichannel "
              "(color) image, set multichannel=True.")
  
      if not (win_size % 2 == 1):
          raise ValueError('Window size must be odd.')
  
      if data_range is None:
          if im1.dtype != im2.dtype:
              warn("Inputs have mismatched dtype.  Setting data_range based on "
                   "im1.dtype.", stacklevel=2)
          dmin, dmax = dtype_range[im1.dtype.type]
          data_range = dmax - dmin
  
      ndim = im1.ndim
  
      if gaussian_weights:
          filter_func = gaussian_filter
          filter_args = {'sigma': sigma, 'truncate': truncate}
      else:
          filter_func = uniform_filter
          filter_args = {'size': win_size}
  
      # ndimage filters need floating point data
      im1 = im1.astype(np.float64)
      im2 = im2.astype(np.float64)
  
      NP = win_size ** ndim
  
      # filter has already normalized by NP
      if use_sample_covariance:
          cov_norm = NP / (NP - 1)  # sample covariance
      else:
          cov_norm = 1.0  # population covariance to match Wang et. al. 2004
  
      # compute (weighted) means
      ux = filter_func(im1, **filter_args)
      uy = filter_func(im2, **filter_args)
  
      # compute (weighted) variances and covariances
      uxx = filter_func(im1 * im1, **filter_args)
      uyy = filter_func(im2 * im2, **filter_args)
      uxy = filter_func(im1 * im2, **filter_args)
      vx = cov_norm * (uxx - ux * ux)
      vy = cov_norm * (uyy - uy * uy)
      vxy = cov_norm * (uxy - ux * uy)
  
      R = data_range
      C1 = (K1 * R) ** 2
      C2 = (K2 * R) ** 2
  
      A1, A2, B1, B2 = ((2 * ux * uy + C1,
                         2 * vxy + C2,
                         ux ** 2 + uy ** 2 + C1,
                         vx + vy + C2))
      D = B1 * B2
      S = (A1 * A2) / D
      #---------------------------------------------------------------Added
      #Helps to Find the Luminance mean of image
      luminance = (A1/B1)
      # Helps to Find the Contast mean of image
      contrast = (A2/B2)
      #Helps to find the structural  mean of image
      C3= C2/2
      structural  = (vxy+C3)/((vx*vy)+C3)
      #-----------------------------------------------------------------
      # to avoid edge effects will ignore filter radius strip around edges
      pad = (win_size - 1) // 2
  
      # compute (weighted) mean of ssim
      mssim = crop(S, pad).mean()
  
      if gradient:
          # The following is Eqs. 7-8 of Avanaki 2009.
          grad = filter_func(A1 / D, **filter_args) * im1
          grad += filter_func(-S / B2, **filter_args) * im2
          grad += filter_func((ux * (A2 - A1) - uy * (B2 - B1) * S) / D,
                              **filter_args)
          grad *= (2 / im1.size)
  
          if full:
              return mssim, grad, S
          else:
              #return mssim, grad #orginal
              #-----------Added
              return mssim, grad,luminance, contrast, structural
              #-----------------
      else:
          if full:
              #return mssim, S  #Orginal
              #-------------------------Added
              return mssim, S,luminance,contrast,structural
              #-------------------------
          else:
              return mssim
  </code></pre>
  
  <p>When I call the function </p>
  
  <pre><code>(score, diff, l, c, s) = structural_similarity(MasterGray, InputGray, multichannel=True, full=True)
  </code></pre>
  
  <p>I am getting the result as </p>
  
  <p>*</p>
  
  <pre><code>SSIM: 1.0
  l: 1.0
  c: 1.0
  s: 0.5550284459443691
  </code></pre>
  
  <p>*</p>
  
  <p>I am NOT understanding, Why "S" is 0.55. whereas SSIM is  1.0? 
  Is is expected . If it is expected what is the reason </p>