背景差分法示例

来源:互联网 发布:三菱fx5u编程手册 编辑:程序博客网 时间:2024/06/07 16:54

背景差分法


背景差分法是一种很常用而且广泛传感的技术,主要用于背景不动的情况下提取前景。它主要的原理是在当前帧和背景做减法,然后使用threshold进行二值化得到前景掩码。下面是背景减法的示意图。
这里写图片描述
背景差分法主要包含以下两个步骤:
1.背景的建立
2.背景的更新
两个关键点,第一个就是如何选择背景,第二个就是什么时候更新背景,这样可以适应背景变化的情况,更新背景的快慢也很重要,如果更新慢了,快速运动的物体如果没有重叠就认为是两个物体了,如果更新快了,慢速运动的物体会被认为是背景,这样就造成漏检。
这里学习一下在opencv中如何使用背景差分法。

代码

使用了两个方法来产生前景掩码:
1. cv::bgsegm::BackgroundSubtractorMog
2. cv::BackgroundSubtractorMog2

//opencv#include "opencv2/imgcodecs.hpp"#include "opencv2/imgproc.hpp"#include "opencv2/videoio.hpp"#include <opencv2/highgui.hpp>#include <opencv2/video.hpp>//C#include <stdio.h>//C++#include <iostream>#include <sstream>using namespace cv;using namespace std;// Global variablesMat frame; //current frameMat fgMaskMOG2; //fg mask fg mask generated by MOG2 methodPtr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractorchar keyboard; //input from keyboardvoid help();void processVideo(char* videoFilename);void processImages(char* firstFrameFilename);void help(){    cout    << "--------------------------------------------------------------------------" << endl    << "This program shows how to use background subtraction methods provided by "  << endl    << " OpenCV. You can process both videos (-vid) and images (-img)."             << endl                                                                                    << endl    << "Usage:"                                                                     << endl    << "./bg_sub {-vid <video filename>|-img <image filename>}"                     << endl    << "for example: ./bg_sub -vid video.avi"                                       << endl    << "or: ./bg_sub -img /data/images/1.png"                                       << endl    << "--------------------------------------------------------------------------" << endl    << endl;}int main(int argc, char* argv[]){    //print help information    help();    //check for the input parameter correctness    if(argc != 3) {        cerr <<"Incorret input list" << endl;        cerr <<"exiting..." << endl;        return EXIT_FAILURE;    }    //create GUI windows    namedWindow("Frame");    namedWindow("FG Mask MOG 2");    //create Background Subtractor objects    pMOG2 = createBackgroundSubtractorMOG2(); //MOG2 approach    if(strcmp(argv[1], "-vid") == 0) {        //input data coming from a video        processVideo(argv[2]);    }    else if(strcmp(argv[1], "-img") == 0) {        //input data coming from a sequence of images        processImages(argv[2]);    }    else {        //error in reading input parameters        cerr <<"Please, check the input parameters." << endl;        cerr <<"Exiting..." << endl;        return EXIT_FAILURE;    }    //destroy GUI windows    destroyAllWindows();    return EXIT_SUCCESS;}void processVideo(char* videoFilename) {    //create the capture object    VideoCapture capture(videoFilename);    if(!capture.isOpened()){        //error in opening the video input        cerr << "Unable to open video file: " << videoFilename << endl;        exit(EXIT_FAILURE);    }    //read input data. ESC or 'q' for quitting    keyboard = 0;    while( keyboard != 'q' && keyboard != 27 ){        //read the current frame        if(!capture.read(frame)) {            cerr << "Unable to read next frame." << endl;            cerr << "Exiting..." << endl;            exit(EXIT_FAILURE);        }        //update the background model        pMOG2->apply(frame, fgMaskMOG2);        //get the frame number and write it on the current frame        stringstream ss;        rectangle(frame, cv::Point(10, 2), cv::Point(100,20),                  cv::Scalar(255,255,255), -1);        ss << capture.get(CAP_PROP_POS_FRAMES);        string frameNumberString = ss.str();        putText(frame, frameNumberString.c_str(), cv::Point(15, 15),                FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));        //show the current frame and the fg masks        imshow("Frame", frame);        imshow("FG Mask MOG 2", fgMaskMOG2);        //get the input from the keyboard        keyboard = (char)waitKey( 30 );    }    //delete capture object    capture.release();}void processImages(char* fistFrameFilename) {    //read the first file of the sequence    frame = imread(fistFrameFilename);    if(frame.empty()){        //error in opening the first image        cerr << "Unable to open first image frame: " << fistFrameFilename << endl;        exit(EXIT_FAILURE);    }    //current image filename    string fn(fistFrameFilename);    //read input data. ESC or 'q' for quitting    keyboard = 0;    while( keyboard != 'q' && keyboard != 27 ){        //update the background model        pMOG2->apply(frame, fgMaskMOG2);        //get the frame number and write it on the current frame        size_t index = fn.find_last_of("/");        if(index == string::npos) {            index = fn.find_last_of("\\");        }        size_t index2 = fn.find_last_of(".");        string prefix = fn.substr(0,index+1);        string suffix = fn.substr(index2);        string frameNumberString = fn.substr(index+1, index2-index-1);        istringstream iss(frameNumberString);        int frameNumber = 0;        iss >> frameNumber;        rectangle(frame, cv::Point(10, 2), cv::Point(100,20),                  cv::Scalar(255,255,255), -1);        putText(frame, frameNumberString.c_str(), cv::Point(15, 15),                FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));        //show the current frame and the fg masks        imshow("Frame", frame);        imshow("FG Mask MOG 2", fgMaskMOG2);        //get the input from the keyboard        keyboard = (char)waitKey( 30 );        //search for the next image in the sequence        ostringstream oss;        oss << (frameNumber + 1);        string nextFrameNumberString = oss.str();        string nextFrameFilename = prefix + nextFrameNumberString + suffix;        //read the next frame        frame = imread(nextFrameFilename);        if(frame.empty()){            //error in opening the next image in the sequence            cerr << "Unable to open image frame: " << nextFrameFilename << endl;            exit(EXIT_FAILURE);        }        //update the path of the current frame        fn.assign(nextFrameFilename);    }}

