【深度学习】OpenCV 视频分析实战:背景建模 + 形态学处理实现运动目标检测

文章目录


完整代码一览

c 复制代码
import cv2
cap = cv2.VideoCapture('test.avi')

kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))

fgbg = cv2.createBackgroundSubtractorMOG2()

while True:
    ret, fream = cap.read()
    if not ret:
        break
    cv2.imshow('fream', fream)

    fgmask = fgbg.apply(fream)
    cv2.imshow('famask', fgmask)

    fgmask_new = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
    cv2.imshow('famask1', fgmask_new)

    contours = cv2.findContours(fgmask_new, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
    for c in contours:
        perimeter = cv2.arcLength(c, True)
        if perimeter > 188:
            x, y, w, h = cv2.boundingRect(c)
            fgmask_new_rect = cv2.rectangle(fream, (x, y), (x + w, y + h), (0, 0, 255), 2)

    cv2.imshow('fgmask_new_rect', fgmask_new_rect)
    k = cv2.waitKey(60)
    if k == 27:   # ESC 键退出
        break

cap.release()
cv2.destroyAllWindows()

打开视频与定义内核

c 复制代码
import cv2
cap = cv2.VideoCapture('test.avi')

cv2.VideoCapture:创建一个视频捕获对象,参数可以是视频文件路径或摄像头设备号(0 表示默认摄像头)。这里指定了 'test.avi',表示读取本地视频文件。

定义结构内核

c 复制代码
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))

定义了一个 十字形结构内核,尺寸为 3×3。这个内核将用于形态学操作(开运算)。十字形结构适合处理细长的噪声或连接断裂的前景区域。

创造背景减除器

c 复制代码
fgbg = cv2.createBackgroundSubtractorMOG2()

创建一个 MOG2 背景减除器。MOG2 是基于高斯混合模型的背景建模算法,它能自动学习背景,并且能区分阴影(通过阴影检测参数)。我们使用默认参数即可。

视频逐帧处理

读取帧

c 复制代码
while True:
    ret, fream = cap.read()
    if not ret:
        break
    cv2.imshow('fream', fream)
#无限循环不断读取视频的下一帧。

cap.read() 返回两个值:ret 表示是否成功读取(布尔值),fream 是当前帧图像(注意变量名拼写错误,但无影响)。

如果 ret 为 False,说明视频已读完或读取失败,则跳出循环,同时显示原始帧,方便观察视频内容。

前景掩码

c 复制代码
    fgmask = fgbg.apply(fream)
    cv2.imshow('famask', fgmask)

fgbg.apply(fream) 将当前帧输入到背景减除器中,返回一个二值图像 fgmask:白色(255)区域表示前景(运动物体),黑色(0)区域表示背景。

显示这个前景掩码,你会看到运动目标呈白色块状,但往往会有很多噪点(小白色斑点)和内部空洞。

运行结果:

去除噪声,分离目标

c 复制代码
    fgmask_new = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
    cv2.imshow('famask1', fgmask_new)

cv2.morphologyEx 执行形态学操作,这里用的是 开运算(MORPH_OPEN),即先腐蚀后膨胀。

开运算的效果:去除细小的白色噪点(相当于"擦掉"孤立的小白点),同时断开黏连在一起的大块目标(如果两个物体靠得太近,开运算能稍微分开它们)。

使用我们之前定义的 3×3 十字内核,强度适中,不会过度破坏目标轮廓。处理后,前景掩码会更干净,噪声减少。

运行结果:

轮廓检测与过滤

c 复制代码
    contours = cv2.findContours(fgmask_new, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]
    for c in contours:
        perimeter = cv2.arcLength(c, True)
        if perimeter > 188:
            x, y, w, h = cv2.boundingRect(c)
            fgmask_new_rect = cv2.rectangle(fream, (x, y), (x + w, y + h), (0, 0, 255), 2)

cv2.findContours 在去噪后的前景掩码上查找轮廓。RETR_EXTERNAL 只检测最外层轮廓,CHAIN_APPROX_SIMPLE 压缩轮廓点以节省内存。

遍历每个轮廓 c:

cv2.arcLength(c, True) 计算轮廓的周长(闭合)。如果周长 大于 188,则认为这个目标足够大(过滤掉小噪点),然后:

cv2.boundingRect© 获取外接矩形的左上角坐标 (x,y) 和宽高 (w,h)。

cv2.rectangle 在原图 fream 上绘制红色矩形框,线宽 2。

显示结果与退出控制

c 复制代码
    cv2.imshow('fgmask_new_rect', fgmask_new_rect)
    k = cv2.waitKey(60)
    if k == 27:
        break

显示绘制了矩形框的帧。

运行结果:

释放资源

c 复制代码
cap.release()
cv2.destroyAllWindows()

释放视频捕获资源,关闭所有 OpenCV 窗口。

调优建议

关键参数调优:

周长阈值 188:需要根据视频中目标的大小调整。如果目标较小,可降低阈值;如果背景噪声多,可提高阈值。

内核尺寸:3×3 适合小目标,若目标大或噪声多,可增大内核(如 5×5)增强去噪效果。

形态学操作:开运算适合去除噪声,如果前景内部空洞多,可改用闭运算(先膨胀后腐蚀)填充空洞。

等待时间 waitKey:数值越大,播放越慢;若设为 1,则播放尽可能快。

如果视频没有运动物体,前景掩码将是全黑,矩形框也不会出现。