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表述法
教学目标应规定学生在教学活动结束后能表现出什么样的学业行为,并限定学生学习过程中知识.技能的获得和情感态度发展的层次.范围.方式及变化效果的量度.对每节课教学目标的准确表述,可以充分发挥教学目标在教学 ...
随机推荐
- hdoj 2899 Strange fuction【二分求解方程】
Strange fuction Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)T ...
- crontab使用--linux下的定时任务程序
crontab是一个linux下的定时运行程序,如果我们想让自己的程序定时执行, 可以把自己的程序交给这个程序来完成 第一步:配置crontab的脚本的默认编辑器,它的默认的编辑器不好用,我们配置vi ...
- 与IO相关的等待事件troubleshooting-系列5
'db file scattered read' 这是另一种常见的等待事件.他产生于Oracle从磁盘读取多个块到Buffer Cache中非连续(" scattered&q ...
- 【Android - MD】之NavigationView的使用
NavigationView是Android 5.0新特性--Material Design中的一个布局控件,可以结合DrawerLayout使用,让侧滑菜单变得更加美观(可以添加头部布局). Nav ...
- 关于session的实现:cookie与url重写
本文讨论的语境是java EE servlet. 我们都知道session的实现主要两种方式:cookie与url重写,而cookie是首选(默认)的方式,因为各种现代浏览器都默认开通cookie功能 ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(39)-在线人数统计探讨
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(39)-在线人数统计探讨 系列目录 基于web的网站在线统计一直处于不是很精准的状态!基本上没有一种方法可 ...
- [置顶] iptables 性能 测试
一直研究iptables 性能,这几天刚好有硬件资源,于是发始下手测试iptables NAT 性…… 硬件环境 : 服务器: IBM x3650 ( 4G E5645 6核 12线程) ESXi ...
- GNU GRUB version 0.97 (630K lower /2053824K upper memory)
昨天把老板的IBM X61笔记本拿过来多系统,结果本以为很容易,直接ghost,结果悲剧发生啦,开机之后提示GNU GRUB version 0.97 (630K lower /2053824K up ...
- 汉诺塔-Hanoi
1. 问题来源: 汉诺塔(河内塔)问题是印度的一个古老的传说. 法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵 ...
- 关于oracle的函数,存储过程,触发器,序列,视图,左右连接一些的应用 带案例
CREATE TABLE STUDENT( --创建学生表 ID NUMBER(10) PRIMARY KEY, --主键ID NAME VARCHAR2(20), CLASSNAME VA ...