视频前景目标提取(一)

最近几天参加了一次比赛,提取监控视频前景目标,前前后后试了很多的方法,帧差法+GMM,四帧差法,改进帧差法,混合高斯模型,改进的混合高斯模型,ViBe前景目标提取,ViBe+GMM前景目标提取等等。最后也尝试了模板匹配和HOG+SVM训练样本图片提取视频目标。大概会用几篇文章介绍一下这些方法的优缺点和各自的适用情况。编程实现均在opencv2.4.13+VS2015环境。

    首先介绍一下帧差法+GMM,GMM又叫混合高斯模型,在进行前景检测前,先对背景进行训练,对图像中每个背景采用一个混合高斯模型进行模拟,每个背景的混合高斯的个数可以自适应。然后在测试阶段,对新来的像素进行GMM匹配,如果该像素值能够匹配其中一个高斯,则认为是背景,否则认为是前景。由于整个过程GMM模型在不断更新学习中,所以对动态背景有一定的鲁棒性。最后通过对一个有树枝摇摆的动态背景进行前景检测,取得了较好的效果。

    混合高斯模型最早在计算机视觉中的应用是用来做前景检测,主要是用于视频监控领域,这个系统和稳定且有自学能力,能在户外环境跑16个多月。KaewTraKulPong将GMM的训练过程做了改进,将训练过程分为2步进行,前L帧采用EM算法进行权值,均值,方差更新,后面的过程就采用中的方法进行更新,取得了更好的检测效果。Zivkovic et al.在中对GMM理论做了全面的论述,使得GMM理论的使用不仅金限于计算机视觉领域。并且该作者将该理论进一步具体到背景减图的前景检测中来,即加入了参数估计的先验知识,取得了很好的效果和稳定性。

实现过程

首先将每个高斯的均值,方差,权值都设置为0,即初始化个模型矩阵参数。
采用视频中的T帧用来训练GMM模型。对每一个像素而言,建立其模型个数最大GMM_MAX_COMPONT个高斯的GMM模型。当第一个像素来,单独为其在程序中设置好其固定的初始均值,方差,并且权值设置为1。
非第一帧训练过程中,当后面来的像素值时,与前面已有的高斯的均值比较,如果该像素点的值与其模型均值差在3倍的方差内,则任务属于该高斯。此时用如下方程进行更新:
当到达训练的帧数T后,进行不同像素点GMM个数自适应的选择。首先用权值除以方差对各个高斯进行从大到小排序,然后选取最前面B个高斯,使
  

  这样就可以很好的消除训练过程中的噪声点。

  5. 在测试阶段,对新来像素点的值与B个高斯中的每一个均值进行比较,如果其差值在2倍的方差之间的话,则认为是背景,否则认为是前景。并且只要其中有一个高斯分量满足该条件就认为是前景。前景赋值为255,背景赋值为0。这样就形成了一副前景二值图。

  6. 由于前景二值图中含有很多噪声,所以采用了形态学的开操作将噪声缩减到0,紧接着用闭操作重建由于开操作丢失的边缘部分的信息。消除了不连通的小噪声点。

  上面是该算法实现的大概流程,但是当我们在具体编程时,还是有很多细节的地方需要注意,比如有些参数值的选择。在这里,经过试验将一些常用的参数值声明如下:

3个参数的值的更新方差中,取其中的学习率为0.005。也就是说T等于200。
定义每个像素的最大混合高斯个数取7。
取视频的前1000帧进行训练。
取Cf为0.3。即满足权值大于0.7的个数为自适应高斯的个数B。
训练过程中,需要新建立一个高斯时,其权值取值设为与学习率大小值相等,即0.05。
训练过程中,需要新建立一个高斯时,取该高斯的均值为该输入像素值大小本身。方差为15。
训练过程中,需要新建立一个高斯时,取该高斯的方差15。


程序设计

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2\opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
 
