OpenCV2帧间差分检测运动目标

来源:互联网 发布:重庆优化网站 编辑:程序博客网 时间:2024/06/06 07:17

对于一个固定的场景(背景),我们感兴趣的是在场景中运动的物体(前景)。前景提取是在智能监控应用中的一个基础步骤。
帧间差分:在运动目标检测中,简单来说,就是背景与当前帧之间的差异。数字图像可以表示成一个矩阵,矩阵中每一个元素叫一个像素点。帧间差分=绝对值(背景-当前帧图像)。
怎么确定前景?
我们取帧间差分足够大的像素点,将这些像素点作为前景像素。

基于帧间差分的运动目标的主要问题:
背景建模的好坏直接影响检测的效果,由于背景是随时间改变的。如果不对背景进行更新,可想而知,检测效果不会好。所以,第一个问题就是,背景如何建模?
另外,发射光(如运动物体在大理石地面的倒影)对运动目标检测也会造成一定影响。那么,怎样处理反射光?
除了这两个问题还有其他许多问题,想了解研究的童鞋可以看看这方面的论文。

准备

在opencv2中,下面的代码实现帧间差分。

cv::absdiff(backImage, //背景gray, //当前帧foreground//前景(未加工前));

帧间差分后,通过阈值,更加精确的标识出前景物体。

//阈值,前景(黑,0),背景(白,255)cv::threshold(foreground, //前景(未加工前)output,//前景(阈值后)threshold, //阈值255, cv::THRESH_BINARY_INV);

针对背景建模问题,我们采用动态建模。每一帧通过加权和更新背景,公式如下:
背景=(1-a)背景+a*第t帧图像(去除前景)
参数a时学习率,如a=0.001。当第t帧时,通过将当前的背景和第t帧图像(除掉前景像素点)加权和,获得新的背景,用来检测第t+1帧前景。

//对背景加(通过掩模去除前景像素)cv::accumulateWeighted(gray, //第t帧图像 background, //背景  learningRate,  //学习率  output //第t帧前景,所有像素为0(黑)的点不参加运算。  );

到此,核心的部分完成。

示例

程序使用了策略模式。策略模式说白了,就是将算法都封装在类中,面向对象的思想,方便扩展和重用。
本程序没有直接写在在一个源文件,而是在以期写好的一些类的基础上进行的。总之,建议用面向对象的方式写程序,掌握策略模式、控制器模式、单件、MVC模式。
VideoProcess类处理每一帧,FrameProcessor抽象类,用来继承,定义自己的帧处理函数。

源.cpp

#include"BGFGSegmentor.h"int main(){    //类对象    VideoProcess processor;    BGFGSegmentor sG;    //设置阈值    sG.setThreshold(30);    //处理的视频    processor.setInput("D:/MatlabWork/classroom.avi");    //设置帧处理函数    processor.setFrameProcessor(&sG);    processor.displayOut("前景");    processor.displayInput("输入");    //显示帧时的延迟    processor.setDelay(1000. / processor.getFrameRate());    //计算检测耗时    double time = cv::getTickCount();    processor.run();    time = cv::getTickCount() - time;    std::cout << time / cv::getTickFrequency() <<"秒"<< std::endl;    cv::waitKey(0);}

VideoProcess.h

#include<opencv2\highgui\highgui.hpp>#include<opencv2\imgproc\imgproc.hpp>#include<opencv2\core\core.hpp>#include<iostream>class FrameProcessor {public:    // 帧处理函数(纯虚函数),用来继承    virtual void process(cv::Mat &input, cv::Mat &output) = 0;};class VideoProcess{private:    void(*process)(cv::Mat&, cv::Mat&);    cv::VideoCapture capture;    bool callIt;    std::string Input;    std::string Output;    int delay;    long fnumbers;    long frameToStop;    bool stop;    cv::VideoWriter w;    std::string outFlie;    FrameProcessor *frameProcessor;public:    VideoProcess() :callIt(true), delay(0), fnumbers(0), stop(false), frameToStop(-1), frameProcessor(0){}    //设置回调函数    void setFrameProcessor(void(*P)(cv::Mat&, cv::Mat&))    {        frameProcessor = 0;        process = P;    }    void setFrameProcessor(FrameProcessor* frameProcessorPtr)    {        process = 0;        frameProcessor = frameProcessorPtr;    }    //得到视频编码类型    int getCodec(char codec[4]) {        union        {            int value;            char code[4];        } returned;        returned.value = static_cast<int>(capture.get(CV_CAP_PROP_FOURCC));        codec[0] = returned.code[0];        codec[1] = returned.code[1];        codec[2] = returned.code[2];        codec[3] = returned.code[3];        return returned.value;    }    //设置输出视频文件名    bool setOutput(const std::string &filename, int codec = 0, double framerate = 0.0, bool isColor = false)    {        outFlie = filename;        if (framerate == 0.0)        {            framerate = getFrameRate();        }        char c[4];        if (codec == 0)        {            codec = getCodec(c);        }        //摄像头        //return w.open(outFlie, -1, framerate, getFrameSize(), isColor);        //视频文件        return w.open(outFlie, codec, framerate, getFrameSize(), isColor);    }    //得到帧的尺寸    cv::Size getFrameSize() {            // get size of from the capture device            int w = static_cast<int>(capture.get(CV_CAP_PROP_FRAME_WIDTH));            int h = static_cast<int>(capture.get(CV_CAP_PROP_FRAME_HEIGHT));            return cv::Size(w, h);    }    //设置输入视频文件    bool setInput(std::string name)    {        fnumbers = 0;        capture.release();        return capture.open(name);    }    //设置摄像头ID    bool setInput(int  i)    {        fnumbers = 0;        capture.release();        return capture.open(i);    }    //显示输入帧窗口    void displayInput(std::string wn)    {        Input = wn;        cv::namedWindow(wn);    }    //显示输出帧窗口    void displayOut(std::string wo)    {        Output = wo;        cv::namedWindow(wo);    }    //清空    void dontDisplay()    {        cv::destroyWindow(Input);        cv::destroyWindow(Output);        Input.clear();        Output.clear();    }    //判断打开帧状态    bool isOpened()    {        return capture.isOpened();    }    //设置停止标记位    bool isStopped()    {        return stop;    }    //读取下一帧    bool readNextFrame(cv::Mat& frame)    {        return capture.read(frame);    }    //调用回调函数    void callProcess()    {        callIt = true;    }    //不调用回调函数    void dontCallProcess()    {        callIt = false;    }    //停止    void stopIt() {        stop = true;    }    //得到帧数    long getFrameNumber() {            long f = static_cast<long>(capture.get(CV_CAP_PROP_POS_FRAMES));            return f;    }    //设置延迟    void setDelay(int d)    {        delay = d;    }    //帧率    double getFrameRate()    {        double r = capture.get(CV_CAP_PROP_FPS);        return r;    }    //写入视频帧    void wNextFrame(cv::Mat& frame)    {        w.write(frame);    }    //run函数    void run()    {        cv::Mat frame;        cv::Mat output;        //判断打开        if (!isOpened())        {            return;        }        stop = false;        //循环        while (!isStopped())        {            if (!readNextFrame(frame))                break;            if (Input.length() != 0)            {                cv::imshow(Input, frame);            }            if (callIt)            {                if (process)                {                    process(frame, output);                }                else                {                    frameProcessor->process(frame, output);                }                fnumbers++;            }            else            {                output = frame;            }            if (outFlie.length() != 0)            {                wNextFrame(output);            }            if (Output.length() != 0)            {                cv::imshow(Output, output);                if (delay >= 0 && cv::waitKey(delay) >= 0)                {                    stopIt();                }                if (frameToStop >= 0 && getFrameNumber() == frameToStop)                    stopIt();            }        }    }};

BGFGSegmentor.h

#include"VideoProcess.h"class BGFGSegmentor :public FrameProcessor{    //当前灰度图    cv::Mat gray;    //累加的背景    cv::Mat background;    //背景图像    cv::Mat backImage;    //前景    cv::Mat foreground;    //学习率    double learningRate;    //阈值    int threshold;public:    BGFGSegmentor() :threshold(10), learningRate(0.01){}    //设置阈值    void setThreshold(int T)    {        threshold = T;    }    //基于帧间差分和动态背景建模的运动目标检测    void process(cv::Mat& frame, cv::Mat& output)    {        cv::cvtColor(frame, gray, CV_BGR2GRAY);        //初始化背景        if (background.empty())        {            gray.convertTo(background, CV_32F);        }        //转换背景为8U格式        background.convertTo(backImage, CV_8U);        //计算差值        cv::absdiff(backImage, gray, foreground);        //阈值,前景(黑,0),背景(白,255)        cv::threshold(foreground, output, threshold, 255, cv::THRESH_BINARY_INV);        //对背景加(通过掩模去除前景像素)        cv::accumulateWeighted(gray, background, learningRate, output);    }};

结果:
这里写图片描述
这里写图片描述
这里写图片描述

0 0
原创粉丝点击