最近在研究Kim写的codebook码本方法来对背景进行建模,其思想实际上是很简单的,就是对视频中每一帧的所对应的像素的建立一个码本,每一个码本又对应一个或多个码字,我是按照那个kim那边文章来进行编程实现的,但是检测的结果不好,不知道为什么?就高手帮忙解答。。。。代码如下:
// TestCodeBookEx2.cpp : Defines the entry point for the console application.
//
/************************************************************************/
/* A few more thoughts on codebook models
In general, the codebook method works quite well across a wide number of conditions,
and it is relatively quick to train and to run. It doesn’t deal well with varying patterns of
light — such as morning, noon, and evening sunshine — or with someone turning lights
on or off indoors. This type of global variability can be taken into account by using
several different codebook models, one for each condition, and then allowing the condition
to control which model is active. */
/************************************************************************/
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <windows.h>
#include<stdlib.h>
#define RGB_CHANNEL 3
typedef struct
{
float rgb[RGB_CHANNEL];
float fMinBright;
float fMaxBright;
long lFrequency;
long lFirstAccessTime;
long lLastAccessTime;
long lMnrl;
}CodeWord;
typedef struct
{
CodeWord **ppCodeWord;
long codeWordNum;
}CodeBook;
#define UNDEFINEDCOLOR -1
float ColorDist(float *pRgbCodeWord,BYTE *pRgb)
{
float xt=pRgb[0]*pRgb[0]+pRgb[1]*pRgb[1]+pRgb[2]*pRgb[2];
float vi=pRgbCodeWord[0]*pRgbCodeWord[0]+pRgbCodeWord[1]*pRgbCodeWord[1]+
pRgbCodeWord[2]*pRgbCodeWord[2];
float xv=pRgbCodeWord[0]*pRgb[0]+pRgbCodeWord[1]*pRgb[1]+pRgbCodeWord[2]*pRgb[2];
xv*=xv;
if (vi==0)
{
return sqrt(xt);
}
float p=xv/vi;
float fResult=sqrt(xt-p);
return fResult;
}
bool Brightness(BYTE *pRgb,CodeWord cw)
{
float fBright=sqrt(pRgb[0]*pRgb[0]+pRgb[1]*pRgb[1]+pRgb[2]*pRgb[2]);
float aLearnRate=0.4;
float bLearnRate=1.5;
float temp1,temp2;
temp1=cw.fMinBright;
temp2=cw.fMaxBright*aLearnRate;
float fLowBright=temp1;
if (temp1>temp2)
{
fLowBright=temp2;
}
temp1=bLearnRate*cw.fMaxBright;
temp2=cw.fMinBright/aLearnRate;
float fHighBright=temp1;
//-------------这地方的>改为了<
if (temp1 < temp2)
{
fHighBright=temp2;
}
if (fLowBright==fHighBright && fLowBright==0)
{
fHighBright=10;
}
if (fLowBright<5)
{
fLowBright=0;
}
if (fBright >= fLowBright && fBright <=fHighBright)
{
return true;
}
return false;
}
void UpdateCodeWord(BYTE *pRgb,CodeWord *pCW,long curFrameNum)
{
float fBright=sqrt(pRgb[0]*pRgb[0]+pRgb[1]*pRgb[1]+pRgb[2]*pRgb[2]);
float fTemp=pCW->lFrequency + 1;
for (int i=0;i<RGB_CHANNEL;i++)
{
pCW->rgb[i]=(pCW->lFrequency*pCW->rgb[i]+pRgb[i])/fTemp;
}
if (pCW->fMinBright>fBright)
{
pCW->fMinBright=fBright;
}
if (pCW->fMaxBright<fBright)
{
pCW->fMaxBright=fBright;
}
pCW->lFrequency++;
long lTemp=curFrameNum-pCW->lLastAccessTime;
if (pCW->lMnrl<lTemp)
{
pCW->lMnrl=lTemp;
}
pCW->lLastAccessTime=curFrameNum;
}
//pCB-------->对应一个像素的码本
void CreateNewCodeWord(CodeBook *pCB,BYTE *pRgb,long curFrameNum)
{
pCB->codeWordNum++;
if (pCB->codeWordNum==1)//说明刚开始是空集时
{
pCB->ppCodeWord=new CodeWord*[pCB->codeWordNum];
}else //非空集的时候
{
CodeWord **ppTempCW=new CodeWord*[pCB->codeWordNum];
memcpy(ppTempCW,pCB->ppCodeWord,sizeof(CodeWord *)*(pCB->codeWordNum-1));
delete []pCB->ppCodeWord;
pCB->ppCodeWord=ppTempCW;
}
CodeWord *pNewCW=new CodeWord;
pNewCW->rgb[0]=(float)pRgb[0];
pNewCW->rgb[1]=(float)pRgb[1];
pNewCW->rgb[2]=(float)pRgb[2];
// memcpy(pNewCW->rgb,pRgb,sizeof(float)*RGB_CHANNEL);
float fBright=sqrt(pRgb[0]*pRgb[0]+pRgb[1]*pRgb[1]+pRgb[2]*pRgb[2]);
pNewCW->fMinBright=fBright;
pNewCW->fMaxBright=fBright;
pNewCW->lFrequency=1;
pNewCW->lMnrl=curFrameNum-1;
pNewCW->lFirstAccessTime=curFrameNum;
pNewCW->lLastAccessTime=curFrameNum;
pCB->ppCodeWord[pCB->codeWordNum-1]=pNewCW;
}
//pCB---->对应一个像素的码本
void ConstructOnePixelCodeWord(BYTE *pRgb,CodeBook *pCB,long curFrameNum)
{
int i;
float fBright=sqrt(pRgb[0]*pRgb[0]+pRgb[1]*pRgb[1]+pRgb[2]*pRgb[2]);
float fThreshold=6.f;//=10.f;
for (i=0;i<pCB->codeWordNum;i++)
{
if (ColorDist(pCB->ppCodeWord[i]->rgb,pRgb)<fThreshold &&
Brightness(pRgb,*(pCB->ppCodeWord[i])))
{
//说明匹配,则更新
UpdateCodeWord(pRgb,pCB->ppCodeWord[i],curFrameNum+1);
return;
}
}
if (pCB->codeWordNum==0 || i==pCB->codeWordNum)
{
//创建一个新的码本
CreateNewCodeWord(pCB,pRgb,curFrameNum+1);
}
}
void InitialConstructCodeBook(CodeBook *pCB,int imageHeight,int imageWidth,char *pImageData,long curFrameNum)
{
int i,j;
BYTE rgb[RGB_CHANNEL];
//码本的构造
for (i=0;i<imageHeight;i++)
{
for (j=0;j<imageWidth;j++)
{
if (i==40 && j==349)
{
int xxxx=3;//用于测试
}
rgb[0]=pImageData[(i*imageWidth+j)*3];
rgb[1]=pImageData[(i*imageWidth+j)*3+1];
rgb[2]=pImageData[(i*imageWidth+j)*3+2];
ConstructOnePixelCodeWord(rgb,&pCB[i*imageWidth+j],curFrameNum);
}
}
}
void ChangeMNRLAndFilter(CodeBook *pCB,int imageHeight,int imageWidth,int InitialConstructNum)
{
int i,j,k;
long lTemp=0;
CodeBook *pCBTemp=NULL;
CodeWord **ppTempCW=NULL;
int nDelCnt=0;
int *pDelIndex=NULL;
int index=0;
for (i=0;i<imageHeight;i++)
{
for(j=0;j<imageWidth;j++)
{
nDelCnt=0;
pCBTemp=&pCB[i*imageWidth+j];
pDelIndex=new int[pCBTemp->codeWordNum];
memset(pDelIndex,-1,sizeof(int)*pCBTemp->codeWordNum);
if (i==24 && j==280)
{
int xxxxxxxx=2;//用于测试
}
for (k=0;k<pCBTemp->codeWordNum;k++)
{
lTemp = InitialConstructNum - pCBTemp->ppCodeWord[k]->lLastAccessTime +
pCBTemp->ppCodeWord[k]->lFirstAccessTime - 1;
if (lTemp > pCBTemp->ppCodeWord[k]->lMnrl)
{
pCBTemp->ppCodeWord[k]->lMnrl = lTemp;
}
if ( pCBTemp->ppCodeWord[k]->lMnrl > InitialConstructNum/2)
{
// delete pCBTemp->ppCodeWord[k];
// pCBTemp->ppCodeWord[k]=NULL;
pDelIndex[k]=1;
nDelCnt++;
}
}
if (pCBTemp->codeWordNum == nDelCnt)
{
// pCBTemp=NULL;
continue;
}
if (nDelCnt==0)
{
continue;
}
ppTempCW=new CodeWord*[pCBTemp->codeWordNum - nDelCnt];
index=0;
for (k = 0; k < pCBTemp->codeWordNum; k++)
{
if (pDelIndex[k]==-1)
{
ppTempCW[index++]=pCBTemp->ppCodeWord[k];
}else
{
delete pCBTemp->ppCodeWord[k];
}
}
pCBTemp->codeWordNum-=nDelCnt;
delete []pCBTemp->ppCodeWord;
pCBTemp->ppCodeWord=ppTempCW;
delete []pDelIndex;
}
}
}
void BackgroundSubtract(CodeBook *pCB,int imageHeight,
int imageWidth,char *pImageData,
long curFrameNum,char *pMoveObjectData,
CodeBook *pCacheCB)
{
int i,j,k;
float fBright;
BYTE rgbTemp[RGB_CHANNEL];
float fThreshold=25.f;
memset(pMoveObjectData,0,sizeof(char)*imageHeight*imageWidth);
for (i = 0;i < imageHeight; i++)
{
for (j = 0; j < imageWidth; j++)
{
if (i==37 && j==151)
{
int xxx=3;//用于测试
}
rgbTemp[0]=pImageData[(i*imageWidth+j)*3];
rgbTemp[1]=pImageData[(i*imageWidth+j)*3+1];
rgbTemp[2]=pImageData[(i*imageWidth+j)*3+2];
for (k=0;k<pCB[i*imageWidth+j].codeWordNum;k++)
{
if (ColorDist(pCB[i*imageWidth+j].ppCodeWord[k]->rgb,rgbTemp) < fThreshold &&
Brightness(rgbTemp,*(pCB[i*imageWidth+j].ppCodeWord[k])))
{
UpdateCodeWord(rgbTemp,pCB[i*imageWidth+j].ppCodeWord[k],curFrameNum);
break;
}
}
if (k==pCB[i*imageWidth+j].codeWordNum)//说明没有匹配
{
pMoveObjectData[i*imageWidth+j]=255;
}
}
}
}
//释放码本申请的空间
void FreeCodeBookMem(CodeBook *pCB,int imageHeight,int imageWidth)
{
int i,j,k;
for (i=0;i<imageHeight;i++)
{
for (j=0;j<imageWidth;j++)
{
for (k=0;k<pCB[i*imageWidth+j].codeWordNum;k++)
{
if (pCB[i*imageWidth+j].ppCodeWord[k] != NULL)
{
delete pCB[i*imageWidth+j].ppCodeWord[k];
}
}
}
}
if (pCB!=NULL)
{
delete []pCB;
}
}
void WriteCodeBookToFile(CodeBook *pCB,int imageHeight,int imageWidth,char *cFileName)
{
int i,j,k;
FILE *fp=fopen(cFileName,"w");
fprintf(fp,"码本的位置(x,y)\t码本中的第几个码字\t码字的RGB\t最大亮度\t最小亮度\t频率\tMNRL\tFirstTime\tLastTime\n");
for (i=0;i<imageHeight;i++)
{
for (j=0;j<imageWidth;j++)
{
for (k=0;k<pCB[i*imageWidth+j].codeWordNum;k++)
{
fprintf(fp,"(%d,%d)\t%d\t%f-%f-%f\t%f\t%f\t%ld\t%ld\t%ld\t%ld\n",
i,j,k,pCB[i*imageWidth+j].ppCodeWord[k]->rgb[0],pCB[i*imageWidth+j].ppCodeWord[k]->rgb[1],
pCB[i*imageWidth+j].ppCodeWord[k]->rgb[2],pCB[i*imageWidth+j].ppCodeWord[k]->fMaxBright,
pCB[i*imageWidth+j].ppCodeWord[k]->fMinBright,pCB[i*imageWidth+j].ppCodeWord[k]->lFrequency,
pCB[i*imageWidth+j].ppCodeWord[k]->lMnrl,pCB[i*imageWidth+j].ppCodeWord[k]->lFirstAccessTime,
pCB[i*imageWidth+j].ppCodeWord[k]->lLastAccessTime);
}
}
}
fclose(fp);
}
int main()
{
///
// 需要使用的变量
CvCapture* capture;
IplImage* rawImage;
IplImage* pFrImg = NULL;
int imageWidth,imageHeight;
CodeBook *pCB=NULL;
CodeBook *pCacheCB=NULL;
int i,j,k;
int initialConstructNum=30;//初始化构造码本所用的帧数
//
// 初始化各变量
cvNamedWindow("Raw");
cvNamedWindow("CodeBook");
//打开视频文件,F:\\实验室任务\\视频浓缩\\测试视频\\镜头切换比较少的视频.avi
//F:\\实验室任务\\视频浓缩\\视频摘要在写字楼的应用.avi
if( !(capture = cvCaptureFromFile("F:\\实验室任务\\视频浓缩\\视频摘要在写字楼的应用.avi")))
{
fprintf(stderr, "Can not open video file %s\n");
return -2;
}
rawImage = cvQueryFrame(capture);
imageWidth=rawImage->width;
imageHeight=rawImage->height;
pFrImg = cvCreateImage(cvSize(rawImage->width, rawImage->height), IPL_DEPTH_8U,1);
pCB = new CodeBook[imageWidth*imageHeight];
pCacheCB = new CodeBook[imageHeight*imageWidth];
for (i=0;i<imageHeight;i++)
{
for (j=0;j<imageWidth;j++)
{
pCB[i*imageWidth+j].codeWordNum=0;
pCacheCB[i*imageWidth+j].codeWordNum=0;
pCB[i*imageWidth+j].ppCodeWord=NULL;
pCacheCB[i*imageWidth+j].ppCodeWord=NULL;
}
}
//码本的构造
for (k=0;k<initialConstructNum;k++)
{
printf("frame=%d\n",k);
cvShowImage("CodeBook", rawImage);
if( cvWaitKey(2) >= 0 )
break;
InitialConstructCodeBook(pCB,imageHeight,imageWidth,rawImage->imageData,k);
// WriteCodeBookToFile(pCB,imageHeight,imageWidth,"beforeFilter.txt");
rawImage=cvQueryFrame(capture);
}
WriteCodeBookToFile(pCB,imageHeight,imageWidth,"beforeFilter.txt");
//改变MNRL的值以及进行filter
ChangeMNRLAndFilter(pCB,imageHeight,imageWidth,initialConstructNum);
WriteCodeBookToFile(pCB,imageHeight,imageWidth,"afterFilter.txt");
long curFrameNum=initialConstructNum;
//BGS
while ((rawImage=cvQueryFrame(capture))!=NULL)
{
printf("frame=%ld\n",curFrameNum);
BackgroundSubtract(pCB,imageHeight,imageWidth,rawImage->imageData,
curFrameNum,pFrImg->imageData,pCacheCB);
curFrameNum++;
cvSaveImage("background.bmp",pFrImg);
cvSaveImage("moveObj.bmp",rawImage);
if (curFrameNum==241)
{
int xxxx=2;
}
cvShowImage("Raw", pFrImg);
cvShowImage("CodeBook", rawImage);
if( cvWaitKey(2) >= 0 )
break;
}
FreeCodeBookMem(pCB,imageHeight,imageWidth);
cvDestroyWindow("Raw");
cvDestroyWindow("CodeBook");
return 0;
}