Small modifications to the scan tutorial and added the video-input-psnr-ssim sample...
authorBernat Gabor <no@email>
Tue, 9 Aug 2011 05:43:58 +0000 (05:43 +0000)
committerBernat Gabor <no@email>
Tue, 9 Aug 2011 05:43:58 +0000 (05:43 +0000)
doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst
samples/cpp/tutorial_code/HighGUI/video-input-psnr-sssim/video-input-psnr-sssim.cpp [new file with mode: 0644]
samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp
samples/cpp/tutorial_code/introduction/windows_visual_studio_Opencv/Test.cpp

index e521b82..11b0d6b 100644 (file)
@@ -126,16 +126,18 @@ In case of the efficient way making sure that you pass through the right amount
 \r
 In case of color images we have three uchar items per column. This may be considered a short vector of uchar items, that has been baptized in OpenCV with the *Vec3b* name. To access the n-th sub column we use simple operator[] access. It's important to remember that OpenCV iterators go through the columns and automatically skip to the next row. Therefore in case of color images if you use a simple *uchar* iterator you'll be able to access only the blue channel values. \r
 \r
-Random Pixel Access\r
-===================\r
+On-the-fly address calculation with reference returning\r
+=======================================================\r
 \r
-The final method isn't recommended for scanning. It was made to acquire or modify random elements in the image. Its basic usage is to specify the row and column number of the item you want to access. During our earlier scanning methods you could already observe that is important through what type we are looking at the image. It's no different here as you need manually to specify what type to use at the automatic lookup. You can observe this in case of the gray scale images for the following source code (the usage of the + :basicstructures:`at() <mat-at>` function):\r
+The final method isn't recommended for scanning. It was made to acquire or modify somehow random elements in the image. Its basic usage is to specify the row and column number of the item you want to access. During our earlier scanning methods you could already observe that is important through what type we are looking at the image. It's no different here as you need manually to specify what type to use at the automatic lookup. You can observe this in case of the gray scale images for the following source code (the usage of the + :basicstructures:`at() <mat-at>` function):\r
 \r
 .. literalinclude:: ../../../../samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp\r
    :language: cpp\r
    :tab-width: 4\r
    :lines: 175-207\r
 \r
+The functions takes your input type and coordinates and calculates on the fly the address of the queried item. Then returns a reference to that. This may be a constant when you *get* the value and non-constant when you *set* the value. As a safety step in **debug mode only*** there is performed a check that your input coordinates are valid and does exist. If this isn't the case you'll get a nice output message of this on the standard error output stream. Compared to the efficient way in release mode the only difference in using this is that for every element of the image you'll get a new row pointer for what we use the C operator[] to acquire the column element. \r
+\r
 If you need to multiple lookups using this method for an image it may be troublesome and time consuming to enter the type and the at keyword for each of the accesses. To solve this problem OpenCV has a :basicstructures:`Mat_ <id3>` data type. It's the same as Mat with the extra need that at definition you need to specify the data type through what to look at the data matrix, however in return you can use the operator() for fast access of items. To make things even better this is easily convertible from and to the usual :basicstructures:`Mat <id3>` data type. A sample usage of this you can see in case of the color images of the upper function. Nevertheless, it's important to note that the same operation (with the same runtime speed) could have been done with the :basicstructures:`at() <mat-at>` function. It's just a less to write for the lazy programmer trick.\r
 \r
 The Core Function\r
@@ -165,12 +167,12 @@ Efficient Way 79.4717 milliseconds
 \r
 Iterator      83.7201 milliseconds\r
 \r
-Random Access 93.7878 milliseconds\r
+On-The-Fly RA 93.7878 milliseconds\r
 \r
 LUT function  32.5759 milliseconds\r
 ============= ====================\r
 \r
