视觉系统"看见一个人"以后,常常需要输出区域进入、停留、姿态变化或某段动作等结论。这里最大的风险是把简单规则包装成智能判断,或反过来用复杂模型处理本可解释的几何条件。本文给出一条从检测结果到视频事件的选型方法。
目录
一、事件结论需要什么证据
| 结论 | 最小证据 | 合适技术 |
|---|---|---|
| 进入指定区域 | 稳定目标中心点跨越区域边界 | 几何规则 + 跟踪 |
| 在区域内停留 | 同一轨迹持续时间 | 轨迹特征 + 时间窗口 |
| 手臂抬起 | 关键点角度持续满足条件 | 姿态规则 |
| 连续动作类别 | 多帧外观或骨架序列 | 时序动作模型 |

图1:规则、轨迹和动作模型解决的是不同复杂度的"判断"问题。
二、规则、轨迹特征和动作模型如何选择
| 条件 | 优先方案 | 优点 | 不适用边界 |
|---|---|---|---|
| 区域边界明确、机位稳定 | 几何规则 | 可解释、成本低、可审计 | 镜头移动或区域语义模糊 |
| 需要持续时间、速度、方向 | 轨迹特征 | 能处理连续对象行为 | 轨迹质量不足时结论不可靠 |
| 动作取决于关节关系 | 姿态序列规则 | 阈值可解释、便于调试 | 遮挡和复杂动作会降低可见性 |
| 动作阶段多、节奏差异大 | 时序动作模型 | 可学习时间模式 | 需要代表性数据和严格评测 |
规则并不"低级"。当输入条件稳定、结论可由公开几何条件定义时,规则往往比黑盒模型更容易验证。只有规则无法覆盖的变形、顺序和上下文,才值得引入时序模型。

图2:复杂模型不是默认答案,证据形式决定技术层级。
三、固定案例、实现与验证
固定案例为匿名轨迹进入区域后连续停留。输入是 track_id、中心点和时间戳;输出是 ENTERED、DWELLING 或 EVIDENCE_INSUFFICIENT。持续时间由单调时间差计算,不能由帧号猜测。
python
def dwell_event(track_points, zone, min_ms=3000):
inside = [p for p in track_points if zone.contains(p.x, p.y)]
if len(inside) < 2:
return "EVIDENCE_INSUFFICIENT"
return "DWELLING" if inside[-1].ts_ms - inside[0].ts_ms >= min_ms else "ENTERED"
sql
CREATE TABLE visual_event_evidence (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
event_no VARCHAR(64) NOT NULL,
event_type VARCHAR(32) NOT NULL,
track_ref VARCHAR(64) NOT NULL,
evidence_start_ms BIGINT NOT NULL,
evidence_end_ms BIGINT NOT NULL,
decision VARCHAR(32) NOT NULL,
UNIQUE KEY uk_visual_event_no (event_no)
);
SELECT event_no FROM visual_event_evidence
WHERE decision = 'DWELLING' AND evidence_end_ms - evidence_start_ms < 3000;
测试必须覆盖边界贴线、轨迹断裂、停留不足和时间戳乱序。轨迹不连续时,不应拼接为一段停留事件。
服务层怎样阻止错误事件进入下游
事件服务接收的是轨迹证据而不是"模型已判断"的布尔值。它先校验轨迹质量和规则版本,再执行规则;无法形成连续窗口就返回证据不足:
java
@Transactional(rollbackFor = Exception.class)
public EventDecision decide(EventCommand command) {
TrackEvidence evidence = trackRepository.requireContinuous(command.trackRef());
if (evidence.quality() != TrackQuality.PASS) {
return EventDecision.insufficient("TRACK_NOT_CONTINUOUS");
}
ZoneRule rule = ruleRepository.requireVersion(command.ruleVersion());
return rule.dwellMillis(evidence.points()) >= command.minDwellMillis()
? EventDecision.accepted("DWELLING")
: EventDecision.accepted("ENTERED");
}
python
def test_short_window_is_not_dwell():
points = [Point(100, 100, 0), Point(100, 100, 2999)]
assert dwell_event(points, zone, 3000) == "ENTERED"
预期输出需要包括 event_type、证据起止时间、规则版本、轨迹引用和决定状态。没有这五项,就无法让人工复核判断事件是规则误触发、轨迹错误还是输入画面本身不可用。

