首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >在浏览器里实现群体光流追踪:从像素运动到可复用的视频素材

在浏览器里实现群体光流追踪:从像素运动到可复用的视频素材

原创
作者头像
用户5557817
发布2026-08-03 13:20:04
发布2026-08-03 13:20:04
930
举报

在视频编辑器中,“光流追踪”很容易被做成满屏箭头:比较前后两帧,计算像素位移,然后把向量绘制到画面上。

这种效果看起来很“技术”,但真正放进产品后会遇到一个问题:背景纹理、字幕、镜头抖动都可能产生大量向量。用户看到的是一片噪声,却不知道画面里的哪一群人、哪个物体正在向什么方向移动。

因此,我们没有把光流追踪简单地理解为“绘制像素位移”,而是把它设计成一个浏览器本地运行的“语义群体运动实验”。

项目地址:https://github.com/MartinDelophy/ai-video-editor

一、为什么单纯的光流不够用

传统光流关注的是相邻帧之间的像素变化。对于视频中的一个采样点,可以得到一个二维运动向量:

v=(Delta x,Delta y)

其中,(Delta x) 表示水平方向位移,(Delta y) 表示垂直方向位移。

把大量向量绘制到视频上,就能形成常见的光流可视化效果。但这些向量并不理解画面内容。

例如:

  • 摄像机轻微晃动会让整个背景产生向量;
  • 字幕变化可能被识别为局部运动;
  • 灯光闪烁和压缩噪声也会造成像素变化;
  • 人物衣服、头发和背景可能拥有相似的局部运动。

如果直接根据这些向量进行聚类,很容易把背景纹理误认为一个“运动群体”。

所以我们调整了思路:先回答“画面里的人或物体在哪里”,再回答“这个主体如何移动”。

二、语义检测与光流结合

完整流程被拆分为五个阶段:

代码语言:plaintext
复制
视频片段
  ↓
主体语义检测
  ↓
生成大块语义区域
  ↓
区域内部计算局部光流
  ↓
聚合主体运动并累积轨迹
  ↓
生成带向量和时间码的结果视频

1. 识别主体

系统首先在锚点帧中寻找人物或主要物体。

浏览器端优先使用轻量级目标检测模型获取候选区域。如果人物检测结果不稳定,则继续使用本地人物分割和已有目标检测能力作为降级方案。

当前策略包括:

  • 使用 NanoDet 获取人物和物体候选;
  • 使用 MediaPipe 人物分割辅助确认人物区域;
  • 使用 YOLOS 作为有边界的检测回退;
  • 对近景、接近全画面的单个人物保持较宽松的面积限制;
  • 当模型运行失败但视频主体明显时,使用保守的画面中心区域作为语义先验。

这里有一个重要原则:一个清晰的人或物体本身就是一个有效群体,并不要求画面中必须出现多个人。

2. 建立大块语义群体

检测结果不会直接变成很多零散的小区域。

对于距离较近、运动方向一致的人物,可以将其合并为一个较大的运动群体;对于主要物体,则保留其核心语义区域。

每个群体包含:

  • 归一化区域边界;
  • 群体类型;
  • 当前中心位置;
  • 平均运动方向;
  • 运动稳定度;
  • 历史轨迹点。

这样,后续计算关注的是“这个人物群体向左移动”,而不只是“这里有几十个像素向左移动”。

三、只在语义区域内计算光流

获得语义区域后,系统会对相邻采样帧进行灰度化,并在区域内部执行块匹配。

假设当前块中心为 ((x,y)),系统会在下一帧的局部搜索窗口中寻找差异最小的位置:

E(dx,dy)=frac{1}{N}\sum{i=1}^{N}|I_t(i)-I{t+1}(i+dx,i+dy)|

误差最小的 ((dx,dy)) 就是这个图像块的候选运动向量。

为了减少噪声,还需要继续过滤:

  • 位移过小的向量;
  • 误差过大的低置信度向量;
  • 明显偏离群体主方向的离群向量;
  • 落在语义区域之外的背景向量。

最后,对保留下来的向量进行加权聚合,得到群体主运动:

V_g=frac{sum_i c_i v_i}{sum_i c_i}

其中,(v_i) 是局部向量,(c_i) 是对应的置信度。

四、从瞬时向量变成累计轨迹

单帧光流只能说明“这一刻往哪里移动”,但用户更容易理解一条连续轨迹。

因此,每处理完一帧,系统会使用群体主向量更新群体中心:

Pt=P{t-1}+V_t

同时把新的中心点写入轨迹序列:

