项目:智能安防系统
本节定位
安防项目很容易做成“检测到人就画框”的 demo。
但真正能交付的安防系统,关注的通常不是框本身,而是:
- 告警准不准
- 会不会重复报警
- 时延够不够低
- 误报会不会把人烦死
所以这节课的重点是把它做成一个作品级系统项目,而不是单次检测展示。
学习目标
- 学会定义一个可交付的安防检测任务边界
- 学会把检测、规则、告警和去重串成一条闭环
- 学会设计最基础的评估与失败分析
- 学会把这个项目做成 有说服力的作品集展示
一、先把项目题目定义清楚
一个适合练手、又很像真实业务的题目可以是:
做一个“禁区入侵告警系统”,输入监控帧序列,输出“是否触发告警 + 告警发生在哪一帧”。
这个题目好在:
- 目标简单
- 业务意义清楚
- 很容易解释误报和漏报
为什么不建议一开始就做很大?
例如:
- 同时做烟火检测、摔倒检测、安全帽检测、车辆识别
这种范围太大,项目容易只剩功能堆叠,没有一个清楚主线。
二、作品级安防项目最小闭环长什么样?
- 定义监控目标和禁区
- 做检测
- 把检测框映射成告警逻辑
- 做去重 / 跟踪
- 评估告警质量
- 展示成功与失败案例
如果只做前两步,那更像模型 demo;
做到后面几步,才更像一个系统项目。
2.1 一张更像真实系统的告警闭环图
这张图很重要,因为它提醒你:
- 安防系统真正交付的是告警体验
- 不是单张图上的框
三、先跑一个“检测 -> 告警 -> 去重”的最小闭环
下面这个示例会做三件非常关键的事:
- 读取逐帧检测结果
- 判断是否进入危险区域
- 对同一目标的连续多帧命中做告警去重
detections = [
{"frame": 1, "track_id": 101, "label": "person", "box": (40, 40, 80, 120)},
{"frame": 2, "track_id": 101, "label": "person", "box": (42, 42, 82, 122)},
{"frame": 3, "track_id": 101, "label": "person", "box": (44, 45, 84, 125)},
{"frame": 4, "track_id": 202, "label": "person", "box": (150, 150, 180, 210)},
]
danger_zone = (30, 30, 100, 140)
def is_inside(box, zone):
bx1, by1, bx2, by2 = box
zx1, zy1, zx2, zy2 = zone
return bx1 >= zx1 and by1 >= zy1 and bx2 <= zx2 and by2 <= zy2
def build_alerts(detections, zone):
active_tracks = set()
alerts = []
for det in detections:
inside = det["label"] == "person" and is_inside(det["box"], zone)
if inside and det["track_id"] not in active_tracks:
alerts.append(
{
"frame": det["frame"],
"track_id": det["track_id"],
"alert": "intrusion",
}
)
active_tracks.add(det["track_id"])
elif not inside and det["track_id"] in active_tracks:
active_tracks.remove(det["track_id"])
return alerts
alerts = build_alerts(detections, danger_zone)
print(alerts)
3.1 这个例子为什么比“检测到人就报警”强得多?
因为它已经体现了安防系统里最重要的一层工程判断:
- 同一个人连续 3 帧都在禁区
- 不能报警 3 次
3.2 为什么 track_id 很重要?
没有跟踪信息,你很难判断:
- 这是同一个人
- 还是三个不同的人
所以安防项目从“检测”走向“系统”,
往往就卡在这一层。
四、一个作品级项目最该怎么评估?
4.1 不是只看检测精度
安防项目更应该至少拆成两层评估:
- 检测层
目标有没有找到 - 告警层
告警触发是否合理
4.2 最小告警评估示例
pred_alerts = [
{"frame": 1, "track_id": 101, "alert": "intrusion"},
]
gold_alerts = [
{"frame": 1, "track_id": 101, "alert": "intrusion"},
{"frame": 8, "track_id": 303, "alert": "intrusion"},
]
def alert_recall(pred_alerts, gold_alerts):
gold_set = {(x["frame"], x["track_id"], x["alert"]) for x in gold_alerts}
pred_set = {(x["frame"], x["track_id"], x["alert"]) for x in pred_alerts}
hit = len(gold_set & pred_set)
return hit / len(gold_set) if gold_set else 1.0
print("alert_recall:", round(alert_recall(pred_alerts, gold_alerts), 4))