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 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 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 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 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