背景检测

简介

这学期选了王菡子老师的《视频分析前沿》,写了一个基于opencv-python的背景检测程序,做了一些了解,作以下记录。

研究背景

视频图像中运动目标跟踪的一种方法,其基本思想是对图像的背景进行检测。一旦背景检测完成,将当前的图像与背景进行某种比较,根据比较结果确定前景目标(需要检测的运动目标)。

背景检测是在视频图像序列中检测出背景,背景就是场景中静止不动的景物。因为摄像机不动,因此图像中的每个像素点都有一个对应的背景值,在一段时间内,这个背景值是比较固定的。背景检测的目标就是根据视频图像序列,找出图像中每一点的背景值。 背景检测有很多算法,有单高斯模型法、混合高斯模型法、码本法CodeBook、自组织背景检测SOBS-Self-organization background subtraction、样本一致性背景建模算法SACON-SAmple CONsensus和ViBe-Visual Background Extractor等。

发展现状

目前应用在背景检测中的有以下方法:

单高斯模型法

该方法认为像素点的像素值在视频序列中服从高斯分布。针对每个固定的像素点,计算N帧训练图像序列中该点像素值的均值和方差,确定该单高斯模型的全部参数,之后利用3倍方差作为阈值比较判断前景或是背景即可。这种方法适用于光照 无明显变化的场合,且运动物体的阴影较小的情况。该方法对光照变化敏感,对阴影处理效果较差。

混合高斯模型法

该方法是为了克服单高斯模型不能处理背景中有运动目标的问题而引入的,它对背景的多个状态分别进行建模, 根据数据属于哪个状态来更新该状态的模型参数。具体地,依次读取N帧训练图像, 每次对每个像素点进行迭代建模。K为每个像素允许的最大模型个数,a为初始标准差,当读入一幅训练图像时,用它的像素值更新背景模型。如果一个像素点的像素值与某个模型的均值小于2.5倍的标准差,则用该像素值更新模型。如果模型个数小于K,则对其建立一个新模型。如果K个模型都不符合,则删除权重最小的模型,对其建立一个新模型。

码本法CodeBook

该方法的基本思想是,根据视频序列生成最初的码本,借助码字中的“最长为出现时间”通过时域滤波器滤除代表运动前景的码字,每个码字代表一个状态。之后再通过空域滤波器将上一步错误删除的较少出现的背景状态的码字恢复,这样就可以处理静止背景和运动背景的检测问题。

自组织背景检测SOBS-Self-organization background subtraction

该方法是一种基于自组织神经网络的背景差分算法,主要是借鉴神经网络的特性,一个网络输入节点,对应多个中间节点,将背景模型中的一个像素映射到模型的多个位置,并采用了像素邻域空间相关的更新方式,使邻域的信息进一步融入模型中,使得算法具有邻域空间相关性。它首先采用HSV颜色空间,选取第一帧数据作为背景模型的初始化数据。计算某个像素点与其对应的模型块里像素点的最小距离,若最小距离小于阈值,则判定为背景,更新背景模型;否则,进行阴影判断,若判定为阴影,则只标识为背景,不更新背景模型。若像素判定为背景,且不属于阴影,则按一定的权值更新背景模型中最佳匹配像素周围的像素点。

样本一致性背景建模算法SACON-SAmple CONsensus

该方法是基于样本一致性的运动目标检测算法。该算法通过对每个像素进行样本一致性判断来判定像素是否为背景。首先直接取视频序列的前N帧建立背景模型,之后采用邻域帧间差分法提取可能的运动像素。对于一个新像素,将其与对应的背景模型比较,判断新像素是否满足背景模型样本的一致性。对于阴影,采用进一步的验证规则,去除阴影区域,对于检测到的前景,采用连通性分析,填充前景区域的内部空洞,主要通过形态学滤波实现。最后采用TOM(Time of Map)进行处理,将背景的移出和运动目标的停止而引起的变化很快的融入到背景中。

ViBe-Visual Background Extractor

ViBe是一种像素级视频背景检测的算法,对硬件内存占用也少。该算法首次将随机选择机制引入到背景建模中,通过随机选择样本,随机选择邻域像素进行估计背景模型的方式描述实际场景的随机波动性。在无法确定像素变化的模型时,随机的更新策略,在一定程度上可以模拟像素变化的不确定性。通过调节时间二次抽样因子,使极少的样本值就能覆盖所有的背景样本,兼顾了准确率和计算负载。抑噪能力强,噪声等干扰信息在传播之前须与背景模型相匹配,显而易见,无法匹配,从而抑制了干扰信息的传播。

代码

代码用的wxpython编写了用户界面,背景检测使用了opencv中的混合高斯模型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import wx
import cv2 as cv


class bucky(wx.Frame):

def __init__(self, parent=None, id=-1):
wx.Frame.__init__(self, parent, id, '背景检测')
panel = wx.Panel(self)

box = wx.MessageDialog(None, '您要使用摄像头吗?不要就选择一个视频吧', '文件/摄像头', wx.YES_NO)
answer = box.ShowModal()
box.Destroy()
if answer == 5103:
cv.namedWindow('background-detection', cv.WINDOW_NORMAL)
cap = cv.VideoCapture(0)
fgbg = cv.bgsegm.createBackgroundSubtractorMOG()

width = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv.CAP_PROP_FPS)
out = cv.VideoWriter('output.avi', -1, fps, (width, height))

while(True):
ret, frame = cap.read()
print(ret)
if ret is True:
fgmask = fgbg.apply(frame)
out.write(fgmask)
cv.imshow('background-detection', fgmask)
else:
break
if cv.waitKey(1) & 0XFF == ord('q'):
break

cap.release()
out.release()
cv.destroyAllWindows()
else:
openFileDialog = wx.FileDialog(None, "Choose", "./", "pets.mp4",
"MP4 files (*.mp4)|*.mp4|AVI files (*.avi)|*.avi",
wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
openFileDialog.ShowModal()
tmp = openFileDialog.GetPath()
openFileDialog.Destroy()
cv.namedWindow('background-detection', cv.WINDOW_NORMAL)
cap = cv.VideoCapture(tmp)
fgbg = cv.bgsegm.createBackgroundSubtractorMOG()

width = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv.CAP_PROP_FPS)
out = cv.VideoWriter('output.avi', -1, 20, (width, height))

while(True):
ret, frame = cap.read()
if ret is True:
fgmask = fgbg.apply(frame)
out.write(fgmask)
cv.imshow('background-detection', fgmask)
else:
break
if cv.waitKey(1) & 0XFF == ord('q'):
break

cap.release()
out.release()
cv.destroyAllWindows()


if __name__=='__main__':
app = wx.PySimpleApp()
frame = bucky(parent=None, id=-1)
frame.Show()
app.MainLoop()
一分一毛,也是心意。