OpenCV 车辆数量统计

涉及到的知识点:

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()
相关推荐
人工智能AI技术40 分钟前
LangGraph 本地 build 频频翻车,最终在服务器跑通流式对话
人工智能
Flynn Liang1 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
汤姆宝库1 小时前
构建一个能用的RAG系统:切块、嵌入、混合检索与融合排序
人工智能
Dawson Zhu1 小时前
【LLM推理与Serving⑪】MoE推理与MLA:DeepSeek-V3架构下的显存与通信权衡
人工智能·语言模型·架构·aigc·agi
github_is_geeksKai2 小时前
50 个 AI 与开源工具怎么选:从实际用途到实现边界
人工智能
IT_陈寒2 小时前
Vite的HMR把我整不会了:一个生产环境的热更新坑
前端·人工智能·后端
github_is_geeksKai2 小时前
浏览器 AI 小工具怎么选?40 个项目与 5 类商业任务的参考指南
人工智能
styshoo2 小时前
[snapshot]7-placeholder与恢复流程
人工智能
这个DBA有点耶2 小时前
库内计算推理:一条SQL背后的算子融合,和外挂方案差了几次网络往返
数据库·人工智能·sql