第十一章

视频理解与目标跟踪:客流动线分析

第十一层 · DeepSORT/ByteTrack 追踪每一个客人的“动线”

一、从"每帧数人"到"追踪同一个人的轨迹"

"第八章我们数了人:每帧检测出几个框。"老周说,"但王姐要的动线分析,难在一个问题——怎么知道'这一帧的这个人'和'上一帧的这个人'是同一个人?"

静态检测 vs 视频跟踪:
  检测(每帧独立):这一帧有 3 个人(框)
  跟踪(跨帧关联):人A从门口→书架→收银台(一条轨迹)

  跟踪要解决的核心:
    跨帧"身份关联"(ID Association)
    这一帧的框A,是上一帧的框B吗?
    → 给每个人一个稳定 ID → 记录它的轨迹

  有了轨迹,就能算:
    客流动线(进→停留→出)
    停留时长(在哪个书架站最久)
    热门区域(哪个货架最吸引人)
    折线图(人群移动方向)

"跟踪 = 给每个目标'续上命'——检测是'瞬间快照',跟踪是'连续追剧'。动线分析、客流统计的进阶,全靠跟踪。"


二、目标跟踪的两大流派

"目标跟踪有两条技术路线。"老周说:

① 多目标跟踪(MOT, Multi-Object Tracking):
   视频里同时追踪多个目标(适合客流统计)
   代表方法:SORT / DeepSORT / ByteTrack
   流程:
     每帧检测 → 关联上一帧的轨迹 → 更新 ID

② 单目标跟踪(SOT, Single Object Tracking):
   只追一个目标(框住后锁定)
   代表方法:KCF / SiamRCNN
   适合:锁定某个目标跟随(如锁定一个人)

  云间书店用 MOT(DeepSORT/ByteTrack):
    多人同时跟踪 + 轨迹记录

  底层技术:光流(Optical Flow)
    光流 = 像素在相邻帧之间的"运动向量"
    图里东西在动 → 算出每个点往哪移
    帮助:运动检测、轨迹预测、视频压缩

"客流统计用 MOT(多目标跟踪),锁定单个目标用 SOT——DeepSORT 和 ByteTrack 是工业标准。光流是运动分析的基础概念,面试常提。"


三、DeepSORT 思路:检测 + 外观 + 运动

"DeepSORT 是 MOT 的经典方案,思路值得理解。"老周说:

DeepSORT(深度多目标跟踪):
  每帧:
    ① 检测器出框(YOLO)
    ② 计算每个框的"外观特征"(ReID 特征,类似人脸特征)
    ③ 和已有轨迹匹配:
       - 运动预测(卡尔曼滤波:预测目标下一帧位置)
       - 外观匹配(新框外观 vs 轨迹外观相似度)
    ④ 匹配上的 → 更新轨迹(延续 ID)
       没匹配上的 → 新目标(新 ID)或结束的轨迹

  关键技巧:
    卡尔曼滤波:根据运动规律"预测"位置(比检测还快)
    外观 ReID:同一人的外观特征稳定(换衣也认得出)
    匈牙利匹配:全局最优分配(谁和谁是一对)

  ByteTrack(更简单更强):
    不只看高置信度框,也处理低置信度框(遮挡恢复)
    → 现在工业界常用

"DeepSORT = 检测(YOLO)+ 预测(卡尔曼)+ 匹配(外观+运动)——理解这套'预测-匹配-更新'循环,你就掌握了 MOT 的精髓。"


四、实战:用 YOLOv8 + ByteTrack 做动线分析

"工程上用现成组合最快:YOLOv8 检测 + ByteTrack 跟踪。"老周演示:

pip install ultralytics opencv-python
from ultralytics import YOLO
import cv2

model = YOLO("yolov8n.pt")

# 打开摄像头/视频
cap = cv2.VideoCapture("store_camera.mp4")

# 轨迹记录:id -> [(x,y,时间)...]
tracks = {}
track_color = {}

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # YOLO 跟踪(track=True 自动分配 ID)
    results = model.track(frame, persist=True, classes=[0], conf=0.4)
    # classes=[0] = 只追人(person)

    if results[0].boxes is not None:
        for box in results[0].boxes:
            x1, y1, x2, y2 = box.xyxy[0].tolist()
            track_id = int(box.id[0].item())      # 跟踪 ID!
            center = ((x1+x2)/2, (y1+y2)/2)

            # 记录轨迹
            tracks.setdefault(track_id, []).append(center)

            # 画框 + ID
            color = track_color.setdefault(
                track_id, tuple(np.random.randint(0,255,3).tolist()))
            cv2.rectangle(frame, (int(x1),int(y1)),
                          (int(x2),int(y2)), color, 2)
            cv2.putText(frame, f"ID:{track_id}",
                        (int(x1), int(y1)-8),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)

    # 画轨迹线(每个人的移动路径)
    for tid, pts in tracks.items():
        if len(pts) > 1:
            for i in range(1, len(pts)):
                cv2.line(frame, (int(pts[i-1][0]), int(pts[i-1][1])),
                         (int(pts[i][0]), int(pts[i][1])),
                         track_color[tid], 1)

    cv2.imshow("track", frame)
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