关键代码说明

  1. 创建三个全局变量,frame用于保存当前图像,fgMaskMog用于保存前景掩码,fgMaskMog2是第二种方法产生的前景掩码。
Mat frame; //current frameMat fgMaskMOG; //fg mask generated by MOG methodMat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
  1. 使用两个方法产生前景掩码,cv::BackgroundSubtractor。在本例中,使用默认的参数,你也可以尝试其它参数。
Ptr<BackgroundSubtractor> pMOG; //MOG Background subtractorPtr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor...//create Background Subtractor objectspMOG = createBackgroundSubtractorMOG(); //MOG approach
  1. 传入视频还是单张图像,使用-vid参数传入视频 使用-img参数传入图像
if(strcmp(argv[1], "-vid") == 0) {  //input data coming from a video  processVideo(argv[2]);}else if(strcmp(argv[1], "-img") == 0) {  //input data coming from a sequence of images  processImages(argv[2]);}
  1. 建议使用视频进行测试。如果要停止,输入“q”或者esc。
while( (char)keyboard != 'q' && (char)keyboard != 27 ){  //read the current frame  if(!capture.read(frame)) {    cerr << "Unable to read next frame." << endl;    cerr << "Exiting..." << endl;    exit(EXIT_FAILURE);  }
  1. 每一帧图像都用于计算前景掩码和更新背景,可以通过传入参数来修改更新速度。
//update the background modelpMOG->apply(frame, fgMaskMOG);pMOG2->apply(frame, fgMaskMOG2);
  1. 在在上角显示当前帧号。
//get the frame number and write it on the current framestringstream ss;rectangle(frame, cv::Point(10, 2), cv::Point(100,20),          cv::Scalar(255,255,255), -1);ss << capture.get(CAP_PROP_POS_FRAMES);string frameNumberString = ss.str();putText(frame, frameNumberString.c_str(), cv::Point(15, 15),        FONT_HERSHEY_SIMPLEX, 0.5 , cv::Scalar(0,0,0));
  1. 然后显示输入图像和结果
//show the current frame and the fg masksimshow("Frame", frame);imshow("FG Mask MOG", fgMaskMOG);imshow("FG Mask MOG 2", fgMaskMOG2);
  1. 如果选择图片进行处理,需要输入几张图像。使用cv::imread进行处理。
//read the first file of the sequenceframe = imread(fistFrameFilename);if(!frame.data){  //error in opening the first image  cerr << "Unable to open first image frame: " << fistFrameFilename << endl;  exit(EXIT_FAILURE);}...//search for the next image in the sequenceostringstream oss;oss << (frameNumber + 1);string nextFrameNumberString = oss.str();string nextFrameFilename = prefix + nextFrameNumberString + suffix;//read the next frameframe = imread(nextFrameFilename);if(!frame.data){  //error in opening the next image in the sequence  cerr << "Unable to open image frame: " << nextFrameFilename << endl;  exit(EXIT_FAILURE);}//update the path of the current framefn.assign(nextFrameFilename);

结果显示:

结果显示如果人停下了,那么就检测不到了。
这里写图片描述

保存

保存使用cv::imread就可以了,代码实现很简单

string imageToSave = "output_MOG_" + frameNumberString + ".png";bool saved = imwrite(imageToSave, fgMaskMOG);if(!saved) {  cerr << "Unable to save " << imageToSave << endl;}
原创粉丝点击