环境准备与依赖安装
在开始编写代码之前,必须先配置好Python运行环境。本指南基于Python 3.8或更高版本进行开发,所有操作均在命令行终端中完成,无需复杂的手动配置。
1. 安装核心依赖库
我们需要使用ultralytics库来加载YOLOv8模型,以及opencv-python来处理视频流。请在终端中直接执行以下命令进行安装:
```
pip install ultralytics opencv-python numpy
```
注意:如果你的网络环境访问官方源较慢,请务必使用国内镜像源加速安装,命令如下:
```
pip install ultralytics opencv-python numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
```
2. 准备测试视频素材
为了验证代码效果,你需要一段包含车辆行驶的视频。你可以使用自己的监控视频,或者下载公开的交通数据集。为了方便测试,建议准备一个名为traffic.mp4的视频文件放在代码同级目录下。如果没有视频,代码也支持直接调用电脑摄像头进行实时检测。
核心代码实现
本系统将实现一个虚拟“检测线”,当车辆穿过该线时自动计数。这种方式比单纯的检测框数量更准确,能够有效避免同一辆车被重复计数。我们将利用YOLOv8内置的追踪功能来实现。
1. 完整代码逻辑
新建一个名为car_counter.py的文件,将以下代码完整复制进去。这段代码包含了模型加载、视频读取、目标追踪、穿越判断以及结果可视化。
```
from ultralytics import YOLO
import cv2
def count_vehicles():
加载预训练的YOLOv8n模型,首次运行会自动下载权重文件
model = YOLO('yolov8n.pt')
初始化视频捕获对象,0表示调用摄像头,或者替换为'traffic.mp4'读取本地视频
cap = cv2.VideoCapture(0)
获取视频的宽度和高度,用于设置检测线位置
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
设置检测线的位置(在画面垂直方向的一半处)
line_y = height // 2
初始化计数器和车辆轨迹记录字典
vehicle_count = 0
track_history = {}
定义我们需要检测的车辆类别,对应COCO数据集的ID
2: car, 3: motorcycle, 5: bus, 7: truck
target_classes = [2, 3, 5, 7]
print(f"系统启动中... 检测线位置: Y={line_y}")
print("按 'q' 键退出程序")
while cap.isOpened():
success, frame = cap.read()
if not success:
print("视频读取结束或摄像头断开")
break
使用model.track进行追踪,persist=True保持轨迹ID在帧之间的连续性
results = model.track(frame, persist=True, classes=target_classes, verbose=False)
if results[0].boxes.id is not None:
获取检测框的ID、坐标和类别
boxes = results[0].boxes.xywh.cpu() 中心点x, 中心点y, 宽, 高
track_ids = results[0].boxes.id.int().cpu().tolist()
for box, track_id in zip(boxes, track_ids):
x, y, w, h = box
current_y = float(y)
判断车辆是否穿越检测线
逻辑:如果车辆ID存在于历史记录中,且上一帧在检测线上方,当前帧在检测线下方
if track_id in track_history:
prev_y = track_history[track_id]
从上往下穿过 (prev_y < line_y <= current_y)
if prev_y < line_y and current_y >= line_y:
vehicle_count += 1
print(f"车辆通过! 当前总数: {vehicle_count}")
从下往上穿过 (prev_y > line_y >= current_y),如果需要双向统计请取消注释
elif prev_y > line_y and current_y <= line_y:
vehicle_count += 1
print(f"车辆通过! 当前总数: {vehicle_count}")
更新该ID的Y坐标记录
track_history[track_id] = current_y
在画面上绘制追踪框和ID
cv2.rectangle(frame, (int(x - w / 2), int(y - h / 2)), (int(x + w / 2), int(y + h / 2)), (0, 255, 0), 2)
cv2.putText(frame, f"ID: {track_id}", (int(x - w / 2), int(y - h / 2) - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
绘制红色检测线
cv2.line(frame, (0, line_y), (width, line_y), (0, 0, 255), 2)
显示当前计数
cv2.putText(frame, f"Count: {vehicle_count}", (20, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 3)
显示处理后的画面
cv2.imshow("Vehicle Counting System", frame)
按q退出
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
if __name__ == "__main__":
count_vehicles()
```
2. 代码关键点解析
为了确保你能够理解并根据实际情况修改代码,以下对核心逻辑进行详细拆解:
- 模型选择 (YOLO('yolov8n.pt')):这里使用的是
yolov8n.pt,即Nano版本。它的优点是速度极快,适合CPU实时运行。如果你需要更高的检测精度(例如识别远处的车辆),可以将文件名替换为yolov8s.pt或yolov8m.pt,但运行速度会相应变慢。
- 视频源设置 (cv2.VideoCapture):代码中默认传入参数
0,这会调用你电脑的默认摄像头。如果你想测试本地视频文件,请将0修改为你的视频文件名,例如cv2.VideoCapture('highway_video.mp4')。
- 目标追踪:这是实现准确计数的核心。普通的
model()只能检测,无法区分帧与帧之间的关系。使用model.track()后,每个检测到的物体都会获得一个唯一的track_id。只有当同一个ID的车辆中心点坐标跨越了设定的line_y时,计数器才会+1。
- 类别过滤 (target_classes):我们只关心车辆。代码中定义的
[2, 3, 5, 7]分别对应COCO数据集中的Car(汽车)、Motorcycle(摩托车)、Bus(巴士)和Truck(卡车)。如果你只想统计轿车,可以将其修改为[2]。
运行与验证
代码编写完成后,直接在终端运行脚本即可启动系统。
1. 启动命令

