由于是第一次写博客,所以难免有错误遗漏,又因为在监控毕业设计中用了这个算法,还挺好用的,所以就打算写一下,如果有错误还请大家指出XD
1 视频帧差CDM介绍
1.1 算法目的
对于该算法目的简单解释就是在基于已有的视频序列下,经过该算法处理,得到该视频序列中的背景图片(即长时间无移动物体的图像),同时可以使用该算法来获得视频或者一系列图像中的背景图像,然后利用背景处理其他数据,如获得人群前景图以及其他在图像中移动的物体前景图。
1.2 算法原理
视频帧差CDM算法原理比较简单理解和易于实现。根据监控画面的背景在短时间内不会有较大的光影变化,适用视频帧差法CDM的方法来构造背景图像。构造此背景的方法的前提为人物在背景是时刻处于移动,图像的整体的背景总是能出现应有细节,即背景能从一系列的图像后表现出来,方法的原理如下[1-2]:
假设图像序列的亮度分量为Ii (x,y),其中x,y表示像素位置,i表示帧数 (i=1,…,N) , N为序列帧的总数,用以下公式来表示:
C
D
M
i
(
x
,
y
)
=
{
d
,
d
≥
T
0
,
d
<
T
CDM_{i}(x,y)=\left\{ \begin{aligned} d,d \geq T\\ 0,d<T \end{aligned} \right.
CDMi(x,y)={d,d≥T0,d<T
I
i
+
1
(
x
,
y
)
−
I
i
(
x
,
y
)
I_{i+1}(x,y)-I_{i}(x,y)
Ii+1(x,y)−Ii(x,y)
其中阈值T用来控制去除噪声。对于位置(x,y),CDMi (x,y)表示了在位置(x,y)像素变化的曲线。根据 CDMi (x,y) 是否大于零来进行曲线分段,并将其中被检测到的静止部分用集合{Sj (x,y),1≤j≤M}表示,像素曲线如下图所示:
其中, Sj的起点和终点分别是 STj和 ENj。在位置(x,y)对应的{Sj}的集合中,选择最长的静止段再得到该分段中点对应的帧号 M(x,y)。第 M(x,y)帧的点用来填充图像中相应位置,用公式来表示:
M
(
x
,
y
)
=
(
S
T
(
x
,
y
)
+
E
N
(
x
,
y
)
)
/
2
M(x,y)=(ST(x,y)+EN(x,y))/2
M(x,y)=(ST(x,y)+EN(x,y))/2
B
(
x
,
y
)
=
I
(
x
,
y
,
M
(
x
,
y
)
)
B(x,y)=I(x,y,M(x,y))
B(x,y)=I(x,y,M(x,y))
其中 ST(x,y)和 EN(x,y)是对应最长静止段的起点以及终点,B(x,y)为重建的背景,这种方法可以恢复相对完整背景图。
2 视频帧差CDM实现
2.1 算法实现
我使用了python3进行算法的实现,使用openCV3进行像素值的处理,由于算法代码为初始版本,时间复杂度为O(NML)(N,M为图像分辨率,L为图像数量),仅为参考:
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 9 13:39:04 2019
@author: APone
"""
# -*- coding: utf-8 -*-
import cv2 as cv
import numpy as np
# 构造背景
def build_background(images, N, T):
height = images[0].shape[0]
width = images[0].shape[1]
s = np.zeros([height, width, 2])
for y in range(height):
for x in range(width):
flag = False
start = 1
end = 1
for n in range(1, N - 1):
#获得前后帧的像素点差分的值d
d = abs(int(images[n][y, x]) - int(images[n - 1][y, x]))
#加入差分值小于阈值T,取值为0,否则保留
if d < T:
result = 0
else:
result = d
#记录单个像素的在整个序列过程中处于“0”平稳的最长分段(即S),
if result == 0 and flag is False:
start = n
flag = True
elif result != 0 and flag is True:
end = n
flag = False
temp = int(s[y][x][1] - s[y][x][0])
if (end - start) > temp:
#记录前后帧标记
s[y][x][0] = start
s[y][x][1] = end
background = images[0].copy()
for y in range(height):
for x in range(width):
#将记录的最长分段的前后帧的标记取中间帧号,最后从帧序列中找出该帧并获取该像素点的像素值
median = int((s[y, x][0] + s[y, x][1]) / 2)
background[y][x] = images[median][y][x]
return background
# 使用背景和图像进行差分提取前景图
def background_diff(background, images):
c_frame = cv.absdiff(images, background)
return c_frame
# 图像的预处理,使用中值滤波和加权平均灰度化
def images_pretreatment(images, T):
images = cv.cvtColor(images, cv.COLOR_BGR2GRAY)
images = cv.medianBlur(images, T)
return images
2.2 算法效果
人群图像序列我使用了UCSD Pedestrian Dataset,里面包括了拍摄的两种不同的人群图像序列。可以从以下网址来获取数据集,如果要使用到论文中,请注明引用:
UCSD Pedestrian Dataset
(1)图像数据集展示:
(2)使用CDM算法处理后获得的背景图像:
3 后记
这个初始算法的一个弊端是有关于像素差分的判断阈值T仍然是由人工调参,不能保证适应所有情况,比如可能用到其他的图像会出现污点的情况等,最佳的理想状态是算法利用额外的统计算法进行阈值自我调整,这个问题还需待改进。
4 相关引用
[1] 唐勇, 姜昱明. 一种基于彩色图像的运动人体分割方法[J]. 微电子学与计算机, 2006, 23(03): 70-72.
[2] 邓玉春,姜昱明. 动态场景中的背景恢复[J]. 微电子学与计算机, 2004, 21(11): 70-72.