Merge pull request #16586 from themechanicalcoder:video-psnr

* add  python version of video-input-psnr-ssim

* remove ret

* documentation changes

* added link for python file

* command line argument
pull/16652/head
Gourav Roy 5 years ago committed by GitHub
parent 95f0c9b19b
commit 8b5efc6f4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 95
      doc/tutorials/videoio/video-input-psnr-ssim/video_input_psnr_ssim.markdown
  2. 5
      samples/cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp
  3. 148
      samples/python/tutorial_code/videoio/video-input-psnr-ssim.py

@ -25,7 +25,13 @@ version of it ](https://github.com/opencv/opencv/tree/3.4/samples/data/Megamind_
You may also find the source code and these video file in the
`samples/data` folder of the OpenCV source library.
@add_toggle_cpp
@include cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp
@end_toggle
@add_toggle_python
@include samples/python/tutorial_code/videoio/video-input-psnr-ssim.py
@end_toggle
How to read a video stream (online-camera or offline-file)?
-----------------------------------------------------------
@ -139,28 +145,15 @@ an invalid divide by zero operation in the PSNR formula. In this case the PSNR i
we'll need to handle this case separately. The transition to a logarithmic scale is made because the
pixel values have a very wide dynamic range. All this translated to OpenCV and a C++ function looks
like:
@code{.cpp}
double getPSNR(const Mat& I1, const Mat& I2)
{
Mat s1;
absdiff(I1, I2, s1); // |I1 - I2|
s1.convertTo(s1, CV_32F); // cannot make a square on 8 bits
s1 = s1.mul(s1); // |I1 - I2|^2
Scalar s = sum(s1); // sum elements per channel
double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels
if( sse <= 1e-10) // for small values return zero
return 0;
else
{
double mse =sse /(double)(I1.channels() * I1.total());
double psnr = 10.0*log10((255*255)/mse);
return psnr;
}
}
@endcode
@add_toggle_cpp
@include cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp get-psnr
@end_toggle
@add_toggle_python
@include samples/python/tutorial_code/videoio/video-input-psnr-ssim.py get-psnr
@end_toggle
Typically result values are anywhere between 30 and 50 for video compression, where higher is
better. If the images significantly differ you'll get much lower ones like 15 and so. This
similarity check is easy and fast to calculate, however in practice it may turn out somewhat
@ -176,60 +169,14 @@ implementation below.
Simoncelli, "Image quality assessment: From error visibility to structural similarity," IEEE
Transactions on Image Processing, vol. 13, no. 4, pp. 600-612, Apr. 2004." article.
@code{.cpp}
Scalar getMSSIM( const Mat& i1, const Mat& i2)
{
const double C1 = 6.5025, C2 = 58.5225;
/***************************** INITS **********************************/
int d = CV_32F;
Mat I1, I2;
i1.convertTo(I1, d); // cannot calculate on one byte large values
i2.convertTo(I2, d);
Mat I2_2 = I2.mul(I2); // I2^2
Mat I1_2 = I1.mul(I1); // I1^2
Mat I1_I2 = I1.mul(I2); // I1 * I2
@add_toggle_cpp
@include cpp/tutorial_code/videoio/video-input-psnr-ssim/video-input-psnr-ssim.cpp get-mssim
@end_toggle
/***********************PRELIMINARY COMPUTING ******************************/
@add_toggle_python
@include samples/python/tutorial_code/videoio/video-input-psnr-ssim.py get-mssim
@end_toggle
Mat mu1, mu2; //
GaussianBlur(I1, mu1, Size(11, 11), 1.5);
GaussianBlur(I2, mu2, Size(11, 11), 1.5);
Mat mu1_2 = mu1.mul(mu1);
Mat mu2_2 = mu2.mul(mu2);
Mat mu1_mu2 = mu1.mul(mu2);
Mat sigma1_2, sigma2_2, sigma12;
GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
sigma1_2 -= mu1_2;
GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
sigma2_2 -= mu2_2;
GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
sigma12 -= mu1_mu2;
///////////////////////////////// FORMULA ////////////////////////////////
Mat t1, t2, t3;
t1 = 2 * mu1_mu2 + C1;
t2 = 2 * sigma12 + C2;
t3 = t1.mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
t1 = mu1_2 + mu2_2 + C1;
t2 = sigma1_2 + sigma2_2 + C2;
t1 = t1.mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
Mat ssim_map;
divide(t3, t1, ssim_map); // ssim_map = t3./t1;
Scalar mssim = mean( ssim_map ); // mssim = average of ssim map
return mssim;
}
@endcode
This will return a similarity index for each channel of the image. This value is between zero and
one, where one corresponds to perfect fit. Unfortunately, the many Gaussian blurring is quite
costly, so while the PSNR may work in a real time like environment (24 frame per second) this will

@ -132,6 +132,7 @@ int main(int argc, char *argv[])
return 0;
}
// ![get-psnr]
double getPSNR(const Mat& I1, const Mat& I2)
{
Mat s1;
@ -152,6 +153,9 @@ double getPSNR(const Mat& I1, const Mat& I2)
return psnr;
}
}
// ![get-psnr]
// ![get-mssim]
Scalar getMSSIM( const Mat& i1, const Mat& i2)
{
@ -205,3 +209,4 @@ Scalar getMSSIM( const Mat& i1, const Mat& i2)
Scalar mssim = mean(ssim_map); // mssim = average of ssim map
return mssim;
}
// ![get-mssim]