在car_counter.py文件所在目录下打开终端,执行:
```
python car_counter.py
```
2. 观察输出
启动后,屏幕会弹出一个名为“Vehicle Counting System”的窗口。你会看到画面中间有一条红色的横线。当你(或视频中的车辆)从上往下穿过这条红线时,左上角的“Count”数值会增加,同时终端控制台会打印出“车辆通过!”的日志。
常见问题处理与优化
在实际落地过程中,可能会遇到以下具体问题,请参考对应的解决方案进行调整。
1. 检测线位置调整
默认检测线在画面正中间(height // 2)。如果你的摄像头架设位置较高,或者车辆主要在画面下方行驶,可以手动修改line_y的值。例如,设置为画面高度的3/4处:
```
line_y = int(height 0.75)
```
2. 摄像头帧率低导致丢车
如果使用的是USB摄像头且帧率较低(低于15FPS),车辆在两帧之间移动距离过大,可能会直接跳过检测线,导致漏计。解决方法是在代码中增加“缓冲区”逻辑,或者改用更粗的“检测带”代替“检测线”。简单的优化是将判断逻辑从==改为范围判断:
```
定义检测带区域
line_y_top = height // 2 - 10
line_y_bottom = height // 2 + 10
修改判断逻辑
if prev_y < line_y_top and current_y >= line_y_bottom:
vehicle_count += 1
```
3. 逆行车辆误计
默认代码只统计“从上往下”穿的车辆。如果是双向车道,且你需要统计双向流量,请取消代码中“从下往上穿过”部分的注释。如果只想统计单向,请确保检测线方向与车流方向垂直,并检查坐标判断逻辑是否符合你的实际车流方向(例如车流是从左往右,则需要判断x坐标的变化)。
4. 保存统计结果
当前的计数只在程序运行时存在于内存中。如果你需要将结果保存到日志文件,可以在vehicle_count += 1下方添加文件写入代码:
```
with open("traffic_log.txt", "a") as f:
f.write(f"Time: {timestamp}, Count: {vehicle_count}\n")
```
通过以上步骤,你已经拥有了一个完全可用的汽车流量统计系统。该方案成本低廉,只需一个普通的USB摄像头或现成的视频文件即可运行,且代码结构清晰,便于后续集成到更大的监控平台中。