Home | History | Annotate | Download | only in video-input-psnr-ssim
      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