Skip to content

Dear Internet Explorer user: Your browser is no longer supported

Please switch to a modern browser such as Microsoft Edge, Mozilla Firefox or Google Chrome to view this website's content.

Structural Similarity Index (SSIM) in Python

Quantitatively check the quality of a compressed image using a simple Python code for calculating the Structural Similarity Index (SSIM) and Mean Square Errors (MSE) between two images.

When images are compressed, resized or converted to different formats, there can be a loss of fidelity between the original and the copy.

A common method for quantitatively checking the effect that format changes or compression may have had on an image is to use the peak signal-to-noise ratio (PSNR), which is the ratio between the maximum possible power of an image and the power of corrupting noise that affects the quality of its representation. However, there are two other methods that can also be used:

MSE is a fairly old method with known limitations, but is still commonly used. Values of MSE closer to zero are better. MSE can be represented mathematically as follows:

\[MSE = \displaystyle \frac{1}{mn} \sum\limits_{i=0}^{m-1}\displaystyle\sum\limits_{j=0}^{n-1}\lVert f(i,j)-g(i,j)\lVert^2\]

where:

The Structural Similarity Index (SSIM) is a much newer equation developed in 2004 by Wang et al. SSIM Index quality assessment index is based on the computation of three factors; luminance (l), contrast (c) and structure (s). The overall index is a multiplicative combination of the three:

\[SSIM(x, y) = [l(x,y)]^a\times[c(x,y)]^\beta\times[s(x,y)]^\gamma\]

where:

\[l(x,y)=\frac{2\mu_x\mu_y+C_1}{\mu^2_x+\mu^2_x+C_1},\]
\[c(x,y)=\frac{2\sigma_x\sigma_y+C_2}{\sigma^2_x+\sigma^2_x+C_2},\]
\[s(x,y)=\frac{\sigma_xy+C_3}{\sigma_x\sigma_y+C_3}\]

where μx, μy, σxy, and σxy are the local means, standard deviations, and cross-covariance for images x, y. If α = β = γ = 1 and C3 = C2/2 (both common assumptions) the equation simplifies to:

\[SSIM(x,y)=\frac{(2\mu_x\mu_y+C_1)(2\sigma_xy+C_2)}{(\mu^2_x+\mu^2_y+C_1)(\sigma^2_x+\sigma^2_y+C_2)}\]

The SSIM values range between 0 to 1 where 1 means a perfect match between the original image and the copy.

The mathematics for SSIM is horrendous, but thankfully both MSE and SSIM functions exist in the scikit-image package in Python. The following Python code can be used to calculate the MSE and SSIM for a pair of images:

#!/usr/bin/env python

from skimage.metrics import structural_similarity as ssim
import numpy as np
import cv2
import argparse

def options():
 parser = argparse.ArgumentParser(description="Read image metadata")
 parser.add_argument("-o", "--first", help="Input image file.", required=True)
 parser.add_argument("-c", "--second", help="Input image file.", required=True)
 args = parser.parse_args()
 return args

def mse(imageA, imageB):
 # the 'Mean Squared Error' between the two images is the sum of the squared difference between the two images
 mse_error = np.sum((imageA.astype("float") - imageB.astype("float")) ** 2)
 mse_error /= float(imageA.shape[0] * imageA.shape[1])
	
 # return the MSE. The lower the error, the more "similar" the two images are.
 return mse_error

def compare(imageA, imageB):
 # Calculate the MSE and SSIM
 m = mse(imageA, imageB)
 s = ssim(imageA, imageB)

 # Return the SSIM. The higher the value, the more "similar" the two images are.
 return s

def main(): 

 # Get options
 args = options()

 # Import images
 image1 = cv2.imread(args.first)
 image2 = cv2.imread(args.second, 1)

 # Convert the images to grayscale
 gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
 gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)

 # Check for same size and ratio and report accordingly
 ho, wo, _ = image1.shape
 hc, wc, _ = image2.shape
 ratio_orig = ho/wo
 ratio_comp = hc/wc
 dim = (wc, hc)

 if round(ratio_orig, 2) != round(ratio_comp, 2):
  print("\nImages not of the same dimension. Check input.")
  exit()

 # Resize first image if the second image is smaller
 elif ho > hc and wo > wc:
  print("\nResizing original image for analysis...")
  gray1 = cv2.resize(gray1, dim)

 elif ho < hc and wo < wc:
  print("\nCompressed image has a larger dimension than the original. Check input.")
  exit()

 if round(ratio_orig, 2) == round(ratio_comp, 2):
  mse_value = mse(gray1, gray2)
  ssim_value = compare(gray1, gray2)
  print("MSE:", mse_value)
  print("SSIM:", ssim_value)

if __name__ == '__main__':
	main()

The code takes two inputs on the command line:

If the python file were saved as mse-ssim.py, implementation would be as follows: /path/to/mse-ssim.py -o /path/to/original.ext -c /path/to/copy.ext

Before calculating MSE and SSIM, the above code will check that the two images are the same size. If the copy is smaller, then the original will be reduced to the same dimensions using OpenCV’s cv2.resize() function which uses a bilinear interpolation. (Ideally, the two images would have identical dimensions and this step would not be necessary but your application will determine this). The aspect ratio of each image is also checked before proceeding.

This code has been published to GitHub.

   

Comments

No comments have yet been submitted. Be the first!

Have Your Say

The following HTML is permitted:
<a href="" title=""> <b> <blockquote cite=""> <code> <em> <i> <q cite=""> <strike> <strong>

Comments will be published subject to the Editorial Policy.