cap.release()

# 动线分析:统计每个 ID 的轨迹
print(f"共跟踪到 {len(tracks)} 个客人")
for tid, pts in tracks.items():
    stay_frames = len(pts)
    print(f"ID {tid}: 在店 {stay_frames} 帧, 移动路径 {len(set(pts))} 个位置")
动线分析数据产出:
  人数(不同 ID 数)
  每人轨迹(点序列 → 画热力路径)
  停留时长(ID 存活帧数)
  区域热度(轨迹点落在哪个分割区域,联动第九章)

  进阶:跨摄像头跟踪(ReID:不同摄像头认出同一人)

"YOLO + ByteTrack 一行代码就给每个人分配 ID——之后所有'动线、停留、热度'分析,都是在统计这些 ID 的轨迹。这套代码是客流分析的标准骨架。"


五、动作识别:让机器知道"在干什么"

"除了'在哪、往哪走',视频理解还能'看懂在干什么'——动作识别(Action Recognition)。"老周说:

动作识别(Action Recognition):
  输入:一段视频(多帧)
  输出:动作类别(行走/跑步/挥手/摔倒……)

  方法:
    ① 视频=多帧:CNN 逐帧提取 + 时间序列建模
       - Two-stream(空间+时间双流)
       - 3D CNN(C3D/I3D:同时卷积空间和时间)
       - Video Transformer(新趋势)
    ② 骨骼点识别(Pose Estimation)+ 动作分类
       - 先提取人体关键点(姿态)
       - 用关键点序列判断动作(ST-GCN)

  云间书店应用(进阶):
    摔倒检测(老人顾客,安全)
    排队检测(收银台排队时长)
    打架/异常行为预警(安防)

  人体姿态(Pose):
    检测人体的关键点(17/25 点:关节位置)
    工具:MediaPipe / OpenPose
    应用:姿态估计 → 动作分类 → 行为分析

"动作识别 = 视频级分类——不只是看'静态的图',而是理解'一段时间的运动'。摔倒检测、行为分析都是它的应用。这是视频理解的最高层。"


六、视频理解的工程挑战:性能与隐私

"视频理解在工程上有两大难题:性能隐私。"老周强调:

视频处理的性能挑战:
  摄像头 25 FPS = 每秒处理 25 帧
  检测+跟踪每帧都要算 → CPU/GPU 压力大

  优化手段:
    ① 跳帧处理:每 3 帧检测一次,中间用跟踪顶住(常用)
    ② 区域裁剪:只在 ROI(感兴趣区域)检测
    ③ 轻量模型:YOLOv8n(nano 版)≈ 每秒几十帧
    ④ 边缘部署:Jetson / 手机端 NPU(终章细讲)
    ⑤ 多线程:采集、检测、后处理流水线并行

隐私合规(视频监控):
  摄像头区域必须张贴告示(告知监控)
  人脸数据最小化:不识别就不提取特征
  视频存储:限时保存 + 加密
  避免"裸奔式"收集(和数据合规一样重要)

"视频应用两大红线:跑不动(性能)和不能跑(合规)——性能靠优化,合规靠自觉。云间书店装摄像头,第一件事是门口贴'您已进入监控区域'。"


七、章末:老周的第十一章总结

第十一层:视频理解与目标跟踪
├── 跟踪 = 跨帧身份关联(给每个人稳定 ID)
├── MOT(多目标):DeepSORT/ByteTrack(客流统计用)
├── SOT(单目标):KCF/SiamRCNN(锁定跟踪)
├── DeepSORT:检测+卡尔曼预测+外观匹配
├── 光流:像素运动向量(运动分析基础)
├── 实战:YOLOv8 + ByteTrack 动线分析(轨迹/停留/热度)
├── 动作识别:视频分类(摔倒/排队/异常,姿态估计)
└── 挑战:性能(跳帧/轻量/边缘) + 隐私(告知/最小化)
第十二层(预告):部署应用 —— 让模型跑在真实环境

"小陈,客流动线出来了——王姐看到热力图,惊讶地发现'悬疑小说区'居然是最多人停留的地方,连夜把那排书架挪到了进门最显眼的位置!"老周说,"现在四个需求全实现了,但都跑在开发机上。最后一步——部署应用:把模型放到服务器/边缘设备上,稳定、快速、可扩展地服务真实业务。走,终章。"

✌ 语言