Search code examples
opencvbackground-subtraction

frame subtraction in video


I am trying to do Frame subtraction in a video.Steps I am following

  1. Get a image , convert it into grayscale.
  2. Subtract it from previous frame grayscale.

All I see in diff2(and diff also) a complete black image.One observation I made is that pixel value of gray1 and gray2 become equal.

My code

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

using namespace cv;
using namespace std;
RNG rng(12345);

    int main( int argc, const char** argv )

{
   VideoCapture cap(0);

    if ( !cap.isOpened() ) 
    {
         cout << "Cannot open the web cam" << endl;
         return -1;
    }

    Mat img1,img2,diff,gray1,gray2,diff2;


        bool bSuccess = cap.read(img1); // read a new frame from video

         if (!bSuccess) //if not success, break loop
        {
             cout << "Cannot read a frame from video stream" << endl;
             return -1;
        }

        cvtColor( img1,gray1, CV_BGR2GRAY );

     while (true)
    {


        bSuccess = cap.read(img2); // read a new frame from video

         if (!bSuccess) //if not success, break loop
        {
             cout << "Cannot read a frame from video stream" << endl;
             break;
        }
        cvtColor( img2,gray2, CV_BGR2GRAY );

        absdiff(gray2,gray1,diff);
        threshold(diff, diff2, 150, 255, CV_THRESH_BINARY); 
        cout<<gray2.at<uchar>(100,200) <<endl;
        cout<<gray1.at<uchar>(100,200) <<endl;

        gray1=gray2;
        imshow("1",gray1);
        imshow("2",diff2);


    if (waitKey(1000) == 27) //wait for 'esc' key press for 30ms. If 'esc' key is pressed, break loop
       {
            cout << "esc key is pressed by user" << endl;
            break; 
       }
    }


     return -1;
}

Solution

  • please try this code. It looks like you're overwriting gray1 so that gray1 and gray2 use the very same data memory positions.

    You could either use gray1=gray2.clone(); instead or use some real "swapping" of the buffers instead of overwriting. My code should perform a simple buffer swapping and has some comments about the problem.

    #include "opencv2/core/core.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/objdetect/objdetect.hpp"
    #include <opencv2/video/background_segm.hpp>
    #include <iostream>
    
    using namespace cv;
    using namespace std;
    RNG rng(12345);
    
    int main( int argc, const char** argv )
    
    {
       VideoCapture cap(0);
    
        if ( !cap.isOpened() ) 
        {
             cout << "Cannot open the web cam" << endl;
             return -1;
        }
    
        Mat img1,img2,diff,gray1,gray2,diff2;
    
        Mat tmp; // used to swap the buffers
    
    
        bool bSuccess = cap.read(img1); // read a new frame from video
    
        if (!bSuccess) //if not success, break loop
        {
             cout << "Cannot read a frame from video stream" << endl;
             return -1;
        }
    
        // this will allocate memory of gray1 if not allocated yet
        cvtColor( img1,gray1, CV_BGR2GRAY );
    
        while (true)
        {
    
    
            bSuccess = cap.read(img2); // read a new frame from video
    
             if (!bSuccess) //if not success, break loop
            {
             cout << "Cannot read a frame from video stream" << endl;
             break;
            }
    
            // memory for gray2 won't be allocated if it is present already => if gray2 and gray1 use the same data memory, you'll overwrite gray1's pixels here and obviously gray1 and gray2 will have the same pixel values then
            cvtColor( img2,gray2, CV_BGR2GRAY );
    
            absdiff(gray2,gray1,diff);
            threshold(diff, diff2, 150, 255, CV_THRESH_BINARY); 
            cout<<gray2.at<uchar>(100,200) <<endl;
            cout<<gray1.at<uchar>(100,200) <<endl;
    
            // don't lose the memory of gray1
            tmp = gray1;
    
            // this means gray1 and gray2 will use the same data memory location
            gray1=gray2;
    
            // give gray2 a new data memory location. Since previous gray1 memory is still present but wont be used anymore, use it here.
            gray2=tmp;
    
            imshow("1",gray1);
            imshow("2",diff2);
    
    
        if (waitKey(1000) == 27) //wait for 'esc' key press for 30ms. If 'esc' key is pressed, break loop
           {
                cout << "esc key is pressed by user" << endl;
                break; 
           }
        }
    
    
         return -1;
    }
    

    in addition, a thres difference threshold of 150 might be very high for common tasks, but it might be ok for your special task. Typical difference values/thresholds in background subtraction for foreground extraction are around 20 to 30 from my experience, but at the end it depends on your task/problem/domain.