我是在MFC中新建进程获取摄像头的图像,并保存在pImage中,每一毫秒更新pImage中的图片(使用waitkey(1)),点击按钮一就进行了背景建模,即程序中的OnBnClickedButton1(),并进行前景检测,程序运行正常,实现了想要的效果,只是几分钟后就耗尽内存了,请教高手如何改进程序。
#define CHANNELS 1
typedef struct cd
{
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;
//////////////////////////////////////////////////////////////////////////
//捕捉背景中相关变化的图像
//p 指向YUV像素的指针
//c 该像素的codebook
//cbBounds codebook的训练边界
//numChannels 训练的通道数
int update_codebook(uchar *p, codeBook& c, unsigned *cbBounds, int numChannels)
{
if(c.numEntries == 0)
c.t = 0;
// 码本中码元为零时初始化时间为0
c.t += 1; // Record learning event
// 每调用一次加一,即每一帧图像加一
unsigned int high[CHANNELS], low[CHANNELS];
// int n;
//设置码元的边界
for (int n=0; n<numChannels; n++)
{
high[n] = *(p + n) + *(cbBounds + n);
if (high[n] > 255)
{
high[n] = 255;
}
low[n] = *(p + n) - *(cbBounds+n);
if (low[n] < 0)
{
low[n] = 0;
}
}
int matchChannel;
int i;
//查看所有的码元,像素是否在现存的码元中
for (i= 0; i<c.numEntries; i++)
{
matchChannel = 0;
//判断像素是否在方块中,每有一条通道的像素符合,则matchChannel加1
for (int n=0; n<numChannels; n++)
{
if ((c.cb[i]->learnLow[n] <= *(p+n)) && (*(p+n) <= c.cb[i]->learnHigh[n]))
{
matchChannel++;
}
}
//如果像素所有通道都在学习边界内,则调整阈值最大和最小值以使该元素被包括在codebook 码元中
if (matchChannel == numChannels)
{
c.cb[i] -> t_last_update = c.t;
for (int n= 0; 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;
}
}
//统计每个码本条目多长时间被访问一次
for (int s=0; 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;
}
}
//添加一个新的codebook
if (i==c.numEntries)
{
code_element **foo = new code_element *[c.numEntries+1];
for (int ii=0; 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 (int n= 0; 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 = 0;
c.numEntries +=1;
}
//如果像素在码元阈值之外,但仍然在其高低范围内,训练边界加1,
for (int n=0; n<numChannels; n++)
{
if(c.cb[i]->learnHigh[n] < high[n])
{
c.cb[i]->learnHigh[n] += 1;
}
if (c.cb[i]->learnLow[n] > low[n])
{
c.cb[i]->learnLow[n] -= 1;
}
}
return (i);
}
//////////////////////////////////////////////////////////////////////////
//避免训练噪声的codebook,需要删除训练过程中很少访问的codebook条目
int clear_stale_entries(codeBook &c)
{
//staleThresh为运行总时间的一般,
int staleThresh = c.t>>1;
int *keep = new int [c.numEntries];
int keepCnt = 0;
//如果codebook的一个box在总时间的一半没有被访问过,则删除该box。
for (int i=0; i<c.numEntries ; i++)
{
if (c.cb[i]->stale > staleThresh)
{
keep [i] = 0;
} else
{
keep[i] = 1;
keepCnt += 1;
}
}
c.t = 0;
code_element **foo = new code_element *[keepCnt];
int k = 0;
//将剩余的box复制到foo中,
for ( int ii=0; ii<c.numEntries ; ii++)
{
if (keep[ii])
{
foo[k] = c.cb[ii];
foo[k]->t_last_update = 0;
k++;
}
}
delete [ ]keep;
delete [] c.cb;
c.cb = foo;
int numCleared = c.numEntries - keepCnt;
c.numEntries = keepCnt;
return (numCleared);
}
//////////////////////////////////////////////////////////////////////////
//从背景中将前景目标的像素分割出来
uchar background_diff(uchar *p, codeBook &c, int numChannels, int *minMod, int *maxMod)
{
int matchChannel;
int i;
//像素是否在codebook的box中
for (i=0; i<c.numEntries ; i++)
{
matchChannel = 0;
for (int n=0; 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;
}
}
/* _CrtDumpMemoryLeaks(); */
//前景返回255
if (i >= c.numEntries )
{
return (255);
}
//背景返回0
return (0);
}
void Cbackground1Dlg::OnBnClickedButton1()
{
///////////////////////////////////////
// 需要使用的变量
CvCapture* capture;
IplImage* rawImage;
IplImage* yuvImage;
IplImage* ImaskCodeBook;
codeBook* cB;
unsigned cbBounds[CHANNELS];
uchar* pColor; //YUV pointer
int imageLen;
int nChannels = CHANNELS;
int minMod[CHANNELS];
int maxMod[CHANNELS];
//////////////////////////////////////////////////////////////////////////
// 初始化各变量
cvNamedWindow("Raw");
cvNamedWindow("CodeBook");
// capture = cvCreateFileCapture("tree.avi");
// if (!capture)
// {
// printf("Couldn't open the capture!");
// return ;
// }
//
// rawImage = cvQueryFrame(capture);
rawImage = cvCreateImage(cvSize(640,512),8,1);
yuvImage = cvCreateImage(cvGetSize(rawImage), 8, 1);
// 给yuvImage 分配一个和rawImage 尺寸相同,8位1通道图像
ImaskCodeBook = cvCreateImage(cvGetSize(rawImage), IPL_DEPTH_8U, 1);
// 为ImaskCodeBook 分配一个和rawImage 尺寸相同,8位单通道图像
cvSet(ImaskCodeBook, cvScalar(255));
// 设置单通道数组所有元素为255,即初始化为白色图像
imageLen = rawImage->width * rawImage->height;
cB = new codeBook[imageLen];
// 得到与图像像素数目长度一样的一组码本,以便对每个像素进行处理
for (int i=0; i<imageLen; i++)
// 初始化每个码元数目为0
cB[i].numEntries = 0;
for (int i=0; i<nChannels; i++)
{
cbBounds[i] = 10; // 用于确定码元各通道的阀值
minMod[i] = 20; // 用于背景差分函数中
maxMod[i] = 20; // 调整其值以达到最好的分割
}
//////////////////////////////////////////////////////////////////////////
// 开始处理视频每一帧图像
for (double i=0;;i++)
{
/*cvCvtColor(rawImage, yuvImage, CV_BGR2YCrCb);*/
// 色彩空间转换,将rawImage 转换到YUV色彩空间,输出到yuvImage
// 即使不转换效果依然很好
cvResize(pImage, rawImage, CV_INTER_AREA);
yuvImage = cvCloneImage(rawImage);
if (i <= 500)
// 30帧内进行背景学习
{
pColor = (uchar *)(yuvImage->imageData);
// 指向yuvImage 图像的通道数据
for (int c=0; c<imageLen; c++)
{
update_codebook(pColor, cB[c], cbBounds, nChannels);
// 对每个像素,调用此函数,捕捉背景中相关变化图像
pColor += 1;
// 1 通道图像, 指向下一个像素通道数据
}
if (i == 30)
// 到30 帧时调用下面函数,删除码本中陈旧的码元
{
for (int c=0; c<imageLen; c++)
clear_stale_entries(cB[c]);
}
}
else
{
uchar maskPixelCodeBook;
pColor = (uchar *)((yuvImage)->imageData); //3 channel yuv image
uchar *pMask = (uchar *)((ImaskCodeBook)->imageData); //1 channel image
// 指向ImaskCodeBook 通道数据序列的首元素
for(int c=0; c<imageLen; c++)
{
maskPixelCodeBook = background_diff(pColor, cB[c], nChannels, minMod, maxMod);
// 我看到这儿时豁然开朗,开始理解了codeBook 呵呵
*pMask++ = maskPixelCodeBook;
pColor += 1;
// pColor 指向的是3通道图像
}
}
// if (!(rawImage = cvQueryFrame(capture)))
// break;
cvShowImage("Raw", rawImage);
cvShowImage("CodeBook", ImaskCodeBook);
if (cvWaitKey(1) == 27)
break;
if (i == 500)
{
CString csMessage ;
csMessage.Format(_T("训练完毕!"));
AfxMessageBox(csMessage);
}
}
/* cvReleaseCapture(&capture);*/
if (yuvImage)
cvReleaseImage(&yuvImage);
if(ImaskCodeBook)
cvReleaseImage(&ImaskCodeBook);
cvDestroyAllWindows();
delete [] cB;
return ;
}
程序运行时的图片
摄像头获取的图片pImage
建立的背景模型
当一只手出现在摄像头前面