基本概念
帧差法是视频处理和计算机视觉领域中用于移动检测的一种简单而有效的方法。它主要依赖于连续视频帧之间的像素差异来识别场景中的移动对象。
帧差法:
- 定义:帧差法通过比较连续的视频帧之间的差异来检测移动对象。基本思想是移动对象会在连续的帧之间产生显著的位置变化,而静止背景则变化不大。
代码步骤
- 读取视频:使用
cv2.VideoCapture
函数读取视频文件test.avi
。cap=cv2.VideoCapture('test.avi') kernel=cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
- 创建背景减除器:使用
cv2.createBackgroundSubtractorMOG2
创建一个MOG2背景减除器对象。# 创建一个背景减除器对象 fgbg=cv2.createBackgroundSubtractorMOG2()
- 逐帧处理视频:通过无限循环读取视频的每一帧,并使用背景减除器处理当前帧,得到前景掩码。
# 开始一个无限循环,用于逐帧处理视频。 while (True): # ret是一个布尔值,表示是否成功读取帧,frame是读取的帧图像。 ret,frame=cap.read() cv2.imshow('1',frame) # 使用背景减除器处理当前帧,得到前景掩码。 fgmask=fgbg.apply(frame)
- 形态学开运算:对前景掩码应用形态学开运算,以去除小的噪点和分离粘连的物体。
# 对前景掩码应用形态学开运算,以去除小的噪点和分离粘连的物体。 fgmask_new=cv2.morphologyEx(fgmask,cv2.MORPH_OPEN,kernel) cv2.imshow('3',fgmask_new)
- 查找轮廓:在处理后的前景掩码中查找轮廓。
# 在处理后的前景掩码中查找轮廓。_是用于忽略返回值的占位符,contours是找到的轮廓列表,h是轮廓的层次结构。 _,contours,h=cv2.findContours(fgmask_new,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
- 轮廓筛选:遍历所有找到的轮廓,计算轮廓的周长,如果周长大于188,则认为是一个移动对象,并计算其边界矩形,在原始帧上绘制一个绿色矩形框以标识移动对象。
for c in contours:#遍历所有找到的轮廓。 perimeter=cv2.arcLength(c,True)#计算当前轮廓的周长 if perimeter>188: x,y,w,h=cv2.boundingRect(c) #在原始帧上绘制一个绿色矩形框 fgmask_new_rect=cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2) cv2.imshow('4',fgmask_new_rect) k=cv2.waitKey(60)
- 退出条件:按下ESC键(ASCII码为27)跳出循环。
# 按下ESC键(ASCII码为27),则跳出循环 if k==27: break
运行结果
帧差法优缺点
优点
- 简单高效:算法简单,易于实现,计算量小,适合实时处理。
- 实时性能:由于计算量小,帧差法可以快速处理视频帧,适用于实时视频监控系统。
- 无需背景模型:不需要预先学习或建模背景,直接比较连续帧的差异。
- 适应性:对于视频中的动态变化,如移动对象的出现和消失,帧差法能够快速响应。
- 易于调整:通过调整阈值,可以控制检测的灵敏度,以适应不同的监控环境和需求。
缺点
- 光照敏感:光照变化(如由于天气或时间变化导致的光照变化)可能会影响帧差法的性能,导致错误的移动检测。
- 阴影问题:移动对象的阴影可能被错误地检测为移动物体,引起误报。
- 背景变化:如果背景发生变化(如植物的生长、人流的变化),帧差法可能无法正确区分背景和移动对象。
- 摄像头抖动:摄像头的微小移动可能导致帧差法检测到错误的移动。
- 动态背景:在有动态背景(如水面、旗帜)的场景中,帧差法可能难以区分背景的自然运动和真正的移动对象。
- 相似颜色:如果移动对象的颜色与背景颜色相似,帧差法可能无法检测到。
- 快速移动对象:对于快速移动的对象,由于帧率的限制,可能会发生漏检。
- 遮挡问题:当一个移动对象被另一个对象遮挡时,帧差法可能无法检测到被遮挡的部分。
- 分辨率限制:在分辨率较低的视频中,重要的细节可能会丢失,导致帧差法的性能下降。
- 噪声敏感:图像噪声可能会增加帧差图像中的假阳性,尤其是在低对比度区域。
完整代码
cap=cv2.VideoCapture('test.avi')
kernel=cv2.getStructuringElement(cv2.MORPH_CROSS,(3,3))
# 创建一个背景减除器对象
fgbg=cv2.createBackgroundSubtractorMOG2()
# 开始一个无限循环,用于逐帧处理视频。
while (True):
# ret是一个布尔值,表示是否成功读取帧,frame是读取的帧图像。
ret,frame=cap.read()
cv2.imshow('1',frame)
# 使用背景减除器处理当前帧,得到前景掩码。
fgmask=fgbg.apply(frame)
# 对前景掩码应用形态学开运算,以去除小的噪点和分离粘连的物体。
fgmask_new=cv2.morphologyEx(fgmask,cv2.MORPH_OPEN,kernel)
cv2.imshow('3',fgmask_new)
# 在处理后的前景掩码中查找轮廓。_是用于忽略返回值的占位符,contours是找到的轮廓列表,h是轮廓的层次结构。
_,contours,h=cv2.findContours(fgmask_new,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for c in contours:#遍历所有找到的轮廓。
perimeter=cv2.arcLength(c,True)#计算当前轮廓的周长
if perimeter>188:
x,y,w,h=cv2.boundingRect(c)
#在原始帧上绘制一个绿色矩形框
fgmask_new_rect=cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow('4',fgmask_new_rect)
k=cv2.waitKey(60)
# 按下ESC键(ASCII码为27),则跳出循环
if k==27:
break