# Structural Similarity Index, Peak Signal-to-Noise Ratio

When comparing images, the **Mean Squared Error** (or MSE), though straightforward to calculate, may not be a very good indicator of their *perceived* similarity.

The **Structural Similarity Index** (or SSIM) aims to address this shortcoming by taking texture into account, and assigning a higher score to images that may *appear* similar.

```
using Images, TestImages
using Random
img_orig = float64.(testimage("cameraman"))
```

We use a grayscale image out of the `TestImages`

package, which provides a standard suite of test images. `float`

/`float32`

/`float64`

preserve colorant information: thus the image is now composed of pixels of type `Gray{Float64}`

.

`assess_ssim(img_orig, img_orig)`

`1.0`

The `assess_ssim`

function, which takes two images as inputs and returns their structural similarity index, is the simplest way to calculate the SSIM of two images.

An SSIM score of `1.00`

indicates perfect structural similarity, as is expected out of identical images.

Now, we create two variations of the original image: `image_const`

on the left has the intensity of all its pixels increased by `0.2`

times the intensity range, while `image_noise`

on the right has the intensity of some of its pixels increased, and that of the others decreased by the same amount. The two images look quite different visually.

```
noise = ones(size(img_orig)) .* 0.2 .* (maximum(img_orig) - minimum(img_orig))
img_const = img_orig + noise
mask = rand(Float64, size(img_orig)) .< 0.5
noise[mask] = noise[mask] .* -1
img_noise = img_orig + noise
mosaicview(img_const, img_noise; nrow=1)
```

We use the `mse`

funtion defined in `ImageDistances`

to calculate the mean squared error between the original and the two modified images.

`mse(img_orig, img_const), mse(img_orig, img_noise)`

`(0.039999999999999016, 0.039999999999999016)`

Despite their visual differences, both the images have the exact same mean squared error of `0.400`

, when compared with the original. This demonstrates how in certain cases, MSE can fail to capture the *perceived* similarity of images.

`assess_ssim(img_orig, img_const), assess_ssim(img_orig, img_noise)`

`(0.8406360281731596, 0.1045754516237563)`

Their SSIM scores vary significantly, with `image_const`

being rated much closer to the original image in terms of perceived similarity, which is in line with what visually seems to be the case.

### Custom Parameters

While `assess_ssim`

is a convenient way to calculate the SSIM of two images, it does not allow for custom parameters to be passed to the SSIM algorithm, for which we have the following syntax.

```
iqi = SSIM(KernelFactors.gaussian(2.0, 11), (0.5, 0.5, 0.5))
assess(iqi, img_orig, img_const)
```

`0.9085673404877026`

Here, the first parameter is the kernel used to weight the neighbourhood of each pixel while calculating the SSIM locally, and defaults to `KernelFactors.gaussian(1.5, 11)`

. The second parameter is the set of weights (α, β, γ) given to the *lunimance* (L), *contrast* (C) and *structure* (S) terms while calculating the SSIM, and defaults to `(1.0, 1.0, 1.0)`

. Recall that SSIM is defined as Lᵅ × Cᵝ × Sᵞ.

# Peak signal-to-noise ratio

Peak signal-to-noise ratio (PSNR) is used to measure the quality of image in present of noise and corruption. It is the ratio between the maximum possible power of a signal and the power of corrupting noise that affects representation fidelity.

Given a noise-free m×n monochrome image I and corresponding noise image K. 'MSE' is defined as:

\[\mathit{MSE} = \frac{1}{m\,n}\sum_{i=0}^{m-1}\sum_{j=0}^{n-1} [I(i,j) - K(i,j)]^2\]

The PSNR (in dB) is defined as:

\[\begin{align}\mathit{PSNR} &= 10 \cdot \log_{10} \left( \frac{\mathit{MAX}_I^2}{\mathit{MSE}} \right)\\ &= 20 \cdot \log_{10} \left( \frac{\mathit{MAX}_I}{\sqrt{\mathit{MSE}}} \right)\\ &= 20 \cdot \log_{10} \left( {\mathit{MAX}_I} \right) - 10 \cdot \log_{10} \left( {{\mathit{MSE}}} \right)\end{align}\]

Here, $MAX_I$ or peakval is the maximum possible pixel value of the image which can be provided in `assess_psnr`

function like `assess_psnr(K, I, [1.0])`

for monochrome images or `assess_psnr(K, I, [1.0,1.0,1.0])`

for multi channel images.

```
peakval = maximum(img_orig) .|> Float64 # peakval is max pixel value in original image
assess_psnr(img_noise, img_orig, [peakval]) # 13.979400086720483
```

```
1-element Vector{Float64}:
13.979400086720483
```

Let's change the contents of the noisy image before applying PSNR. Note that peakval is not required to be passed as it can be automatically calculated too.

```
noise = ones(size(img_orig)) .* 0.2 .* (maximum(img_orig) - minimum(img_orig))
mask = rand(Float64, size(img_orig)) .< 0.5
noise[mask] = noise[mask] .* -3
img_noise = img_orig + noise
assess_psnr(img_noise, img_orig)
```

`6.994978188506817`

Generally, for non-gray image `K`

, PSNR is reported against each channel of `I`

and outputs a `Vector`

, `peakval`

needs to be a vector as well.

### References

- 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.
- 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.

*This page was generated using DemoCards.jl and Literate.jl.*