video-input-psnr-ssim.cpp 6.7 KB

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