1. 程式人生 > >Opencv視訊分析---使用背景減法方法

Opencv視訊分析---使用背景減法方法

理論

  • 背景減法(BS)是用於通過使用靜態相機生成前景蒙版(即,包含屬於場景中的運動物件的畫素的二值影象)的常用且廣泛使用的技術。
  • 顧名思義,BS計算前景蒙版,在當前幀和背景模型之間執行減法,包含場景的靜態部分,或者更一般地說,考慮到觀察場景的特徵,可以將所有內容視為背景。
  • 背景建模包括兩個主要步驟:
  1. 背景初始化;
  2. 背景更新。
  • 在第一步中,計算背景的初始模型,而在第二步中更新模型以適應場景中的可能變化。

程式碼

//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
int 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
    << "./bs {-vid <video filename>|-img <image filename>}"                         << endl
    << "for example: ./bs -vid video.avi"                                           << endl
    << "or: ./bs -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
    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);
        }
        //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 = 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
    while( (char)keyboard != 'q' && (char)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 = 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);
    }
}

解釋

  • 首先,分配三個Mat物件來儲存當前幀和兩個前景掩碼,這兩個前景掩碼通過使用兩種不同的BS演算法獲得。
  • 兩個cv :: BackgroundSubtractor物件將用於生成前景蒙版。 使用預設引數,但也可以在create函式中宣告特定引數。
  • 分析命令列引數。 使用者可以選擇兩個選項:
  1. 視訊檔案(通過選擇-vid選項);
  2. 影象序列(通過選擇-img選項)。
  • 假設您要處理視訊檔案。 讀取視訊直到到達終點或使用者按下按鈕“q”或按鈕“ESC”。
  • 每個幀都用於計算前景蒙版和更新背景。 如果要更改用於更新背景模型的學習速率,可以通過將第三個引數傳遞給“apply”方法來設定特定的學習速率。
  • 當前幀編號可以從cv :: VideoCapture物件中提取並標記在當前幀的左上角。 白色矩形用於突出顯示黑色框架編號。
  • 我們準備顯示當前的輸入框架和結果。
  • 可以使用一系列影象作為輸入來執行上面列出的相同操作。 呼叫processImage函式,而不是使用cv :: VideoCapture物件,在為下一幀讀取的正確路徑個別化之後,使用cv :: imread讀取影象。

效果

  • 給定以下輸入引數:
  • 程式的輸出如下所示:
  • The video file Video_001.avi is part of the Background Models Challenge (BMC) data set and it can be downloaded from the following link 
    Video_001
     (about 32 MB).
  • 如果要處理一系列影象,則必須選擇“-img”選項:
  • 程式的輸出如下所示:
  • 為了儲存輸出影象,我們可以使用cv :: imwrite。 新增以下程式碼可以儲存前景蒙版。