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表述法
教学目标应规定学生在教学活动结束后能表现出什么样的学业行为,并限定学生学习过程中知识.技能的获得和情感态度发展的层次.范围.方式及变化效果的量度.对每节课教学目标的准确表述,可以充分发挥教学目标在教学 ...
随机推荐
- 青蛙的约会 - poj 1061(扩展欧几里得)
分析:这个东西在数论里面应该叫做不定方程式,可以搜一下,有很精彩的证明,先求出来方程式的一组特解,然后用这组特解来求通解,但是求出来特解之后怎么求这些解里面的最小非负x值?我们知道 x = x0 + ...
- 使用Python扫描端口情况
#!/usr/bin/python# -*- coding:utf8 -*-# Python: 2.7.8# Platform: Windows# Authro: ...
- JAVA学习.java.sql.date 与java.util.date以及gettime()方法的分析
java.sql.Date 是针对SQL语句使用的,它只包含日期而没有时间部分. java.util.Date 就是在除了SQL语句的情况下面使用. 它都有getTime方法返回毫秒数,返回的是自19 ...
- hibernate Java 时间和日期类型的 Hibernate 映射
基础知识: 在 Java 中, 代表时间和日期的类型包含: java.util.Date 和 java.util.Calendar. 此外, 在 JDBC API 中还提供了 3 个扩展了 java. ...
- linux_2.6内核内存缓冲与I/O调度机制:
http://blog.csdn.net/kaiwii/article/details/7030178 到底是BIO还是BH?答案是BIO与BH
- Qt 学习之路:Canvas
在 QML 刚刚被引入到 Qt 4 的那段时间,人们往往在讨论 Qt Quick 是不是需要一个椭圆组件.由此,人们又联想到,是不是还需要其它的形状?这种没玩没了的联想导致了一个最直接的结果:除了圆角 ...
- iOS UIScrollView 你可能不知道的奇技淫巧
iOS 的 UIScrollView 可以说是十分强大,巧妙地运用它可以得到一些意想不到的效果.本文将举几个 ScrollView 不常见运用的例子. 自带信息应用 这个界面既可以上下卷动,也可以左右 ...
- js添加遮罩层
直接用代码来说明 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="MaskT ...
- HTML简单介绍及常见元素
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- Conversion Between DataTable and List in C#
1.List to DataTable public static DataTable ToDataTable<TSource>(this IList<TSource> dat ...