设计架构:模型推理+生成证据视频 (一)
简介
项目目标:
- 在标准化检测基础上,为了满足实际业务中对安全事件可追溯、可审计、可通知的硬性需求,增加报警事件自动化处理系统。其目标是将单纯的“检测”能力,升级为一套完整的“检测-记录-通知”自动化解决方案。
- 满足报警条件 ,合成10秒异常视频。
- 由于合成视频操作会阻碍实时视频流的处理,需要合理设计架构
合成视频的条件:
- 检测到报警目标
- 处理完10秒视频的帧数,防止合成重复的视频。
缓冲区冲突问题:速度不匹配和数据丢失
- 合成视频的消费者取帧的速度 慢于 生产者实时视频流存帧的速度
- 导致异常视频合成错误、失败
设计架构:
- 方案一
- 方案二
- 方案三
方案一:预处理画框(提前消费)
流程描述:
视频流读取:一个线程持续从网络视频流中读取原始帧。
实时处理:对每一帧都立即进行检测算法推理 + NMS(非极大值抑制) + 画框操作。
存储:将已经画好框的图像帧存入一个固定大小的缓冲区(如250帧)。
触发报警:当报警条件满足时,直接从队列中取出最新的250帧已经画好框的图像。
合成视频:将这些帧直接合成为一个视频文件,无需任何额外处理。
优点:
响应速度极快:触发报警后,省去了所有的计算过程,只需要进行视频编码,可以近乎实时地生成证据视频。
缺点:
资源浪费极其严重:系统需要日以继夜地对每一帧进行画框操作,即使其中99.9%的帧永远不会被使用。这消耗了大量的CPU/GPU计算资源和电力。
总结:
内存占用:
一帧“画框后图像”的内存大小:
它依然是和原始图像同样尺寸、同样格式的一幅新图像。
大小: ≈ 6.2 MB
250帧画框后的图像:
≈ 1.55 GB
(画框操作修改了像素值,但并没有改变数据总量)。
缓冲区帧冲突:无
方案二:后处理画框(延迟消费)
流程描述:
视频流读取:一个线程持续从网络视频流中读取原始帧。
实时推理:立即对每一帧进行安全帽检测算法推理 + NMS(非极大值抑制),以便实时判断是否满足报警条件。
存储:直接将原始帧存入一个固定大小的缓冲区(如250帧)。检测结果用于触发判断后即丢弃(被下一帧结果覆盖),不与原始帧关联保存。
触发报警:当报警条件满足时,从队列中取出最新的250帧原始图像。
全面处理:对这250帧原始图像,重新执行一遍安全帽检测算法推理 + NMS + 画框的操作。
合成视频:将处理好的画框帧合成为视频文件。
优点:
日常资源消耗极低:平时系统只负责存储原始帧,几乎不进行任何额外计算,节省了大量计算资源。
灵活性高:后期可以随时更换更好的模型、调整检测阈值或改变画框样式来重新处理原始帧,生成新的证据视频。
保证数据一致性:每次处理都使用最新的算法,确保所有证据视频的标准统一。
缺点:
报警响应速度极慢:触发报警后,需要重新对250帧进行模型推理(最耗时的步骤)
计算冗余:报警时需要集中进行大量重复计算(重复推理)。导致了“计算浪费”。
总结:
内存占用:
一帧原始图像的内存大小:
格式:未压缩的BGR图像(OpenCV默认)
计算:
1920 * 1080 * 3个通道 ≈ 6.2 MB
250帧原始图像:
6.2 MB * 250 ≈ 1.55 GB
队列帧冲突和数据丢失:严重
方案三:存原始帧与结果,按需画框(推荐方案)
流程描述:
视频流读取:一个线程持续读取原始帧。
实时推理:对每一帧都立即进行安全帽检测算法推理 + NMS,但不进行画框操作。
存储:将原始帧和轻量级的检测结果(如边界框坐标、类别、置信度)关联起来,存入一个固定大小的缓冲区。
触发报警:当报警条件满足时,从缓冲区中取出最新的250组数据(包含250帧原始图像和对应的250组检测结果)。
按需画框:根据检测结果,快速地对每一帧原始图像进行画框操作。
合成视频:将画好框的帧合成为视频文件。
优点:
响应速度快:触发后只需执行轻量级的画框和编码操作,无需耗时巨大的模型推理,能快速生成证据视频。
资源效率高:日常只进行必要的模型推理,避免了方案一中“画框”这个最浪费资源的步骤。存储检测结果的开销微乎其微。
缺点:
架构稍复杂:需要设计一个能关联存储原始帧和检测结果的数据结构(如环形缓冲区)。
有微量存储开销:需要额外存储检测结果,但这个开销相比图像本身非常小。
总结:
内存占用:
一帧原始图像的内存大小:
格式:未压缩的BGR图像(OpenCV默认)
计算:
1920 * 1080 * 3个通道 ≈ 6.2 MB
250帧原始图像:
6.2 MB * 250 ≈ 1.55 GB
一帧检测结果的内存大小:
它不是图像,而是一个轻量级的Python列表,里面包含了一些字典或对象。
这样一条数据,即使包含10个检测目标,其占用的内存也最多只有几KB。
我们往大了估算,一份检测结果占 10 KB。
250帧的检测结果:
10 KB * 250 = 2500 KB ≈ 2.5 MB
队列帧冲突和数据丢失:改善。
方案三通常使用一个固定大小的环形缓冲区(Ring Buffer) 来存储数据(原始帧 + 结果)。这个缓冲区的特性决定了如何处理冲突:
缓冲区已满时的行为:当生产者(视频流)试图向一个已经满了的缓冲区插入新数据时,它会自动覆盖最旧的数据。
这意味着什么:如果报警触发得不够及时,在你处理那“250帧”之前,其中一些比较旧的帧可能已经被新来的帧覆盖掉了。
所以,你从缓冲区取出的,永远是从触发时刻倒推回去的、最新的250帧(或缓冲区所能提供的最大帧数)。你无法获取到比缓冲区大小更久远的历史帧。
方案三在资源消耗、响应速度和灵活性上取得了最佳平衡,是绝大多数场景下的最优选择。
解决冲突问题 :
触发报警:假设在时间点
T
,系统检测到需要报警(例如,有人没戴安全帽)。关键动作:系统立即(比如在
T + 0.1秒
内)执行以下操作:锁定缓冲区。
将当前缓冲区内所有的250帧(即从
T - 8.3秒
到T
时刻的画面)做一份快照(snapshot)。释放缓冲区。
合成视频:系统拿着这份快照副本,在不干扰缓冲区正常运行的情况下,慢慢地进行画框和视频合成操作,哪怕这个过程需要好几秒。
缓冲区的状态:在此期间,视频流线程仍在持续运行,新的帧仍在不断覆盖缓冲区中最旧的帧。但这已经完全不影响正在合成的那份快照副本了