运动目标检测MATLAB代码实现——差分法、GMM、ViBe

运动目标检测MATLAB代码实现——差分法、GMM、ViBe

此文章会使用简练而清晰的语言描述下列三种算法,分别为帧间差分法(Temporal Difference)、混合高斯法(GMM)、ViBe算法,并使用MATLAB进行实现。

帧间差分法

帧间差分法分为两种:二帧差分法和三帧差分法,原理类似,这里一并介绍。

摄像机采集的视频序列具有连续性的特点,如果场景内没有运动目标,则连续帧的变化很微弱,如果存在运动目标,则连续的帧和帧之间会有明显地变化。帧间差分法就是借鉴了这个思想。该类算法对时间上连续的两帧或三帧图像进行差分运算,不同帧对应的像素点相减,判断灰度差的绝对值,当绝对值超过一定阈值时,即可判断为运动目标,从而实现运动目标的检测功能。

差分示意图

算法整体的流程图如下所示

三帧差分法

下面是二帧差分法的MATLAB代码实现

clear;clc;%读取视频name = 'fanfan.mp4';%文件名字可自行修改v = VideoReader(name);videoSource = vision.VideoFileReader(name,'ImageColorSpace', 'RGB', 'VideoOutputDataType', 'uint8');n = v.NumberOfFrames;frame_last = rgb2gray(step(videoSource));frame = step(videoSource);figure('units','normalized','outerposition',[0 0 1 1])for i=1:n

    frame = step(videoSource);

    frame_now = rgb2gray(frame);

    frame_now = medfilt2(frame_now);

    frame_diff = abs(frame_now - frame_last);

    fgMask = imbinarize(frame_diff);

    fgMask = imdilate(fgMask,strel('rectangle',[3 3]));

    fgMask = imerode(fgMask,strel('arbitrary',eye(3)));

    frame_last = frame_now;

    figure(1)

    subplot(122);imshow(fgMask);title('Original Image')

    subplot(121);imshow(frame);title('Segmentation')end

下面是三帧差分法的MATLAB代码实现

clear;clc%读取视频name = 'fanfan.mp4';%文件名字可自行修改v = VideoReader(name);videoSource = vision.VideoFileReader(name,'ImageColorSpace', 'RGB', 'VideoOutputDataType', 'uint8');n = v.NumberOfFrames;frame_last = rgb2gray(step(videoSource));frame_first = rgb2gray(step(videoSource));frame = step(videoSource);figure('units','normalized','outerposition',[0 0 1 1])for i=1:n

    figure(1)

    frame = step(videoSource);

    subplot(121);imshow(frame);title('Original Image')

    frame_second = rgb2gray(frame);

    frame = step(videoSource);

    frame_third = rgb2gray(frame);

    frame_diff1 = abs(frame_second - frame_first);

    frame_diff2 = abs(frame_third - frame_second);

    fgMask = imbinarize(min(frame_diff1,frame_diff2));

    fgMask = imdilate(fgMask,strel('rectangle',[3 3]));

    fgMask = imerode(fgMask,strel('arbitrary',eye(3)));

    fgMask = imfill(fgMask, 'hole');

    frame_first = frame_second;

    subplot(122);imshow(fgMask);title('Segmentation')end

效果如下

原图像

二帧差分法

三帧差分法

GMM——高斯混合建模

假设每个像素在时域上符合正态分布,在一定阈值范围内的像素判定为背景,并用来更新模型,不符合该分布的像素即为前景。

