引言
很多时候,计算机的处理能力并非十分强大,这种时候我们需要尽可能用简单的算法去实现目标。
本文对用静态相机做动态视频的处理进行了一个基本介绍,容许我们对动态的视频做一个静态的背景估计。
环境&工具
Win10
Python3.8.6 & Opencv4.4
中位数介绍
中位数的意义不言而喻,很多时候对一组数据采用平均数是一种简单有效的方式,然而有些时候,取中位数则是更好的方法。
中位数(Median)又称中值,是按顺序排列的一组数据中居于中间位置的数。
1.用中位数做背景估计
对于一个短视频流,我们可以从所有帧中提取一部分,对其中每一个像素进行分析,只要某一个像素没有被移动的物体遮挡超过一半的时间,我们可以取这一像素在每一帧中的值的中位数,以其值作为对该位置背景良好的估计。
我们会用到如下几种函数:
np.median(frames, axis=0).astype(dtype=np.uint8)
cap.get(cv2.CAP_PROP_FRAME_COUNT)
np.median利用了numpy包中的median函数直接求解数组中位数
cap.get(cv2.CAP_PROP_FRAME_COUNT)可以获取cap视频文件的总帧数(实际上是总帧数-1)
接下来我们来看背景估计的源代码(注释为英文且以及比较详尽):
import cv2 as cv
import numpy as np
from skimage import data, filters
# Capture Game Video
cap=cv.VideoCapture('NBA 2K20 2020-10-13 21-44-03.mp4')
# Random 100 frames (7*60=420 frames in total)
# Use np.random.uniform to generate 100
#cv.CAP_PROP_FRAME_COUNT counts the number of frames in the video
frameIds=cap.get(cv.CAP_PROP_FRAME_COUNT)*np.random.uniform(size=30)
# Store selected frames in an array 'frame'
frames=[]
for i in frameIds:
cap.set(cv.CAP_PROP_POS_FRAMES,i)
ret,frame=cap.read()
frames.append(frame)
#Calculate the median along the time axis
medianFrame = np.median(frames, axis=0).astype(dtype=np.uint8)
cv.imshow('Median Frame',medianFrame)
cv.waitKey(0)
原视频为运动员从一侧跑到另一侧投篮的视频
原视频截图:
进行背景中位估算之后,得到的背景图如下:
2.运动帧检测
很多时候,由于计算机计算能力有限,如果对图片中的每一个像素都进行检测,计算量将很大。我们可以类比某些动物,它们只对运动中的物体比较敏感。类似的,我们可以对每一帧建立mask遮罩,先把之前获得的中值图和现有帧率图变为灰度图,然后做减法,与背景(Background)相差较大的像素便是运动的部分,因为他们遮挡了背景像素。
下面来看代码:
# Reset frame number to 0
cap.set(cv.CAP_PROP_POS_FRAMES, 0)
#Convert background to grayscale
grayMedianFrame=cv.cvtColor(medianFrame,cv.COLOR_BGR2GRAY)
#Loop over all frames
ret=True
while(ret):
#Read frame
ret, frame = cap.read()
# Convert current frame to grayscale
frame = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
# difference of two frames
dframe = cv.absdiff(frame, grayMedianFrame)
# Threshold to binarize 阈值去噪声,二值化输出
th,dframe = cv.threshold(dframe,30,255,cv.THRESH_BINARY)
# Display image
cv.imshow("Binary",dframe)
cv.waitKey(20)
# Release video object
cap.release()
# Destroy all windows
cv2.destroyAllWindows()
输出截图如下:
有时间后续我会补上输入输出的动图,以便观察。
参考教程
参考了learnopencv官网的博客,有一定修改
LearnOpencv Simple Background estimation