对视频逐帧检查相似性或差异性,最常用的算法是PSNR(又名峰值信噪比)。最简单的定义是从平方差开始。设两个图像:
I
1
I1
I1 和
I
2
I2
I2 ;具有二维尺寸
i
i
i 和
j
j
j,由
c
c
c 个通道组成。
M
S
E
=
1
c
∗
i
∗
j
∑
(
I
1
−
I
2
)
2
MSE=\frac{1}{c*i*j}\sum(I_1-I_2)^2
MSE=c∗i∗j1∑(I1−I2)2
则PSNR表示为:
P
S
N
R
=
10
∗
log
10
(
M
A
X
I
2
M
S
E
)
PSNR=10*\log_{10}(\frac{MAX_I^2}{MSE})
PSNR=10∗log10(MSEMAXI2)
这里MAXI
是像素的最大有效值。对于每个通道每个像素的简单单字节图像,这里是
255
255
255。当两幅图像相同时,MSE
将给出零,从而导致PSNR
公式中的除零运算无效。在这种情况下,PSNR
是未定义的,因此我们需要单独处理这个问题。由于像素值具有非常宽的动态范围,所以可以转换到对数比例。所有这些转换为OpenCV
和C++
函数看起来像:
#!/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):
"""
PSNR算法:计算图像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] end
# [get-mssim]
def getMSSISM(i1, i2):
"""
SSIM算法:计算图像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] end
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()