1 // Video Image PSNR and SSIM
2 #include <iostream> // for standard I/O
3 #include <string> // for strings
4 #include <iomanip> // for controlling float print precision
5 #include <sstream> // string to number conversion
7 #include <opencv2/imgproc/imgproc.hpp> // Gaussian Blur
8 #include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
9 #include <opencv2/highgui/highgui.hpp> // OpenCV window I/O
14 double getPSNR ( const Mat& I1, const Mat& I2);
15 Scalar getMSSIM( const Mat& I1, const Mat& I2);
20 << "\n--------------------------------------------------------------------------" << endl
21 << "This program shows how to read a video file with OpenCV. In addition, it tests the"
22 << " similarity of two input videos first with PSNR, and for the frames below a PSNR " << endl
23 << "trigger value, also with MSSIM."<< endl
25 << "./video-source referenceVideo useCaseTestVideo PSNR_Trigger_Value Wait_Between_Frames " << endl
26 << "--------------------------------------------------------------------------" << endl
29 int main(int argc, char *argv[])
34 cout << "Not enough parameters" << endl;
39 const string sourceReference = argv[1],sourceCompareWith = argv[2];
40 int psnrTriggerValue, delay;
41 conv << argv[3] << argv[4]; // put in the strings
42 conv >> psnrTriggerValue >> delay;// take out the numbers
45 int frameNum = -1; // Frame counter
47 VideoCapture captRefrnc(sourceReference),
48 captUndTst(sourceCompareWith);
50 if ( !captRefrnc.isOpened())
52 cout << "Could not open reference " << sourceReference << endl;
56 if( !captUndTst.isOpened())
58 cout << "Could not open case test " << sourceCompareWith << endl;
62 Size refS = Size((int) captRefrnc.get(CAP_PROP_FRAME_WIDTH),
63 (int) captRefrnc.get(CAP_PROP_FRAME_HEIGHT)),
64 uTSi = Size((int) captUndTst.get(CAP_PROP_FRAME_WIDTH),
65 (int) captUndTst.get(CAP_PROP_FRAME_HEIGHT));
69 cout << "Inputs have different size!!! Closing." << endl;
73 const char* WIN_UT = "Under Test";
74 const char* WIN_RF = "Reference";
77 namedWindow(WIN_RF, WINDOW_AUTOSIZE );
78 namedWindow(WIN_UT, WINDOW_AUTOSIZE );
79 moveWindow(WIN_RF, 400 , 0); //750, 2 (bernat =0)
80 moveWindow(WIN_UT, refS.width, 0); //1500, 2
82 cout << "Frame resolution: Width=" << refS.width << " Height=" << refS.height
83 << " of nr#: " << captRefrnc.get(CAP_PROP_FRAME_COUNT) << endl;
85 cout << "PSNR trigger value " <<
86 setiosflags(ios::fixed) << setprecision(3) << psnrTriggerValue << endl;
88 Mat frameReference, frameUnderTest;
92 for(;;) //Show the image captured in the window and repeat
94 captRefrnc >> frameReference;
95 captUndTst >> frameUnderTest;
97 if( frameReference.empty() || frameUnderTest.empty())
99 cout << " < < < Game over! > > > ";
104 cout <<"Frame:" << frameNum;
106 ///////////////////////////////// PSNR ////////////////////////////////////////////////////
107 psnrV = getPSNR(frameReference,frameUnderTest); //get PSNR
108 cout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";
110 //////////////////////////////////// MSSIM /////////////////////////////////////////////////
111 if (psnrV < psnrTriggerValue)
113 mssimV = getMSSIM(frameReference,frameUnderTest);
116 << "R" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[2] * 100
117 << "G" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[1] * 100
118 << "B" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[0] * 100;
123 ////////////////////////////////// Show Image /////////////////////////////////////////////
124 imshow( WIN_RF, frameReference);
125 imshow( WIN_UT, frameUnderTest);
127 c = (char)waitKey(delay);
134 double getPSNR(const Mat& I1, const Mat& I2)
137 absdiff(I1, I2, s1); // |I1 - I2|
138 s1.convertTo(s1, CV_32F); // cannot make a square on 8 bits
139 s1 = s1.mul(s1); // |I1 - I2|^2
141 Scalar s = sum(s1); // sum elements per channel
143 double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels
145 if( sse <= 1e-10) // for small values return zero
149 double mse =sse /(double)(I1.channels() * I1.total());
150 double psnr = 10.0*log10((255*255)/mse);
155 Scalar getMSSIM( const Mat& i1, const Mat& i2)
157 const double C1 = 6.5025, C2 = 58.5225;
158 /***************************** INITS **********************************/
162 i1.convertTo(I1, d); // cannot calculate on one byte large values
165 Mat I2_2 = I2.mul(I2); // I2^2
166 Mat I1_2 = I1.mul(I1); // I1^2
167 Mat I1_I2 = I1.mul(I2); // I1 * I2
169 /*************************** END INITS **********************************/
171 Mat mu1, mu2; // PRELIMINARY COMPUTING
172 GaussianBlur(I1, mu1, Size(11, 11), 1.5);
173 GaussianBlur(I2, mu2, Size(11, 11), 1.5);
175 Mat mu1_2 = mu1.mul(mu1);
176 Mat mu2_2 = mu2.mul(mu2);
177 Mat mu1_mu2 = mu1.mul(mu2);
179 Mat sigma1_2, sigma2_2, sigma12;
181 GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
184 GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
187 GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
190 ///////////////////////////////// FORMULA ////////////////////////////////
193 t1 = 2 * mu1_mu2 + C1;
194 t2 = 2 * sigma12 + C2;
195 t3 = t1.mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
197 t1 = mu1_2 + mu2_2 + C1;
198 t2 = sigma1_2 + sigma2_2 + C2;
199 t1 = t1.mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
202 divide(t3, t1, ssim_map); // ssim_map = t3./t1;
204 Scalar mssim = mean( ssim_map ); // mssim = average of ssim map