代码语言:javascript
复制
track.points.push({
  x: nextCenterX,
  y: nextCenterY,
  time: frameTime
});

连续处理后,便可以得到主体从 A 点移动到 B 点的轨迹。

为了让实验结果更直观,画面中同时保留:

  • 原始视频;
  • 青色局部光流箭头;
  • 无文字干扰的群体区域边界;
  • 不同颜色的累计运动轨迹;
  • 群体当前主方向;
  • 右上角实验时间码。

相比满屏绘制检测框和标签,这种方式更接近“完成后的科学可视化结果”。

五、低分辨率计算,高分辨率生成

浏览器本地计算必须兼顾性能。

如果直接在 1080P 或 4K 视频上逐帧计算光流,CPU 开销、内存占用和处理时间都会明显增加。因此,我们将分析与成片渲染分成了两条路径。

分析路径:

  • 将视频帧缩小到较低分辨率;
  • 计算灰度图和局部光流;
  • 得到群体区域、向量和轨迹数据。

渲染路径:

  • 保留清晰的源视频采样帧;
  • 将低分辨率坐标映射回成片尺寸;
  • 在清晰画面上重新绘制向量、轨迹和时间码;
  • 输出宽度最高限制为1280像素,避免浏览器内存压力过大。

也就是说,计算可以使用192像素宽的分析帧,但最终视频不会把这张小图直接放大,而是重新在源视频清晰帧上完成合成。

这个设计解决了早期版本中结果视频明显模糊的问题。

六、把实验结果变成真正的素材

如果光流分析结束后只停留在右侧面板,它仍然只是一个临时预览,无法参与后续编辑。

因此,完成分析后,系统会继续执行:

  1. 渲染每一个采样帧;
  2. 使用 WebCodecs 编码为 WebM;
  3. 生成时间线缩略帧;
  4. 创建视频素材对象;
  5. 自动加入“我的素材”;
  6. 选中新生成的结果;
  7. 不自动插入时间线。

素材中会保存:

代码语言:javascript
复制
{
  type: "video",
  generatedBy: "optical-flow-tracking",
  duration,
  width,
  height,
  trackFrames,
  trackFrameDuration,
  diagnostics: {
    detector,
    sampleRate,
    vectors,
    cohorts,
    dominantAngle,
    stability
  }
}

这样生成的视频可以继续作为主画面或画中画素材使用,也能在时间线中显示真实帧序列,而不是一个空白色块。

七、浏览器本地处理的意义

整个实验不需要把用户视频上传到服务器。

视频解码、人物或物体识别、光流计算、轨迹聚合和结果编码都在当前浏览器中完成。

这种方式具有几个优势:

  • 视频数据无需离开本地设备;
  • 不依赖后端推理队列;
  • 参数调整后可以直接重新运行;
  • 结果能立刻进入编辑器素材库;
  • 更适合原型验证、教学演示和隐私敏感内容。

当然,浏览器本地处理也有边界。长视频、高采样率和高分辨率会增加计算时间;快速遮挡、镜头切换以及严重运动模糊,也可能导致群体轨迹不稳定。

因此,这项功能目前仍被定位为实验能力,而不是精密的工业测量工具。

八、一次实际测试

我们使用一段分辨率为852×480、时长约2秒的视频进行测试,并将时间采样设置为4fps。

处理结果包含:

  • 9个分析采样帧;
  • 268个有效运动向量;
  • 1个主要语义运动群体;
  • 主运动方向约235度;
  • 轨迹稳定度约78%。

分析完成后,浏览器自动生成了一段852×480的光流结果视频,并将其加入“我的素材”。

输出画面保留原始视频清晰度,同时叠加青色向量、群体边界、累计轨迹和右上角时间码。

九、总结

这次开发中最重要的变化,不是换了一种光流算法,而是改变了光流在产品里的语义。

原来的问题是:

帧与帧之间有哪些像素发生了变化?

现在的问题是:

画面中的人物或物体群体正在如何移动,它的运动轨迹是什么?

通过“语义检测、区域光流、群体聚合、轨迹累计、高分辨率渲染和素材入库”这条完整链路,光流不再只是铺满画面的技术特效,而是成为一种能够被理解、复用和继续编辑的运动分析素材。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、为什么单纯的光流不够用
  • 二、语义检测与光流结合
    • 1. 识别主体
    • 2. 建立大块语义群体
  • 三、只在语义区域内计算光流
  • 四、从瞬时向量变成累计轨迹
  • 五、低分辨率计算,高分辨率生成
  • 六、把实验结果变成真正的素材
  • 七、浏览器本地处理的意义
  • 八、一次实际测试
  • 九、总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档