Project-PedestrianDetection

概述

  这个项目是本周数字图像处理课程的一个project,目标是识别道路上的运行的人。其中需要着重解决的问题是提取运动的行人的同时,去除背景中因相机抖动等原因产生的运动的噪点。我主要使用的帧比较的方法来提取运动的像素,并根据开操作来实现噪点的去除。

相关知识

数字图像的形态学操作

  基本的形态学操作包含了腐蚀和膨胀,实现是利用对应核对图像进行扫描。这两种基本操作可以组合成开操作和闭操作,其中开操作就是先经过腐蚀再进行膨胀,可以图像中断开较窄的狭颈和消除细的突出物,主要用来去除图像噪点和分割连续区域。有关形态学操作详细的介绍可以参考冈萨雷斯的《数字图像处理》。

高斯混合模型

  高斯混合模型是指一个多概率分布的数据集中,每类数据都符合单一高斯分布。混合高斯模型主要用来实现数据样本的聚类,求解模型参数可以选择EM算法。更详细的介绍参考:Wenliang:高斯混合模型(GMM)

思路整理

我的思路

  首先观察测试视频,我们可以发现4个测试视频中运动的物体只有人,视频中其他像素点变动是因为相机的抖动和树叶的摆动等原因造成的。所以本次作业实际的操作是检测视频中的运动物体,并且去除因相机抖动和背景晃动产生的噪点。我的整体思路是先对于测试视频中的每帧做处理,包括变为灰度图,高斯模糊,二值化处理。然后找测试视频中仅有背景的一帧或者人的区域所占像素点最少的一帧作为背景帧,之后逐帧与背景帧进行检测,找到变化的帧,之后利用开操作进行噪点的去除。
  以第二个测试视频为例,我们来看一看逐步的效果。我截取视频中的一帧来展示:
photo1
  首先进行每帧的前期处理:

1
2
3
4
5
6
7
8
9
10
11
12
# some simlpe operate for each frame before finding people in it
def simlpe_operate(img):
# change img into gray
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# Gaussian
gb_img = cv2.GaussianBlur(gray_img,(5,5),0)
# threshold
(T, threshold_img) = cv2.threshold(gb_img, 0, 255, cv2.THRESH_OTSU+cv2.THRESH_BINARY)
# Canny
edge_img = cv2.Canny(threshold_img,100,200)

return threshold_im

  然后进行背景帧的选择,这一步我根据上述的要求选择最后一帧作为背景帧。之后每帧与背景帧进行逐一判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# use background to find the people in the picture
def find_people(gray_0,gray_1):
img_size = gray_1.shape
img_result = gray_1[:]
# use a threshold value to judge whether the pixel is in "people" or not
for i in range (0,img_size[0]):
for j in range(0,img_size[1]):
if (abs(gray_0[i][j] - gray_1[i][j]) > 50):
img_result[i][j] = 0
else:
img_result[i][j] = 255
# change the gray img to RGB for writing into the video
img_result_RGB = cv2.cvtColor(img_result, cv2.COLOR_GRAY2BGR)

return img_result_RGB

  效果如下:
photo2
  可以看出现在由于相机的抖动使得当前像素点与背景帧有一些差异,我们利用开操作进行边界噪点的去除。对于测试1和策2,我选择腐蚀4次膨胀2次进行去噪;对于测试3和测试4,由于人比较小,噪点也是分散的点,所以我选择腐蚀2次膨胀1次进行去噪。

1
2
3
4
5
6
7
8
9
10
11
# remove the noise
def noise_removal(frame):
kernel = np.ones((3,3),np.uint8)
# in test 1 and 2: erode 4 times (because the people is black, we shoule use dilate )
# in test 3 and 4: erode 2
img_erode = cv2.dilate(frame,kernel,iterations = 2)
# in test 1 and 2: dilate 2 times (because the people is black, we shoule use erode )
# in test 3 and 4: dilate 1
img_erosion = cv2.erode(img_erode,kernel,iterations = 1)