算法流程如下:

  1. 初始化背景模型,令 Ix,y∼N(μ0,σ0)
  2. 按照如下公式检测像素 Ix,y 属于前景还是背景,其中 λ 为阈值参数 {if|Ix,y−μ(x,y)|<λ×σ(x,y)Ix,y∈bgelseIx,y∈fg
  3. 按照如下公式更新参数,其中 α 为更新速率 μ(x,y)=(1−α)×μ(x,y)+α×Ix,yσ(x,y)=(1−α)×σ2(x,y)+α×(Ix,y−μ(x,y))2
  4. 重复步骤2、3直到结束。

下面是GMM算法的MATLAB代码实现

clc;clear;filename = 'car.avi';                   %文件名字可自行修改vidObj = VideoReader(filename);I=readFrame(vidObj);                    % 读入第一帧作为背景帧I = rgb2gray(I);fr_bw = I;     [height,width] = size(fr_bw);           %求每帧图像大小fg = zeros(height, width);              %定义前景和背景矩阵bg_bw = zeros(height, width);

C = 3;                                  % 单高斯模型的个数(通常为3-5)M = 3;                                  % 代表背景的模型个数D = 2.5;                                % 偏差阈值alpha = 0.01;                           % 学习率thresh = 0.25;                          % 前景阈值sd_init = 15;                            % 初始化标准差w = zeros(height,width,C);              % 初始化权重矩阵mean = zeros(height,width,C);           % 像素均值sd = zeros(height,width,C);             % 像素标准差u_diff = zeros(height,width,C);         % 像素与某个高斯模型均值的绝对距离p = alpha/(1/C);                        % 初始化p变量,用来更新均值和标准差rank = zeros(1,C);                      %各个高斯分布的优先级(w/sd)

pixel_depth = 8;                        % 每个像素8bit分辨率pixel_range = 2^pixel_depth -1;         % 像素值范围[0,255]figure('units','normalized','outerposition',[0 0 1 1])for i=1:height

    for j=1:width

        for k=1:C

            mean(i,j,k) = rand*pixel_range;     %初始化第k个高斯分布的均值

            w(i,j,k) = 1/C;                     % 初始化第k个高斯分布的权重

            sd(i,j,k) = sd_init;                % 初始化第k个高斯分布的标准差

            

        end

    endend

while hasFrame(vidObj)

    fr=readFrame(vidObj);  % 依次读入各帧图像

    fr_bw =rgb2gray(fr);       

    

    % 计算新像素与第m个高斯模型均值的绝对距离

    for m=1:C

        u_diff(:,:,m) = abs(double(fr_bw) - double(mean(:,:,m)));

    end

     

    % 更新高斯模型的参数

    for i=1:height

        for j=1:width

            

            match = 0;                                       %匹配标记;

            for k=1:C                       

                if (abs(u_diff(i,j,k)) <= D*sd(i,j,k))       % 像素与第k个高斯模型匹配

                    

                    match = 1;                               %将匹配标记置为1

                    

                    % 更新权重、均值、标准差、p

                    w(i,j,k) = (1-alpha)*w(i,j,k) + alpha;

                    p = alpha/w(i,j,k);                  

                    mean(i,j,k) = (1-p)*mean(i,j,k) + p*double(fr_bw(i,j));

                    sd(i,j,k) =   sqrt((1-p)*(sd(i,j,k)^2) + p*((double(fr_bw(i,j)) - mean(i,j,k)))^2);

                else                                         % 像素与第k个高斯模型不匹配

                    w(i,j,k) = (1-alpha)*w(i,j,k);           %略微减少权重

                    

                end

            end

            

                  

            bg_bw(i,j)=0;

            for k=1:C

                bg_bw(i,j) = bg_bw(i,j)+ mean(i,j,k)*w(i,j,k);

            end

            

            % 像素值与任一高斯模型都不匹配,则创建新的模型

            if (match == 0)

                [min_w, min_w_index] = min(w(i,j,:));      %寻找最小权重

                mean(i,j,min_w_index) = double(fr_bw(i,j));%初始化均值为当前观测像素的均值

                sd(i,j,min_w_index) = sd_init;             %初始化标准差为6

                end

            rank = w(i,j,:)./sd(i,j,:);                    % 计算模型优先级

            rank_ind = [1:1:C];%优先级索引

           

            

            % 计算前景

            fg(i,j) = 0;

            while ((match == 0)&&(k<=M))

           

                    if (abs(u_diff(i,j,rank_ind(k))) <= D*sd(i,j,rank_ind(k)))% 像素与第k个高斯模型匹配

                        fg(i,j) = 0; %该像素为背景,置为黑色

           

                    else

                        fg(i,j) = 255;    %否则为前景,置为白色

                    end

                    

         

                k = k+1;

            end

        end

    end

    figure(1)

    subplot(1,2,1),imshow(fr);title('Original Image') %显示最后一帧图像

    fg = imdilate(fg,strel('rectangle',[6 6]));

    fg = imerode(fg,strel('arbitrary',eye(5)));

    subplot(1,2,2),imshow(fg);title('Segmentation')  %显示前景end

效果如下

GMM

ViBe算法——随机背景更新策略

像素的变化存在不确定性,很难用一个固定的模型来表征。ViBe算法基于这样一个假设:在无法确定像素变化的模型时,随机模型在一定程度上更适合模拟像素变化的不确定性。

记图片中的每一个像素为 v(x) 样本集中的值为 vi ,样本的个数为 N ,那么样本可以表示为: M(x)={v1,v2,…,vN}

为了比较像素点与本集合中的值,定义 SR(v(x)) 是以为中心,半径为 R 的区域,若样本集中的值与当前像素点的距离在该范围内的个数大于预先规定的阈值 #min ,则将该像素点分类为背景点,即如下公式

{if#{SR(v(x))}>#minv(x)∈bgelsev(x)∈fg

算法流程如下

1.模型初始化

对于一个像素点,结合周围像素的分布特性,随机选取 N 次作为它的模型样本值。

8邻域分布抽取

假设视频每一帧的分辨率为 X×Y ,那么一共需要选取 X×Y×N 模型样本值。

2.背景判定

对于一个像素点 (x,y) ,计算其和背景样本值的距离 d ,判断它是否在 SR(v(x)) 圆内

颜色相近即距离d比较小

计算得出一共有 #SR(v(x)) 个点在阈值内,若 #SR(v(x)) 大于 #min ,则判定这个像素点是背景,否则判定为前景。

3.模型更新

对背景模型的更新就是使得背景模型能够适应背景的不断变化,比如光照的变化,背景物体的变更。

每一个背景点 (x,y) 有  的概率去更新自己的模型样本值,同时也有  的概率去更新它的邻居点 (x′,y′) 的模型样本值。

更新邻居的样本值利用了像素值的空间传播特性,背景模型逐渐向外扩散,同时当前景点计数达到临界值时也将其变为背景,并有  的概率去更新自己的模型样本值。

代码如下,一共有4个程序

%主程序main.mclear; clc; %参数param.numberOfSamples           = 20;param.matchingThreshold         = 20;param.matchingNumber            = 2;param.updateFactor              = 5;param.numberOfHistoryImages     = 20;param.lastHistoryImageSwapped   = 0;

filename = 'car.avi';vidObj = VideoReader(filename);

firstFrame = true;height = vidObj.Height;width = vidObj.Width;

param.height = height;param.width = width;

figure('units','normalized','outerposition',[0 0 1 1])while hasFrame(vidObj)

    vidFrame = readFrame(vidObj);

    figure(1)

    subplot(121),imshow(vidFrame), title('Original Image');

    vidFrame = rgb2gray(vidFrame);

    vidFrame = double(vidFrame);

    if firstFrame

        firstFrame = false;

        initViBe;

    end

    segmentationMap = vibeSegmentation(vidFrame, historyImages, historyBuffer, param);    

    [historyImages, historyBuffer] = vibeUpdate(vidFrame, segmentationMap, historyImages, historyBuffer, param, ...

        jump, neighborX, neighborY, position);

    segmentationMap = medfilt2(segmentationMap);

    subplot(122),imshow(segmentationMap), title('Segmentation');end

%initViBe.m%% ParametersnumberOfSamples         = param.numberOfSamples;matchingThreshold       = param.matchingThreshold;matchingNumber          = param.matchingNumber;updateFactor            = param.updateFactor;numberOfHistoryImages   = param.numberOfHistoryImages;

%% Initialize ViBehistoryImages = cell(1, numberOfHistoryImages);for ii = 1:length(historyImages)

    historyImages{ii} = vidFrame;end

historyBuffer = cell(1, numberOfSamples - numberOfHistoryImages);for ii = 1:length(historyBuffer)

    historyBuffer{ii} = vidFrame + double(floor(rand(height, width))*20 - 10);end

%% Random Partsize = 2*max(height, width) + 1;jump = floor(rand(1, size)*2*updateFactor) + 1;neighborX = floor(rand(1, size)*3) - 1;neighborY = floor(rand(1, size)*3) - 1;position = floor(rand(1, size)*numberOfSamples) + 1;

%vibeSegmentation.mfunction segmentationMap = vibeSegmentation(buffer, historyImages, historyBuffer, param)    %% Parameters

    height  = param.height;

    width   = param.width;

    numberOfSamples         = param.numberOfSamples;

    matchingThreshold       = param.matchingThreshold;

    matchingNumber          = param.matchingNumber;

    numberOfHistoryImages   = param.numberOfHistoryImages;

    

    %% Segmentation

    segmentationMap = uint8(ones(height, width)*(matchingNumber - 1));

    % First and Second history Image structure

    distance1 = abs(buffer - historyImages{1}) <= matchingThreshold;

    distance2 = abs(buffer - historyImages{2}) <= matchingThreshold;

    for ii = 1:height

        for jj = 1:width

            if ~distance1(ii, jj)

                segmentationMap(ii, jj) = matchingNumber;

            end

            if distance2(ii, jj)

                segmentationMap(ii, jj) = segmentationMap(ii, jj) - 1;

            end

        end

    end

    % match the image and samples

    numberOfTests = numberOfSamples - numberOfHistoryImages;

    for kk = 1:numberOfTests

        distance3 = uint8(abs(buffer - historyBuffer{kk}) <= matchingThreshold);

        segmentationMap = segmentationMap - distance3;

    end

    

    segmentationMap = uint8(segmentationMap*255);end

%vibeUpdate.mfunction [historyImages, historyBuffer] = vibeUpdate(buffer, updatingMask, historyImages, historyBuffer, param, ...

    jump, neighborX, neighborY, position)

    %% Parameters

    height  = param.height;

    width   = param.width;

    numberOfHistoryImages   = param.numberOfHistoryImages;   

    

    %% Update Model

    for indY = 2:height - 1

        shift = floor(rand()*width) + 1;

        indX = jump(shift) + 1;

        while indX < width

            if updatingMask(indY, indX) == 0

                value = buffer(indY, indX);

                if position(shift) <= numberOfHistoryImages

                    historyImages{position(shift)}(indY, indX) = value;

                    historyImages{position(shift)}...

                        (indY + neighborY(shift), indX + neighborX(shift)) = value;

                else

                    pos = position(shift) - numberOfHistoryImages;

                    historyBuffer{pos}(indY, indX) = value;

                    historyBuffer{pos}...

                        (indY + neighborY(shift), indX + neighborX(shift)) = value;

                end

            end

            shift = shift + 1;

            indX = indX + jump(shift);

        end

    end

    end

效果如下

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值