算法链路从读取视频帧开始,再进行采样、推理、跟踪和事件判断。先把“读到帧”和“判断出事件”分清,避免将进程在线当作算法正常。

做一个解码与采样实验

使用 Python 3 和 OpenCV,先创建虚拟环境并安装依赖:

Terminal window
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install opencv-python==4.12.0.88

安装 FFmpeg 后生成一段测试文件:

Terminal window
ffmpeg -f lavfi -i "testsrc2=size=640x360:rate=25:duration=5" -an -c:v libx264 -pix_fmt yuv420p input.mp4

保存 sample_frames.py:

from pathlib import Path
import sys
import cv2
source = sys.argv[1] if len(sys.argv) > 1 else 'input.mp4'
output = Path('frames')
output.mkdir(exist_ok=True)
capture = cv2.VideoCapture(source, cv2.CAP_FFMPEG, [
cv2.CAP_PROP_OPEN_TIMEOUT_MSEC, 5000,
cv2.CAP_PROP_READ_TIMEOUT_MSEC, 5000,
])
if not capture.isOpened():
capture.release()
raise RuntimeError('无法打开视频,请检查路径、地址和 FFmpeg 后端支持')
read_count = 0
saved_count = 0
try:
while read_count < 250 and saved_count < 10:
ok, frame = capture.read()
if not ok:
break
if read_count % 25 == 0:
filename = output / f'frame_{read_count:06d}.jpg'
if not cv2.imwrite(str(filename), frame):
raise RuntimeError(f'图片保存失败:{filename}')
saved_count += 1
read_count += 1
finally:
capture.release()
print(f'read={read_count}, saved={saved_count}')
if read_count == 0:
raise RuntimeError('打开成功但没有读到帧')
Terminal window
.\.venv\Scripts\python.exe .\sample_frames.py .\input.mp4

对 5 秒、25 FPS 的测试素材,应读取 125 帧并保存 5 张图片。换成 RTSP URI 时,编码、账号和网络也会影响读取;URI 中凭据不应公开在日志里。

这个例子每 25 帧取一帧。只有固定 25 FPS 素材才可近似理解为每秒一帧;实时或可变帧率来源应按时间戳设计采样。循环有帧数和保存数量限制,没有无限重连。

接入自己的算法

flowchart LR
V[视频流] --> D[解码]
D --> S[按策略采样]
S --> P[模型预处理与推理]
P --> T[目标跟踪]
T --> E[事件规则]
E --> F[图片与录像证据]
F --> U[上传与通知]

模型的输入尺寸、颜色顺序、归一化、类别与输出坐标必须按实际模型检查。OpenCV 常用 BGR,其他模型可能要求 RGB;一段能读取 JPEG 的代码不代表已经实现目标检测。

结果应带摄像头/任务标识、帧编号或时间戳、模型版本和坐标约定。异步结果迟到时,要回到对应的帧,不能随便画到最新画面。

从检测到告警

单帧检测是候选结果,告警通常还需要持续时间、区域、阈值、去重和恢复条件。例如“对象连续在区域内”要记录状态转换,而不是每帧发送一条通知。

先准备正常、触发、临界和恢复样本,检查事件与证据时间能否对应。模型分数不是统一可信概率,不同场景的阈值需依据实际样本确定。

下一篇:运维与容量规划。参考:OpenCV 视频读取、超时属性。