-We can conclude a couple of things. If possible, use the already made functions of OpenCV (instead reinventing these). The fastest method turns out to be the LUT function. This is because the OpenCV library is multi-thread enabled via Intel Threaded Building Blocks. However, if you need to write a simple image scan prefer the pointer method. The iterator is a safer bet, however quite slower. Using the random access method for full image scan is the most costly. Your compiler might observe what you want to do and optimize it out somewhat, however in general case this approach is with a little slower than the iterator method.\r
+We can conclude a couple of things. If possible, use the already made functions of OpenCV (instead reinventing these). The fastest method turns out to be the LUT function. This is because the OpenCV library is multi-thread enabled via Intel Threaded Building Blocks. However, if you need to write a simple image scan prefer the pointer method. The iterator is a safer bet, however quite slower. Using the on-the-fly reference access method for full image scan is the most costly in debug mode. In the release mode it may beat the iterator approach or not, however it surely sacrifices for this the safety trait of iterators.\r
 \r
 Finally, you may watch a sample run of the program on the `video posted <https://www.youtube.com/watch?v=fB3AN5fjgwc>`_ on our YouTube channel.\r
 \r
diff --git a/samples/cpp/tutorial_code/HighGUI/video-input-psnr-sssim/video-input-psnr-sssim.cpp b/samples/cpp/tutorial_code/HighGUI/video-input-psnr-sssim/video-input-psnr-sssim.cpp
new file mode 100644 (file)
index 0000000..11cfa00
--- /dev/null
@@ -0,0 +1,206 @@
+// Video Image PSNR and SSIM\r
+#include <iostream>    // for standard I/O\r
+#include <string>   // for strings\r
+#include <iomanip>  // for controlling float print precision\r
+#include <sstream>  // string to number conversion\r
+\r
+#include <opencv2/imgproc/imgproc.hpp>  // Gaussian Blur\r
+#include <opencv2/core/core.hpp>        // Basic OpenCV structures (cv::Mat, Scalar)\r
+#include <opencv2/highgui/highgui.hpp>  // OpenCV window I/O\r
+\r
+using namespace std;\r
+using namespace cv;\r
+\r
+double getPSNR ( const Mat& I1, const Mat& I2);\r
+Scalar getMSSIM( const Mat& I1, const Mat& I2);\r
+\r
+void help()\r
+{\r
+    cout\r
+        << "\n--------------------------------------------------------------------------" << endl\r
+        << "This program shows how to read a video file with OpenCV. In addition, it tests the"\r
+        << " similarity of two input videos first with PSNR, and for the frames below a PSNR "  << endl\r
+        << "trigger value, also with MSSIM."<< endl\r
+        << "Usage:"                                                                       << endl\r
+        << "./video-source referenceVideo useCaseTestVideo PSNR_Trigger_Value Wait_Between_Frames " << endl\r
+        << "--------------------------------------------------------------------------"   << endl\r
+        << endl;\r
+}\r
+int main(int argc, char *argv[], char *window_name)\r
+{\r
+    help();\r
+    if (argc != 5)\r
+    {\r
+        cout << "Not enough parameters" << endl;\r
+        return -1;\r
+    }\r
+    stringstream conv;\r
+\r
+    const string sourceReference = argv[1],sourceCompareWith = argv[2];\r
+    int psnrTriggerValue, delay;\r
+    conv << argv[3] << argv[4];                  // put in the strings\r
+    conv >> psnrTriggerValue >> delay;// take out the numbers\r
+\r
+    char c;\r
+    int frameNum = -1;                 // Frame counter\r
+\r
+    VideoCapture captRefrnc(sourceReference),\r
+                 captUndTst(sourceCompareWith);\r
+\r
+    if ( !captRefrnc.isOpened())\r
+    {\r
+        cout  << "Could not open reference " << sourceReference << endl;\r
+        return -1;\r
+    }\r
+\r
+    if( !captUndTst.isOpened())\r
+    {\r
+        cout  << "Could not open case test " << sourceCompareWith << endl;\r
+        return -1;\r
+    }\r
+\r
+    Size refS = Size((int) captRefrnc.get(CV_CAP_PROP_FRAME_WIDTH),\r
+                     (int) captRefrnc.get(CV_CAP_PROP_FRAME_HEIGHT)),\r
+         uTSi = Size((int) captUndTst.get(CV_CAP_PROP_FRAME_WIDTH),\r
+                     (int) captUndTst.get(CV_CAP_PROP_FRAME_HEIGHT));\r
+\r
+    if (refS != uTSi)\r
+    {\r
+        cout << "Inputs have different size!!! Closing." << endl;\r
+        return -1;\r
+    }\r
+\r
+    const char* WIN_UT = "Under Test";\r
+    const char* WIN_RF = "Reference";\r
+\r
+    // Windows\r
+            namedWindow(WIN_RF, CV_WINDOW_AUTOSIZE );\r
+            namedWindow(WIN_UT, CV_WINDOW_AUTOSIZE );\r
+            cvMoveWindow(WIN_RF, 400       ,            0);             //750,  2 (bernat =0)\r
+            cvMoveWindow(WIN_UT, refS.width,            0);             //1500, 2\r
+\r
+    cout << "Frame resolution: Width=" << refS.width << "  Height=" << refS.height\r
+         << " of nr#: " << captRefrnc.get(CV_CAP_PROP_FRAME_COUNT) << endl;\r
+\r
+    cout << "PSNR trigger value " <<\r
+          setiosflags(ios::fixed) << setprecision(3) << psnrTriggerValue << endl;\r
+\r
+    Mat frameReference, frameUnderTest;\r
+    double psnrV;\r
+    Scalar mssimV;\r
+\r
+    while( true) //Show the image captured in the window and repeat\r
+    {\r
+        captRefrnc >> frameReference;\r
+        captUndTst >> frameUnderTest;\r
+\r
+        if( frameReference.empty()  || frameUnderTest.empty())\r
+        {\r
+            cout << " < < <  Game over!  > > > ";\r
+            break;\r
+        }\r
+\r
+        ++frameNum;\r
+        cout <<"Frame:" << frameNum;\r
+\r
+        ///////////////////////////////// PSNR ////////////////////////////////////////////////////\r
+        psnrV = getPSNR(frameReference,frameUnderTest);                                        //get PSNR\r
+        cout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";\r
+\r
+        //////////////////////////////////// MSSIM /////////////////////////////////////////////////\r
+        if (psnrV < psnrTriggerValue)\r
+        {\r
+            mssimV = getMSSIM(frameReference,frameUnderTest);\r
+\r
+            cout << " MSSIM: "\r
+                 << "R" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[2] * 100\r
+                 << "G" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[1] * 100\r
+                 << "B" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[0] * 100;\r
+        }\r
+\r
+       cout << endl;\r
+\r
+        ////////////////////////////////// Show Image /////////////////////////////////////////////\r
+        imshow( WIN_RF, frameReference);\r
+        imshow( WIN_UT, frameUnderTest);\r
+\r
+        c = cvWaitKey(delay);\r
+        if (c == 27) break;\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+double getPSNR(const Mat& I1, const Mat& I2)\r
+{\r
+    Mat s1;\r
+    absdiff(I1, I2, s1);       // |I1 - I2|\r
+    s1.convertTo(s1, CV_32F);  // cannot make a square on 8 bits\r
+    s1 = s1.mul(s1);           // |I1 - I2|^2\r
+\r
+    Scalar s = sum(s1);         // sum elements per channel\r
+\r
+    double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels\r
+\r
+    if( sse <= 1e-10) // for small values return zero\r
+        return 0;\r
+    else\r
+    {\r
+        double  mse =sse /(double)(I1.channels() * I1.total());\r
+        double psnr = 10.0*log10((255*255)/mse);\r
+        return psnr;\r
+    }\r
+}\r
+\r
+Scalar getMSSIM( const Mat& i1, const Mat& i2)\r
+{\r
+    const double C1 = 6.5025, C2 = 58.5225;\r
+    /***************************** INITS **********************************/\r
+    int d     = CV_32F;\r
+\r
+    Mat I1, I2;\r
+    i1.convertTo(I1, d);           // cannot calculate on one byte large values\r
+    i2.convertTo(I2, d);\r
+\r
+    Mat I2_2   = I2.mul(I2);        // I2^2\r
+    Mat I1_2   = I1.mul(I1);        // I1^2\r
+    Mat I1_I2  = I1.mul(I2);        // I1 * I2\r
+\r
+    /*************************** END INITS **********************************/\r
+\r
+    Mat mu1, mu2;   // PRELIMINARY COMPUTING\r
+    GaussianBlur(I1, mu1, Size(11, 11), 1.5);\r
+    GaussianBlur(I2, mu2, Size(11, 11), 1.5);\r
+\r
+    Mat mu1_2   =   mu1.mul(mu1);\r
+    Mat mu2_2   =   mu2.mul(mu2);\r
+    Mat mu1_mu2 =   mu1.mul(mu2);\r
+\r
+    Mat sigma1_2, sigma2_2, sigma12;\r
+\r
+    GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);\r
+    sigma1_2 -= mu1_2;\r
+\r
+    GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);\r
+    sigma2_2 -= mu2_2;\r
+\r
+    GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);\r
+    sigma12 -= mu1_mu2;\r
+\r
+    ///////////////////////////////// FORMULA ////////////////////////////////\r
+    Mat t1, t2, t3;\r
+\r
+    t1 = 2 * mu1_mu2 + C1;\r
+    t2 = 2 * sigma12 + C2;\r
+    t3 = t1.mul(t2);              // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))\r
+\r
+    t1 = mu1_2 + mu2_2 + C1;\r
+    t2 = sigma1_2 + sigma2_2 + C2;\r
+    t1 = t1.mul(t2);               // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))\r
+\r
+    Mat ssim_map;\r
+    divide(t3, t1, ssim_map);      // ssim_map =  t3./t1;\r
+\r
+    Scalar mssim = mean( ssim_map ); // mssim = average of ssim map\r
+    return mssim;\r
+}
\ No newline at end of file
index d9d37a0..bce6a5c 100644 (file)
@@ -12,7 +12,7 @@ void help()
         << "\n--------------------------------------------------------------------------" << endl\r
         << "This program shows how to scan image objects in OpenCV (cv::Mat). As use case"\r
         << " we take an input image and divide the native color palette (255) with the "  << endl\r
