
简介这份资源面向具备一定 Python 基础、希望将深度学习目标检测落地到实时视频流的开发者与学习者核心解决如何通过 RTSP 协议拉取网络摄像头视频流并借助 YOLO、OpenCV 与 Python 完成实时对象检测的问题。包内共 14 个文件涵盖 py 主程序脚本、cfg 与 txt 模型配置及类别文件、xml 工程配置、png 效果图、mp4 示例视频以及 md 说明与 license 等压缩包约 56.34MB结构紧凑便于直接运行与二次修改。资源利用 OpenCV dnn 模块加载 YOLO/DarkNet 预训练模型进行推理并将识别出的对象按日期分类存入对应类别文件夹方便后续用于再训练或人脸识别等场景。目前已有 6514 人学习下载适合想快速搭建 RTSP 实时检测流水线、理解模型配置与结果归档思路的读者参考。1. 从一路 RTSP 流到分类归档这套 yolo-python-rtsp 到底能干什么手里有一台支持 RTSP 的摄像头想让它自己认出画面里的人、车、背包还要把识别结果按日期、按类别存成文件夹方便后面拿去做二次训练或者人脸比对——这套yolo-python-rtsp-master就是干这个的。它把 YOLOv3 的 DarkNet 权重塞进 OpenCV 的 dnn 模块用 Python 拉 RTSP 流逐帧推理检测框画出来同时把命中的目标裁剪下来按日期/类别的目录结构落盘。整包不大核心就一个yolo_opencv.py加上cfg目录里的yolov3.txt、yolov3.cfg、yolov3-tiny.cfg三份配置sampledata里还塞了一段commuters.mp4当离线测试素材。依赖只有numpy、opencv-python、imageio-ffmpeg三个Python 2.x 直接不支持。适合谁做安防预览、工地/园区计数、零售客流统计这类需要「先跑起来看效果」的从业者也适合拿它当 OpenCV dnn 跑 YOLO 的最小可运行样板。下面按「先立住原理、再动手复现、最后避坑」的顺序拆开讲。2. 拆开压缩包文件结构、依赖与 RTSP 拉流原理2.1 目录里每个文件是干嘛的解压yolo-python-rtsp-master.zip之后先别急着python yolo_opencv.py把结构看清楚能省掉后面一半的报错。整包大致长这样路径作用是否要改yolo_opencv.py主程序拉流、推理、画框、存图要改 RTSP 地址和输出路径cfg/yolov3.txt80 类 COCO 类别名一行一个一般不动cfg/yolov3.cfgYOLOv3 网络结构定义不动cfg/yolov3-tiny.cfg轻量版网络结构速度快精度低按算力切换sampledata/commuters.mp4离线测试视频可替换object-detection.pngREADME 里的效果图不动README.md/LICENSE说明和许可不动.idea/、yolo-python.imlPyCharm 工程文件可删注意一点压缩包里没有.weights权重文件。YOLO 的推理必须同时有.cfg结构和.weights权重缺一不可。权重得自己去 DarkNet 官方渠道拿yolov3.weights或yolov3-tiny.weights放到项目根目录路径要和脚本里写的一致。这是新手第一个翻车点后面避坑章还会展开。2.2 为什么用 OpenCV dnn 而不是装 DarkNet对象检测这块主流框架就三个YOLO、SSD、Faster R-CNN。SSD 对小目标弱Faster R-CNN 精度高但两阶段推理慢YOLO 单阶段、速度快最适合 RTSP 这种要连续处理的流。传统玩法是编译 DarkNet 源码再调但那样得配 CUDA、cuDNN、OpenCV 编译链环境成本高。OpenCV 从 3.4.2 之后在 dnn 模块里加了 YOLO/DarkNet 支持可以直接readNetFromDarknet加载.cfg.weights推理走blobFromImageforward。好处是不用装 DarkNet不用编译pip install opencv-python就能跑坏处是默认 CPU 推理帧率取决于机器。常见做法是先用 CPU 把流程跑通确认检测逻辑没问题再考虑换opencv-python的 CUDA 版本或者上 V100 这类卡做加速。2.3 RTSP 拉流在代码里是怎么落地的RTSP 是实时流协议摄像头把 H.264/H.265 编码的流推出来客户端用rtsp://user:passip:554/...这种地址去拉。OpenCV 的VideoCapture底层走 FFmpeg能直接吃 RTSP 地址所以拉流这一步在代码里就是一行cv2.VideoCapture(rtsp_url)。但 RTSP 是长连接网络抖动、摄像头重启都会让连接断掉裸写while True: ret, frame cap.read()一旦ret变False就会疯狂空转。所以合格的写法必须带重连和缓冲控制这部分在第 3 章给完整代码。2.4 环境准备三个依赖装完就能跑依赖就三个Python 3.6 以上# 建议先建虚拟环境避免污染系统 Python python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 三个依赖一次装齐 pip install numpy opencv-python imageio-ffmpegnumpy负责数组运算opencv-python提供 dnn 和 VideoCaptureimageio-ffmpeg是给某些环境下 OpenCV 找不到 FFmpeg 后端时兜底的。装完用下面这段验证 dnn 模块和 FFmpeg 后端都在import cv2 import numpy as np print(OpenCV:, cv2.__version__) # 确认 dnn 模块可用 print(dnn available:, hasattr(cv2, dnn)) # 确认能构建 FFmpeg 后端RTSP 拉流依赖它 print(FFmpeg backend:, cv2.videoio_registry.getBackendName(cv2.CAP_FFMPEG))如果dnn available是False说明装的是精简版 OpenCV换opencv-python完整包如果 FFmpeg 后端报错多半是imageio-ffmpeg没装好或者系统缺动态库。这一步过了再往下走。3. 把 RTSP 流接进 YOLO推理主循环与分类落盘3.1 加载网络与类别三份配置怎么配合YOLO 推理需要三样东西对齐网络结构.cfg、权重.weights、类别名.txt。yolov3.txt里 80 行对应 COCO 的 80 类顺序必须和权重训练时一致否则会出现「框对了但标签全错」的玄学现象。加载逻辑如下import cv2 import numpy as np # 类别名一行一个读成列表 with open(cfg/yolov3.txt, r) as f: classes [line.strip() for line in f.readlines()] # 网络结构 权重两者必须配套 net cv2.dnn.readNet(yolov3.weights, cfg/yolov3.cfg) # 取输出层名字YOLOv3 有三个 yolo 输出层 layer_names net.getLayerNames() output_layers [layer_names[i - 1] for i in net.getUnconnectedOutLayers()]getUnconnectedOutLayers()返回的是输出层索引OpenCV 里索引从 0 开始但getLayerNames()返回的列表要减 1 对齐这是 OpenCV 的老约定写错会直接IndexError。output_layers拿到的是三个 YOLO 检测层的名字forward时要把它们传进去否则只拿到部分尺度的检测结果小目标会漏。3.2 逐帧推理blobFromImage 的参数怎么设每一帧进来先转成网络要的输入格式。YOLOv3 输入是 416×416blobFromImage负责缩放、减均值、换通道def detect(frame, net, output_layers, conf_threshold0.5, nms_threshold0.4): height, width frame.shape[:2] # 416x416 是 YOLOv3 标准输入swapRBTrue 因为 OpenCV 读的是 BGR blob cv2.dnn.blobFromImage( frame, 1/255.0, (416, 416), swapRBTrue, cropFalse ) net.setInput(blob) outputs net.forward(output_layers) boxes, confidences, class_ids [], [], [] for output in outputs: for detection in output: scores detection[5:] # 前 5 位是 cx,cy,w,h,obj class_id int(np.argmax(scores)) confidence float(scores[class_id]) if confidence conf_threshold: # 检测框是相对坐标乘回原图尺寸 cx, cy int(detection[0] * width), int(detection[1] * height) w, h int(detection[2] * width), int(detection[3] * height) x, y int(cx - w / 2), int(cy - h / 2) boxes.append([x, y, w, h]) confidences.append(confidence) class_ids.append(class_id) # NMS 去重否则同一目标会叠一堆框 indices cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, nms_threshold) return boxes, confidences, class_ids, indices参数说明1/255.0是归一化系数YOLO 训练时输入就是 0~1swapRBTrue把 BGR 转成 RGB因为 DarkNet 按 RGB 训练conf_threshold是置信度门槛0.5 是通用起点漏检多就降到 0.3误检多就升到 0.6nms_threshold是 NMS 的 IoU 阈值0.4 意味着重叠超过 40% 的框会被合并。NMSBoxes返回的indices在某些 OpenCV 版本里是二维数组取用时最好np.array(indices).flatten()一下不然遍历会报错。3.3 分类落盘按日期和类别建目录摘要里说的「识别出的对象按日期存储在每个类的文件夹中」落地就是检测到目标后裁剪 ROI按输出根目录/日期/类别名/存图import os from datetime import datetime def save_crop(frame, box, class_name, rootoutput): x, y, w, h box # 边界保护防止裁剪越界 x, y max(0, x), max(0, y) roi frame[y:y h, x:x w] if roi.size 0: return day datetime.now().strftime(%Y-%m-%d) save_dir os.path.join(root, day, class_name) os.makedirs(save_dir, exist_okTrue) # 用时间戳命名避免同秒覆盖 ts datetime.now().strftime(%H%M%S%f) cv2.imwrite(os.path.join(save_dir, f{ts}.jpg), roi)os.makedirs(..., exist_okTrue)保证目录不存在时自动建、存在时不报错。时间戳精确到微秒避免同一秒内多个目标互相覆盖。裁剪前做max(0, ...)边界保护因为 YOLO 的框可能超出画面边缘直接切片会得到空数组。3.4 主循环拉流、重连、跳帧把上面拼起来主循环要处理 RTSP 断流重连和推理节流import time RTSP_URL rtsp://admin:password192.168.1.64:554/Streaming/Channels/101 cap cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG) # 减小缓冲降低延迟设太大画面会滞后好几秒 cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) frame_count 0 while True: if not cap.isOpened(): print(流断开3 秒后重连) time.sleep(3) cap cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG) continue ret, frame cap.read() if not ret: print(读帧失败重连) cap.release() time.sleep(1) cap cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG) continue frame_count 1 # 每 3 帧推理一次CPU 上能明显提帧率 if frame_count % 3 ! 0: continue boxes, confs, ids, indices detect(frame, net, output_layers) for i in np.array(indices).flatten(): save_crop(frame, boxes[i], classes[ids[i]]) x, y, w, h boxes[i] cv2.rectangle(frame, (x, y), (x w, y h), (0, 255, 0), 2) cv2.putText(frame, classes[ids[i]], (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2) cv2.imshow(RTSP-YOLO, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()CAP_PROP_BUFFERSIZE设成 1 是关键默认缓冲会攒好几帧导致画面延迟越来越大。跳帧推理frame_count % 3是在 CPU 上提帧率最直接的手段代价是快速移动的目标可能被跳过。cv2.waitKey(1)里的 1 是毫秒配合imshow刷新窗口按q退出。这套跑通你就有了一个能落盘的 RTSP 检测基线。4. 避坑与排查权重、延迟、编码、路径的五个血泪经验4.1 报错Unable to load weights或检测框全乱现象readNet直接抛异常或者能跑但标签和框完全对不上。原因通常是权重文件缺失、路径写错或者.cfg和.weights版本不配套比如用yolov3-tiny.cfg配yolov3.weights。解决确认根目录有yolov3.weights且readNet第一个参数是权重、第二个是 cfg顺序别反tiny 模型必须配 tiny 权重。标签错乱则检查yolov3.txt行数和权重类别数是否都是 80。4.2 画面延迟越跑越大像慢动作现象imshow出来的画面比现实慢好几秒且时间越久越严重。原因OpenCV 默认缓冲会攒帧推理速度跟不上拉流速度时缓冲队列越积越长。解决cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)把缓冲压到最小再配合跳帧推理让消费速度追上生产速度。如果还慢说明 CPU 推理本身扛不住得换 tiny 模型或上 GPU。4.3 RTSP 地址能 ping 通但VideoCapture打不开现象网络通但cap.isOpened()一直是False。原因RTSP 地址格式不对或者用户名密码里的特殊字符没转义或者摄像头要求 TCP 传输而默认走了 UDP。解决先用ffplay或 VLC 验证地址本身能播地址里的、:等字符要 URL 编码OpenCV 可以在地址后加?tcp强制 TCP比如rtsp://.../101?tcp弱网下比 UDP 稳。4.4 存图目录建了一堆空文件夹现象output/日期/类别/建出来了但里面没图。原因save_crop里 ROI 越界得到空数组imwrite静默失败或者indices是二维数组没flatten循环根本没进。解决裁剪前做边界保护并判断roi.size 0直接返回indices统一np.array(indices).flatten()再遍历。另外确认输出根目录有写权限。4.5 中文路径或类别名导致imwrite失败现象cv2.imwrite返回False图没存下来。原因OpenCV 的imwrite对非 ASCII 路径支持不好中文目录名会翻车。解决输出路径全用英文和数字类别名直接用yolov3.txt里的英文原词person、car别自己改成中文。真需要中文名用cv2.imencode加文件流写入绕开。5. 进阶换 tiny 模型、调阈值与验证落盘质量跑通基线之后真正决定这套东西好不好用的是两个旋钮模型和阈值。先看模型切换把yolov3.cfgyolov3.weights换成yolov3-tiny.cfgyolov3-tiny.weights代码里只改readNet的两个路径即可其他逻辑不动。tiny 模型在 CPU 上帧率通常能翻两三倍代价是小目标和密集场景漏检明显适合「先看有没有人」这种粗筛场景。我一般会准备两套路径用命令行参数切换import argparse parser argparse.ArgumentParser() parser.add_argument(--model, choices[full, tiny], defaultfull) parser.add_argument(--conf, typefloat, default0.5) parser.add_argument(--nms, typefloat, default0.4) args parser.parse_args() if args.model tiny: weights, cfg yolov3-tiny.weights, cfg/yolov3-tiny.cfg else: weights, cfg yolov3.weights, cfg/yolov3.cfg net cv2.dnn.readNet(weights, cfg)阈值这块conf和nms要联动调。只降conf会引入大量误检得同时把nms从 0.4 降到 0.3 压重叠框只升conf会漏掉遮挡目标。我的习惯是拿sampledata/commuters.mp4当固定测试集分别跑conf0.3/0.5/0.7三档对比落盘图片数量和误检率选一个平衡点再上 RTSP。验证落盘质量有个笨但有效的办法跑十分钟后统计output/日期/下每个类别文件夹的图片数如果某个类别的数量远超画面里实际出现的次数基本就是误检在刷图回去调阈值。还有一个容易被忽略的点blobFromImage的输入尺寸。YOLOv3 标准是 416×416但如果你换成 608×608小目标召回会提升CPU 推理时间也线性上涨。改尺寸时.cfg里的width/height不用动OpenCV 会按blobFromImage的尺寸缩放但精度和速度的权衡得自己实测。从那以后我每次换模型或改阈值都强制先用离线视频跑一遍对比落盘数量确认没异常再接实时流——RTSP 上直接调参出了问题连回放都没有纯靠猜。希望帮到你。本文还有配套的精品资源点击获取