基于Python+OpenCV的交通车辆计数器:如何实现车辆唯一计数?
解决OpenCV车辆重复计数问题:实现唯一车辆统计
你目前的代码只是逐帧检测车辆并统计数量,自然会把同一辆车在连续帧里的检测结果当成多辆不同的车重复计数。要实现每辆车仅统计一次,核心是要把目标检测和目标跟踪结合起来——先检测车辆,再跟踪每辆车的运动轨迹,只有当车辆首次被确认(或者穿过你预设的计数线)时,才计入总数。
下面给你几种可行的实现思路,以及结合你现有代码的修改示例:
一、入门方案:基于IOU的简单跟踪
这是最容易上手的方式,核心逻辑是:
- 维护一个跟踪列表,存储每辆被跟踪车辆的ID和最新的 bounding box
- 对当前帧检测到的每个车辆框,和跟踪列表里的所有框计算交并比(IOU)
- 如果IOU超过阈值(比如0.5),就认为是同一辆车,更新跟踪列表里的框位置
- 如果没有匹配到任何跟踪框,就把它当作新车辆,分配唯一ID并加入跟踪列表
- 可以设置一条计数线(比如画面下方的一条水平线),当车辆从线的一侧穿过到另一侧时,才增加计数(比首次检测就计数更准确,避免误检)
修改后的代码示例
import cv2 import numpy as np def calculate_iou(box1, box2): # 计算两个bbox的IOU x1, y1, w1, h1 = box1 x2, y2, w2, h2 = box2 # 计算交集坐标 inter_x1 = max(x1, x2) inter_y1 = max(y1, y2) inter_x2 = min(x1 + w1, x2 + w2) inter_y2 = min(y1 + h1, y2 + h2) # 计算交集面积 inter_area = max(0, inter_x2 - inter_x1) * max(0, inter_y2 - inter_y1) # 计算并集面积 box1_area = w1 * h1 box2_area = w2 * h2 union_area = box1_area + box2_area - inter_area # 计算IOU return inter_area / union_area if union_area > 0 else 0 print(cv2.__version__) cascade_src = 'cars.xml' video_src = 'dataset/video2.avi' cap = cv2.VideoCapture(video_src) car_cascade = cv2.CascadeClassifier(cascade_src) # 跟踪相关变量 tracked_cars = [] # 存储格式: [(car_id, (x,y,w,h)), ...] next_car_id = 1 total_cars = 0 # 设置计数线(比如画面高度的80%位置) count_line_y = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT) * 0.8) while True: ret, img = cap.read() if type(img) == type(None): break gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) cars = car_cascade.detectMultiScale(gray, 1.1, 1) # 存储当前帧未匹配的检测框 unmatched_detections = list(cars) # 更新现有跟踪车辆的匹配情况 new_tracked_cars = [] for car_id, tracked_box in tracked_cars: matched = False # 遍历未匹配的检测框找最佳匹配 for i, det_box in enumerate(unmatched_detections): iou = calculate_iou(tracked_box, det_box) if iou > 0.5: # 匹配成功,更新跟踪框 new_tracked_cars.append((car_id, det_box)) unmatched_detections.pop(i) matched = True break if not matched: # 没有匹配到,暂时保留(可以加超时逻辑移除消失的车辆) new_tracked_cars.append((car_id, tracked_box)) # 处理未匹配的检测框,当作新车辆 for det_box in unmatched_detections: x, y, w, h = det_box # 判断车辆是否即将穿过计数线(这里假设车辆从下往上或者从上往下,根据你的视频调整) # 比如车辆底部超过计数线时计数 if y + h > count_line_y and y < count_line_y: total_cars += 1 new_tracked_cars.append((next_car_id, det_box)) next_car_id += 1 tracked_cars = new_tracked_cars # 绘制可视化内容 for car_id, (x,y,w,h) in tracked_cars: cv2.rectangle(img, (x,y), (x+w,y+h), (0,0,255), 2) cv2.putText(img, f"ID: {car_id}", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2) # 绘制计数线 cv2.line(img, (0, count_line_y), (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), count_line_y), (0,255,0), 2) cv2.putText(img, f"Total Cars: {total_cars}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,0,0), 2) # 交通状态判断 current_detected = len(cars) if current_detected >=5: print(f"Found {current_detected} car(s) - more traffic") else: print(f"Found {current_detected} car(s) - no traffic") cv2.imshow('video', img) if cv2.waitKey(33) == 27: break cv2.destroyAllWindows()
二、进阶方案:使用成熟的跟踪算法
如果你的场景车辆较多、运动复杂,上面的简单IOU跟踪可能会出现跟踪丢失的情况,推荐使用SORT或DeepSORT算法:
- SORT结合了卡尔曼滤波和IOU匹配,跟踪速度快、效果稳定
- DeepSORT在SORT基础上加入了外观特征匹配,适合遮挡较多的场景
你可以直接使用开源的Python实现,只需要把你的车辆检测结果传入跟踪算法,然后根据跟踪结果计数即可。
三、OpenCV内置跟踪器
OpenCV提供了cv2.TrackerCSRT_create()、cv2.TrackerKCF_create()等跟踪器,你可以在检测到新车辆时初始化一个跟踪器,后续帧用跟踪器获取车辆位置,避免重复检测。不过这种方式在车辆较多时,性能会有所下降,适合小场景使用。
内容的提问来源于stack exchange,提问作者Ramineni Ravi Teja




