add multichannel support to structural_similarity

This commit is contained in:
Gregory R. Lee
2015-05-11 13:40:42 -04:00
parent 49a1060719
commit 44f1fd37f2
2 changed files with 54 additions and 3 deletions
+38 -3
View File
@@ -27,8 +27,8 @@ def gaussian_filter2(X, sigma=1.5, size=11):
X : ndarray
filtered image
Note
----
Notes
-----
scipy.ndimage.gaussian is very similar, but uses a 13 tap FIR filter
rather than the 11 tap one of Wang. et. al.
"""
@@ -75,7 +75,8 @@ def _discard_edges(X, pad):
def structural_similarity(X, Y, win_size=None, gradient=False,
dynamic_range=None, gaussian_weights=False):
dynamic_range=None, multichannel=None,
gaussian_weights=False):
"""Compute the mean structural similarity index between two images.
Parameters
@@ -91,6 +92,10 @@ def structural_similarity(X, Y, win_size=None, gradient=False,
Dynamic range of the input image (distance between minimum and maximum
possible values). By default, this is estimated from the image
data-type.
multichannel : int or None
If True, treat the last dimension of the array as channels. Similarity
calculations are done independently for each channel then averaged.
Defaults to True only if X is 3D and X.shape[2] == 3.
gaussian_weights : bool
If True, each patch (of size win_size) has its mean and variance
spatially weighted by a normalized Gaussian kernel of width sigma=1.5.
@@ -125,6 +130,36 @@ def structural_similarity(X, Y, win_size=None, gradient=False,
if not X.shape == Y.shape:
raise ValueError('Input images must have the same dimensions.')
# default treats 3D arrays with shape[2] == 3 as multichannel
if multichannel is None:
if X.ndim == 3 and X.shape[2] == 3:
multichannel = True
else:
multichannel = False
if multichannel:
# loop over channels
args = locals()
args.pop('X')
args.pop('Y')
args['multichannel'] = False
nch = X.shape[-1]
mssim = np.empty(nch)
if gradient:
G = np.empty(X.shape)
for ch in range(nch):
if gradient:
mssim[..., ch], G[..., ch] = structural_similarity(
X[..., ch], Y[..., ch], **args)
else:
mssim[..., ch] = structural_similarity(
X[..., ch], Y[..., ch], **args)
mssim = mssim.mean()
if gradient:
return mssim, G
else:
return mssim
if win_size is None:
if gaussian_weights:
win_size = 11 # 11 to match Wang et. al. 2004
@@ -42,6 +42,22 @@ def test_ssim_image():
assert(S1 < 0.3)
def test_ssim_multichannel():
N = 100
X = (np.random.rand(N, N) * 255).astype(np.uint8)
Y = (np.random.rand(N, N) * 255).astype(np.uint8)
S1 = ssim(X, Y, win_size=3)
# replicate across three channels. should get identical value
Xc = np.tile(X[..., np.newaxis], (1, 1, 3))
Yc = np.tile(Y[..., np.newaxis], (1, 1, 3))
S2 = ssim(Xc, Yc, win_size=3)
assert_almost_equal(S1, S2)
# fail if win_size exceeds any non-channel dimension
assert_raises(ValueError, ssim, Xc, Yc, win_size=7, multichannel=False)
# NOTE: This test is known to randomly fail on some systems (Mac OS X 10.6)
def test_ssim_grad():
N = 30