|
1 | 1 | """ |
2 | | -=========================== |
3 | | -Structural similarity index |
4 | | -=========================== |
| 2 | +======================================== |
| 3 | +Compute mean structural similarity index |
| 4 | +======================================== |
5 | 5 |
|
6 | 6 | When comparing images, the mean squared error (MSE)--while simple to |
7 | | -implement--is not highly indicative of perceived similarity. Structural |
8 | | -similarity aims to address this shortcoming by taking texture into account |
9 | | -[1]_, [2]_. |
| 7 | +implement--is not highly indicative of perceived similarity. The structural |
| 8 | +similarity index measure (SSIM) aims to address this shortcoming by taking |
| 9 | +texture into account [1]_, [2]_. |
10 | 10 |
|
11 | | -The example shows two modifications of the input image, each with the same MSE, |
| 11 | +The example shows two modifications of an original image, each with the same MSE, |
12 | 12 | but with very different mean structural similarity indices. |
13 | 13 |
|
14 | 14 | .. [1] Zhou Wang; Bovik, A.C.; ,"Mean squared error: Love it or leave it? A new |
|
37 | 37 | img_noise = img + noise |
38 | 38 | img_const = img + abs(noise) |
39 | 39 |
|
40 | | -fig, axes = plt.subplots(ncols=3, figsize=(10, 4), sharex=True, sharey=True) |
41 | | -ax = axes.ravel() |
| 40 | +##################################################################### |
| 41 | +# Ensure that the (floating-point) values in the original image range |
| 42 | +# from 0 to 1. |
| 43 | + |
| 44 | +assert img.min() == 0.0 |
| 45 | +assert img.max() == 1.0 |
42 | 46 |
|
43 | 47 | mse_none = ski.metrics.mean_squared_error(img, img) |
44 | | -ssim_none = ski.metrics.structural_similarity( |
45 | | - img, img, data_range=img.max() - img.min() |
46 | | -) |
| 48 | +ssim_none = ski.metrics.structural_similarity(img, img, data_range=1.0) |
47 | 49 |
|
48 | 50 | mse_noise = ski.metrics.mean_squared_error(img, img_noise) |
49 | | -ssim_noise = ski.metrics.structural_similarity( |
50 | | - img, img_noise, data_range=img_noise.max() - img_noise.min() |
51 | | -) |
| 51 | +ssim_noise = ski.metrics.structural_similarity(img, img_noise, data_range=1.0) |
52 | 52 |
|
53 | 53 | mse_const = ski.metrics.mean_squared_error(img, img_const) |
54 | | -ssim_const = ski.metrics.structural_similarity( |
55 | | - img, img_const, data_range=img_const.max() - img_const.min() |
56 | | -) |
| 54 | +ssim_const = ski.metrics.structural_similarity(img, img_const, data_range=1.0) |
| 55 | + |
| 56 | +fig, axes = plt.subplots(ncols=3, figsize=(10, 4), sharex=True, sharey=True) |
| 57 | +ax = axes.ravel() |
57 | 58 |
|
58 | 59 | ax[0].imshow(img, cmap='gray', vmin=0, vmax=1) |
59 | | -ax[0].set_xlabel(f'MSE: {mse_none:.2f}, SSIM: {ssim_none:.2f}') |
| 60 | +ax[0].set_xlabel(f'MSE = {mse_none:.2f}, SSIM = {ssim_none:.2f}') |
60 | 61 | ax[0].set_title('Original image') |
61 | 62 |
|
62 | 63 | ax[1].imshow(img_noise, cmap='gray', vmin=0, vmax=1) |
63 | | -ax[1].set_xlabel(f'MSE: {mse_noise:.2f}, SSIM: {ssim_noise:.2f}') |
| 64 | +ax[1].set_xlabel(f'MSE = {mse_noise:.2f}, SSIM = {ssim_noise:.2f}') |
64 | 65 | ax[1].set_title('Image with noise') |
65 | 66 |
|
66 | 67 | ax[2].imshow(img_const, cmap=plt.cm.gray, vmin=0, vmax=1) |
67 | | -ax[2].set_xlabel(f'MSE: {mse_const:.2f}, SSIM: {ssim_const:.2f}') |
| 68 | +ax[2].set_xlabel(f'MSE = {mse_const:.2f}, SSIM = {ssim_const:.2f}') |
68 | 69 | ax[2].set_title('Image plus constant') |
69 | 70 |
|
70 | 71 | plt.tight_layout() |
|
0 commit comments