codeBook背景建模

(1)codeBook算法:

CodeBook算法的基本思想是得到每个像素的时间序列模型。这种模型能很好地处理时间起伏,缺点是需要消耗大量的内存。CodeBook算法为当前图像的每一个像素建立一个CodeBook(CB)结构,每个CodeBook结构又由多个CodeWord(CW)组成。
  CB和CW的形式如下:
  CB={CW1,CW2,…CWn,t}
  CW={lHigh,lLow,max,min,t_last,stale}
  其中n为一个CB中所包含的CW的数目,当n太小时,退化为简单背景,当n较大时可以对复杂背景进行建模;t为CB更新的次数。CW是一个6元组,其中IHigh和ILow作为更新时的学习上下界,max和min记录当前像素的最大值和最小值。上次更新的时间t_last和陈旧时间stale(记录该CW多久未被访问)用来删除很少使用的CodeWord。
假设当前训练图像I中某一像素为I(x,y),该像素的CB的更新算法如下,另外记背景阈值的增长判定阈值为Bounds:
  (1) CB的访问次数加1;
  (2) 遍历CB中的每个CW,如果存在一个CW中的IHigh,ILow满足ILow≤I(x,y)≤ IHigh,则转(4);
  (3) 创建一个新的码字CWnew加入到CB中, CWnew的max与min都赋值为I(x,y), IHigh <- I(x,y) + Bounds,ILow <- I(x,y) – Bounds,并且转(6);
  (4) 更新该码字的t_last,若当前像素值I(x,y)大于该码字的max,则max <- I(x,y),若 I(x,y)小于该码字的min,则min <- I(x,y);
  (5) 更新该码字的学习上下界,以增加背景模型对于复杂背景的适应能力,具体做法是: 若IHigh < I(x,y) + Bounds,则IHigh 增长1,若ILow > I(x,y) – Bounds,则ILow 减少1;
  (6) 更新CB中每个CW的stale。
  使用已建立好的CB进行运动目标检测的方法很简单,记判断前景的范围上下界为minMod和maxMod,对于当前待检测图像上的某一像素I(x,y),遍历它对应像素背景模型CB中的每一个码字CW,若存在一个CW,使得I(x,y) < max + maxMod并且I(x,y) > min – minMod,则I(x,y)被判断为背景,否则被判断为前景。
  在实际使用CodeBook进行运动检测时,除了要隔一定的时间对CB进行更新的同时,需要对CB进行一个时间滤波,目的是去除很少被访问到的CW,其方法是访问每个CW的stale,若stale大于一个阈值(通常设置为总更新次数的一半),移除该CW。
  综上所述,CodeBook算法检测运动目标的流程如下:
  (1) 选择一帧到多帧使用更新算法建立CodeBook背景模型;
  (2) 按上面所述方法检测前景(运动目标);
  (3) 间隔一定时间使用更新算法更新CodeBook模型,并对CodeBook进行时间滤波;
  (4) 若检测继续,转(2),否则结束。

(2)代码:

