Files
scikit-image/skimage/measure/_structural_similarity.py
T

106 lines
3.0 KiB
Python

from __future__ import division
__all__ = ['structural_similarity']
import numpy as np
from ..util.dtype import dtype_range
from ..util.shape import view_as_windows
def structural_similarity(X, Y, win_size=7,
gradient=False, dynamic_range=None):
"""Compute the mean structural similarity index between two images.
Parameters
----------
X, Y : (N,N) ndarray
Images.
win_size : int
The side-length of the sliding window used in comparison. Must
be an odd value.
gradient : bool
If True, also return the gradient.
dynamic_range : int
Dynamic range of the input image (distance between minimum and
maximum possible values). By default, this is estimated from
the image data-type.
Returns
-------
s : float
Strucutural similarity.
grad : (N * N,) ndarray
Gradient of the structural similarity index between X and Y.
This is only returned if `gradient` is set to True.
References
----------
.. [1] Wang, Z., Bovik, A. C., Sheikh, H. R., & Simoncelli, E. P.
(2004). Image quality assessment: From error visibility to
structural similarity. IEEE Transactions on Image Processing,
13, 600-612.
"""
if not X.dtype == Y.dtype:
raise ValueError('Input images must have the same dtype.')
if not X.shape == Y.shape:
raise ValueError('Input images must have the same dimensions.')
if not (win_size % 2 == 1):
raise ValueError('Window size must be odd.')
if dynamic_range is None:
dmin, dmax = dtype_range[X.dtype.type]
dynamic_range = dmax - dmin
XW = view_as_windows(X, (win_size, win_size))
YW = view_as_windows(Y, (win_size, win_size))
NS = len(XW)
NP = win_size * win_size
ux = np.mean(np.mean(XW, axis=2), axis=2)
uy = np.mean(np.mean(YW, axis=2), axis=2)
# Compute variances var(X), var(Y) and var(X, Y)
cov_norm = 1 / (win_size**2 - 1)
XWM = XW - ux[..., None, None]
YWM = YW - uy[..., None, None]
vx = cov_norm * np.sum(np.sum(XWM**2, axis=2), axis=2)
vy = cov_norm * np.sum(np.sum(YWM**2, axis=2), axis=2)
vxy = cov_norm * np.sum(np.sum(XWM * YWM, axis=2), axis=2)
R = dynamic_range
K1 = 0.01
K2 = 0.03
C1 = (K1 * R)**2
C2 = (K2 * R)**2
A1, A2, B1, B2 = (v[..., None, None] for v in
(2 * ux * uy + C1,
2 * vxy + C2,
ux**2 + uy**2 + C1,
vx + vy + C2))
S = np.mean((A1 * A2) / (B1 * B2))
if gradient:
local_grad = 2 / (NP * B1**2 * B2**2) * \
(
A1 * B1 * (B2 * XW - A2 * YW) - \
B1 * B2 * (A2 - A1) * ux[..., None, None] + \
A1 * A2 * (B1 - B2) * uy[..., None, None]
)
grad = np.zeros_like(X, dtype=float)
OW = view_as_windows(grad, (win_size, win_size))
OW += local_grad
grad /= NS
return S, grad
else:
return S