跳到主要内容

视频分析【选修】

本节定位

视频分析最容易被误解成:

  • 把很多图片一帧一帧跑一遍

这当然是起点,但不是全部。
视频真正带来的新问题在于:

同一个目标会随着时间连续变化,而时间本身也带着信息。

所以这节重点是把“时间维度”这件事讲清楚。

学习目标

  • 理解视频任务和单帧图像任务的根本区别
  • 理解抽帧、跟踪、时序建模各自解决什么问题
  • 通过可运行示例建立视频分析最小直觉
  • 理解为什么很多视频系统其实是“图像模型 + 时间逻辑”的组合

一、视频为什么比单张图更复杂?

1.1 因为同一目标会跨帧出现

一张图里,你只需要回答当前画面。
视频里还要考虑:

  • 它刚刚在哪
  • 接下来会去哪

1.2 因为“变化”本身就是信息

很多视频任务里,真正重要的不是某一帧长什么样,
而是:

  • 动作怎么发生
  • 轨迹怎么移动

1.3 一个类比

单张图像分析像看照片。
视频分析更像看监控回放,你会自然关心:

  • 前后关系
  • 事件过程

二、视频分析里最常见的几种处理方式

2.1 抽帧 + 单帧模型

最简单的方法:

  • 定期抽帧
  • 每帧单独分析

优点:

  • 简单

缺点:

  • 容易丢掉时间信息

2.2 检测 + 跟踪

适合:

  • 行人轨迹
  • 车辆轨迹

它的核心是:

  • 每一帧先检测
  • 再在时间上关联同一目标

2.3 时序建模

例如:

  • 动作识别
  • 事件识别

这类任务更依赖:

  • 多帧共同表达一个模式

三、先跑一个最小轨迹跟踪示例

frames = [
[{"id": None, "x": 10, "y": 10}],
[{"id": None, "x": 12, "y": 11}],
[{"id": None, "x": 15, "y": 13}],
]


def assign_track_ids(frames, max_distance=5):
next_id = 1
prev_objects = []

for frame in frames:
for obj in frame:
matched_id = None
for prev in prev_objects:
distance = abs(obj["x"] - prev["x"]) + abs(obj["y"] - prev["y"])
if distance <= max_distance:
matched_id = prev["id"]
break

if matched_id is None:
matched_id = next_id
next_id += 1

obj["id"] = matched_id

prev_objects = [dict(item) for item in frame]

return frames


tracked = assign_track_ids(frames)
for frame in tracked:
print(frame)

3.1 这个例子最想表达什么?

视频分析里很多系统的第一步不是复杂时序网络,
而是:

  • 把跨帧的同一目标串起来

3.2 为什么这对业务很重要?

如果不能把同一目标在不同帧里关联起来,
很多任务根本做不下去:

  • 计数
  • 行为分析
  • 越界告警

四、最容易踩的坑

4.1 把视频当作独立图片集合

这样很容易丢掉:

  • 轨迹
  • 动作
  • 事件顺序

4.2 抽帧策略过粗

抽帧太稀,会漏掉关键瞬间。

4.3 只看单帧精度,不看时序稳定性

真实视频系统更该关注:

  • 抖动
  • 漏跟踪
  • ID 切换

小结

这节最重要的是建立一个判断:

视频分析的难点不只是“帧更多”,而是必须把时间维度纳入建模,理解目标和事件是如何跨帧连续发生的。


练习

  1. 把示例改成两个目标同时移动,看看简单跟踪逻辑是否会混乱。
  2. 为什么说很多视频系统其实是“单帧模型 + 时间逻辑”的组合?
  3. 抽帧太稀可能带来什么风险?
  4. 想一想:哪些视频任务必须显式建模时间,而不能只看单帧?