return img_erosion

  我们查看测试2中去玩噪点的一帧进行查看:
photo3
  可以发现噪点完全去除干净了,至此这个ptoject基本完成。我把测试2的完整输出放到了Github项目里。

整体思路总结

  我来利用这个部分总结下老师上课讲解这个问题处理的思路。
  首先动态物体检测的总体思路是拿当前帧与背景帧或前面的帧进行比较,得到像素变化的点。但是在这个简单模型上,会由于光照,人物的影子,以及其他物体(如树叶)的移动,以及与人物衣服颜色相似的背景等原因产生误差或噪点。我们主要讨论的是光照的变化和杂物摆动引起噪点的问题。
  对于光照问题,解决的思路包含两个方进行像素级的聚类。一个方向是可以选择像素在一定数量的相邻帧上的变化情况判断变化是否大,如果是人经过,则会有较大的变化,如果是光线,则应该是缓慢的渐变。另一个方向是采用统计学的角度进行聚类,可以采用正态分布进行拟合的方式进行判断,思路参考高斯混合模型。
  对于树叶的扰动问题,有两个大的有推进性的思路。一个是去图片中的许多小区域进行统计和判断,这样在树叶摆动的小区域内像素值的和应该差异不大,而人经过的小区域像素点的和会差异很大,这样就把图像的处理和判断从像素级提升到了区域级。第二个推进的思路即使用高斯混合模型。
  对于树叶摆动噪点的去除,我还有一个思路,就是对于单像素点进行时间轴上的滤波。因为人经过的化,像素的时间轴的曲线应该是一个单一的冲激峰,而如果是树叶的摆动的化,则应该是一个震荡波形。我们可以对单像素点的时间轴波形求一遍一阶导,即差分。这样就可以检测出曲线是一个峰值还是震荡波,从而分辨出那些是树叶的摆动造成的噪点。可以想到一个问题,就是可能人不止经过一次,所以人经过的噪点不一定是单一的峰,而可能是数量较少的几个峰,所以在进行检测的时候我们可以选择一个波峰数目的阈值进行选择。但是这样有一个问题,就是这样处理的计算复杂度偏高。
  目前比较成熟的一个动态检测的思路是使用混合高斯模型进行操作,对于运动物体的检测效果较好。但是也有一些问题,由于背景模型的高斯分布是一直在更新的,所以人如果在由运动变为静止,则检测的bounding-boxes则会逐渐减小至消失。并且如果有光线的突然变化,在变化瞬间即之后的一段时间内,bounding-boxes会圈住整个光线变化的区域。
  还有一个思路是进行背景的加权更新。即采样每帧后,由背景帧和当前帧进行加权后得到更新的背景帧。对于这个思路我做了一些实验,发现这样也有一定的局限性,即如果前几帧就有人物在内的话,背景再怎么更新效果也没有很好,因为选择合适的权重就比较困难。可以尝试将前背景的权重给的低一点,新帧的权重给的高一点,然后在把更新的帧与当前帧提前几帧,效果会好一点。
  还有一点,就是这个任务可能让人想到使用深度学习的方法做每帧的object-detection,对于样例1和样例2来说,人的特征比较清晰,深度学习效果还可以,但是对于test3和test4来说,人比较小,像素点比较模糊,所以效果不好。我使用了yolo跑了一下,无法识别样例3和样例4中的人。

代码实现

  代码完整实现参考我的github:SuperYanyann/pedestrianDetection。其中包含了测试视频单帧效果查看的代码和完整视频处理的代码。

小结

  需要说明的是,这种思路在实际应用中,如果外界光线变化不是很大(例如不是白天和晚上的变化),在测试区域找好无人的背景即可实现。但是若想实现白天和夜晚同时检测,则可以需要截取不同时段的背景来实现这种思路或者采用其他算法。此处由于测试样例的录取时间较短,光线变化不明显,所以效果尚可。我觉得这种算法有一定的推广意义。

参考

[1] 李航.统计学习方法
[2] Wenliang:高斯混合模型(GMM)