一、帧差分法
摄像机采集的视频序列具有连续性的特点。如果场景内没有运动目标,则连续帧的变化很微弱,如果存在运动目标,则连续的帧和帧之间会有明显地变化。
1.1 两帧差分法
帧间差分法(Temporal Difference)就是借鉴了上述思想。由于场景中的目标在运动,目标的影像在不同图像帧中的位置不同。该类算法对时间上连续的两帧或三帧图像进行差分运算,不同帧对应的像素点相减,判断灰度差的绝对值,当绝对值超过一定阈值时,即可判断为运动目标,从而实现目标的检测功能。
两帧差分法的运算过程如图2-2所示。记视频序列中第n帧和第n−1帧图像为fn和fn−1,两帧对应像素点的灰度值记为fn(x,y)和fn−1(x , y),按照式2.13将两帧图像对应像素点的灰度值进行相减,并取其绝对值,得到差分图像Dn:
设定阈值T,按照式2.14逐个对像素点进行二值化处理,得到二值化图像Rn'。其中,灰度值为255的点即为前景(运动目标)点,灰度值为0的点即为背景点;对图像Rn'进行连通性分析,最终可得到含有完整运动目标的图像Rn。
1.2 三帧差分法
两帧差分法适用于目标运动较为缓慢的场景,当运动较快时,由于目标在相邻帧图像上的位置相差较大,两帧图像相减后并不能得到完整的运动目标,因此,人们在两帧差分法的基础上提出了三帧差分法。
三帧差分法的运算过程如图2-3所示。记视频序列中第n+1帧、第n帧和第n−1帧的图像分别为fn+1、fn和fn−1,三帧对应像素点的灰度值记为fn+1(x , y) 、fn(x , y) 和fn−1(x , y) , 按照式2.13分别得到差分图像Dn+1和Dn,对差分图像Dn+1和Dn按照式2.15进行与操作,得到图像Dn',然后再进行阈值处理、连通性分析,最终提取出运动目标。
在帧间差分法中,阈值 T 的选择非常重要。如果阈值T选取的值太小,则无法抑制差分图像中的噪声;如果阈值T选取的值太大,又有可能掩盖差分图像中目标的部分信息;而且固定的阈值T无法适应场景中光线变化等情况。为此,有人提出了在判决条件中加入对整体光照敏感的添加项的方法,将判决条件修改为:
其中, N A为待检测区域中像素的总数目,λ为光照的抑制系数,A可设为整帧图像。添加项表达了整帧图像中光照的变化情况。如果场景中的光照变化较小,则该项的值趋向于零;如果场景中的光照变化明显,则该项的值明显增大,导致式2.16右侧判决条件自适应地增大,最终的判决结果为没有运动目标,这样就有效地抑制了光线变化对运动目标检测结果的影响。
图 2-5 是采用帧间差分法对自拍序列 lab 序列进行运动目标检测的实验结果,(b)图是采用两帧差分法的检测结果,(c)图是采用三帧差分法的检测结果。lab序列中的目标运动较快,在这种情况下,运动目标在不同图像帧内的位置明显不同,采用两帧差分法检测出的目标会出现“重影”的现象,采用三帧差分法,可以检测出较为完整的运动目标。
综上所述,帧间差分法的原理简单,计算量小,能够快速检测出场景中的运动目标。但由实验结果可以看出,帧间差分法检测的目标不完整,内部含有“空洞”,这是因为运动目标在相邻帧之间的位置变化缓慢,目标内部在不同帧图像中相重叠的部分很难检测出来。帧间差分法通常不单独用在目标检测中,往往与其它的检测算法结合使用。
二、混合高斯建模法
高斯模型有单高斯模型(SGM)和混合高斯模型(GMM)两种。
(1)单高斯模型:
为简单起见,阈值t的选取一般靠经验值来设定。通常意义下,我们一般取t=0.7-0.75之间。
二维情况如下所示:
(2)混合高斯模型:
对于(b)图所示的情况,很明显,单高斯模型是无法解决的。为了解决这个问题,人们提出了高斯混合模型(GMM),顾名思义,就是数据可以看作是从数个高斯分布中生成出来的。虽然我们可以用不同的分布来随意地构造 XX Mixture Model ,但是 GMM是 最为流行。另外,Mixture Model 本身其实也是可以变得任意复杂的,通过增加 Model 的个数,我们可以任意地逼近任何连续的概率密分布。
每个 GMM 由 K 个 Gaussian 分布组成,每个 Gaussian 称为一个“Component”,这些 Component 线性加成在一起就组成了 GMM 的概率密度函数:
(1)
其中,πk表示选中这个component部分的概率,我们也称其为加权系数。
根据上面的式子,如果我们要从 GMM 的分布中随机地取一个点的话,实际上可以分为两步:
(1)首先随机地在这 K 个 Component 之中选一个,每个 Component 被选中的概率实际上就是它的系数 πk,选中了 Component 之后,再单独地考虑从这个 Component 的分布中选取一个点就可以了──这里已经回到了普通的 Gaussian 分布,转化为了已知的问题。假设现在有 N 个数据点,我们认为这些数据点由某个GMM模型产生,现在我们要需要确定 πk,μk,σk 这些参数。很自然的,我们想到利用最大似然估计来确定这些参数,GMM的似然函数如下:
(2)
在最大似然估计里面,由于我们的目的是把乘积的形式分解为求和的形式,即在等式的左右两边加上一个log函数,但是由上文博客里的(2)式可以看出,转化为log后,还有log(a+b)的形式,因此,要进一步求解。
三、仿真代码
function varargout = object_detect(varargin)
% OBJECT_DETECT MATLAB code for object_detect.fig
% OBJECT_DETECT, by itself, creates a new OBJECT_DETECT or raises the existing
% singleton*.
%
% H = OBJECT_DETECT returns the handle to a new OBJECT_DETECT or the handle to
% the existing singleton*.
%
% OBJECT_DETECT('CALLBACK',hObject,eventData,handles,...) calls the local
% function named CALLBACK in OBJECT_DETECT.M with the given input arguments.
%
% OBJECT_DETECT('Property','Value',...) creates a new OBJECT_DETECT or raises the
% existing singleton*. Starting from the left, property value pairs are
% applied to the GUI before object_detect_OpeningFcn gets called. An
% unrecognized property name or invalid value makes property application
% stop. All inputs are passed to object_detect_OpeningFcn via varargin.
%
% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one
% instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES
% Edit the above text to modify the response to help object_detect
% Last Modified by GUIDE v2.5 16-May-2018 08:41:00
% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @object_detect_OpeningFcn, ...
'gui_OutputFcn', @object_detect_OutputFcn, ...
'gui_LayoutFcn', [] , ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
% --- Executes just before object_detect is made visible.
function object_detect_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to object_detect (see VARARGIN)
% Choose default command line output for object_detect
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
% UIWAIT makes object_detect wait for user response (see UIRESUME)
% uiwait(handles.figure1);
% --- Outputs from this function are returned to the command line.
function varargout = object_detect_OutputFcn(hObject, eventdata, handles)
% varargout cell array for returning output args (see VARARGOUT);
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Get default command line output from handles structure
varargout{1} = handles.output;
% --- Executes on button press in pushbutton1.
function pushbutton1_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
[filename, pathname] = uigetfile({'*.avi'; '*.mp4'},'打开视频');
str = [pathname filename];
set(handles.edit1, 'String', str);
function edit1_Callback(hObject, eventdata, handles)
% hObject handle to edit1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Hints: get(hObject,'String') returns contents of edit1 as text
% str2double(get(hObject,'String')) returns contents of edit1 as a double
% --- Executes during object creation, after setting all properties.
function edit1_CreateFcn(hObject, eventdata, handles)
% hObject handle to edit1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called
% Hint: edit controls usually have a white background on Windows.
% See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor','white');
end
% --- Executes on button press in pushbutton2.
function pushbutton2_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton2 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
videoName = get(handles.edit1, 'String');
videoSource = vision.VideoFileReader(videoName,...
'ImageColorSpace', 'RGB', 'VideoOutputDataType', 'uint8');
videoInfo = info(videoSource);
videoRate = videoInfo.VideoFrameRate;
waitTime = 1.0/videoRate;
frame_last = rgb2gray(step(videoSource));
count = 1
global exit_flag;
global pause_flag;
exit_flag = false;
pause_flag = false;
while ~isDone(videoSource) && ~exit_flag
if pause_flag
uiwait(handles.figure1);
end
frame = step(videoSource);
frame_now = rgb2gray(frame);
frame_now = medfilt2(frame_now);
frame_diff = abs(frame_now - frame_last);
fgMask = imbinarize(frame_diff);
fgMask = imopen(fgMask, strel('rectangle', [3, 3]));
fgMask = imfill(fgMask, 'holes');
frame_last = frame_now;
axes(handles.axes1);
imshow(frame);
axes(handles.axes2);
imshow(fgMask);
pause(waitTime - 0.02);
count = count + 1
end
release(videoSource);
% --- Executes on button press in pushbutton3.
function pushbutton3_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton3 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global exit_flag;
exit_flag = true;
% --- Executes on button press in pushbutton4.
function pushbutton4_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton4 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global pause_flag;
pause_flag = true;
% --- Executes on button press in pushbutton5.
function pushbutton5_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton5 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global pause_flag;
pause_flag = false;
uiresume(handles.figure1);
% --- Executes on button press in pushbutton6.
function pushbutton6_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton6 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
videoName = get(handles.edit1, 'String');
videoSource = vision.VideoFileReader(videoName,...
'ImageColorSpace', 'RGB', 'VideoOutputDataType', 'uint8');
videoInfo = info(videoSource);
videoRate = videoInfo.VideoFrameRate;
waitTime = 1.0/videoRate;
frame_first = rgb2gray(step(videoSource));
frame = step(videoSource);
global exit_flag;
global pause_flag;
exit_flag = false;
pause_flag = false;
while ~isDone(videoSource) && ~exit_flag
if pause_flag
uiwait(handles.figure1);
end
% 显示该帧图像
axes(handles.axes1);
imshow(frame);
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 = imopen(fgMask, strel('rectangle', [3, 3]));
fgMask = imfill(fgMask, 'hole');
frame_first = frame_second;
axes(handles.axes2);
imshow(fgMask);
pause(waitTime - 0.02);
end
release(videoSource);
% --- Executes on button press in pushbutton7.
function pushbutton7_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton7 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
close(gcf);
% --- Executes on button press in pushbutton8.
function pushbutton8_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton8 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
videoName = get(handles.edit1, 'String');
videoSource = vision.VideoFileReader(videoName,...
'ImageColorSpace', 'RGB', 'VideoOutputDataType', 'uint8');
videoInfo = info(videoSource);
videoRate = videoInfo.VideoFrameRate;
waitTime = 1.0/videoRate;
global exit_flag;
global pause_flag;
exit_flag = false;
pause_flag = false;
% 已经匹配,则不需要再进行优先级排序判定
if match_ground
fgMask(i, j) = 0;
continue;
end
% 优先级排序
rank = W(i, j, :) ./ sd(i, j, :);
[~, rank_index] = sort(rank, 'descend');
fgMask(i, j) = 0;
k = 1;
temp_T = 0;
while (match == 0) && (temp_T < T)
index = rank_index(k);
if abs(u_dist(i, j, index) <= D * sd(i, j, index))
fgMask(i, j) = 0;
break;
else
temp_T = temp_T + W(i, j, index);
fgMask(i, j) = 255;
end
k = k + 1;
end
end
end
% 使用混合高斯背景建模
fgMask = logical(fgMask);
fgMask = imdilate(imerode(fgMask, disk1), disk2);
axes(handles.axes1);
imshow(frame);
axes(handles.axes2);
imshow(fgMask);
drawnow;
frame_count = frame_count + 1
end
release(videoSource);
% videoName = get(handles.edit1, 'String');
% videoSource = vision.VideoFileReader(videoName,...
% 'ImageColorSpace', 'RGB', 'VideoOutputDataType', 'uint8');
% detector = vision.ForegroundDetector(...
% 'NumTrainingFrames', 5, ...
% 'InitialVariance', 30 * 30);
% videoInfo = info(videoSource);
% videoRate = videoInfo.VideoFrameRate;
% waitTime = 1.0/videoRate;
% global exit_flag;
% global pause_flag;
% exit_flag = false;
% pause_flag = false;
%
% while ~isDone(videoSource) && ~exit_flag
% if pause_flag
% uiwait(handles.figure1);
% end
% frame = step(videoSource);
% frame_now = rgb2gray(frame);
% fgMask = step(detector, frame_now);
% fgMask = imopen(fgMask, strel('rectangle', [3, 3]));
% fgMask = imfill(fgMask, 'holes');
% axes(handles.axes1);
% imshow(frame);
% axes(handles.axes2);
% imshow(fgMask);
% pause(waitTime - 0.02);
% end
% release(detector);
% release(videoSource);
% --- Executes on button press in pushbutton9.
function pushbutton9_Callback(hObject, eventdata, handles)
% hObject handle to pushbutton9 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
videoName = get(handles.edit1, 'String');
videoSource = vision.VideoFileReader(videoName,...
'ImageColorSpace', 'RGB', 'VideoOutputDataType', 'uint8');
videoInfo = info(videoSource);
% 参数设置
sample_num = 10; % 样本库
match_thres = 20; % 匹配阈值
match_num = 2; % 最小匹配数
update_factor = 1; % 一开始50帧内采用该更新因
next_update_factor = 5; % 50帧以后的更新因子
cols = videoInfo.VideoSize(1); % 图像的宽度
rows = videoInfo.VideoSize(2); % 图像的高度
fore_thres = 20; % 前景阈值
frame_count = 0; % 处理帧数
neighbor = [1, 0, -1, -1, 1, 0, 0, -1, 1]; % 邻域选择
% 外部控制标记
global exit_flag;
global pause_flag;
exit_flag = false;
pause_flag = false;
% 判断是否为第一帧
first_flag = true;
while ~isDone(videoSource) && ~exit_flag
if pause_flag
uiwait(handles.figure1);
end
frame = step(videoSource);
frame_gray = double(rgb2gray(frame));
if first_flag
first_flag = false;
%% 开始初始化
samples = cell(1, sample_num);
% 前两个样本设置初始像素
samples{1} = frame_gray;
samples{2} = frame_gray;
% 剩下的样本初始化
for i = 3:sample_num
samples{i} = frame_gray + double(floor(rand(rows, cols) * 20) - 10);
end
fore_count = uint8(zeros(rows, cols));
frame_count = frame_count + 1
% 初始化结束
continue
end
%% 前景分割
fgMask = uint8(ones(rows, cols) * match_num);
for i = 1:sample_num
distance = uint8(abs(samples{i} - frame_gray) <= match_thres);
fgMask = fgMask - distance;
end
fgMask = logical(fgMask * 255);
% 更新前景计数
for r = 1 : rows
for c = 1:cols
if fgMask(r, c) == 1
fore_count(r, c) = fore_count(r, c) + 1;
if fore_count(r, c) >= fore_thres
fore_count(r,c) = 0;
fgMask(r, c) = 0;
end
else
fore_count(r, c) = 0;
end
end
end
updateMask = fgMask;
updateMask = imfill(updateMask, 'hole');
;
imshow(fgMask);
drawnow;
end
release(videoSource);
四、仿真结果