工业张三 2015-09-22 07:34 采纳率: 66.7%
浏览 1758
已采纳

使用codebook建立背景模型,内存很快就耗尽的问题,

我是在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

建立的背景模型

当一只手出现在摄像头前面

  • 写回答

2条回答 默认 最新

  • LongRui888 2015-09-23 08:13
    关注

    内存使用后,要及时释放,不然会有内存泄漏,另外,你每一秒就存一次,数据量会很大,你可以考虑把数据持久化到磁盘里,否则内存也会不够用的

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥100 需要跳转番茄畅听app的adb命令
  • ¥50 寻找一位有逆向游戏盾sdk 应用程序经验的技术
  • ¥15 请问有用MZmine处理 “Waters SYNAPT G2-Si QTOF质谱仪在MSE模式下采集的非靶向数据” 的分析教程吗
  • ¥50 opencv4nodejs 如何安装
  • ¥15 adb push异常 adb: error: 1409-byte write failed: Invalid argument
  • ¥15 nginx反向代理获取ip,java获取真实ip
  • ¥15 eda:门禁系统设计
  • ¥50 如何使用js去调用vscode-js-debugger的方法去调试网页
  • ¥15 376.1电表主站通信协议下发指令全被否认问题
  • ¥15 物体双站RCS和其组成阵列后的双站RCS关系验证