图3:事件不是一个标签,而是一段可回放、可复核的证据范围。
难例集怎样验证事件判断
有了事件证据结构后,下一步不是立刻展示"识别成功"的视频,而是建立会挑战规则和模型边界的难例集。以区域进入和停留为例,至少应包含四类片段:目标中心点贴着边界移动、目标短暂离开又返回、跟踪编号在区域内断裂、视频丢帧导致时间间隔突变。对于肢体动作,还应加入动作只出现一半、关键关节被遮挡、多人骨架重叠和没有动作但姿态相近的反例。
| 难例 | 主要风险 | 预期系统行为 | 不应出现的结果 |
|---|---|---|---|
| 贴边移动 | 中心点抖动导致反复跨线 | 应用滞回或时间窗口,必要时请求复核 | 同一对象连续产生多个进入事件 |
| 轨迹断裂 | 两段轨迹被错误拼成停留 | 输出证据不足或重新跟踪 | 用不同 track_id 拼接持续时间 |
| 时间戳跳变 | 丢帧让停留时长被放大 | 标记输入异常,阻止自动发布 | 按帧号推算出虚假时长 |
| 姿态相近但未完成动作 | 单帧姿势误触发 | 要求完整动作窗口或送人工复核 | 单帧就给出动作结论 |
评测时除了统计事件 recall,还要统计误触发率 、重复事件数 和起止时间偏差。例如一个停留事件虽然被识别到,但开始时间早了 2 秒、结束时间晚了 3 秒,可能仍不满足业务使用条件。难例评测的输出应包含事件原始结论、人工标注结论、差异类型和证据片段索引;这些数据才是调整区域边界、时间窗口或动作模型的依据。
python
def event_result(expected, actual, start_error_ms, end_error_ms):
if expected != actual:
return "CLASSIFICATION_MISMATCH"
if abs(start_error_ms) > 500 or abs(end_error_ms) > 500:
return "BOUNDARY_MISMATCH"
return "MATCHED"
def test_large_time_error_is_not_a_pass():
assert event_result("DWELLING", "DWELLING", 800, 0) == "BOUNDARY_MISMATCH"

图4:难例验证比展示成功片段更能说明视频理解是否可靠。
四、反例、复核与事件发布
事件规则或动作模型通过离线样本集后,仍要为每一次运行保留"不确定"的出口。典型反例包括:轨迹刚好贴着区域边界、同一对象因遮挡被拆成两段、关键点短暂消失、动作窗口只有前半段、视频存在跳帧或时间戳倒退。它们不应被硬塞进 ENTERED、DWELLING 或某个动作类别。
text
AUTO_PUBLISH:证据连续、规则/模型版本已验证,且结论远离阈值边界。
REVIEW_REQUIRED:时间、空间或置信度接近阈值;需要查看证据片段。
EVIDENCE_INSUFFICIENT:轨迹、关键点或时间戳缺失,不能推断结论。
RULE_VERSION_MISMATCH:任务引用的规则版本未被当前验收集覆盖。
| 复核问题 | 要展示的证据 | 可以做的决定 |
|---|---|---|
| 是否真的进入区域 | 标注视频、区域轮廓、轨迹中心点与时间戳 | 发布、拒绝或调整区域版本 |
| 是否满足停留时长 | 连续轨迹、开始/结束时间、断轨标记 | 发布、证据不足或重跑跟踪 |
| 是否触发肢体动作 | 骨架叠加、窗口帧、置信度曲线 | 发布、复核不通过或改用时序模型 |
| 是否可由规则处理 | 反例集表现、规则版本与覆盖范围 | 保留规则或升级为模型评测 |
事件发布必须是幂等的:同一个任务、规则版本、轨迹引用和时间窗口组合只能形成一个正式事件。这样重跑任务或重复点"确认"不会产生多个看似不同的结论。
sql
ALTER TABLE visual_event_evidence
ADD COLUMN rule_version VARCHAR(64) NOT NULL DEFAULT 'v1',
ADD UNIQUE KEY uk_event_evidence_once
(track_ref, event_type, evidence_start_ms, evidence_end_ms, rule_version);
-- 自动发布的事件必须具备完整证据区间,预期为空。
SELECT event_no FROM visual_event_evidence
WHERE decision = 'AUTO_PUBLISH'
AND (evidence_end_ms <= evidence_start_ms OR rule_version IS NULL);
人工复核界面只应暴露本次事件附近的脱敏片段和证据摘要,不需要将整段原始视频交给每个审核人。复核结果反向记录为"规则边界错误""轨迹证据不足""输入质量问题"或"模型判断偏差",这样下一轮改进才有具体方向。

图5:允许输出证据不足,才能避免视觉系统在不可靠条件下强行下结论。
五、边界与验收
事件结论不能替代人的意图判断;区域规则依赖机位与区域版本;动作模型依赖标注定义和样本分布。上线时应记录规则/模型版本、证据窗口、轨迹质量和人工复核结果,并对"证据不足"建立正常处理路径。验收报告需分别统计自动发布、人工确认、复核拒绝和证据不足的比例,不能只展示已经成功发布的事件数量。