涉及到的知识点:
1)基本图像运算与处理
2)形态学
3)轮廓查找
4)加载视频
5)通过形态学识别车辆
6)对车辆进行统计
7)显示车辆统计信息
1. 视频加载
cv2.VideoCapture(filename) 用来打开视频文件或摄像头。参数 filename 填视频路径,比如 'car.mp4';如果填 0、1,就表示打开摄像头。1013
cap.read() 用来逐帧读取视频。它返回两个值:ret 和 frame。ret 是布尔值,True 表示成功读到一帧,False 表示视频结束或读取失败;frame 是当前帧图像,通常是 BGR 三通道图像。1013
cap.release() 用来释放视频或摄像头资源,避免程序结束后继续占用设备。25
2. 去背景
cv2.createBackgroundSubtractorMOG2(history, varThreshold, detectShadows) 用来创建背景减除器。history 表示历史帧数,默认 500,值越大背景更新越慢;varThreshold 是方差阈值,默认 16,值越大越不敏感;detectShadows 控制是否检测阴影,默认 True,不需要可以设为 False。38
bgsubmog.apply(image, learningRate) 用来对当前帧做背景减除。image 是当前帧图像;learningRate 是背景学习率,-1 表示自动更新,0 表示不更新。返回值 mask 是前景掩码:255 表示前景,0 表示背景,127 表示阴影。3
注意你原来的 cv2.bgsegm.createBackgroundSubtractorMOG() 已经不建议使用,改成 cv2.createBackgroundSubtractorMOG2() 更稳定。14
3. 形态学处理
cv2.getStructuringElement(cv2.MORPH_RECT, (5,5)) 生成一个矩形结构核。
cv2.erode(mask, kernel) 做腐蚀,缩小白色区域,用来去除小噪点。
cv2.dilate(erode, kernel, iterations=3) 做膨胀,放大白色区域,恢复目标形状。cv2.morphologyEx(dilate, cv2.MORPH_CLOSE, kernel) 做闭运算,先膨胀后腐蚀,用来填充前景内部的小黑洞,让车辆区域更完整。
4. 逻辑处理
cv2.findContours(image, mode, method) 用来查找轮廓。
image 必须是 8 位单通道二值图;mode 是轮廓检索模式,cv2.RETR_EXTERNAL 只检测最外层轮廓,cv2.RETR_LIST 检测所有轮廓但不建立层级关系,cv2.RETR_TREE 检测所有轮廓并建立完整层级关系;method 是轮廓近似方法,cv2.CHAIN_APPROX_NONE 存储轮廓上所有点,cv2.CHAIN_APPROX_SIMPLE 只存储关键角点,压缩数据。
OpenCV 4.x 中它返回两个值:contours, hierarchy。
cv2.boundingRect(contour) 用来获取轮廓的外接矩形,返回 x, y, w, h,分别是左上角坐标和宽高。然后用 cx = x + int(w/2)、cy = y + int(h/2) 计算车辆中心点。
计数判断要写成:if (cy > line_h - offset) and (cy < line_h + offset):,表示车辆中心点进入检测线附近范围时计数加 1。不要在遍历 cars 列表时直接 remove,容易漏判,建议每帧处理完后用 cars.clear() 清空。
5. 显示信息
cv2.line(frame, pt1, pt2, color, thickness) 画检测线。
cv2.rectangle(frame, (x,y), (x+w,y+h), (0,0,255), 2) 给车辆画红色边框。
cv2.putText(frame, text, org, fontFace, fontScale, color, thickness) 在画面上显示车辆数量。
cv2.imshow('video', frame) 显示窗口,
cv2.waitKey(30) 控制每帧间隔 30 毫秒,按 Esc 退出。
cv2.destroyAllWindows() 关闭窗口。
代码示例:
python
import cv2
import numpy as np
# 车辆最小宽度,低于此值的轮廓视为无效
min_w = 90
# 车辆最小高度,低于此值的轮廓视为无效
min_h = 90
# 检测线的高度,用于判断车辆是否穿过
line_h = 600
# 存放检测到的车辆中心点
cars = []
# 检测线的偏移量,用于判断车辆是否靠近检测线
offset = 3
# 统计车辆数量
carno = 0
def center(x, y, w, h):
"""计算外接矩形的中心点坐标"""
x1 = int(w / 2)
y1 = int(h / 2)
cx = x + x1
cy = y + y1
return cx, cy
# 打开视频文件
cap = cv2.VideoCapture('car.mp4')
# 创建背景减除器
bgsubmog = cv2.bgsegm.createBackgroundSubtractorMOG()
# 创建形态学操作的结构元素
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
while True:
# 逐帧读取视频
ret, frame = cap.read()
if ret == True:
# 灰度转换,注意这里需要接收返回值
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 高斯去噪
blur = cv2.GaussianBlur(frame, (3, 3), 5)
# 背景减除,提取前景
mask = bgsubmog.apply(frame)
# 腐蚀,去除图像中的小斑块
erode = cv2.erode(mask, kernel)
# 膨胀,放大并还原目标区域
dilate = cv2.dilate(erode, kernel, iterations=3)
# 闭操作,去除物体内部的小块
close = cv2.morphologyEx(dilate, cv2.MORPH_CLOSE, kernel)
close = cv2.morphologyEx(close, cv2.MORPH_CLOSE, kernel)
# 查找轮廓
cnts, h = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 画检测线
cv2.line(frame, (10, line_h), (1200, line_h), (255, 0, 0), 3)
for (i, c) in enumerate(cnts):
# 获取轮廓的外接矩形
(x, y, w, h) = cv2.boundingRect(c)
# 判断车辆宽高是否有效
isValid = (w >= min_w) and (h >= min_h)
if not isValid:
continue
# 绘制有效车辆的外接矩形
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)
# 计算车辆中心点
cpoint = center(x, y, w, h)
cars.append(cpoint)
for (cx, cy) in cars:
# 判断车辆是否穿过检测线
if (y > line_h - offset) and (y < line_h + offset):
carno += 1
cars.remove((cx, cy))
print(carno)
# 显示车辆统计信息
cv2.putText(frame, "Cars Count:" + str(carno), (500, 60), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 2, (255, 0, 0), 5)
# 显示视频画面
cv2.imshow('video', frame)
# cv2.imshow('erode', close)
# 等待按键,按 Esc 退出
key = cv2.waitKey(10)
if key == 27:
break
# 释放视频资源
cap.release()
# 关闭所有窗口
cv2.destroyAllWindows()