int main()
{
    //int argc, char* argv[]
    CvCapture *capture = cvCreateFileCapture("E:\\数学建模\\input2.avi");
    IplImage *img, *img_gray, *img_cny, *back, *fore;//原图像、灰度图像、边缘检测得到的图像、背景图像、前景图像
    
    CvVideoWriter *writer=NULL;
    double fps = cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
    CvSize size = cvSize(
        (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH),
        (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT));
    writer = cvCreateVideoWriter("E:\\数学建模\\input2out.avi", CV_FOURCC('M','J','P','G'), fps, size, 0);
 
    int width, height;//图像的长、宽
    int cnt = 0; //视频帧数
    img = cvQueryFrame(capture);
    cnt++;
    width = img->width; height = img->height;
    vector< vector<double> > mean(width*height), sd(width*height), w(width*height);
    img_gray = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    img_cny = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    back = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    fore = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    cvCvtColor(img, img_gray, CV_BGR2GRAY);
    cvCopy(img_gray, back);
    //初始化混合高斯背景模型
    int K = 5, sd_init = 16, T = 200, match = 0;//高斯分布的个数、标准差的初始值 帧数阈值 匹配参数
    double w_init = 0.005, D = 2.5, alph, p, threold = 0.7;//权重的初始值、置信参数、学习速率、参数更新速率、预定阈值(用于选取背景模型)
    for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            mean[i*width + j].push_back((uchar)img_gray->imageData[i*width + j]);
            sd[i*width + j].push_back(sd_init);
            w[i*width + j].push_back(1);
        }
    }
    IplImage* logpolar_frame = cvCreateImage(size, IPL_DEPTH_8U, 3);
    while (1)
    {
        img = cvQueryFrame(capture);
        if (!img) break;
 
        cvCvtColor(img, img_gray, CV_BGR2GRAY);
        cnt++;
 
        //计算学习速率
        if (cnt < T) alph = (double)1 / (2 * cnt);
        else alph = (double)1 / (2 * T);
 
        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < width; j++)
            {
                match = 0;
                int len = mean[i*width + j].size();
                for (int k = 0; k < len; k++)
                {
                    //判断一个像素是否与背景模型匹配
                    if (fabs((uchar)img_gray->imageData[i*width + j] - mean[i*width + j][k]) < D*sd[i*width + j][k])
                    {
                        match = 1;
                        //更新权重、均值、标准差
                        w[i*width + j][k] = (1 - alph)*w[i*width + j][k] + alph;
                        p = alph / w[i*width + j][k];
                        mean[i*width + j][k] = (1 - p)*mean[i*width + j][k] + p*(uchar)img_gray->imageData[i*width + j];
                        sd[i*width + j][k] = sqrt((1 - p)*sd[i*width + j][k] * sd[i*width + j][k] + p*pow((uchar)img_gray->imageData[i*width + j] - mean[i*width + j][k], 2));
                    }
                    else
                    {
                        w[i*width + j][k] = (1 - alph)*w[i*width + j][k];
                    }
                }
                if (match == 0)
                {
                    //增加新的背景模型
                    if (len < K)
                    {
                        mean[i*width + j].push_back((uchar)img_gray->imageData[i*width + j]);
                        sd[i*width + j].push_back(sd_init);
                        w[i*width + j].push_back(w_init);
                    }
                    //更改权重最小的背景模型
                    else
                    {
                        int min = 0;
                        double tmp = w[i*width + j][0];
                        for (int k = 1; k < len; k++)
                        {
                            if (tmp >  w[i*width + j][k])
                            {
                                min = k;
                                tmp = w[i*width + j][k];
                            }
                        }
                        mean[i*width + j][min] = (uchar)img_gray->imageData[i*width + j];
                        sd[i*width + j][min] = sd_init;
                        w[i*width + j][min] = w_init;
                    }
                }
                //每隔50帧 删除权重最小的高斯分布    
                if (cnt % 50 == 0)
                {
                    vector<double>::iterator it_w, it_sd, it_mean;
                    it_w = w[i*width + j].begin();
                    it_sd = sd[i*width + j].begin();
                    it_mean = mean[i*width + j].begin();
                    for (; it_w != w[i*width + j].end() && it_sd != sd[i*width + j].end() && it_mean != mean[i*width + j].end();)
                    {
                        if (*it_w < w_init && (*it_w / *it_sd) < (w_init / sd_init))
                        {
                            it_w = w[i*width + j].erase(it_w);
                            it_sd = sd[i*width + j].erase(it_sd);
                            it_mean = mean[i*width + j].erase(it_mean);
                        }
                        else
                        {
                            it_w++;
                            it_sd++;
                            it_mean++;
                        }
                    }
                }
                //权重归一化
                len = mean[i*width + j].size();
                double tmp = 0;
                for (int k = 0; k < len; k++)
                {
                    tmp += w[i*width + j][k];
                }
                for (int k = 0; k < len; k++)
                {
                    w[i*width + j][k] /= tmp;
                }
                //计算各背景模型的优先级,并以从大到小的顺序排序
                double *rand;//优先级
                int *rand_ind;//排序后的优先级索引
                rand = (double *)malloc(sizeof(double)*len);
                rand_ind = (int *)malloc(sizeof(int)*len);
                for (int k = 0; k < len; k++)
                {
                    rand[k] = w[i*width + j][k] / sd[i*width + j][k];
                    rand_ind[k] = k;
                }
                for (int k = 1; k< len; k++)
                {
                    for (int l = 0; l < k; l++)
                    {
                        if (rand[k] > rand[l])
                        {
                            double rand_tmp = rand[k];
                            rand[k] = rand[l];
                            rand[l] = rand_tmp;
 
                            double rand_ind_tmp = rand_ind[k];
                            rand_ind[k] = rand_ind[l];
                            rand_ind[l] = rand_ind_tmp;
 
                        }
                    }
                }
                double sum = 0;
                int B;
                for (int k = 0; k < len; k++)
                {
                    int temp = rand_ind[k];
                    sum += w[i*width + j][temp];
                    if (sum>threold)
                    {
                        B = k;
                        break;
                    }
                }
                match = 0;
                back->imageData[i*width + j] = 0;
                //求背景模型
                for (int k = 0; k <= B; k++)
                {
                    int temp = rand_ind[k];
                    back->imageData[i*width + j] += w[i*width + j][temp] * mean[i*width + j][temp];
                }
                /*
                //求前景图像
                for (int k = 0; k <= B; k++)
                {
                int temp = rand_ind[k];
                if (fabs((uchar)img_gray->imageData[i*width + j] - mean[i*width + j][temp]) < D*sd[i*width + j][temp])
                {
                match = 1;
                break;
                }
                }
                if (match == 1) fore->imageData[i*width + j] = 0;
                else fore->imageData[i*width + j] = (uchar)img_gray->imageData[i*width + j];
                */
                free(rand);
                free(rand_ind);
            }
        }
 
        cvAbsDiff(img_gray, back, fore);
        cvCanny(img_gray, img_cny, 40, 100,3);
        cvShowImage("canny", img_cny);
        cvAnd(img_cny, fore, fore);
        
        cvDilate(fore, fore, NULL, 3);
        cvErode(fore, fore, NULL, 3);
        cvThreshold(fore, fore, 55, 255, CV_THRESH_BINARY);
        cvWriteFrame(writer, fore);
 
        cvShowImage("fore", fore);
        cvShowImage("back", back);
        cvShowImage("src", img);
        char s = cvWaitKey(1);
        if (s == 27) break;
    }
    cvDestroyAllWindows();
    cvReleaseCapture(&capture);
    cvReleaseImage(&img_gray);
    cvReleaseImage(&fore);
    cvReleaseImage(&back);
    cvReleaseImage(&img_cny);
    cvReleaseVideoWriter(&writer);
    return (0);
}

