背景差分法示例

背景差分法


背景差分法是一种很常用而且广泛传感的技术,主要用于背景不动的情况下提取前景。它主要的原理是在当前帧和背景做减法,然后使用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 variables
Mat frame; //current frame
Mat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
Ptr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor
char keyboard; //input from keyboard
void 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 frame
Mat fgMaskMOG; //fg mask generated by MOG method
Mat fgMaskMOG2; //fg mask fg mask generated by MOG2 method
  1. 使用两个方法产生前景掩码,cv::BackgroundSubtractor。在本例中,使用默认的参数,你也可以尝试其它参数。
Ptr<BackgroundSubtractor> pMOG; //MOG Background subtractor
Ptr<BackgroundSubtractor> pMOG2; //MOG2 Background subtractor
...
//create Background Subtractor objects
pMOG = 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 model
pMOG->apply(frame, fgMaskMOG);
pMOG2->apply(frame, fgMaskMOG2);
  1. 在在上角显示当前帧号。
//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));
  1. 然后显示输入图像和结果
//show the current frame and the fg masks
imshow("Frame", frame);
imshow("FG Mask MOG", fgMaskMOG);
imshow("FG Mask MOG 2", fgMaskMOG2);
  1. 如果选择图片进行处理,需要输入几张图像。使用cv::imread进行处理。
//read the first file of the sequence
frame = 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 sequence
ostringstream oss;
oss << (frameNumber + 1);
string nextFrameNumberString = oss.str();
string nextFrameFilename = prefix + nextFrameNumberString + suffix;
//read the next frame
frame = 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 frame
fn.assign(nextFrameFilename);

结果显示:

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

保存

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

string imageToSave = "output_MOG_" + frameNumberString + ".png";
bool saved = imwrite(imageToSave, fgMaskMOG);
if(!saved) {
  cerr << "Unable to save " << imageToSave << endl;
}
  • 7
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
背景差分法是一种常用的图像处理技术,可以用于目标检测、运动跟踪等应用。在Matlab中,可以使用以下步骤来实现背景差分法: 1.读取帧序列并将其转换为灰度图像。 2.选择一个适当的时间窗口,例如10帧,作为背景模型。 3.对于每个新帧,将其与背景模型进行比较,并计算其灰度值与背景模型之间的差异。如果该差异超过了预定义的阈值,则将该像素标记为前景。 4.对于连续的前景像素点,可以使用形态学操作(如膨胀和腐蚀)进行处理,以去除噪声和孤立的点。 以下是使用Matlab实现背景差分法示例代码: ```matlab % 读取视频文件 vidReader = VideoReader('myVideo.avi'); % 设置背景模型的帧数 numFrames = 10; % 读取前numFrames帧并计算背景模型 for i = 1:numFrames frame = readFrame(vidReader); grayFrame = rgb2gray(frame); if i == 1 background = grayFrame; else background = background + grayFrame; end end background = background / numFrames; % 处理之后的帧 while hasFrame(vidReader) frame = readFrame(vidReader); grayFrame = rgb2gray(frame); % 计算前景 diff = abs(double(grayFrame) - double(background)); threshold = 30; % 阈值 foreground = diff > threshold; % 去除噪声和孤立的点 se = strel('disk', 2); foreground = imopen(foreground, se); foreground = imclose(foreground, se); % 显示结果 imshow(frame); hold on; viscircles([500,500],5,'EdgeColor','b'); [B,L] = bwboundaries(foreground,'noholes'); for k = 1:length(B) boundary = B{k}; plot(boundary(:,2), boundary(:,1), 'r', 'LineWidth', 2); end hold off; end ``` 在这个示例中,我们使用了一个简单的视频文件“myVideo.avi”。在读取了前10帧之后,我们计算了背景模型,并使用该模型对后续的帧进行背景差分。最后,使用形态学操作去除噪声和孤立的点,并将结果可视化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值