-        << "input. Shows C operator[] method, iterators and at function for random access"<< endl\r
+        << "input. Shows C operator[] method, iterators and at function for on-the-fly item address calculation."<< endl\r
         << "Usage:"                                                                       << endl\r
         << "./howToScanImages imageNameToUse divideWith [G]"                              << endl\r
         << "if you add a G parameter the image is processed in gray scale"                << endl\r
@@ -92,7 +92,7 @@ int main( int argc, char* argv[])
     t = 1000*((double)getTickCount() - t)/getTickFrequency();\r
     t /= times;\r
 \r
-    cout << "Time of reducing with the random access operator (averaged for " \r
+    cout << "Time of reducing with the on-the-fly address generation - at function (averaged for " \r
         << times << " runs): " << t << " milliseconds."<< endl;  \r
 \r
     Mat lookUpTable(1, 256, CV_8U);\r
index 6affabb..11cfa00 100644 (file)
-#include <opencv2/core/core.hpp>\r
-#include <opencv2/highgui/highgui.hpp>\r
-#include <iostream>\r
+// Video Image PSNR and SSIM\r
+#include <iostream>    // for standard I/O\r
+#include <string>   // for strings\r
+#include <iomanip>  // for controlling float print precision\r
+#include <sstream>  // string to number conversion\r
 \r