#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\video.hpp>
#include <opencv2\video\tracking.hpp>
#include <opencv2\videoio.hpp>
#include <iostream>
#include <vector>
#include <conio.h>
#include <sstream>
using namespace cv;
using namespace std;
Ptr <BackgroundSubtractorMOG2> PMOG2;//背景减法对象
int frameCount = 0; //存储当前帧数 #define CHANNELS 3  //通道数
typedef struct ce {uchar learnHigh[CHANNELS]; //学习高侧阈uchar learnLow[CHANNELS]; //学习低端门槛uchar max[CHANNELS]; //框边界的高边uchar min[CHANNELS]; //框边界的低边int t_last_update; //让我们删除旧有的条目int stale; //最大负运行(最长不活动时间)
} code_element;//YUV颜色空间下的codebook(编码本)结构
typedef struct code_book {code_element **cb;int numEntries;int t; //记录第一次或最后一次清除操作之间累积的像素点数量
} codeBook;//update_codebook:捕获背景中相关变化的图像
/*
//使用新数据点更新代码簿条目
//
// p: 指向YUV像素的指针
// c: 此像素的编码本
// cbBounds: 学习码本的范围(经验法则:10)
// numChannels: 我们正在学习的颜色通道数
//
//注意:cvBounds的长度必须等于numChannels
//
//返回:编码本索引
*/
int update_codebook(uchar* p, codeBook& c, unsigned* cbBounds, int numChannels)
{if (c.numEntries == 0) c.t = 0;//编码本中码元为0时初始化为0c.t++;//每次调用加一,即每帧图像加一unsigned int high[3], low[3];int n, i;for (n = 0; n < numChannels; n++){high[n] = *(p + n) + *(cbBounds + n);if (high[n] > 255) high[n] = 255;low[n] = *(p + n) - *(cbBounds + n);if (low[n] < 0) low[n] = 0;//用p 所指像素通道数据,加减cbBonds中数值,作为此像素阀值的上下限}int matchChannel;// SEE IF THIS FITS AN EXISTING CODEWORDfor (i = 0; i < c.numEntries; i++) {matchChannel = 0;遍历此编码本每个码元,测试p像素是否满足其中之一for (n = 0; n < numChannels; n++) {if ((c.cb[i]->learnLow[n] <= *(p + n)) &&//如果p 像素通道数据在该码元阀值上下限之间(*(p + n) <= c.cb[i]->learnHigh[n])){matchChannel++;}}if (matchChannel == numChannels) //如果p 像素各通道都满足上面条件{c.cb[i]->t_last_update = c.t;//更新该码元时间为当前时间for (n = 0; n < numChannels; n++) {//调整该码元各通道最大最小值if (c.cb[i]->max[n] < *(p + n)){c.cb[i]->max[n] = *(p + n);}else if (c.cb[i]->min[n] > *(p + n)){c.cb[i]->min[n] = *(p + n);}}break;}}// OVERHEAD TO TRACK POTENTIAL STALE ENTRIESfor (int s = 0; s < c.numEntries; s++) {// Track which codebook entries are going stale://int negRun = c.t - c.cb[s]->t_last_update;if (c.cb[s]->stale < negRun) c.cb[s]->stale = negRun;}// ENTER A NEW CODEWORD IF NEEDEDif (i == c.numEntries) //if no existing codeword found, make one{code_element **foo = new code_element*[c.numEntries + 1];for (int ii = 0; ii < c.numEntries; ii++) {foo[ii] = c.cb[ii];}foo[c.numEntries] = new code_element;if (c.numEntries) delete[] c.cb;c.cb = foo;for (n = 0; n < numChannels; n++) {c.cb[c.numEntries]->learnHigh[n] = high[n];c.cb[c.numEntries]->learnLow[n] = low[n];c.cb[c.numEntries]->max[n] = *(p + n);c.cb[c.numEntries]->min[n] = *(p + n);}c.cb[c.numEntries]->t_last_update = c.t;c.cb[c.numEntries]->stale = 0;c.numEntries += 1;}// SLOWLY ADJUST LEARNING BOUNDSfor (n = 0; n < numChannels; n++){if (c.cb[i]->learnHigh[n] < high[n]) c.cb[i]->learnHigh[n] += 1;if (c.cb[i]->learnLow[n] > low[n]) c.cb[i]->learnLow[n] -= 1;}return(i);
}///
//clear_stale_entries:训练有移动的前景目标(数目很小)的背景
/*
//学习期间,当你学习了一段时间后,
//定期调用此方法清除陈旧的代码簿条目
// c: 要清理的密码本
//
//返回:清除的条目数
*/
int clear_stale_entries(codeBook &c) {int staleThresh = c.t >> 1; //设定刷新时间int *keep = new int[c.numEntries];// 申请一个标记数组int keepCnt = 0;// SEE WHICH CODEBOOK ENTRIES ARE TOO STALE//for (int i = 0; i < c.numEntries; i++) {if (c.cb[i]->stale > staleThresh)keep[i] = 0; //Mark for destructionelse{keep[i] = 1; //Mark to keepkeepCnt += 1;}}// KEEP ONLY THE GOODc.t = 0; //Full reset on stale trackingcode_element **foo = new code_element*[keepCnt];int k = 0;for (int ii = 0; ii < c.numEntries; ii++) {if (keep[ii]){foo[k] = c.cb[ii];//We have to refresh these entries for next clearStalefoo[k]->t_last_update = 0;k++;}}// CLEAN UPdelete[] keep;delete[] c.cb;c.cb = foo;int numCleared = c.numEntries - keepCnt;c.numEntries = keepCnt;return(numCleared);
}///
//背景减法:寻找前景目标
/*
//给定像素和码本,确定像素是否为
//由码本覆盖
//
// p: 像素指针(YUV交错)
// c: 代码簿参考
// numChannels: 我们正在测试的通道数
// maxMod: 从中加上这个(可能为负数)数字,以确定新像素是否为前景时的最大级别
// minMod: 从中减去这个(可能是负数)数字,以确定新像素是否为前景时的最小水平
//
//注意: minMod和maxMod的长度必须为numChannels,
//例如 3个通道=> minMod [3],maxMod [3]。 有一分钟和
//每个频道一个最大阈值。
//
//返回: 0 =>背景,255 =>前景
*/
uchar background_diff(uchar* p, codeBook& c, int numChannels, int* minMod, int* maxMod)
{int matchChannel, i;// SEE IF THIS FITS AN EXISTING CODEWORD//for (i = 0; i < c.numEntries; i++) {matchChannel = 0;for (int n = 0; n < numChannels; n++) {if ((c.cb[i]->min[n] - minMod[n] <= *(p + n)) &&(*(p + n) <= c.cb[i]->max[n] + maxMod[n])) {matchChannel++; //Found an entry for this channel}else {break;}}if (matchChannel == numChannels) {break; //Found an entry that matched all channels}}if (i >= c.numEntries) return(255);return(0);
}
int main() {///// 需要使用的变量CvCapture* capture;IplImage*   rawImage;IplImage*  yuvImage;IplImage*  ImaskCodeBook;codeBook* cB;unsigned cbBounds[CHANNELS];uchar*       pColor; //YUV pointerint            imageLen;int            nChannels = CHANNELS;int           minMod[CHANNELS];int            maxMod[CHANNELS];//构造各种尺寸的元素以用于形态学变换cv::Mat structuringElement2x2 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2, 2));cv::Mat structuringElement3x3 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));cv::Mat structuringElement5x5 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));cv::Mat structuringElement7x7 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7));//// 初始化各变量//cvNamedWindow("Raw");cvNamedWindow("CodeBook");char* file_path = "..//Data//shake.avi";capture = cvCreateFileCapture(file_path);if (!capture){printf("Couldn't open the capture!");return -1;}rawImage = cvQueryFrame(capture);yuvImage = cvCreateImage(cvGetSize(rawImage), 8, 3);// 给yuvImage 分配一个和rawImage 尺寸相同,8位3通道图像ImaskCodeBook = cvCreateImage(cvGetSize(rawImage), IPL_DEPTH_8U, 1);// 为ImaskCodeBook 分配一个和rawImage 尺寸相同,8位单通道图像cvSet(ImaskCodeBook, cvScalar(255));// 设置单通道数组所有元素为255,即初始化为白色图像imageLen = rawImage->width * rawImage->height;cB = new codeBook[imageLen];// 得到与图像像素数目长度一样的一组码本,以便对每个像素进行处理for (int i = 0; i < imageLen; i++)// 初始化每个码元数目为0cB[i].numEntries = 0;for (int i = 0; i < nChannels; i++){cbBounds[i] = 10;    // 用于确定码元各通道的阀值minMod[i] = 50; // 用于背景差分函数中maxMod[i] = 50;    // 调整其值以达到最好的分割}//Ptr<BackgroundSubtractorMOG2> pMOG2 = createBackgroundSubtractorMOG2(200, 36.0, false);// 开始处理视频每一帧图像for (int i = 0;; i++){cvCvtColor(rawImage, yuvImage, CV_BGR2YCrCb);// 色彩空间转换,将rawImage 转换到YUV色彩空间,输出到yuvImage// 即使不转换效果依然很好// yuvImage = cvCloneImage(rawImage);cvShowImage("frame", rawImage);cv::Mat frame2 = cv::cvarrToMat(rawImage), fgMaskMOG2, bgImg;pMOG2->apply(frame2, fgMaskMOG2);pMOG2->getBackgroundImage(bgImg);medianBlur(fgMaskMOG2, fgMaskMOG2, 5);//           imshow("medianBlur", fgMaskMOG2);// Fill black holesmorphologyEx(fgMaskMOG2, fgMaskMOG2, MORPH_CLOSE, getStructuringElement(MORPH_RECT, Size(5, 5)));// Fill white holesmorphologyEx(fgMaskMOG2, fgMaskMOG2, MORPH_OPEN, getStructuringElement(MORPH_RECT, Size(5, 5)));imshow("morphologyEx", fgMaskMOG2);if (i <= 30)// 30帧内进行背景学习{pColor = (uchar *)(yuvImage->imageData);// 指向yuvImage 图像的通道数据for (int c = 0; c < imageLen; c++){update_codebook(pColor, cB[c], cbBounds, nChannels);// 对每个像素,调用此函数,捕捉背景中相关变化图像pColor += 3;// 3 通道图像, 指向下一个像素通道数据}if (i == 30)// 到30 帧时调用下面函数,删除码本中陈旧的码元{for (int c = 0; c < imageLen; c++)clear_stale_entries(cB[c]);}}else{uchar maskPixelCodeBook;pColor = (uchar *)((yuvImage)->imageData); //3 channel yuv imageuchar *pMask = (uchar *)((ImaskCodeBook)->imageData); //1 channel image// 指向ImaskCodeBook 通道数据序列的首元素for (int c = 0; c < imageLen; c++){maskPixelCodeBook = background_diff(pColor, cB[c], nChannels, minMod, maxMod);// 我看到这儿时豁然开朗,开始理解了codeBook 呵呵*pMask++ = maskPixelCodeBook;pColor += 3;// pColor 指向的是3通道图像}}if (!(rawImage = cvQueryFrame(capture)))break;cvShowImage("CodeBook", ImaskCodeBook);cv::Mat imgDifference = cv::cvarrToMat(ImaskCodeBook);medianBlur(imgDifference, imgDifference, 5);cv::Mat imgThresh;erode(imgDifference, imgDifference, structuringElement2x2);  //两次侵蚀处理,以消除噪音//erode(imgDifference, imgDifference, structuringElement2x2);cv::threshold(imgDifference, imgThresh, 20, 255.0, CV_THRESH_BINARY); //执行阈值处理并获得阈值掩码cv::dilate(imgThresh, imgThresh, structuringElement2x2);//dilate(imgThresh, imgThresh, structuringElement3x3);cv::imshow("imgTresh", imgThresh);//cvShowImage("Raw", rawImage);if (cvWaitKey(30) == 27)break;}cvReleaseCapture(&capture);if (yuvImage)cvReleaseImage(&yuvImage);if (ImaskCodeBook)cvReleaseImage(&ImaskCodeBook);cvDestroyAllWindows();delete[] cB;return 0;
}

