帧差法原理
移动侦测即是根据视频每帧或者几帧之间像素的差异,对差异值设置阈值,筛选大于阈值的像素点,做掩模图即可选出视频中存在变化的桢。帧差法较为简单的视频中物体移动侦测,帧差法分为:单帧差和三桢差。随着帧数的增加是防止检测结果的重影。
单帧差法
算法原理
以视频为例进行单帧差法移动侦测
算法实现
import cv2
import pandas as pd
import numpy as np
video_path = "./test.mp4"
cam = cv2.VideoCapture(video_path) # 打开一个视频
input_fps = cam.get(cv2.CAP_PROP_FPS) # 获取视频帧率
ret_val, input_image = cam.read() # 读取视频第一帧
gray_lwpCV = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY) # 将第一帧转为灰度
gray_lwpCV = cv2.GaussianBlur(gray_lwpCV, (21, 21), 0) # 对转换后的灰度图进行高斯模糊
background=gray_lwpCV # 将高斯模糊后的第一帧作为初始化背景
area_threh = 100 # 物体bbox面积阈值
while(cam.isOpened()) and ret_val == True:
ret_val, input_image = cam.read() # 继续读取视频帧
gray_lwpCV = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
gray_lwpCV = cv2.GaussianBlur(gray_lwpCV, (21, 21), 0) # 对读取到的视频帧进行灰度处理+高斯模糊
diff = cv2.absdiff(background, gray_lwpCV) # 将最新读取的视频帧和背景做差
#跟着图像变换背景,如果背景变化区域小于20%或者75%,则将当前帧作为新得背景区域
tem_diff=diff.flatten()
tem_ds=pd.Series(tem_diff)
tem_per=1-len(tem_ds[tem_ds==0])/len(tem_ds)
if (tem_per <0.2 )| (tem_per>0.75):
background=gray_lwpCV
else:
ret,diff_binary = cv2.threshold(diff, 10, 255, cv2.THRESH_BINARY)# 对差值diff进行二值化
contours, hierarchy = cv2.findContours(diff_binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) # 对二值化之后得结果进行轮廓提取
for c in contours:
if (cv2.contourArea(c) < area_threh): # 对于矩形区域,只显示大于给定阈值的轮廓(去除微小的变化等噪点)
continue
(x, y, w, h) = cv2.boundingRect(c) # 该函数计算矩形的边界框
cv2.rectangle(input_image, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.imshow('frame diff', np.hstack((input_image,cv2.cvtColor(diff,cv2.COLOR_GRAY2BGR))))
if cv2.waitKey(50)&0xFF==ord("q"):
break
cam.release()
cv2.destroyAllWindows()
实现效果
算法分析
优点
- 实现简单,运行速度快
缺点
- 存在"鬼影"问题(指在物体原来得位置和现在得位置都出现了该物体),
三帧差法
算法原理
连续三帧,12相减,23相减,结果做与运算。相减公式:
其中阈值T需要手动调整。结果得到一个二值图,对二值图进行形态学处理,再进行轮廓提取。
算法实现
import cv2
import numpy as np
video_path = "./test.mp4"
cap = cv2.VideoCapture(video_path)
width =int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height =int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 初始化第1.2.3帧
one_frame = np.zeros((height,width),dtype=np.uint8)
two_frame = np.zeros((height,width),dtype=np.uint8)
three_frame = np.zeros((height,width),dtype=np.uint8)
area_threh = 100 # 物体bbox面积阈值
while cap.isOpened():
ret,frame = cap.read()
frame_gray =cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
if not ret:
break
one_frame,two_frame,three_frame = two_frame,three_frame,frame_gray
# 1.2帧做差
abs1 = cv2.absdiff(one_frame,two_frame)#相减
_,thresh1 = cv2.threshold(abs1,15,255,cv2.THRESH_BINARY)#二值,大于40的为255,小于0
# 2.3帧做差
abs2 =cv2.absdiff(two_frame,three_frame)
_,thresh2 =cv2.threshold(abs2,15,255,cv2.THRESH_BINARY)
binary =cv2.bitwise_and(thresh1,thresh2)#与运算
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5))
# erode = cv2.erode(binary,kernel)#腐蚀
# dilate =cv2.dilate(binary,kernel)#膨胀
# dilate =cv2.dilate(dilate,kernel)#膨胀
# 轮廓提取
contours, hierarchy = cv2.findContours(binary.copy(),mode=cv2.RETR_EXTERNAL,method=cv2.CHAIN_APPROX_SIMPLE)#寻找轮廓
for contour in contours:
if cv2.contourArea(contour)>area_threh:
x,y,w,h =cv2.boundingRect(contour)#找方框
cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0), 2)
img_show = np.hstack((frame,cv2.cvtColor(binary,cv2.COLOR_GRAY2BGR)))
cv2.imshow('three frame diff',img_show)
if cv2.waitKey(50)&0xFF==ord("q"):
break
cap.release()
cv2.destroyAllWindows()
实现效果
- 不进行形态学处理
- 膨胀一次
- 膨胀两次
- 先腐蚀一次,再膨胀两次
算法分析
优点
- 实现简单,运行速度快
- 解决了帧差法存在的“鬼影”问题
- 能大致检测出物体的运动区域
缺点
- 不进行膨胀会存在“空洞”问题
- 进行膨胀之后会存在着多个物体的”牵连“问题
对物体的运动区域的检测不够全面
- eg:对于部分人运动区域的检测会存在着只检测出半个人的情况
参考资料
- python+opencv实现移动侦测(帧差法):https://www.jb51.net/article/183203.htm
- opencv python 三帧差法实现运动目标区域检测与完整代码:https://blog.csdn.net/pengpengloveqiaoqiao/article/details/89487049
评论 (0)