+#include <opencv2/imgproc/imgproc.hpp>  // Gaussian Blur\r
+#include <opencv2/core/core.hpp>        // Basic OpenCV structures (cv::Mat, Scalar)\r
+#include <opencv2/highgui/highgui.hpp>  // OpenCV window I/O\r
+\r
+using namespace std;\r
 using namespace cv;\r
 \r
-int main( int argc, char** argv )\r
+double getPSNR ( const Mat& I1, const Mat& I2);\r
+Scalar getMSSIM( const Mat& I1, const Mat& I2);\r
+\r
+void help()\r
+{\r
+    cout\r
+        << "\n--------------------------------------------------------------------------" << endl\r
+        << "This program shows how to read a video file with OpenCV. In addition, it tests the"\r
+        << " similarity of two input videos first with PSNR, and for the frames below a PSNR "  << endl\r
+        << "trigger value, also with MSSIM."<< endl\r
+        << "Usage:"                                                                       << endl\r
+        << "./video-source referenceVideo useCaseTestVideo PSNR_Trigger_Value Wait_Between_Frames " << endl\r
+        << "--------------------------------------------------------------------------"   << endl\r
+        << endl;\r
+}\r
+int main(int argc, char *argv[], char *window_name)\r
+{\r
+    help();\r
+    if (argc != 5)\r
+    {\r
+        cout << "Not enough parameters" << endl;\r
+        return -1;\r
+    }\r
+    stringstream conv;\r
+\r
+    const string sourceReference = argv[1],sourceCompareWith = argv[2];\r
+    int psnrTriggerValue, delay;\r
+    conv << argv[3] << argv[4];                  // put in the strings\r
+    conv >> psnrTriggerValue >> delay;// take out the numbers\r
+\r
+    char c;\r
+    int frameNum = -1;                 // Frame counter\r
+\r
+    VideoCapture captRefrnc(sourceReference),\r
+                 captUndTst(sourceCompareWith);\r
+\r
+    if ( !captRefrnc.isOpened())\r
+    {\r
+        cout  << "Could not open reference " << sourceReference << endl;\r
+        return -1;\r
+    }\r
+\r
+    if( !captUndTst.isOpened())\r
+    {\r
+        cout  << "Could not open case test " << sourceCompareWith << endl;\r
+        return -1;\r
+    }\r
+\r
+    Size refS = Size((int) captRefrnc.get(CV_CAP_PROP_FRAME_WIDTH),\r
+                     (int) captRefrnc.get(CV_CAP_PROP_FRAME_HEIGHT)),\r
+         uTSi = Size((int) captUndTst.get(CV_CAP_PROP_FRAME_WIDTH),\r
+                     (int) captUndTst.get(CV_CAP_PROP_FRAME_HEIGHT));\r
+\r
+    if (refS != uTSi)\r
+    {\r
+        cout << "Inputs have different size!!! Closing." << endl;\r
+        return -1;\r
+    }\r
+\r
+    const char* WIN_UT = "Under Test";\r
+    const char* WIN_RF = "Reference";\r
+\r
+    // Windows\r
+            namedWindow(WIN_RF, CV_WINDOW_AUTOSIZE );\r
+            namedWindow(WIN_UT, CV_WINDOW_AUTOSIZE );\r
+            cvMoveWindow(WIN_RF, 400       ,            0);             //750,  2 (bernat =0)\r
+            cvMoveWindow(WIN_UT, refS.width,            0);             //1500, 2\r
+\r
+    cout << "Frame resolution: Width=" << refS.width << "  Height=" << refS.height\r
+         << " of nr#: " << captRefrnc.get(CV_CAP_PROP_FRAME_COUNT) << endl;\r
+\r
+    cout << "PSNR trigger value " <<\r
+          setiosflags(ios::fixed) << setprecision(3) << psnrTriggerValue << endl;\r
+\r
+    Mat frameReference, frameUnderTest;\r
+    double psnrV;\r
+    Scalar mssimV;\r
+\r
+    while( true) //Show the image captured in the window and repeat\r
+    {\r
+        captRefrnc >> frameReference;\r
+        captUndTst >> frameUnderTest;\r
+\r
+        if( frameReference.empty()  || frameUnderTest.empty())\r
+        {\r
+            cout << " < < <  Game over!  > > > ";\r
+            break;\r
+        }\r
+\r
+        ++frameNum;\r
+        cout <<"Frame:" << frameNum;\r
+\r
+        ///////////////////////////////// PSNR ////////////////////////////////////////////////////\r
+        psnrV = getPSNR(frameReference,frameUnderTest);                                        //get PSNR\r
+        cout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";\r
+\r
+        //////////////////////////////////// MSSIM /////////////////////////////////////////////////\r
+        if (psnrV < psnrTriggerValue)\r
+        {\r
+            mssimV = getMSSIM(frameReference,frameUnderTest);\r
+\r
+            cout << " MSSIM: "\r
+                 << "R" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[2] * 100\r
+                 << "G" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[1] * 100\r
+                 << "B" << setiosflags(ios::fixed) << setprecision(3) << mssimV.val[0] * 100;\r
+        }\r
+\r
+       cout << endl;\r
+\r
+        ////////////////////////////////// Show Image /////////////////////////////////////////////\r
+        imshow( WIN_RF, frameReference);\r
+        imshow( WIN_UT, frameUnderTest);\r
+\r
+        c = cvWaitKey(delay);\r
+        if (c == 27) break;\r
+    }\r
+\r
+    return 0;\r
+}\r
+\r
+double getPSNR(const Mat& I1, const Mat& I2)\r
+{\r
+    Mat s1;\r
+    absdiff(I1, I2, s1);       // |I1 - I2|\r
+    s1.convertTo(s1, CV_32F);  // cannot make a square on 8 bits\r
+    s1 = s1.mul(s1);           // |I1 - I2|^2\r
+\r
+    Scalar s = sum(s1);         // sum elements per channel\r
+\r
+    double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels\r
+\r
+    if( sse <= 1e-10) // for small values return zero\r
+        return 0;\r
+    else\r
+    {\r
+        double  mse =sse /(double)(I1.channels() * I1.total());\r
+        double psnr = 10.0*log10((255*255)/mse);\r
+        return psnr;\r
+    }\r
+}\r
+\r
+Scalar getMSSIM( const Mat& i1, const Mat& i2)\r
 {\r
-       Mat image;\r
-       image = imread("opencv-logo.png");      // Read the file\r
+    const double C1 = 6.5025, C2 = 58.5225;\r
+    /***************************** INITS **********************************/\r
+    int d     = CV_32F;\r
+\r
+    Mat I1, I2;\r
+    i1.convertTo(I1, d);           // cannot calculate on one byte large values\r
+    i2.convertTo(I2, d);\r
+\r
+    Mat I2_2   = I2.mul(I2);        // I2^2\r
+    Mat I1_2   = I1.mul(I1);        // I1^2\r
+    Mat I1_I2  = I1.mul(I2);        // I1 * I2\r
+\r
+    /*************************** END INITS **********************************/\r
+\r
+    Mat mu1, mu2;   // PRELIMINARY COMPUTING\r
+    GaussianBlur(I1, mu1, Size(11, 11), 1.5);\r
+    GaussianBlur(I2, mu2, Size(11, 11), 1.5);\r
+\r
+    Mat mu1_2   =   mu1.mul(mu1);\r
+    Mat mu2_2   =   mu2.mul(mu2);\r
+    Mat mu1_mu2 =   mu1.mul(mu2);\r
+\r
+    Mat sigma1_2, sigma2_2, sigma12;\r
+\r
+    GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);\r
+    sigma1_2 -= mu1_2;\r
+\r
+    GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);\r
+    sigma2_2 -= mu2_2;\r
+\r
+    GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);\r
+    sigma12 -= mu1_mu2;\r
+\r
+    ///////////////////////////////// FORMULA ////////////////////////////////\r
+    Mat t1, t2, t3;\r
 \r
-       if(! image.data )                    // Check for invalid input\r
-       {\r
-               std::cout <<  "Could not open or find the image" << std::endl ;\r
-               return -1;\r
-       }\r
+    t1 = 2 * mu1_mu2 + C1;\r
+    t2 = 2 * sigma12 + C2;\r
+    t3 = t1.mul(t2);              // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))\r
 \r
-       namedWindow( "Display window", CV_WINDOW_FREERATIO );// Create a window for display.\r
-       imshow( "Display window", image );                   // Show our image inside it.\r
+    t1 = mu1_2 + mu2_2 + C1;\r
+    t2 = sigma1_2 + sigma2_2 + C2;\r
+    t1 = t1.mul(t2);               // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))\r
 \r
-       waitKey(0);                                                                                      // Wait for a keystroke in the window\r
+    Mat ssim_map;\r
+    divide(t3, t1, ssim_map);      // ssim_map =  t3./t1;\r
 \r
-       return 0;\r
+    Scalar mssim = mean( ssim_map ); // mssim = average of ssim map\r
+    return mssim;\r
 }
\ No newline at end of file