codebook法分割前景目标
利用codebook法训练得到背景模型后,对背景差分得到的掩模图像去噪声并找到较大连通域。相对于平均背景法,它的分割效果更好些。当然,分割效果和背景模型训练的帧数有很大关系,适当调整一些参数会得到更好的效果。
#include "stdafx.h"
#include "cv.h"
#include "highgui.h" #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; typedef struct code_book{
code_element **cb;
int numEntries;
int t;
}codeBook; #define CVCONTOUR_APPROX_LEVEL 2 // Approx.threshold - the bigger it is, the simpler is the boundary
#define CVCLOSE_ITR 1 // How many iterations of erosion and/or dialation there should be #define CV_CVX_WHITE CV_RGB(0xff,0xff,0xff)
#define CV_CVX_BLACK CV_RGB(0x00,0x00,0x00) ///////////////////////////////////////////////////////////////////////////////////
// int updateCodeBook(uchar *p, codeBook &c, unsigned cbBounds)
// Updates the codebook entry with a new data point
//
// p Pointer to a YUV pixel
// c Codebook for this pixel
// cbBounds Learning bounds for codebook (Rule of thumb: 10)
// numChannels Number of color channels we're learning
//
// NOTES:
// cvBounds must be of size cvBounds[numChannels]
//
// RETURN
// codebook index 更新码本
int update_codebook(uchar* p,codeBook &c, unsigned* cbBounds, int numChannels)
{
if(c.numEntries==)c.t=; //码本中码元数为0初始化时间为0
c.t+=; //每调用一次时间数加1 int n;
unsigned int high[],low[];
for(n=;n<numChannels;n++)
{
//加减cbBonds作为此像素阈值上下界
high[n]=*(p+n) + *(cbBounds+n); //直接使用指针操作更快
if(high[n]>) high[n]=;
low[n]=*(p+n)-*(cbBounds+n);
if(low[n]<) low[n]=;
} int matchChannel;
int i;
for(i=;i<c.numEntries;i++)
{
matchChannel=;
for(n=;n<numChannels;n++)
{
if((c.cb[i]->learnLow[n]<=*(p+n)) && (*(p+n)<=c.cb[i]->learnHigh[n]))
{
matchChannel++;
}
}
if(matchChannel==numChannels)
{
c.cb[i]->t_last_update=c.t; //更新码元时间
for(n=;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;
}
} //像素p不满足码本中任何一个码元,创建一个新码元
if(i == c.numEntries)
{
code_element **foo=new code_element*[c.numEntries+];
for(int ii=;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=;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 = ;
c.numEntries += ;
} //计算码元上次更新到现在的时间
for(int s=; s<c.numEntries; s++)
{
int negRun=c.t-c.cb[s]->t_last_update;
if(c.cb[s]->stale < negRun)
c.cb[s]->stale = negRun;
} //如果像素通道值在高低阈值内,但在码元阈值之外,则缓慢调整此码元学习界限(max,min相当于外墙,粗调;learnHigh,learnLow相当于内墙,细调)
for(n=; n<numChannels; n++)
{
if(c.cb[i]->learnHigh[n]<high[n])
c.cb[i]->learnHigh[n]+=;
if(c.cb[i]->learnLow[n]>low[n])
c.cb[i]->learnLow[n]-=;
} return i;
} // 删除一定时间内未访问的码元,避免学习噪声的codebook
int cvclearStaleEntries(codeBook &c)
{
int staleThresh=c.t>>; //设定刷新时间
int *keep=new int[c.numEntries];
int keepCnt=; //记录不删除码元码元数目
for(int i=; i<c.numEntries; i++)
{
if(c.cb[i]->stale > staleThresh)
keep[i]=; //保留标志符
else
{
keep[i]=; //删除标志符
keepCnt+=;
}
} c.t=;
code_element **foo=new code_element*[keepCnt];
int k=;
for(int ii=; ii<c.numEntries; ii++)
{
if(keep[ii])
{
foo[k]=c.cb[ii];
foo[k]->stale=; //We have to refresh these entries for next clearStale
foo[k]->t_last_update=;
k++;
}
} delete[] keep;
delete[] c.cb;
c.cb=foo;
int numCleared=c.numEntries-keepCnt;
c.numEntries=keepCnt;
return numCleared; //返回删除的码元
} ///////////////////////////////////////////////////////////////////////////////////
// uchar cvbackgroundDiff(uchar *p, codeBook &c, int minMod, int maxMod)
// Given a pixel and a code book, determine if the pixel is covered by the codebook
//
// p pixel pointer (YUV interleaved)
// c codebook reference
// numChannels Number of channels we are testing
// maxMod Add this (possibly negative) number onto max level when code_element determining if new pixel is foreground
// minMod Subract this (possible negative) number from min level code_element when determining if pixel is foreground
//
// NOTES:
// minMod and maxMod must have length numChannels, e.g. 3 channels => minMod[3], maxMod[3].
//
// Return
// 0 => background, 255 => foreground 背景差分,寻找前景目标
uchar cvbackgroundDiff(uchar *p, codeBook &c, int numChannels, int *minMod, int *maxMod)
{
int matchChannel;
int i;
for(i=; i<c.numEntries; i++)
{
matchChannel=;
for(int n=; n<numChannels; n++)
{
if((c.cb[i]->min[n]-minMod[n]<=*(p+n)) && (*(p+n)<=c.cb[i]->max[n]+maxMod[n]))
matchChannel++;
else
break;
}
if(matchChannel==numChannels)
break;
}
if(i==c.numEntries) //像素p各通道值不满足所有的码元,则为前景,返回白色
return ;
return ; //匹配到一个码元时,则为背景,返回黑色
} ///////////////////////////////////////////////////////////////////////////////////////////
//void cvconnectedComponents(IplImage *mask, int poly1_hull0, float perimScale, int *num, CvRect *bbs, CvPoint *centers)
// This cleans up the foreground segmentation mask derived from calls to cvbackgroundDiff
//
// mask Is a grayscale (8 bit depth) "raw" mask image which will be cleaned up
//
// OPTIONAL PARAMETERS:
// poly1_hull0 If set, approximate connected component by (DEFAULT) polygon, or else convex hull (0)
// perimScale Len = image (width+height)/perimScale. If contour len < this, delete that contour (DEFAULT: 4)
void cvconnectedComponents(IplImage *mask, int poly1_hull0, float perimScale)
{
static CvMemStorage* mem_storage=NULL;
static CvSeq* contours=NULL;
cvMorphologyEx( mask, mask, NULL, NULL, CV_MOP_OPEN, CVCLOSE_ITR);
cvMorphologyEx( mask, mask, NULL, NULL, CV_MOP_CLOSE, CVCLOSE_ITR); if(mem_storage==NULL)
mem_storage=cvCreateMemStorage();
else
cvClearMemStorage(mem_storage); CvContourScanner scanner=cvStartFindContours(mask,mem_storage,sizeof(CvContour),CV_RETR_EXTERNAL);
CvSeq* c;
int numCont=; //轮廓数
while((c=cvFindNextContour(scanner))!=NULL)
{
double len=cvContourPerimeter(c);
double q=(mask->height+mask->width)/perimScale; //轮廓长度阀值设定
if(len<q)
cvSubstituteContour(scanner,NULL); //删除太短轮廓
else
{
CvSeq* c_new;
if(poly1_hull0) //用多边形拟合轮廓
c_new = cvApproxPoly(c, sizeof(CvContour), mem_storage,
CV_POLY_APPROX_DP, CVCONTOUR_APPROX_LEVEL);
else //计算轮廓Hu矩
c_new = cvConvexHull2(c,mem_storage, CV_CLOCKWISE, ); cvSubstituteContour(scanner,c_new); //替换拟合后的多边形轮廓
numCont++;
}
}
contours = cvEndFindContours(&scanner); //结束扫描,并返回最高层的第一个轮廓指针 cvZero(mask);
for(c=contours; c!=NULL; c=c->h_next)
cvDrawContours(mask,c,CV_CVX_WHITE, CV_CVX_BLACK,-,CV_FILLED,);
} int main()
{
///////////////////////////////////////
// 需要使用的变量
CvCapture* capture=NULL;
IplImage* rawImage=NULL; //视频的每一帧原图像
IplImage* yuvImage=NULL; //比经验角度看绝大部分背景中的变化倾向于沿亮度轴,而不是颜色轴,故YUV颜色空间效果更好
IplImage* ImaskCodeBook=NULL; //掩模图像
IplImage* ImaskCodeBookCC=NULL; //清除噪声后并采用多边形法拟合轮廓连通域的掩模图像 codeBook* cB=NULL;
unsigned cbBounds[CHANNELS];
uchar* pColor=NULL; //yuvImage像素指针
int imageLen=;
int nChannels=CHANNELS;
int minMod[CHANNELS];
int maxMod[CHANNELS]; //////////////////////////////////////////////////////////////////////////
// 初始化各变量
cvNamedWindow("原图");
cvNamedWindow("掩模图像");
cvNamedWindow("连通域掩模图像"); //capture = cvCreateFileCapture("C:/Users/shark/Desktop/eagle.flv");
capture=cvCreateCameraCapture();
if(!capture)
{
printf("Couldn't open the capture!");
return -;
} rawImage=cvQueryFrame(capture);
int width=(int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_WIDTH);
int height=(int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_HEIGHT);
CvSize size=cvSize(width,height);
yuvImage=cvCreateImage(size,,);
ImaskCodeBook = cvCreateImage(size, IPL_DEPTH_8U, );
ImaskCodeBookCC = cvCreateImage(size, IPL_DEPTH_8U, );
cvSet(ImaskCodeBook,cvScalar()); imageLen=width*height;
cB=new codeBook[imageLen]; //得到与图像像素数目长度一样的一组码本,以便对每个像素进行处理 for(int i=;i<imageLen;i++)
cB[i].numEntries=;
for(int i=;i<nChannels;i++)
{
cbBounds[i]=;
minMod[i]=; //用于背景差分函数中
maxMod[i]=; //调整其值以达到最好的分割
} //////////////////////////////////////////////////////////////////////////
// 开始处理视频每一帧图像
for(int i=;;i++)
{
if(!(rawImage=cvQueryFrame(capture)))
break;
cvCvtColor(rawImage, yuvImage, CV_BGR2YCrCb);
// 色彩空间转换,将rawImage 转换到YUV色彩空间,输出到yuvImage
// 即使不转换效果依然很好
// yuvImage = cvCloneImage(rawImage); if(i<=) //前30帧进行背景学习
{
pColor=(uchar*)yuvImage->imageData;
for(int c=; c<imageLen; c++)
{
update_codebook(pColor, cB[c], cbBounds, nChannels); //对每个像素调用此函数
pColor+=;
}
if(i==)
{
for(int c=;c<imageLen;c++)
{
cvclearStaleEntries(cB[c]); //第30时帧时,删除每个像素码本中陈旧的码元
}
}
}
else
{
uchar maskPixel;
pColor=(uchar*)yuvImage->imageData;
uchar* pMask=(uchar*)ImaskCodeBook->imageData;
for(int c=;c<imageLen;c++)
{
maskPixel=cvbackgroundDiff(pColor,cB[c],nChannels,minMod,maxMod);
*pMask++=maskPixel;
pColor+=;
}
cvCopy(ImaskCodeBook,ImaskCodeBookCC);
cvconnectedComponents(ImaskCodeBookCC,,4.0);
cvShowImage("掩模图像",ImaskCodeBook);
cvShowImage("连通域掩模图像",ImaskCodeBookCC);
}
cvShowImage("原图",rawImage);
if (cvWaitKey() == )
break;
} cvReleaseCapture(&capture);
if (yuvImage)
cvReleaseImage(&yuvImage);
if(ImaskCodeBook)
cvReleaseImage(&ImaskCodeBook);
cvDestroyAllWindows();
delete [] cB; return ; }
codebook法分割前景目标的更多相关文章
- 2017年研究生数学建模D题(前景目标检测)相关论文与实验结果
一直都想参加下数学建模,通过几个月培训学到一些好的数学思想和方法,今年终于有时间有机会有队友一起参加了研究生数模,but,为啥今年说不培训直接参加国赛,泪目~_~~,然后比赛前也基本没看,直接硬刚.比 ...
- CVPR2020:三维实例分割与目标检测
CVPR2020:三维实例分割与目标检测 Joint 3D Instance Segmentation and Object Detection for Autonomous Driving 论文地址 ...
- Java基于opencv实现图像数字识别(五)—投影法分割字符
Java基于opencv实现图像数字识别(五)-投影法分割字符 水平投影法 1.水平投影法就是先用一个数组统计出图像每行黑色像素点的个数(二值化的图像): 2.选出一个最优的阀值,根据比这个阀值大或小 ...
- 3D点云点云分割、目标检测、分类
3D点云点云分割.目标检测.分类 原标题Deep Learning for 3D Point Clouds: A Survey 作者Yulan Guo, Hanyun Wang, Qingyong H ...
- LeetCode刷题笔记-回溯法-分割回文串
题目描述: 给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串. 返回 s 所有可能的分割方案. 示例: 输入: "aab"输出:[ ["aa", ...
- paper 83:前景检测算法_1(codebook和平均背景法)
前景分割中一个非常重要的研究方向就是背景减图法,因为背景减图的方法简单,原理容易被想到,且在智能视频监控领域中,摄像机很多情况下是固定的,且背景也是基本不变或者是缓慢变换的,在这种场合背景减图法的应用 ...
- 【数字图像处理】帧差法与Kirsch边缘检测实现运动目标识别与分割
本文链接:https://blog.csdn.net/qq_18234121/article/details/82763385 作者:冻人的蓝鲸梁思成 视频分割算法可以从时域和空域两个角度考虑.时域分 ...
- CVPR目标检测与实例分割算法解析:FCOS(2019),Mask R-CNN(2019),PolarMask(2020)
CVPR目标检测与实例分割算法解析:FCOS(2019),Mask R-CNN(2019),PolarMask(2020)1. 目标检测:FCOS(CVPR 2019)目标检测算法FCOS(FCOS: ...
- 教学目标的表述方式──行为目标的ABCD表述法
教学目标应规定学生在教学活动结束后能表现出什么样的学业行为,并限定学生学习过程中知识.技能的获得和情感态度发展的层次.范围.方式及变化效果的量度.对每节课教学目标的准确表述,可以充分发挥教学目标在教学 ...
随机推荐
- ios打包ipa的四种实用方法
总结一下,目前.app包转为.ipa包的方法有以下几种: 1.Apple推荐的方式,即实用xcode的archive功能 Xcode菜单栏->Product->Archive->三选 ...
- Windows 安装Django并创建第一个应用
学习python 也有一段时间了,语法也学得差不多了,突然就想学一学python的web开源开源框架Django,我用的是Django-1.6.2.tar.gz,可以在官网https://www.dj ...
- 关于python写GUI桌面应用的一些研究结果
研究了一下python开发GUI桌面应用的解决方案,研究结果记录如下: EasyGui:控件极为简单,连个基本的grid.list组件都没有,不适合商用,甚至是普通的应用都不行,放弃! Tkinter ...
- 深入理解C#:编程技巧总结(一)
原创文章,转载请注明出处! 以下总结参阅了:MSDN文档.<C#高级编程>.<C#本质论>.前辈们的博客等资料,如有不正确的地方,请帮忙及时指出!以免误导! 1.实现多态性的两 ...
- Boa服务器在ARM+Linux上的移植
下面给大家介绍一下Boa服务器移植的具体操作步骤,希望能够有帮助. 环境 主机:ubuntu8.10 交叉工具链:gcc-3.4.5-glibc-2.3.6 ...
- dreamvc框架(一)ioc容器的集成
我的dreamvc框架最终写得差点儿相同了,借鉴了非常多开源框架,SpringMVC.Struts2等,眼下放在github上面.地址请猛戳我 写得差点儿相同了,是要写一个总结,把自己当时的思路记录下 ...
- 移植QT到ZedBoard(制作运行库镜像) 交叉编译 分类: ubuntu shell ZedBoard OpenCV 2014-11-08 18:49 219人阅读 评论(0) 收藏
制作运行库 由于ubuntu的Qt运行库在/usr/local/Trolltech/Qt-4.7.3/下,由makefile可以看到引用运行库是 INCPATH = -I/usr//mkspecs/d ...
- Kruskal算法模拟讲解
Kruskal 算法是一个求最小生成树的算法,即求最小的开销等 算法可以这样,要求得最小生成树,那么n个节点只能包括n-1条边 所以我们应该转换为寻找这最短的n-1条边,因此,可以先对所有的 边进行从 ...
- java中的mmap实现--转
什么是mmap mmap对于c程序员很熟悉,对于java程序员有点陌生.简而言之,将文件直接映射到用户态的内存地址,这样对文件的操作不再是write/read,而是直接对内存地址的操作. 在c中提供了 ...
- HTML及简单标签介绍
什么是HTML: HTML 语言是一种超文本的标记语言,简单来讲就是构建一套标记符号和语法规则,将所要显示出来的文字.图象.声音等要素按照一定的标准要求排放,形成一定的标题.段落.列表等单元. 标签 ...