利用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法分割前景目标的更多相关文章

  1. 2017年研究生数学建模D题(前景目标检测)相关论文与实验结果

    一直都想参加下数学建模,通过几个月培训学到一些好的数学思想和方法,今年终于有时间有机会有队友一起参加了研究生数模,but,为啥今年说不培训直接参加国赛,泪目~_~~,然后比赛前也基本没看,直接硬刚.比 ...

  2. CVPR2020:三维实例分割与目标检测

    CVPR2020:三维实例分割与目标检测 Joint 3D Instance Segmentation and Object Detection for Autonomous Driving 论文地址 ...

  3. Java基于opencv实现图像数字识别(五)—投影法分割字符

    Java基于opencv实现图像数字识别(五)-投影法分割字符 水平投影法 1.水平投影法就是先用一个数组统计出图像每行黑色像素点的个数(二值化的图像): 2.选出一个最优的阀值,根据比这个阀值大或小 ...

  4. 3D点云点云分割、目标检测、分类

    3D点云点云分割.目标检测.分类 原标题Deep Learning for 3D Point Clouds: A Survey 作者Yulan Guo, Hanyun Wang, Qingyong H ...

  5. LeetCode刷题笔记-回溯法-分割回文串

    题目描述: 给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串. 返回 s 所有可能的分割方案. 示例: 输入: "aab"输出:[ ["aa", ...

  6. paper 83:前景检测算法_1(codebook和平均背景法)

    前景分割中一个非常重要的研究方向就是背景减图法,因为背景减图的方法简单,原理容易被想到,且在智能视频监控领域中,摄像机很多情况下是固定的,且背景也是基本不变或者是缓慢变换的,在这种场合背景减图法的应用 ...

  7. 【数字图像处理】帧差法与Kirsch边缘检测实现运动目标识别与分割

    本文链接:https://blog.csdn.net/qq_18234121/article/details/82763385 作者:冻人的蓝鲸梁思成 视频分割算法可以从时域和空域两个角度考虑.时域分 ...

  8. CVPR目标检测与实例分割算法解析:FCOS(2019),Mask R-CNN(2019),PolarMask(2020)

    CVPR目标检测与实例分割算法解析:FCOS(2019),Mask R-CNN(2019),PolarMask(2020)1. 目标检测:FCOS(CVPR 2019)目标检测算法FCOS(FCOS: ...

  9. 教学目标的表述方式──行为目标的ABCD表述法

    教学目标应规定学生在教学活动结束后能表现出什么样的学业行为,并限定学生学习过程中知识.技能的获得和情感态度发展的层次.范围.方式及变化效果的量度.对每节课教学目标的准确表述,可以充分发挥教学目标在教学 ...

随机推荐

  1. 微信公众平台--网页授权获取用户基本信息(snsapi_base方式)

    关于snsapi_base网页授权的说明 以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的.用户感知的就是直接进入了回调 ...

  2. PopupWindow源码分析

    PopupWindow是我们经常使用的一个控件,严格来说这个PopuWindow就用来在指定位置显示一个View. 经过分析源码,PopupWindow里面没有Window对象,只是把View设置到屏 ...

  3. android uri , file , string 互转

    1:android Uri 介绍 http://www.cnblogs.com/lingyun1120/archive/2012/04/18/2455212.html 2:File 转成Uri < ...

  4. unity3D与网页的交互

    由于项目需要,要求用unity来展示三维场景,并在三维中能够方便的查询数据库等.一开始尝试在unity中直接连接数据库,当时连的xml,然而每次发布成网页后都会出现路径找不到等问题,所以迫不得已采用了 ...

  5. Bash远程文件传输命令scp

    备份远程文件(远程——>本地) scp -r 远程用户名@ip:文件绝对路径 本地绝对路径 还原远程文件(本地——>远程) scp -r 本地路径 远程用户名@ip:远程绝对路径 如果SS ...

  6. 阿里大于验证码发送 (ThinkPhp框架)

    1.登录平台 阿里大于2.登陆之后我们可以看到资费,使用场景等,在进入正题之前我们需要一些准备工作,首先我们先了解下短信的请求参数,在这里我们需要注意的是sms_param这个参数,在接下来我们申请短 ...

  7. 10.30 afternoon

    P76竞赛时间: ????年??月??日??:??-??:?? 题目名称 他 她 它 名称 he she it 输入 he.in she.in it.in 输出 he.out she.out it.o ...

  8. Castle Windsor 使MVC Controller能够使用依赖注入

    以在MVC中使用Castle Windsor为例 1.第一步要想使我们的Controller能够使用依赖注入容器,先定义个WindsorControllerFactory类, using System ...

  9. http学习笔记一

  10. EF Lambda 多表查询

    using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.Mv ...