1 #include <iostream> // for standard I/O
2 #include <string> // for strings
3 #include <iomanip> // for controlling float print precision
4 #include <sstream> // string to number conversion
5
6 #include <opencv2/core/core.hpp> // Basic OpenCV structures (cv::Mat, Scalar)
7 #include <opencv2/imgproc/imgproc.hpp> // Gaussian Blur
8 #include <opencv2/videoio/videoio.hpp>
9 #include <opencv2/highgui/highgui.hpp> // OpenCV window I/O
10
11 using namespace std;
12 using namespace cv;
13
14 double getPSNR ( const Mat& I1, const Mat& I2);
15 Scalar getMSSIM( const Mat& I1, const Mat& I2);
16
help()17 static void help()
18 {
19 cout
20 << "------------------------------------------------------------------------------" << endl
21 << "This program shows how to read a video file with OpenCV. In addition, it "
22 << "tests the similarity of two input videos first with PSNR, and for the frames "
23 << "below a PSNR trigger value, also with MSSIM." << endl
24 << "Usage:" << endl
25 << "./video-source referenceVideo useCaseTestVideo PSNR_Trigger_Value Wait_Between_Frames " << endl
26 << "--------------------------------------------------------------------------" << endl
27 << endl;
28 }
29
main(int argc,char * argv[])30 int main(int argc, char *argv[])
31 {
32 help();
33
34 if (argc != 5)
35 {
36 cout << "Not enough parameters" << endl;
37 return -1;
38 }
39
40 stringstream conv;
41
42 const string sourceReference = argv[1], sourceCompareWith = argv[2];
43 int psnrTriggerValue, delay;
44 conv << argv[3] << endl << argv[4]; // put in the strings
45 conv >> psnrTriggerValue >> delay; // take out the numbers
46
47 char c;
48 int frameNum = -1; // Frame counter
49
50 VideoCapture captRefrnc(sourceReference), captUndTst(sourceCompareWith);
51
52 if (!captRefrnc.isOpened())
53 {
54 cout << "Could not open reference " << sourceReference << endl;
55 return -1;
56 }
57
58 if (!captUndTst.isOpened())
59 {
60 cout << "Could not open case test " << sourceCompareWith << endl;
61 return -1;
62 }
63
64 Size refS = Size((int) captRefrnc.get(CAP_PROP_FRAME_WIDTH),
65 (int) captRefrnc.get(CAP_PROP_FRAME_HEIGHT)),
66 uTSi = Size((int) captUndTst.get(CAP_PROP_FRAME_WIDTH),
67 (int) captUndTst.get(CAP_PROP_FRAME_HEIGHT));
68
69 if (refS != uTSi)
70 {
71 cout << "Inputs have different size!!! Closing." << endl;
72 return -1;
73 }
74
75 const char* WIN_UT = "Under Test";
76 const char* WIN_RF = "Reference";
77
78 // Windows
79 namedWindow(WIN_RF, WINDOW_AUTOSIZE);
80 namedWindow(WIN_UT, WINDOW_AUTOSIZE);
81 moveWindow(WIN_RF, 400 , 0); //750, 2 (bernat =0)
82 moveWindow(WIN_UT, refS.width, 0); //1500, 2
83
84 cout << "Reference frame resolution: Width=" << refS.width << " Height=" << refS.height
85 << " of nr#: " << captRefrnc.get(CAP_PROP_FRAME_COUNT) << endl;
86
87 cout << "PSNR trigger value " << setiosflags(ios::fixed) << setprecision(3)
88 << psnrTriggerValue << endl;
89
90 Mat frameReference, frameUnderTest;
91 double psnrV;
92 Scalar mssimV;
93
94 for(;;) //Show the image captured in the window and repeat
95 {
96 captRefrnc >> frameReference;
97 captUndTst >> frameUnderTest;
98
99 if (frameReference.empty() || frameUnderTest.empty())
100 {
101 cout << " < < < Game over! > > > ";
102 break;
103 }
104
105 ++frameNum;
106 cout << "Frame: " << frameNum << "# ";
107
108 ///////////////////////////////// PSNR ////////////////////////////////////////////////////
109 psnrV = getPSNR(frameReference,frameUnderTest);
110 cout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";
111
112 //////////////////////////////////// MSSIM /////////////////////////////////////////////////
113 if (psnrV < psnrTriggerValue && psnrV)
114 {
115 mssimV = getMSSIM(frameReference, frameUnderTest);
116
117 cout << " MSSIM: "
118 << " R " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[2] * 100 << "%"
119 << " G " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[1] * 100 << "%"
120 << " B " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[0] * 100 << "%";
121 }
122
123 cout << endl;
124
125 ////////////////////////////////// Show Image /////////////////////////////////////////////
126 imshow(WIN_RF, frameReference);
127 imshow(WIN_UT, frameUnderTest);
128
129 c = (char)waitKey(delay);
130 if (c == 27) break;
131 }
132
133 return 0;
134 }
135
getPSNR(const Mat & I1,const Mat & I2)136 double getPSNR(const Mat& I1, const Mat& I2)
137 {
138 Mat s1;
139 absdiff(I1, I2, s1); // |I1 - I2|
140 s1.convertTo(s1, CV_32F); // cannot make a square on 8 bits
141 s1 = s1.mul(s1); // |I1 - I2|^2
142
143 Scalar s = sum(s1); // sum elements per channel
144
145 double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels
146
147 if( sse <= 1e-10) // for small values return zero
148 return 0;
149 else
150 {
151 double mse = sse / (double)(I1.channels() * I1.total());
152 double psnr = 10.0 * log10((255 * 255) / mse);
153 return psnr;
154 }
155 }
156
getMSSIM(const Mat & i1,const Mat & i2)157 Scalar getMSSIM( const Mat& i1, const Mat& i2)
158 {
159 const double C1 = 6.5025, C2 = 58.5225;
160 /***************************** INITS **********************************/
161 int d = CV_32F;
162
163 Mat I1, I2;
164 i1.convertTo(I1, d); // cannot calculate on one byte large values
165 i2.convertTo(I2, d);
166
167 Mat I2_2 = I2.mul(I2); // I2^2
168 Mat I1_2 = I1.mul(I1); // I1^2
169 Mat I1_I2 = I1.mul(I2); // I1 * I2
170
171 /*************************** END INITS **********************************/
172
173 Mat mu1, mu2; // PRELIMINARY COMPUTING
174 GaussianBlur(I1, mu1, Size(11, 11), 1.5);
175 GaussianBlur(I2, mu2, Size(11, 11), 1.5);
176
177 Mat mu1_2 = mu1.mul(mu1);
178 Mat mu2_2 = mu2.mul(mu2);
179 Mat mu1_mu2 = mu1.mul(mu2);
180
181 Mat sigma1_2, sigma2_2, sigma12;
182
183 GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
184 sigma1_2 -= mu1_2;
185
186 GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
187 sigma2_2 -= mu2_2;
188
189 GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
190 sigma12 -= mu1_mu2;
191
192 ///////////////////////////////// FORMULA ////////////////////////////////
193 Mat t1, t2, t3;
194
195 t1 = 2 * mu1_mu2 + C1;
196 t2 = 2 * sigma12 + C2;
197 t3 = t1.mul(t2); // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))
198
199 t1 = mu1_2 + mu2_2 + C1;
200 t2 = sigma1_2 + sigma2_2 + C2;
201 t1 = t1.mul(t2); // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))
202
203 Mat ssim_map;
204 divide(t3, t1, ssim_map); // ssim_map = t3./t1;
205
206 Scalar mssim = mean(ssim_map); // mssim = average of ssim map
207 return mssim;
208 }
209