Peak Signal-to-Noise Ratio (PSNR) in Python
Quantitatively check the quality of a compressed image using a simple Python code for calculating the Peak Signal-to-Noise Ratio (PSNR) between two images.
When images are compressed, resized or converted to different formats, there can be a loss of fidelity between the original and the copy.
A common method for quantitatively checking the effect that format changes or compression may have had on an image is to use the peak signal-to-noise ratio (PSNR), which is the ratio between the maximum possible power of an image and the power of corrupting noise that affects the quality of its representation.
Mathematically, PSNR is represented as follows:
\[PSNR = 20\times\log_{10}\left(\frac{MAX_{f}}{\sqrt{MSE}}\right)\]
where the Mean Squared Error (MSE) is:
\[MSE = \displaystyle \frac{1}{mn} \sum\limits_{i=0}^{m-1}\displaystyle\sum\limits_{j=0}^{n-1}\lVert f(i,j)-g(i,j)\lVert^2\]
and:
- f represents the matrix data of our original image
- g represents the matrix data of our copied or degraded image in question
- m represents the numbers of rows of pixels of the images and i represents the index of that row
- n represents the number of columns of pixels of the image and j represents the index of that column
- MAXf is the maximum signal value that exists in the original “known to be good” image
The mean squared error (MSE) permits a comparison of the “true” pixel values in the original image with our copied image. The MSE represents the average of the squares of the “errors” between the two. The error is the amount by which the values of the original image differ from the copied image.
PSNR is usually expressed in terms of the logarithmic decibel scale where the larger the value of PSNR, the more efficient is a corresponding compression or filter method.
Fortunately, it is fairly easy to implement PSNR in Python on the command line, using OpenCV:
#!/usr/bin/env python
from math import log10, sqrt
import cv2
import numpy as np
import argparse
def options():
parser = argparse.ArgumentParser(description="Read image metadata")
parser.add_argument("-o", "--original", help="Input image file.", required=True)
parser.add_argument("-c", "--copy", help="Input image file.", required=True)
args = parser.parse_args()
return args
def PSNR(original, compressed):
mse = np.mean((original - compressed) ** 2)
if(mse == 0):
return 100
max_pixel = 255.0
psnr = 20 * log10(max_pixel / sqrt(mse))
return psnr
def main():
# Get options
args = options()
# Import images
original = cv2.imread(args.original)
compressed = cv2.imread(args.copy, 1)
# Check for same size and ratio and report accordingly
ho, wo, _ = original.shape
hc, wc, _ = compressed.shape
ratio_orig = ho/wo
ratio_comp = hc/wc
dim = (wc, hc)
if round(ratio_orig, 2) != round(ratio_comp, 2):
print("\nImages not of the same dimension. Check input.")
exit()
# Resize original if the compressed image is smaller
elif ho > hc and wo > wc:
print("\nResizing original image for analysis...")
original = cv2.resize(original, dim)
elif ho < hc and wo < wc:
print("\nCompressed image has a larger dimension than the original. Check input.")
exit()
value = PSNR(original, compressed)
print("\nPeak Signal-to-Noise Ratio (PSNR) value is", value, "dB")
if __name__ == '__main__':
main()
The code takes two inputs on the command line:
-o
: A full path to the original image-c
: A full path to the comparison (copy or degraded) image
If the python file were saved as psnr.py, implemenation would be as follows: /path/to/psnr.py -o /path/to/original.ext -c /path/to/copy.ext
Before calculating PSNR, the above code will check that the two images are the same size. If the copy is smaller, then the original will be reduced to the same dimensions using OpenCV’s cv2.resize()
function which uses a bilinear interpolation. (Ideally, the two images would have identical dimensions and this step would not be necessary but your application will determine this). The aspect ratio of each image is also checked before proceeding.
This code has been posted to GitHub Gist.
Comments
No comments have yet been submitted. Be the first!