codeBook背景建模
codeBook背景建模
(1)codeBook算法:
(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背景建模相关推荐
- 背景建模与前景检测3(Background Generation And Foreground Detection Phase 3)
背景建模与前景检测之三(Background Generation And Foreground Detection Phase 3) 作者:王先荣 在上一篇文章里,我尝试翻译了<Nonpara ...
- 背景建模与前景检测2(Background Generation And Foreground Detection Phase 2)
背景建模与前景检测2(Background Generation And Foreground Detection Phase 2) 作者:王先荣 本文尝试对<学习OpenCV>中推荐的论 ...
- 目标检测中背景建模方法
FROM: http://www.cnblogs.com/ronny/archive/2012/04/12/2444053.html 最近一直在做前景检测方面的研究,刚开始主要是做一些工程性的应用,为 ...
- 常见的目标检测中的背景建模方法总结
最近一直在做前景检测方面的研究,刚开始主要是做一些工程性的应用,为了解决工程方面的问题,下了不少功夫,也看了不少最近国内外的文章.一直想做个总结,拖着拖着,终究却写成这篇极不成功的总结.(文章下载:h ...
- 背景建模方法论文总结
文章名称:Real-tine tracking of the human body 年份:1997 作者:Christopher Richard Wren 算法名称:单高斯背景建模 简单描述: 将每个 ...
- 【opencv源码剖析】背景建模mog2
前言 opencv实现的背景建模方法有很多,早期的opencv版本modules/video/src下有acmmm2003(2.3.1版本).codebook(2.3.1版本).gmg(2.4.8版本 ...
- 背景建模或前景检测(Background Generation And Foreground Detection) 二
转自:http://www.cnblogs.com/xrwang/archive/2010/03/27/BackgroundGenerationAndForegroundDetectionPhase2 ...
- 常见的目标检测中的背景建模方法
Author: JW. ZHOU 2014/6/13 最近一直在做前景检测方面的研究,刚开始主要是做一些工程性的应用,为了解决工程方面的问题,下了不少功夫,也看了不少最近国内外的文章.一直想做个总结, ...
- 背景建模(一) Evaluation of Background Subtraction Techniques
转自http://blog.sina.com.cn/s/blog_78fd98af0101h9bf.html 以前做过一些关于背景建模,运动目标检测的工作,打算进行一下小结,那么就先从这篇CVPR20 ...
- 目标检测中背景建模方法总结
转自:http://www.cnblogs.com/ronny/archive/2012/04/12/2444053.html 背景建模或前景检测的算法主要有: 1. Single Gaussian ...
最新文章
- CMake4:安装与测试
- 一个完整的mybatis项目,包含增删改查
- MFC 列表控件CListCtrl加载类似QQ界面的头像与文字
- 公共平台服务治理与鉴权
- 终端编译opengl程序编译运行_ubuntu编译opengl和demo之二(glfw版本)
- 解决方案-vector初始化后存放Mat,出现Mat矩阵数据同变问题
- 四川专利代理机构列表
- Python爬虫代理池搭建
- Ubuntu 数字小键盘不能用解决方法
- linux 服务器搭建opvn
- 如何用python搜索要用的素材_一篇文章教会你利用Python网络爬虫获取素材图片
- 速腾聚创16线激光雷达rslidar-16的ros驱动安装与rviz点云显示
- Zookeeper--简介
- 计量经济学及Stata应用案例(一)
- 肺癌新易感位点的发现及多基因遗传评分在肺癌风险预测中的应用--基于中国超大型前瞻性队列研究
- 2022-07-11 Python TCP服务器与客户端
- HTML5期末大作业:在线动漫网站设计——火影忍者(1页) HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码
- 【建站篇】如何将本地搭建的织梦站点上传到服务器空间?
- Firefox备份图文教程
- 地铁调色,打造金属质感