简介:面向视频监控、智能交通、工业自动化等场景开发者,资源提供了一套基于YOLOv8的RTSP实时视频流目标检测方案,覆盖视频流接入、模型推理、结果可视化等环节,适合具备一定深度学习基础、希望快速落地应用的工程人员。包体共467个文件,压缩包约169.25MB,文件类型以Python源码(145个py)、编译文件(215个pyc)为主,并含80个yaml模型配置、6个pt预训练权重、部署脚本、测试图片,以及基于Vue和Element UI的Web演示界面,便于直接运行与二次开发。资源已有220人次学习下载。借助README说明、带标注的测试样张和摄像头演示视频,可直观理解YOLOv8在RTSP流上的工作方式,也能根据自身摄像头地址与识别目标调整配置和参数,显著节省从零搭建环境的时间。 做视觉落地的同学应该都有过这种经历:模型在本地图片测试集上跑得好好的,一接到“把检测接到摄像头实时流上”的需求就各种翻车。我去年接到一个项目,要把工位附近的海康摄像头拉RTSP流,实时检测人员和车辆,最后还要部署到边缘盒子。折腾了半个多月,把拉流、解码、推理、部署这条链路里能踩的坑基本都踩了一遍。这篇文章就把整个思路和实操记录下来,让后来的人少走弯路。
这个项目用到的核心组合就是YOLOv8和RTSP。前者是目前工业界应用最广泛的检测模型之一,后者是网络摄像头的标准传输协议,两者搭配基本覆盖了安防、交通、园区管理这类常见的实时检测场景。无论你是刚入门想做个小demo,还是已经在做嵌入式部署,这篇内容都会对你有帮助。我会从技术选型、拉流解码、数据训练、推理代码组织,一直到边缘设备部署,把每个环节的具体做法和容易出错的地方讲清楚。
1. 技术选型不是拍脑袋:为什么是YOLOv8和RTSP
1.1 从YOLOv5到YOLOv8,到底改了什么
很多老项目还在用YOLOv5,不是说不能用,而是YOLOv8作为Ultralytics推出的迭代版本,在结构上确实做了几处让我觉得“值得迁移”的改动。最明显的是它把检测头换成了anchor-free,不再需要预设anchor box,省去了聚类计算anchor尺寸的步骤,训练配置更简单。同时主干网络里的C2f模块替换了原来的C3模块,通过更丰富的梯度流来提升特征提取能力,在同等算力下精度普遍好一点。模型的Decoupled Head把分类和回归分支分开,收敛速度更快,对小目标也友好一些。
这些改动带来的实际收益是:同样的数据集,YOLOv8s往往比YOLOv5s的mAP高1到3个点,推理速度不会有明显下降。对于RTSP实时流这种要求低延迟的场景,我优先考虑的是模型的吞吐量,而YOLOv8的n/s/m系列提供了不错的精度-速度平衡。
1.2 RTSP协议在实时检测里的定位
RTSP全称Real Time Streaming Protocol,是网络摄像机IPC领域的事实标准。海康、大华、小米等品牌摄像头都支持通过RTSP地址输出视频流。它可以基于TCP或UDP传输,默认端口554,地址格式大致是:
rtsp://用户名:密码@IP地址:554/Streaming/Channels/101不同品牌的路径不同。海康的101代表主码流第一通道,102代表子码流第一通道;小米一般类似rtsp://user:pass@ip:8554/live;大华则是/cam/realmonitor?channel=1&subtype=0。
做目标检测时,一般建议拉主码流拿清晰度,但如果硬件性能紧张,可以拉子码流,分辨率低一档,检测速度会快不少。我实测过一个500万像素的海康摄像头,主码流25帧1080p,拉流加解码就已经占了单核CPU大量资源,所以后面一定要做硬解码或降低输入分辨率。
1.3 先想清楚检测对象和硬件,再选模型尺寸
很多人一上来就问“预测代码怎么写”,其实第一步应该反问自己:检测对象是什么?部署在什么硬件上?
如果是接GPU服务器做园区车辆检测,那直接上YOLOv8x都可以;如果是部署到Jetson Orin Nano或者RK3588这类边缘设备,YOLOv8n/s加上TensorRT或RKNN加速可能是唯一可行方案。我的原则是:先定硬件,再定模型。硬件决定了你能跑多大的模型,模型再决定能达到多少精度。GTX 1660Ti这种6GB显存的卡,跑YOLOv8s的实时视频流完全没有问题,也就是几百FPS的推理能力,瓶颈反而不在GPU,而在解码和传输。
2. 拉流解码:被很多人低估的第一道坎
2.1 OpenCV直接读流,为什么延迟高
最直观的做法是cv2.VideoCapture("rtsp://..."),然后循环读取。这个方案在小项目里确实能跑通,但实际使用中会遇到两个让人头疼的问题:延迟越拉越大,以及断流后不会自动重连。
延迟大的原因在于OpenCV在拉RTSP流时内部会把多帧缓冲到队列里,解码速度跟不上采集速度时,队列积压,你看到的画面就会越来越“慢”。网上有人教你把CAP_PROP_BUFFERSIZE设置为1,但在许多平台这个参数对RTSP根本不生效。我这里给出一个从实践中来的方案:使用FFmpeg的-fflags nobuffer参数,或者直接用FFmpeg把RTSP转成原始帧再送进OpenCV,延迟能明显降低。
ffmpeg -rtsp_transport tcp -fflags nobuffer \ -i "rtsp://user:pass@ip:554/Streaming/Channels/101" \ -f rawvideo -pix_fmt bgr24 -s 1280x720 - \然后用管道读取,或者直接用FFmpeg的Python绑定(比如ffmpeg-python)。如果你用的是NVIDIA显卡,可以加-hwaccel cuda做硬解码,CPU占用能降一大截。
2.2 TCP还是UDP,断流重连的姿势
RTSP默认传输协议在不同客户端里可能不太一样,OpenCV默认用UDP,但UDP在弱网环境下丢包严重,画面容易出现马赛克和绿屏。做检测时我都是强制切到TCP:
cv2.VideoCapture("rtsp://user:pass@ip:554/Streaming/Channels/101?tcp")或者用FFmpeg时加-rtsp_transport tcp。TCP消耗带宽多一点,但网络稳定,画质可靠。
断流重连是另一个不得不处理的问题。摄像头重启、网络抖动,都可能让拉流线程挂掉。我的处理方法是:开一个专门线程循环读取,每次read()返回False时,主动释放cap对象,sleep 1秒后重新创建。这个逻辑看起来简单,但很多项目没有写重连机制,导致挂在墙上的监控画面黑屏之后,程序里还在空转。
2.3 不要忽略分辨率与像素格式
RTSP流的分辨率、帧率、编码格式都影响检测效果。我经常看到有人把2K主码流直接喂给YOLOv8,推理速度慢不说,小目标也没变清晰多少。最合理的做法是把输入分辨率统一到640x640或者1280x640,通过解码端缩放尽量保持宽高比,而不是直接resize到正方形导致形变。
在OpenCV任务流里,CAP_PROP_FRAME_WIDTH和CAP_PROP_FRAME_HEIGHT在读取RTSP时设置不一定生效,更靠谱的做法是用FFmpeg的-s参数控制输出尺寸。像素格式这边,大部分摄像头输出H.264,解码后是YUV,OpenCV读取时已经转成BGR,后续不需要再额外转。
3. 模型准备:从随便用用到针对你的场景训练
3.1 先跑通预训练模型再谈自定义
接触一个新项目时,我会先用Ultralytics提供的COCO预训练权重把RTSP流跑通,验证整个管道正常,再考虑要不要自己训练。这一步能帮你把问题隔离:如果预训练模型检测框正常,说明拉流解码没问题;如果框乱跳,那就要检查解码帧率和输入尺寸。
pip install ultralytics yolo predict model=yolov8s.pt source='rtsp://user:pass@ip:554/Streaming/Channels/101'这条命令可以直接跑起一个检测窗口。预览模式下能看到效果,如果要在代码里集成再写Python调用。先跑这个demo,能帮你快速判断摄像头取流是否OK,避免一上来就陷入代码调试。
3.2 标注自己的数据集,yaml文件配置最容易错
业务场景里我们常常只关心人、车、安全帽之类,这时候COCO的80类就不够用了,需要自己准备数据集。如果数据量不大,用LabelImg或X-AnyLabeling标注成YOLO格式:每张图对应一个txt文件,每一行是class_id cx cy w h,坐标值归一化到0~1。
训练前要写一个dataset.yaml,我最常犯的错是路径写成了绝对路径,换机器就要改;更好的做法是相对路径加path:字段指向数据集根目录。一个标准示例:
path: ./datasets/my_dataset train: images/train val: images/val names: 0: person 1: car 2: helmet尤其需要注意类别ID必须从0开始连续,不能跳过。如果之前标注时类别从1开始,训练时会直接报错或类别错乱。
3.3 训练命令与两个必盯的指标
训练命令很简单:
yolo train data=dataset.yaml model=yolov8s.yaml epochs=100 batch=16 imgsz=640但真正重要的不是命令,而是训练过程中要盯的两类指标:loss曲线的收敛情况和验证集上的mAP50、mAP50-95。
很多人只看mAP涨没涨,忽略了过拟合信号。如果训练loss一直下降、验证loss却开始上升,那就是过拟合了,可以加早停或者加大数据增强。我习惯用TensorBoard或Ultralytics自动生成的results.png看四条曲线:box_loss、cls_loss、dfl_loss以及验证集mAP。前面三个都在稳步下降,说明训练正常;如果后面某条曲线在某个epoch开始抖动上升,就要考虑减小学习率。
3.4 YOLOv8数据增强参数别拉满
Ultralytics默认开启的增强包括马赛克、随机透视、翻转、颜色抖动等。马赛克增强在训练初期很有用,但训到最后阶段再继续用会干扰模型学习,所以官方会在最后10个epoch自动关闭Mosaic。实际项目中,如果数据集本身是小样本(几百张),增强拉满容易学不到真实分布,我一般只开hsv_h=0.015、fliplr=0.5这类轻度增强,默认值和上述类似。
我也踩过“过分增强导致检测框偏移”的坑:开启了大量旋转和透视后,安全帽这类小目标在增强后的图片里可能已经严重变形,模型学到的是变形的特征,真实场景里反而性能下降。所以我的经验是:小目标场景少用旋转增强,多补数据。
4. 核心推理代码怎么组织才不像玩具项目
4.1 单路RTSP检测的骨架
当拉流、模型都就绪后,可以用一个相对简洁的类来组织代码。下面是一个基础的推理线程骨架,核心思想是采集和推理解耦。
import cv2 import time import threading from collections import deque from ultralytics import YOLO class RTSPDetector: def __init__(self, rtsp_url, model_path, use_tcp=True): self.rtsp_url = rtsp_url + ('?tcp' if use_tcp else '') self.model = YOLO(model_path) self.frame_queue = deque(maxlen=8) self.running = True def capture_loop(self): cap = cv2.VideoCapture(self.rtsp_url) while self.running: ret, frame = cap.read() if not ret: cap.release() time.sleep(1) cap = cv2.VideoCapture(self.rtsp_url) continue if len(self.frame_queue) < self.frame_queue.maxlen: self.frame_queue.append(frame) cap.release() def detect_loop(self): while self.running: if not self.frame_queue: time.sleep(0.001) continue frame = self.frame_queue.popleft() results = self.model.predict(frame, imgsz=640, conf=0.35, verbose=False) annotated = results[0].plot() cv2.imshow('detection', annotated) if cv2.waitKey(1) & 0xFF == ord('q'): self.running = False def start(self): threading.Thread(target=self.capture_loop, daemon=True).start() threading.Thread(target=self.detect_loop, daemon=True).start()这段代码里我用了一个有界队列deque(maxlen=8),控制内存的同时自然形成滑动窗口。采集线程只负责把最新帧塞进队列,推理线程只负责从队列取帧。如果推理慢,队列会被填满,采集线程会跳过中间帧,保证实时性而不是无限积压。
4.2 多线程:视频采集和推理必须分离
很多人把cap.read()和model.predict()写在一个循环里面,这是延迟的主要来源之一。因为read()是阻塞式的,底层在等待网络数据;predict()也是阻塞式的,GPU跑一次推理要十几毫秒到几十毫秒。两者串行,一卡都卡。
分离之后,采集线程能稳定保持25到30帧的读取,即使推理只能跑到10FPS,我们看到的画面依然是接近实时的,只是检测框更新频率低一些,不会出现越拖越慢的卡顿感。这个方法在CPU和GPU上都适用,强烈建议所有RTSP实时检测项目都采用“生产者-消费者”模式。
4.3 如果有多路视频流,怎么压榨GPU
做安防项目往往不会只有一路摄像头。如果同时处理4路RTSP,我的做法是先用FFmpeg把所有解码帧收集起来,凑成一个batch喂给YOLOv8。YOLOv8的predict支持传入batch,实际上model.predict([frame1, frame2, frame3, frame4])会自动做batch推理。这样可以明显提升GPU利用率。但要注意在不同摄像头分辨率不一致时,预先将它们统一尺寸,否则batch会报错。
5. 部署到边缘设备:GTX1660Ti / Jetson / RK3588
5.1 先搞清楚你的GPU能不能跑
在PC上用GTX1660Ti跑YOLOv8s视频流基本没压力,实测1080p输入、640推理尺寸下,FP16精度能跑到50到70FPS。瓶颈不再是推理,而是解码和显示。所以我给所有准备上线的项目都加一句:先跑一次nvidia-smi看看显存占用和GPU利用率,不要一上来就怀疑模型太慢。
如果是Jetson Orin Nano或者RK3588,事情就不一样。Jetson需要把YOLOv8导出为TensorRT的engine文件,RK3588需要导出为RKNN格式。它们都不适合直接跑PyTorch模型。Ultralytics提供了一键导出命令:
yolo export model=yolov8s.pt format=engine device=0 half=TrueRK3588的话,通常需要先转ONNX,再用RKNN-Toolkit2做量化转换,量化精度需要拿几百张真实场景图做校准集。
5.2 TensorRT部署:一次转换,处处踩坑
TensorRT导出成功后,你在新设备上重新推理时不要再走PyTorch路径。加载engine文件的代码和普通模型加载不一样,通常通过tensorrt库或者Ultralytics的YOLO('model.engine')。注意TensorRT是绑定硬件和精度的,比如你在RTX 3060上导出的engine,放到GTX 1660Ti上就加载不了,必须在目标设备上重新导出。
边缘设备上我还建议优先使用FP16推理。INT8虽然更快,但量化后可能掉点明显,尤其是小目标。我的实测是,YOLOv8s在RK3588上FP16(实际上是RKNN的混合量化)能跑到20到30FPS,INT8能到50FPS,但小目标mAP下降2到3个点。具体怎么选,看业务对召回率的要求。
5.3 部署包体积和工程化
边缘设备上的运行环境通常精简,不要把整个ultralytics包都打进去。更稳的做法是用ONNX Runtime加载导出的ONNX模型,自己写前处理和NMS。这样包体积小、依赖少,还避免版本冲突。YOLOv8的ONNX输出通常是一个(1, 84, 8400)的张量,前4维是cx, cy, w, h,后面80维是每个类别的score。解析这个输出,再做非极大值抑制,整个推理代码只需要不到200行。
6. 实测中绕不开的坑与优化方向
6.1 小目标检测:为什么远处的人总是会丢
在园区场景里,摄像头往往架得很高,远处的人可能只占几十个像素。YOLOv8虽然有P5输出层,但对微小目标依然吃力。我第一次部署到现场,发现画面里2米外的人几乎检不到,回来后做了一系列改进:
- 把输入分辨率从640提到960甚至1280,虽然推理慢一些,但小目标特征更清晰。
- 在模型配置里增加P2检测头,也就是所谓的“小目标检测头”,特殊场景下可以提高小目标的召回率。
- 增大训练集中小目标样本的比例,利用sahi切片推理库对大图做切片检测,但切片推理不适合实时流,只适合离线分析。
在实时视频流中,如果小目标漏检严重,我建议优先调大输入尺寸,而不是换复杂模型。YOLOv8x的n/s/m系列在不同分辨率下的表现我整理过一个粗略参考见下表,具体数值因设备而异。
| 模型 | 输入尺寸 | 950像素宽画面中的小目标检测率(定性) | 推理耗时 |
|---|---|---|---|
| YOLOv8n | 640 | 低 | 最省 |
| YOLOv8s | 640 | 中低 | 省 |
| YOLOv8s | 1280 | 中高 | 中等 |
| YOLOv8m | 1280 | 高 | 较重 |
6.2 运动物体经过摄像头只识别一次?别慌,这是缺跟踪
热词里有一个问题很典型:“运动的物体经过摄像头只识别一次YOLOv8 seg”。如果只做逐帧检测,物体经过视野的过程中每帧都应该是检测到的,出现只识别一次,大概率是置信度阈值设太高,或者关键帧被队列丢弃。逐帧检测本来就不稳定,需要给检测框加一个简单的跟踪器(如ByteTrack),把短时间的漏检帧自动补上,让同一个ID持续存在。
我在很多项目里都把YOLOv8和ByteTrack搭配使用。检测器只负责每一帧输出框,跟踪器负责给框分配ID并维持轨迹。这样物体被遮挡一两帧后,轨迹不会立刻丢,满足“只识别一次”的需求,本质上是要“只识别一个ID”,而不是每帧都打新框。
6.3 用公开RTSP测试流验证延时和FPS
没有摄像头时,可以用公开的RTSP测试流来验证代码。网上有很多公开的RTSP地址,比如一些演示流媒体服务器提供的h264测试源。搜索“public rtsp test stream”可以找到,但这类地址容易失效,稳定性没法保证。更推荐的方式是本地用FFmpeg推一路测试流:
ffmpeg -re -i local_video.mp4 -c copy -f rtsp -rtsp_transport tcp rtsp://192.168.1.100:8554/test这样既能测试代码,又能完全掌控推流端的状态。测延迟也方便,把拍摄到的时钟画面和当前系统时间对比,基本能算出真实延迟。
这里说一个我的经验:视频检测项目里95%的问题都出在视频链路上,而不是模型。模型输出一个框很简单,怎么稳定、低延迟、不丢帧地把视频画面送进模型、再把结果送到业务方,才是真正的工程题。
对整个链路做稳定性测试时,我一般跑四十八小时的长时间压测,重点观察内存是否缓慢上涨、队列长度是否持续堆积、断流重连是否生效。如果这三项都通过,项目基本就能上线。最后分享一个很实用的调试技巧:在部署现场随身带一台能显示系统进程和GPU利用率的电脑,一旦画面卡顿,先看CPU解码线程是不是已经满了。大多数卡顿都不是模型慢,而是解码线程卡死,拖累了整个生产者消费者链路。
本文还有配套的精品资源,点击获取