@ -0,0 +1,148 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2 as cv
import argparse
import sys
# [get-psnr]
def getPSNR(I1, I2):
s1 = cv.absdiff(I1, I2) #|I1 - I2|
s1 = np.float32(s1) # cannot make a square on 8 bits
s1 = s1 * s1 # |I1 - I2|^2
sse = s1.sum() # sum elements per channel
if sse <= 1e-10: # sum channels
return 0 # for small values return zero
else:
shape = I1.shape
mse = 1.0 * sse / (shape[0] * shape[1] * shape[2])
psnr = 10.0 * np.log10((255 * 255) / mse)
return psnr
# [get-psnr]
# [get-mssim]
def getMSSISM(i1, i2):
C1 = 6.5025
C2 = 58.5225
# INITS
I1 = np.float32(i1) # cannot calculate on one byte large values
I2 = np.float32(i2)
I2_2 = I2 * I2 # I2^2
I1_2 = I1 * I1 # I1^2
I1_I2 = I1 * I2 # I1 * I2
# END INITS
# PRELIMINARY COMPUTING
mu1 = cv.GaussianBlur(I1, (11, 11), 1.5)
mu2 = cv.GaussianBlur(I2, (11, 11), 1.5)
mu1_2 = mu1 * mu1
mu2_2 = mu2 * mu2
mu1_mu2 = mu1 * mu2
sigma1_2 = cv.GaussianBlur(I1_2, (11, 11), 1.5)
sigma1_2 -= mu1_2
sigma2_2 = cv.GaussianBlur(I2_2, (11, 11), 1.5)
sigma2_2 -= mu2_2
sigma12 = cv.GaussianBlur(I1_I2, (11, 11), 1.5)
sigma12 -= mu1_mu2
t1 = 2 * mu1_mu2 + C1
t2 = 2 * sigma12 + C2
t3 = t1 * t2 # t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
t1 = mu1_2 + mu2_2 + C1
t2 = sigma1_2 + sigma2_2 + C2
t1 = t1 * t2 # t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
ssim_map = cv.divide(t3, t1) # ssim_map = t3./t1;
mssim = cv.mean(ssim_map) # mssim = average of ssim map
return mssim
# [get-mssim]
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-d", "--delay", type=int, default=30, help=" Time delay")
parser.add_argument("-v", "--psnrtriggervalue", type=int, default=30, help="PSNR Trigger Value")
parser.add_argument("-r", "--ref", type=str, default="Megamind.avi", help="Path to reference video")
parser.add_argument("-t", "--undertest", type=str, default="Megamind_bugy.avi",
help="Path to the video to be tested")
args = parser.parse_args()
sourceReference = args.ref
sourceCompareWith = args.undertest
delay = args.delay
psnrTriggerValue = args.psnrtriggervalue
framenum = -1 # Frame counter
captRefrnc = cv.VideoCapture(sourceReference)
captUndTst = cv.VideoCapture(sourceCompareWith)
if not captRefrnc.isOpened():
print("Could not open the reference " + sourceReference)
sys.exit(-1)
if not captUndTst.isOpened():
print("Could not open case test " + sourceCompareWith)
sys.exit(-1)
refS = (int(captRefrnc.get(cv.CAP_PROP_FRAME_WIDTH)), int(captRefrnc.get(cv.CAP_PROP_FRAME_HEIGHT)))
uTSi = (int(captUndTst.get(cv.CAP_PROP_FRAME_WIDTH)), int(captUndTst.get(cv.CAP_PROP_FRAME_HEIGHT)))
if refS != uTSi:
print("Inputs have different size!!! Closing.")
sys.exit(-1)
WIN_UT = "Under Test"
WIN_RF = "Reference"
cv.namedWindow(WIN_RF, cv.WINDOW_AUTOSIZE)
cv.namedWindow(WIN_UT, cv.WINDOW_AUTOSIZE)
cv.moveWindow(WIN_RF, 400, 0) #750, 2 (bernat =0)
cv.moveWindow(WIN_UT, refS[0], 0) #1500, 2
print("Reference frame resolution: Width={} Height={} of nr#: {}".format(refS[0], refS[1],
captRefrnc.get(cv.CAP_PROP_FRAME_COUNT)))
print("PSNR trigger value {}".format(psnrTriggerValue))
while True: # Show the image captured in the window and repeat
_, frameReference = captRefrnc.read()
_, frameUnderTest = captUndTst.read()
if frameReference is None or frameUnderTest is None:
print(" < < < Game over! > > > ")
break
framenum += 1
psnrv = getPSNR(frameReference, frameUnderTest)
print("Frame: {}# {}dB".format(framenum, round(psnrv, 3)), end=" ")
if (psnrv < psnrTriggerValue and psnrv):
mssimv = getMSSISM(frameReference, frameUnderTest)
print("MSSISM: R {}% G {}% B {}%".format(round(mssimv[2] * 100, 2), round(mssimv[1] * 100, 2),
round(mssimv[0] * 100, 2)), end=" ")
print()
cv.imshow(WIN_RF, frameReference)
cv.imshow(WIN_UT, frameUnderTest)
k = cv.waitKey(delay)
if k == 27:
break
sys.exit(0)
if __name__ == "__main__":
main()
Loading…
Cancel
Save