cutcutcodec.core.signal.metric.ssim

cutcutcodec.core.signal.metric.ssim()

Compute the Structural similarity index measure of 2 images in C language.

This fonction is nearly equivalent to these functions:

import cv2
import numpy as np

def ssim(
  im1: np.ndarray, im2: np.ndarray, data_range : float = 1.0, weights = None, sigma: float = 1.5
) -> float:
    # get gaussian window
    r = int(3.5 * sigma + 0.5)  # same as skimage.metrics.structural_similarity
    gauss = np.exp(-(np.arange(-r, r+1)**2) / (2.0 * sigma**2))
    gauss_i, gauss_j = np.meshgrid(gauss, gauss, indexing="ij")
    gauss = gauss_i * gauss_j
    gauss /= gauss.sum()
    # compute statistics for all patches
    mu1 = cv2.filter2D(im1, ddepth=-1, kernel=gauss)
    mu2 = cv2.filter2D(im2, ddepth=-1, kernel=gauss)
    mu11, mu22, mu12 = mu1 * mu1, mu2 * mu2, mu1 * mu2
    s11 = cv2.filter2D(im1*im1, ddepth=-1, kernel=gauss) - mu11
    s22 = cv2.filter2D(im2*im2, ddepth=-1, kernel=gauss) - mu22
    s12 = cv2.filter2D(im1*im2, ddepth=-1, kernel=gauss) - mu12
    # crop patches
    mu11, mu22, mu12 = mu11[r:-r, r:-r], mu22[r:-r, r:-r], mu12[r:-r, r:-r]
    s11, s22, s12 = s11[r:-r, r:-r], s22[r:-r, r:-r], s12[r:-r, r:-r]
    # ssim formula
    c1, c2 = (0.01 * data_range)**2, (0.03 * data_range)**2
    ssim = ((2.0*mu12 + c1) * (2.0*s12 + c2)) / ((mu11 + mu22 + c1) * (s11 + s22 + c2))
    # average
    if weights is None:
      weights = [1.0 for _ in range(im1.shape[2])]
    weights = np.asarray(weights, dtype=im1.dtype)
    return float((ssim.mean(axis=(0, 1)) * weights).sum() / weights.sum())

Or directly using scikit-image:

from skimage.metrics import structural_similarity

def ssim(
  im1: np.ndarray, im2: np.ndarray, data_range : float = 1.0, weights = None, sigma: float = 1.5
) -> float:
  if weights is None:
      weights = [1.0 for _ in range(im1.shape[2])]
  ssim = 0.0
  for i in range(im1.shape[2]):
      ssim += structural_similarity(
          im1[:, :, i], im2[:, :, i], data_range=data_range, sigma=sigma, gaussian_weights=True
      ) * weights[i]
  return ssim / sum(weights)

Parameters

im1, im2np.ndarray

The 2 images to be compared, of shape (height, width, channels). Supported types are float32 and float64.

data_rangefloat, default=1.0

The data range of the input image (difference between maximum and minimum possible values).

weightsiterable[float], optional

The relative weight of each channel. By default, all channels have the same weight.

sigmafloat, default=1.5

The standard deviation of the gaussian.

threadsint, optional

Defines the number of threads. The value -1 means that the function uses as many calculation threads as there are cores. The default value (0) allows the same behavior as (-1) if the function is called in the main thread, otherwise (1) to avoid nested threads. Any other positive value corresponds to the number of threads used.

Returns

ssimfloat

The ponderated structural similarity index measure of each layers.

Examples

>>> import numpy as np
>>> from cutcutcodec.core.signal.metric import ssim
>>> im1 = np.random.random((1080, 1920, 3))
>>> im2 = 0.8*im1 + 0.2*np.random.random((1080, 1920, 3))
>>> round(ssim(im1, im2), 2)
0.95
>>>