《学习openCV》例程解析 ex_9_2(背景减除)
56帧时分割出手效果
63帧时分割出手的效果
/***
平均背景法进行图像分割, 在图像背景相对稳定的情况下, 检测闯入图像的前景目标
*/
/*** Averaging Background Method We’ve just seen a simple method of learning background scenes and segmenting fore- ground objects. It will work well only with scenes that do not contain moving background components (like a waving curtain or waving trees). It also assumes that the lighting remains fairly constant (as in indoor static scenes). */ #include "stdafx.h" #include "cv.h" #include "highgui.h" /***************************************************/ //我们为需要的不同临时图像和统计属性的图像创建指针 //Float 3-channel images IplImage *IavgF, *IdiffF, *IprevF, *IhiF, *IlowF; IplImage *Iscratch, *Iscratch2; //Float 1-channel images IplImage *Igray1, *Igray2, *Igray3; IplImage *Ilow1, *Ilow2, *Ilow3; IplImage *Ihi1, *Ihi2, *Ihi3; //Byte 1-channel image IplImage *Imaskt; IplImage *Imask; float Icount; /**************************************************/ void AllocateImages(IplImage* I) //该函数为需要的所有临时图像分配内存,传入来自视频的首帧图像作为大小参考 { CvSize sz = cvGetSize(I); IavgF = cvCreateImage(sz, IPL_DEPTH_32F, 3); IdiffF = cvCreateImage(sz, IPL_DEPTH_32F, 3); IprevF = cvCreateImage(sz, IPL_DEPTH_32F, 3); IhiF = cvCreateImage(sz, IPL_DEPTH_32F, 3); IlowF = cvCreateImage(sz, IPL_DEPTH_32F, 3); Ilow1 = cvCreateImage(sz, IPL_DEPTH_32F, 1); Ilow2 = cvCreateImage(sz, IPL_DEPTH_32F, 1); Ilow3 = cvCreateImage(sz, IPL_DEPTH_32F, 1); Ihi1 = cvCreateImage(sz, IPL_DEPTH_32F, 1); Ihi2 = cvCreateImage(sz, IPL_DEPTH_32F, 1); Ihi3 = cvCreateImage(sz, IPL_DEPTH_32F, 1); cvZero(IavgF); cvZero(IdiffF); cvZero(IprevF); cvZero(IhiF); cvZero(IlowF); Icount = 1e-5; Iscratch = cvCreateImage(sz, IPL_DEPTH_32F, 3); Iscratch2 = cvCreateImage(sz, IPL_DEPTH_32F, 3); cvZero(Iscratch); cvZero(Iscratch2); Igray1 = cvCreateImage(sz, IPL_DEPTH_32F, 1); Igray2 = cvCreateImage(sz, IPL_DEPTH_32F, 1); Igray3 = cvCreateImage(sz, IPL_DEPTH_32F, 1); Imaskt = cvCreateImage(sz, IPL_DEPTH_8U, 1); Imask = cvCreateImage(sz, IPL_DEPTH_8U, 1); } void accumulateBackground(IplImage* I) //累积背景图像和前后帧图像差值的绝对值 //当累积够一定数量后就将其转换成一个背景统计模型 { static int first = 1; //局部静态变量,只初始化一次,意思就是第一次被赋值为1 cvCvtScale(I, Iscratch, 1, 0); //将I指向的图像复制给Iscratch 不能用cvCopy,因为像素的位深度不同 if (!first) { cvAcc(Iscratch, IavgF); //累积原始的浮点图像到IIavgF cvAbsDiff(Iscratch, IprevF, Iscratch2); //计算前后帧图像绝对差图像到Iscratch2 cvAcc(Iscratch2, IdiffF); //将前后帧差值图像累加到IdiffF 中 Icount += 1.0; //记录累加的次数用于背景统计时计算均值 } first = 0; //first 为局部静态变量,以后调用该函数将不再初始化为1 //意思就是除了第一次,以后调用该函数均进入if 语句 cvCopy(Iscratch, IprevF); //IprevF用来保存前一帧图像 } void setHighThreshold(float scale) { cvConvertScale(IdiffF, Iscratch, scale); //将统计的绝对差分图像值放大scale 倍赋给Iscratch cvAdd(Iscratch, IavgF, IhiF); //IhiF = Iscratch + IavgF cvSplit(IhiF, Ihi1, Ihi2, Ihi3, 0); //将阀值上限分割为多通道 } void setLowThreshold(float scale) { cvConvertScale(IdiffF, Iscratch, scale); cvSub(IavgF, Iscratch, IlowF); //IlowF = IavgF - Iscratch cvSplit(IlowF, Ilow1, Ilow2, Ilow3, 0); //将阀值下限分割为多通道 } void createModelsfromStats() //当累积足够多的帧图后,就将其转化成一个背景统计模型 //该函数用于计算每个像素的均值和平均绝对差分 { cvConvertScale(IavgF, IavgF, (double)(1.0/Icount)); //计算平均原始图像到 IavgF cvConvertScale(IdiffF, IdiffF, (double)(1.0/Icount)); //计算绝对差分图像到 IdiffF cvAddS(IdiffF, cvScalar(1.0, 1.0, 1.0), IdiffF); //使得到的绝对差分图像每个像素值均不为空 setHighThreshold(7.0); setLowThreshold(6.0); //根据统计的背景模型设定一个阀值上限和下限 //如果 IlowF <= Temp < IhiF 时认为其为背景,否则为视频中出现的运动目标物体 } void backgroundDiff(IplImage* I) { cvCvtScale(I, Iscratch, 1, 0); //将I指向的图像复制给Iscratch 不能用cvCopy, 因为像素的位深度不同 cvSplit(Iscratch, Igray1, Igray2, Igray3, 0); //得到的当前帧分割成3个单通道图像 cvInRange(Igray1, Ilow1, Ihi1, Imask); // src lower upper dst //检查这些单通道图像是否在平均背景像素高低阀值之间 //如果src(I)在范围内(lower <= src < upper)dst(I)被设置为0xff(每一位都是 '1')否则置0 cvInRange(Igray2, Ilow2, Ihi2, Imaskt); cvOr(Imask, Imaskt, Imask); //计算两个数组每个元素的按位或值赋值给第三个参数 cvInRange(Igray3, Ilow3, Ihi3, Imaskt); cvOr(Imask, Imaskt, Imask); //最后Imask 为分离出的前景二值图 cvSubRS(Imask, cvScalar(255), Imask); //计算数量和数组之间的差,将Imask反相处理 } void DeallocateImages() //解除分配的内存 { cvReleaseImage(&IavgF); cvReleaseImage(&IdiffF); cvReleaseImage(&IprevF); cvReleaseImage(&IhiF); cvReleaseImage(&IlowF); cvReleaseImage(&Ilow1); cvReleaseImage(&Ilow2); cvReleaseImage(&Ilow3); cvReleaseImage(&Ihi1); cvReleaseImage(&Ihi2); cvReleaseImage(&Ihi3); cvReleaseImage(&Iscratch); cvReleaseImage(&Iscratch2); cvReleaseImage(&Igray1); cvReleaseImage(&Igray2); cvReleaseImage(&Igray3); cvReleaseImage(&Imaskt); cvReleaseImage(&Imask); } int main() { CvCapture* capture = cvCreateFileCapture("tree.avi"); //初始化从文件中获取视频 if (!capture) { printf("Couldn't Open the file."); return -1; } cvNamedWindow("raw"); cvNamedWindow("avg"); IplImage* rawImage = cvQueryFrame(capture); //这个函数仅仅是函数cvGrabFrame和函数cvRetrieveFrame在一起调用的组合 cvShowImage("raw", rawImage); AllocateImages(rawImage); for (int i=0;;i++) { if (i <= 30) { accumulateBackground(rawImage); //前30帧用于累积计算背景图像 if (i == 30) //将前30真转换成一个背景统计模型 createModelsfromStats(); } else //建立好背景模型后调用此函数进行图像分割 backgroundDiff(rawImage); cvShowImage("avg", Imask); //播放分割后的目标图像结果 if (cvWaitKey(33) == 27) //每33ms 播放一帧 break; if (!(rawImage = cvQueryFrame(capture))) break; cvShowImage("raw", rawImage); //显示原图像 if (i == 56 || i == 63) //56帧和63帧时暂停 cvWaitKey(); } DeallocateImages(); return 0; }