原图: 背景:前景目标:

总的来说,高斯混合算法在使用形态学处理后还是可以的,但是训练过程比较满,实时性不好。
如果使用连通域处理,用多边形拟合的话,效果还是可以的。
--------------------- 
作者:spw_1201 
来源:CSDN 
原文:https://blog.csdn.net/spw_1201/article/details/78044541 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
视频运动前景目标提取算法是一种常见的计算机视觉技术,它可以从视频序列中提取出运动目标的轮廓或者运动前景。常见的实现方法包括基于背景建模、光流法和深度学习等多种技术。 这里以基于背景建模的算法为例,介绍如何使用Python实现视频运动前景目标提取算法。 1. 导入必要的库和模块 ```python import cv2 import numpy as np ``` 2. 读入视频序列并预处理 ```python cap = cv2.VideoCapture('video.mp4') fgbg = cv2.createBackgroundSubtractorMOG2() ``` 3. 循环读取视频帧,并进行前景目标提取 ```python while(1): ret, frame = cap.read() if not ret: break fgmask = fgbg.apply(frame) fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel) fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_CLOSE, kernel) contours, hierarchy = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in contours: if cv2.contourArea(c) < 1000: continue (x, y, w, h) = cv2.boundingRect(c) cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.imshow('frame', frame) cv2.imshow('fgmask', fgmask) k = cv2.waitKey(30) & 0xff if k == 27: break ``` 4. 释放资源 ```python cap.release() cv2.destroyAllWindows() ``` 在以上代码中,我们使用了`cv2.createBackgroundSubtractorMOG2()`函数创建了一个背景建模器,并使用`apply()`函数对每一帧图像进行背景建模,得到前景掩膜。 接着,我们对前景掩膜进行形态学处理,包括开运算和闭运算等操作,以去除噪声。 然后,使用`cv2.findContours()`函数对前景掩膜进行轮廓检测,得到所有的运动目标轮廓。如果轮廓的面积小于一定阈值,则将其过滤掉。最后,使用`cv2.rectangle()`函数将运动目标的外接矩形框出,并在原始视频帧上显示出来。 以上就是一个简单的基于背景建模的视频运动前景目标提取算法的Python实现方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值