Save foreground and background in Remove Background

Hi

I have compiled the Remove Background tutorial successfully and it gives me impressive results for my use case. I now have two questions:

  1. The tutorial shows the foreground on the screen - how can I save these in a video?
  2. How can I save the background in a video?

Thanks

Rainer

old tutorial: OpenCV: Creating a video with OpenCV

comparable information, but in python, and smells less dusty: OpenCV: Getting Started with Videos

1 Like

also:
https://docs.opencv.org/3.4.15/d7/df6/classcv_1_1BackgroundSubtractor.html#a98cb8e292a6cbcb32436dad62a82f974

1 Like

OK - I am one step further - but I can only save the frame, and not the foreground.

I am using the following code, based on the above mentioned example:

This does not produce a valid output file, while when I replace output.write(fgMask) with output.write(frame) it gives me a valid video, but as expected with the original content.

#include <iostream>
#include <sstream>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
using namespace cv;
using namespace std;
const char* params
    = "{ help h         |           | Print usage }"
      "{ input          | vtest.avi | Path to a video or a sequence of image }"
      "{ algo           | MOG2      | Background subtraction method (KNN, MOG2) }";
int main(int argc, char* argv[])
{
    CommandLineParser parser(argc, argv, params);
    parser.about( "This program shows how to use background subtraction methods provided by "
                  " OpenCV. You can process both videos and images.\n" );
    if (parser.has("help"))
    {
        //print help information
        parser.printMessage();
        return 0;
    }
    //create Background Subtractor objects
    Ptr<BackgroundSubtractor> pBackSub;
    if (parser.get<String>("algo") == "MOG2")
        pBackSub = createBackgroundSubtractorMOG2(500, 16, false);
    else
        pBackSub = createBackgroundSubtractorKNN(500, 400.0, false);
    VideoCapture capture( samples::findFile( parser.get<String>("input") ) );
    //Initialize video writer object
    // for saving
int frame_width = static_cast<int>(capture.get(3));
int frame_height = static_cast<int>(capture.get(4));
Size frame_size(frame_width, frame_height);
int fps = 25;
    VideoWriter output("foreground.avi", 
    	VideoWriter::fourcc('M', 'J', 'P', 'G'),
    	fps, 
    	frame_size);
    if (!capture.isOpened()){
        //error in opening the video input
        cerr << "Unable to open: " << parser.get<String>("input") << endl;
        return 0;
    }
    Mat frame, fgMask;
    while (true) {
        capture >> frame;
        if (frame.empty())
            break;
        //update the background model
        pBackSub->apply(frame, fgMask, 0.8);
        //write frame to output file 
        output.write(fgMask);
        //show the current frame and the fg masks
        //imshow("Frame", frame);
        imshow("FG Mask", fgMask);
        //get the input from the keyboard
        int keyboard = waitKey(30);
        if (keyboard == 'q' || keyboard == 27)
            break;
    }
    // Release video writer output
    output.release();

    return 0;
}

but maybe, you want to save the colour information instead of the bw masks:

Mat bgr_mask(frame.size(), CV_8UC3, Scalar::all(0));
frame.copyTo(bgr_mask, fgMask);
output.write(bgr_mask);
1 Like

Thanks @berak - makes sense. But I have tried both approaches you mentioned, but I still don’t get a video which I can play in e.g. vlc player. It is much smaller than the original, which could be explained by the colour depth, but it does not play anything. Am I misunderstanding something here?

it wont write any frames, if those dont match what you gave to the VideoWriter ctor (so there’s only the container header in the file on disk)

Thanks berak - it is working now. Perfect. It creates the two videos of the mask and the other one, which are the trails?

For reference, the working code:

#include <iostream>
#include <sstream>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
using namespace cv;
using namespace std;
const char* params
    = "{ help h         |           | Print usage }"
      "{ input          | vtest.avi | Path to a video or a sequence of image }"
      "{ algo           | MOG2      | Background subtraction method (KNN, MOG2) }";
int main(int argc, char* argv[])
{
    CommandLineParser parser(argc, argv, params);
    parser.about( "This program shows how to use background subtraction methods provided by "
                  " OpenCV. You can process both videos and images.\n" );
    if (parser.has("help"))
    {
        //print help information
        parser.printMessage();
        return 0;
    }
    //create Background Subtractor objects
    Ptr<BackgroundSubtractor> pBackSub;
    if (parser.get<String>("algo") == "MOG2")
        pBackSub = createBackgroundSubtractorMOG2(500, 16, false);
    else
        pBackSub = createBackgroundSubtractorKNN(500, 400.0, false);
    VideoCapture capture( samples::findFile( parser.get<String>("input") ) );
    //Initialize video writer object
    // for saving
    if (!capture.isOpened()){
        //error in opening the video input
        cerr << "Unable to open: " << parser.get<String>("input") << endl;
        return 0;
    }
int frame_width = static_cast<int>(capture.get(3));
int frame_height = static_cast<int>(capture.get(4));
Size frame_size(frame_width, frame_height);
int fps = 25;
    VideoWriter foregroundmask("foregroundmask.avi", 
    	VideoWriter::fourcc('M', 'J', 'P', 'G'),
    	fps, 
    	frame_size,
    	false);
    VideoWriter bgrmask("bgrmask.avi", 
    	VideoWriter::fourcc('M', 'J', 'P', 'G'),
    	fps, 
    	frame_size,
    	true);
    Mat frame, fgMask;
    Mat bgr_mask(frame.size(), CV_8UC3, Scalar::all(0));
    while (true) {
        capture >> frame;
        if (frame.empty())
            break;
        //update the background model
        pBackSub->apply(frame, fgMask, 0.8);

	imshow("FG Mask", fgMask);

	//foregroundmask
	foregroundmask.write(fgMask);
        //imshow("FG Mask", fgMask);
 
        frame.copyTo(bgr_mask, fgMask);
        //write background colour info to foregroundmask file
	bgrmask.write(bgr_mask);
        // imshow("colour", bgr_mask);
        
       //get the input from the keyboard
        int keyboard = waitKey(30);
        if (keyboard == 'q' || keyboard == 27)
            break;
    }
    // Release video writer foregroundmask
    foregroundmask.release();
    bgrmask.release();

    return 0;
}

sorry i did not understand, explain ? trails ?

These are the trails

And this is the foreground

looks cool :wink:
(but you probably did not want it …)

as long as this is outside/above the loop, you consecutively add new dots to an existing image in the copyTo()
(it’ll only copy, where the mask is white, and leave anything else untouched !)
you have to build a fresh, black bgr_mask each time, if you want to avoid it

1 Like

Makes sense. But even in its current form it is useful.

Thanks a lot.