Structural similarity indexΒΆ

When comparing images, the mean squared error (MSE)–while simple to implement–is not highly indicative of perceived similarity. Structural similarity aims to address this shortcoming by taking texture into account [1], [2].

The example shows two modifications of the input image, each with the same MSE, but with very different mean structural similarity indices.

[1]Zhou Wang; Bovik, A.C.; ,”Mean squared error: Love it or leave it? A new look at Signal Fidelity Measures,” Signal Processing Magazine, IEEE, vol. 26, no. 1, pp. 98-117, Jan. 2009.
[2]Z. Wang, A. C. Bovik, H. R. Sheikh and E. P. Simoncelli, “Image quality assessment: From error visibility to structural similarity,” IEEE Transactions on Image Processing, vol. 13, no. 4, pp. 600-612, Apr. 2004.
../_images/plot_ssim_1.png

import numpy as np
import matplotlib
import matplotlib.pyplot as plt

from skimage import data, img_as_float
from skimage.measure import structural_similarity as ssim


matplotlib.rcParams['font.size'] = 9


img = img_as_float(data.camera())
rows, cols = img.shape

noise = np.ones_like(img) * 0.2 * (img.max() - img.min())
noise[np.random.random(size=noise.shape) > 0.5] *= -1


def mse(x, y):
    return np.linalg.norm(x - y)

img_noise = img + noise
img_const = img + abs(noise)

fig, (ax0, ax1, ax2) = plt.subplots(nrows=1, ncols=3, figsize=(8, 4))

mse_none = mse(img, img)
ssim_none = ssim(img, img, dynamic_range=img.max() - img.min())

mse_noise = mse(img, img_noise)
ssim_noise = ssim(img, img_noise, dynamic_range=img_const.max() - img_const.min())

mse_const = mse(img, img_const)
ssim_const = ssim(img, img_const, dynamic_range=img_noise.max() - img_noise.min())

label = 'MSE: %2.f, SSIM: %.2f'

ax0.imshow(img, cmap=plt.cm.gray, vmin=0, vmax=1)
ax0.set_xlabel(label % (mse_none, ssim_none))
ax0.set_title('Original image')

ax1.imshow(img_noise, cmap=plt.cm.gray, vmin=0, vmax=1)
ax1.set_xlabel(label % (mse_noise, ssim_noise))
ax1.set_title('Image with noise')

ax2.imshow(img_const, cmap=plt.cm.gray, vmin=0, vmax=1)
ax2.set_xlabel(label % (mse_const, ssim_const))
ax2.set_title('Image plus constant')

plt.show()

STDOUT


        

STDERR


        

Python source code: download (generated using skimage 0.11dev)

IPython Notebook: download (generated using skimage 0.11dev)

aW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBtYXRwbG90bGliCmltcG9ydCBtYXRwbG90bGliLnB5cGxvdCBhcyBwbHQKCmZyb20gc2tpbWFnZSBpbXBvcnQgZGF0YSwgaW1nX2FzX2Zsb2F0CmZyb20gc2tpbWFnZS5tZWFzdXJlIGltcG9ydCBzdHJ1Y3R1cmFsX3NpbWlsYXJpdHkgYXMgc3NpbQoKCm1hdHBsb3RsaWIucmNQYXJhbXNbJ2ZvbnQuc2l6ZSddID0gOQoKCmltZyA9IGltZ19hc19mbG9hdChkYXRhLmNhbWVyYSgpKQpyb3dzLCBjb2xzID0gaW1nLnNoYXBlCgpub2lzZSA9IG5wLm9uZXNfbGlrZShpbWcpICogMC4yICogKGltZy5tYXgoKSAtIGltZy5taW4oKSkKbm9pc2VbbnAucmFuZG9tLnJhbmRvbShzaXplPW5vaXNlLnNoYXBlKSA+IDAuNV0gKj0gLTEKCgpkZWYgbXNlKHgsIHkpOgogICAgcmV0dXJuIG5wLmxpbmFsZy5ub3JtKHggLSB5KQoKaW1nX25vaXNlID0gaW1nICsgbm9pc2UKaW1nX2NvbnN0ID0gaW1nICsgYWJzKG5vaXNlKQoKZmlnLCAoYXgwLCBheDEsIGF4MikgPSBwbHQuc3VicGxvdHMobnJvd3M9MSwgbmNvbHM9MywgZmlnc2l6ZT0oOCwgNCkpCgptc2Vfbm9uZSA9IG1zZShpbWcsIGltZykKc3NpbV9ub25lID0gc3NpbShpbWcsIGltZywgZHluYW1pY19yYW5nZT1pbWcubWF4KCkgLSBpbWcubWluKCkpCgptc2Vfbm9pc2UgPSBtc2UoaW1nLCBpbWdfbm9pc2UpCnNzaW1fbm9pc2UgPSBzc2ltKGltZywgaW1nX25vaXNlLCBkeW5hbWljX3JhbmdlPWltZ19jb25zdC5tYXgoKSAtIGltZ19jb25zdC5taW4oKSkKCm1zZV9jb25zdCA9IG1zZShpbWcsIGltZ19jb25zdCkKc3NpbV9jb25zdCA9IHNzaW0oaW1nLCBpbWdfY29uc3QsIGR5bmFtaWNfcmFuZ2U9aW1nX25vaXNlLm1heCgpIC0gaW1nX25vaXNlLm1pbigpKQoKbGFiZWwgPSAnTVNFOiAlMi5mLCBTU0lNOiAlLjJmJwoKYXgwLmltc2hvdyhpbWcsIGNtYXA9cGx0LmNtLmdyYXksIHZtaW49MCwgdm1heD0xKQpheDAuc2V0X3hsYWJlbChsYWJlbCAlIChtc2Vfbm9uZSwgc3NpbV9ub25lKSkKYXgwLnNldF90aXRsZSgnT3JpZ2luYWwgaW1hZ2UnKQoKYXgxLmltc2hvdyhpbWdfbm9pc2UsIGNtYXA9cGx0LmNtLmdyYXksIHZtaW49MCwgdm1heD0xKQpheDEuc2V0X3hsYWJlbChsYWJlbCAlIChtc2Vfbm9pc2UsIHNzaW1fbm9pc2UpKQpheDEuc2V0X3RpdGxlKCdJbWFnZSB3aXRoIG5vaXNlJykKCmF4Mi5pbXNob3coaW1nX2NvbnN0LCBjbWFwPXBsdC5jbS5ncmF5LCB2bWluPTAsIHZtYXg9MSkKYXgyLnNldF94bGFiZWwobGFiZWwgJSAobXNlX2NvbnN0LCBzc2ltX2NvbnN0KSkKYXgyLnNldF90aXRsZSgnSW1hZ2UgcGx1cyBjb25zdGFudCcpCgpwbHQuc2hvdygp