(3)感官

这个codeBook建模方法个人感觉适合于一些摇晃的树荫场景,用了感觉变化不大,与BackgroundSubtractorMOG2建立的模型相比变化不大。

codeBook背景建模相关推荐

  1. 背景建模与前景检测3(Background Generation And Foreground Detection Phase 3)

    背景建模与前景检测之三(Background Generation And Foreground Detection Phase 3) 作者:王先荣 在上一篇文章里,我尝试翻译了<Nonpara ...

  2. 背景建模与前景检测2(Background Generation And Foreground Detection Phase 2)

    背景建模与前景检测2(Background Generation And Foreground Detection Phase 2) 作者:王先荣 本文尝试对<学习OpenCV>中推荐的论 ...

  3. 目标检测中背景建模方法

    FROM: http://www.cnblogs.com/ronny/archive/2012/04/12/2444053.html 最近一直在做前景检测方面的研究,刚开始主要是做一些工程性的应用,为 ...

  4. 常见的目标检测中的背景建模方法总结

    最近一直在做前景检测方面的研究,刚开始主要是做一些工程性的应用,为了解决工程方面的问题,下了不少功夫,也看了不少最近国内外的文章.一直想做个总结,拖着拖着,终究却写成这篇极不成功的总结.(文章下载:h ...

  5. 背景建模方法论文总结

    文章名称:Real-tine tracking of the human body 年份:1997 作者:Christopher Richard Wren 算法名称:单高斯背景建模 简单描述: 将每个 ...

  6. 【opencv源码剖析】背景建模mog2

    前言 opencv实现的背景建模方法有很多,早期的opencv版本modules/video/src下有acmmm2003(2.3.1版本).codebook(2.3.1版本).gmg(2.4.8版本 ...

  7. 背景建模或前景检测(Background Generation And Foreground Detection) 二

    转自:http://www.cnblogs.com/xrwang/archive/2010/03/27/BackgroundGenerationAndForegroundDetectionPhase2 ...

  8. 常见的目标检测中的背景建模方法

    Author: JW. ZHOU 2014/6/13 最近一直在做前景检测方面的研究,刚开始主要是做一些工程性的应用,为了解决工程方面的问题,下了不少功夫,也看了不少最近国内外的文章.一直想做个总结, ...

  9. 背景建模(一) Evaluation of Background Subtraction Techniques

    转自http://blog.sina.com.cn/s/blog_78fd98af0101h9bf.html 以前做过一些关于背景建模,运动目标检测的工作,打算进行一下小结,那么就先从这篇CVPR20 ...

  10. 目标检测中背景建模方法总结

    转自:http://www.cnblogs.com/ronny/archive/2012/04/12/2444053.html 背景建模或前景检测的算法主要有: 1. Single Gaussian ...

最新文章

  1. CMake4:安装与测试
  2. 一个完整的mybatis项目,包含增删改查
  3. MFC 列表控件CListCtrl加载类似QQ界面的头像与文字
  4. 公共平台服务治理与鉴权
  5. 终端编译opengl程序编译运行_ubuntu编译opengl和demo之二(glfw版本)
  6. 解决方案-vector初始化后存放Mat,出现Mat矩阵数据同变问题
  7. 四川专利代理机构列表
  8. Python爬虫代理池搭建
  9. Ubuntu 数字小键盘不能用解决方法
  10. linux 服务器搭建opvn
  11. 如何用python搜索要用的素材_一篇文章教会你利用Python网络爬虫获取素材图片
  12. 速腾聚创16线激光雷达rslidar-16的ros驱动安装与rviz点云显示
  13. Zookeeper--简介
  14. 计量经济学及Stata应用案例(一)
  15. 肺癌新易感位点的发现及多基因遗传评分在肺癌风险预测中的应用--基于中国超大型前瞻性队列研究
  16. 2022-07-11 Python TCP服务器与客户端
  17. HTML5期末大作业:在线动漫网站设计——火影忍者(1页) HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码
  18. 【建站篇】如何将本地搭建的织梦站点上传到服务器空间?
  19. Firefox备份图文教程
  20. 地铁调色,打造金属质感

热门文章

  1. 【C语言实现】goto语句实现简易关机程序
  2. java实现五子棋_Java的五子棋实现
  3. 软件平台与中间技术复习
  4. 【持续更新】书籍推荐
  5. Linux网络-MAC协议
  6. PC版Android系统声卡驱动,android下调试声卡驱动之概述
  7. CSDN早报-2019-04-29
  8. 小米平板4twrp一键刷入工具_不要浪费小米10的双扬声器!杜比全景音刷入教程分享...
  9. spring的核心组件
  10. 项目开发的完整流程(详解版)