Search code examples
c++imageopencvimage-processingartificial-intelligence

Detecting difference between 2 images


I am working on the following code

#include <iostream>
#include <opencv2/core/core.hpp>
#include <string>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/background_segm.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat current,currentGrey,next,abs;
    VideoCapture cam1,cam2;

    std:: vector<vector<Point>>contours;
    vector<vector<Point>>contoursPoly(contours.size());

    cam1.open(0);
    cam2.open(0);

    namedWindow("Normal");
    namedWindow("Difference");

    if(!cam1.isOpened())
    {
        cout << "Cam not found" << endl;
        return -1;
    }



    while(true)
    {
        //Take the input
        cam1 >> current;
        currentGrey = current;
        cam2 >> next;

        //Convert to grey
        cvtColor(currentGrey,currentGrey,CV_RGB2GRAY);
        cvtColor(next,next,CV_RGB2GRAY);



        //Reduce Noise
        cv::GaussianBlur(currentGrey,currentGrey,Size(0,0),4);
        cv::GaussianBlur(next,next,Size(0,0),4);

        imshow("Normal",currentGrey);

        //Get the absolute difference
        absdiff(currentGrey,next,abs);
        imshow("Difference",abs);


       for(int i=0;i<abs.rows;i++)
        {
            for(int j=0;j<abs.cols;j++)
            {
                if(abs.at<int>(j,i)>0)
                {
                    cout << "Change Detected" << endl;

                    j = abs.cols+1;
                    i = abs.rows+1;
                }

            }
        }


        if(waitKey(30)>=0)
        {
            break;
        }
    }

}

In here, what I am trying to do is print a message whenever a difference between images are detected. Following part is the technique

for(int i=0;i<abs.rows;i++)
            {
                for(int j=0;j<abs.cols;j++)
                {
                    if(abs.at<int>(j,i)>0)
                    {
                        cout << "Change Detected" << endl;

                        j = abs.cols+1;
                        i = abs.rows+1;
                    }

                }
            }

Unfortunately, instead of printing messages when a difference is detected, it prints the message always. Why is this?


Solution

  • You should calculate the mean square error between the two frames.

    MSE = sum((frame1-frame2)^2 ) / no. of pixels
    

    There is an example of calculating it in an OpenCV tutorial.

    Based on that code you could have

    double getMSE(const Mat& I1, const Mat& I2)
    {
        Mat s1;
        absdiff(I1, I2, s1);       // |I1 - I2|
        s1.convertTo(s1, CV_32F);  // cannot make a square on 8 bits
        s1 = s1.mul(s1);           // |I1 - I2|^2
    
        Scalar s = sum(s1);         // sum elements per channel
    
        double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels
    
        if( sse <= 1e-10) // for small values return zero
            return 0;
        else
        {
            double  mse =sse /(double)(I1.channels() * I1.total());
            return mse;
            // Instead of returning MSE, the tutorial code returned PSNR (below).
            //double psnr = 10.0*log10((255*255)/mse);
            //return psnr;
        }
    }
    

    You can use it in your code like this:

       if(getMSE(currentGrey,next) > some_threshold)
            cout << "Change Detected" << endl;
    

    It is up to you to decide the magnitude of MSE below which you consider the images to be the same. Also you should prefilter with GaussianBlur() to reduce noise, like you already do. The blur method suggested by @fatih_k is not a Gaussian filter; it is a box filter and although faster may introduce artifacts.