OpenCV学习(一)目标检测之背景差分法

运动目标检测就是先判断在视频序列的帧图像中是否由前景目标的运动,然后再对目标进行初始定位的过程。
传统的目标检测算法主要有相邻帧差法、光流法、背景差分法(又称背景减法)等等。2000年以来,随着神经网络的再次兴起,许多利用神经网络进行目标检测的算法也应运而生。
本文介绍的是背景差分法,它的主要原理是利用当前图像额背景图像的差来检测目标区域。首先对固定摄像机拍摄的视频序列进行计算,得到一个场景的静态背景初始化模型,之后将当前帧于 静态背景图像相比,从当前帧中将背景剔除,从而得到前景运动目标。不过由于背景可能发生变化,所以算法再实现的过程中,将不断地自动实现背景更新,之后再次利用新的背景模型来确定前景目标,以期得到更精确的结果。
用opencv2+vs2010编写代码如下:

#include<iostream>    
#include<highgui.h>
#include<opencv2/imgproc/imgproc.hpp>
#include <iomanip>
using namespace std;
using namespace cv;
//帧处理基类  
class FrameProcessor{
public:
 virtual void process(Mat &input, Mat &ouput) = 0;
};
class VideoProcessor{
private:
 //Opencv视频捕捉对象
 VideoCapture capture;
 //每帧调用的回调函数
 void(*process)(Mat&, Mat&);
 //确定是否调用回调函数的bool变量
 bool callIt;
 //输入窗口的名称
 string windowNameInput;
 string windowNameOutput;
 //延迟
 int delay;
 //已处理的帧数
 long fnumber;
 //在该帧数停止
 long frameToStop;
 //是否停止处理
 bool stop;
 //opencv的写视频对象
 VideoWriter writer;
 //输出文件名称
 string outputFile;
 //输出图像的当前索引
 int currentIndex;
 //输出图像名称中的数字位数
 int digits;
 //输出图像的扩展名
 string extension;
 FrameProcessor *frameprocessor;
 //图像序列作为输入视频流  
 vector<string> images;
public:
 VideoProcessor() :callIt(true), delay(0), fnumber(0), stop(false), frameToStop(-1){}
 //设置回调函数
 void setFrameProcessor(
  void(*frameProcessingCallback)(Mat&, Mat&)){
  process = frameProcessingCallback;
 }
 //设置视频文件的名称
 bool setInput(string filename){
  fnumber = 0;
  //释放之前打开过的资源
  capture.release();
  //打开视频文件
  return capture.open(filename);
 }
 //创建输入窗口
 void displayInput(string wn){
  windowNameInput = wn;
  namedWindow(windowNameInput);
 }
 //创建输出窗口
 void displayOutput(string wn){
  windowNameOutput = wn;
  namedWindow(windowNameOutput);
 }
 //不再显示处理后的帧
 void dontDisplay(){
  destroyWindow(windowNameInput);
  destroyWindow(windowNameOutput);
  windowNameInput.clear();
  windowNameOutput.clear();
 }
 //获取并处理序列帧
 void run(){
  //当前帧
  Mat frame;
  //输出帧
  Mat output;
  //打开失败时
  if (!isOpened())return;
  stop = false;
  while (!isStopped())
  {
   //读取下一帧
   if (!readNextFrame(frame))break;
   //显示输出帧
   if (windowNameInput.length() != 0)
    imshow(windowNameInput, frame);
   //调用处理函数
   if (callIt){
    //处理当前帧
    if (process)
     process(frame, output);
    else if (frameprocessor)
     frameprocessor->process(frame, output);
    //增加帧数
    fnumber++;
   }
   else
   {
    output = frame;
   }
   //输出图像序列
   if (outputFile.length() != 0)
    writeNextFrame(output);
   //显示输出帧
   if (windowNameOutput.length() != 0)
    imshow(windowNameOutput, output);
   //引入延迟
   if (delay >= 0 && waitKey(delay) >= 0)
    stopIt();
   //检查是否需要停止运行
   if (frameToStop >= 0 && getFrameNumber() == frameToStop)
    stopIt();
  }
 }
 //停止运行
 void stopIt(){
  stop = true;
 }
 //是否已停止
 bool isStopped(){
  return stop;
 }
 //是否开始了捕获设备
 bool isOpened(){
  return capture.isOpened();
 }
 //设置帧间的延迟 0意味着在每帧都等待用户按键 负数意味着没有延迟
 void setDelay(int d){
  delay = d;
 }
 //得到下一帧 可能是视频文件或摄像头
 bool readNextFrame(Mat &frame){
  return capture.read(frame);
 }
 //需要调用回调函数
 void callProcess(){
  callIt = true;
 }
 //不需要调用回调函数
 void dontCallProcess(){
  callIt = false;
 }
 void stopAtFrameNo(long frame){
  frameToStop = frame;
 }
 //返回下一帧的帧数
 long getFrameNumber(){
  //得到捕获设备的信息
  long fnumber = static_cast<long>(capture.get(CV_CAP_PROP_POS_FRAMES));
  return fnumber;
 }
 int getFrameRate(){
  return capture.get(CV_CAP_PROP_FPS);
 }
 //获得编码类型  
 int getCodec(char codec[4]) {
  if (images.size() != 0)
   return -1;
  union { // 数据结构4-char  
   int value;
   char code[4];
  } returned;
  //获得编码值  
  returned.value = static_cast<int>(
   capture.get(CV_CAP_PROP_FOURCC));
  // get the 4 characters  
  codec[0] = returned.code[0];
  codec[1] = returned.code[1];
  codec[2] = returned.code[2];
  codec[3] = returned.code[3];
  return returned.value;
 }
 //获得帧大小  
 Size getFrameSize() {
  if (images.size() == 0) {
   // 从视频流获得帧大小  
   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 Size(w, h);
  }
  else {
   //从图像获得帧大小  
   cv::Mat tmp = cv::imread(images[0]);
   return (tmp.data) ? (tmp.size()) : (Size(0, 0));
  }
 }
 //设置输出视频文件
 //默认使用于输入视频相同的参数
 bool setOutput(const string &filename, int codec = 0, double framerate = 0.0, bool isColor = true){

  outputFile = filename;
  extension.clear();
  if (framerate == 0.0)
   framerate = getFrameRate();//与输入相同
  char c[4];
  //使用相同的编码格式
  if (codec == 0)
  {
   codec = getCodec(c);
  }
  //打开输出视频
  return writer.open(outputFile, //文件名
   codec, //将使用的编码格式
   framerate, //帧率
   getFrameSize(), //尺寸
   isColor); //是否是彩色视频?
 }
 //输出帧 可能是视频文件或图像文件
 void writeNextFrame(Mat &frame){
  if (extension.length())//我们输出到图像文件
  {
   stringstream ss;
   //组成输出文件名称
   ss << outputFile << setfill('0')
    << setw(digits)
    << currentIndex++ << extension;
   imwrite(ss.str(), frame);
  }
  else
  {//我们输出到视频文件
   writer.write(frame);
  }
 }
 //设置输出为独立的图像文件 扩展名必须是".jpg" ".bmp"
 bool setOutput(const string &filename,//前缀
  const string&ext,//后缀
  int numberofDigits = 3,//数字位数
  int startIndex = 0){ //开始索引
  //数字位数必须是正的
  if (numberofDigits < 0)return false;
  //文件名及其后缀
  outputFile = filename;
  extension = ext;
  //文件名中的数字位数
  digits = numberofDigits;
  //开始索引
  currentIndex = startIndex;
  return true;
 }
 void  setFrameProcessor(FrameProcessor *frameprocessor){
  process = 0;
  this->frameprocessor = frameprocessor;
  callProcess();
 }
};
class BGFGSegmentor :public FrameProcessor{
 Mat gray;
 Mat background;
 Mat backImage;
 Mat foreground;
 //背景累加中的学习率
 double learningRate;
 int ithreshold;
public:
 BGFGSegmentor() :ithreshold(10), learningRate(0.01){}
 //处理方法
 void process(Mat &frame, Mat &output){
  //转换为灰度图
  cvtColor(frame, gray, CV_BGR2GRAY);
  //初始化背景为第一帧
  if (background.empty()) gray.convertTo(background, CV_32F);
  //转换背景图像为8U格式
  background.convertTo(backImage, CV_8U);
  //计算差值
  absdiff(backImage, gray, foreground);
  //应用阈值化到前景图像
  threshold(foreground, output, ithreshold, 255, THRESH_BINARY_INV);
  //对图像累加
  accumulateWeighted(gray, background, learningRate, output);
 }
 void setThreshold(int ithreshold){
  this->ithreshold = ithreshold;
 }
};
int main(){
 //创建视频处理器实例
 VideoProcessor processor;
 //创建背景 / 前景分段器
 BGFGSegmentor segmentor;
 segmentor.setThreshold(25);
 //打开视频文件
 processor.setInput("../people.avi");
 //设置帧处理器对象
 processor.setFrameProcessor(&segmentor);
 //声明显示窗口
 processor.displayOutput("Extracted Foreground");
 //以原始帧率播放视频
 processor.setDelay(1000. / processor.getFrameRate());
 //开始处理过程
 processor.run();
 return 0;
}

用一个监控视角作为示例得到的结果如下:
在这里插入图片描述

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值