• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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