在浏览器中实现视频四倍高清修复:NanoVSR 与 WebGPU 实战

简介: 本项目基于WebGPU与ONNX Runtime Web,在浏览器中实现端到端视频四倍超分修复。采用NanoVSR 644K模型,支持五帧时序推理、自动宽高比适配、Worker异步处理及FFmpeg.wasm音视频合成,全程无需服务端参与。

在浏览器中实现视频四倍高清修复:NanoVSR 与 WebGPU 实战

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

在线体验:https://video-editor.ai-creator.top/

前言

在 Timeline Studio 中,我们实现了一套完全运行在浏览器中的视频高清修复流程。

用户选择视频片段后,浏览器会提取画面,使用 NanoVSR 模型执行四倍超分辨率处理,再将生成的图片序列编码为 MP4,并保留原视频中的音频。

这套实现主要使用以下技术:

  • NanoVSR 644K
  • ONNX Runtime Web
  • WebGPU
  • Web Worker
  • OffscreenCanvas
  • FFmpeg.wasm
  • React

本文介绍其中几个关键的工程设计。

整体处理流程

视频高清修复的完整流程如下:

读取视频片段
  |
  v
按照时间提取视频帧
  |
  v
每五帧组成一个处理分组
  |
  v
转换为 NanoVSR 输入张量
  |
  v
通过 WebGPU 执行 ONNX 推理
  |
  v
生成高清图片序列
  |
  v
使用 FFmpeg.wasm 编码视频
  |
  v
加入原视频音频
  |
  v
生成新的 MP4 素材

项目中的主要代码位于:

为什么使用五帧模型

如果把视频拆成图片,然后独立处理每一帧,虽然单帧看起来比较清晰,但连续播放时可能出现细节变化。

例如:

  • 头发纹理在相邻帧中发生变化
  • 细线和文字边缘轻微抖动
  • 压缩纹理在播放时出现闪烁
  • 相同物体的细节不够连续

为改善这个问题,图片和视频使用了不同的模型输入。

场景 输入张量 输出张量
图片 [1, 1, 3, 180, 320] [1, 1, 3, 720, 1280]
视频 [1, 5, 3, 180, 320] [1, 5, 3, 720, 1280]

视频模型一次接收五帧画面,可以同时参考前后帧的信息。

在代码中,视频按照五帧进行分组:

const groupStart = Math.floor(index / 5) * 5;
const count = Math.min(5, totalFrames - groupStart);

最后一组不足五帧时,使用最后一个有效画面补齐输入,但只输出实际需要的帧。

这样既能利用时序信息,也能限制一次推理所需的内存。

在 Web Worker 中运行模型

视频模型推理比较耗时。如果直接在页面主线程执行,可能影响界面渲染、视频播放和时间线操作。

因此,我们将 ONNX Runtime 放在独立的 Web Worker 中:

const worker = new Worker(
  new URL("../workers/nanovsr.worker.js", import.meta.url),
  {
    type: "module" },
);

主线程负责:

  • 加载视频
  • 定位时间
  • 创建 ImageBitmap
  • 管理任务状态
  • 接收处理进度
  • 保存最终结果

Worker 负责:

  • 加载模型
  • 创建 WebGPU Session
  • 构造输入张量
  • 执行模型推理
  • 生成高清图片

图片通过 transferable object 发送给 Worker:

worker.postMessage(
  {
   
    type: "enhance",
    requestId,
    bitmaps,
    outputCount,
  },
  bitmaps,
);

这种方式会转移 ImageBitmap 的所有权,减少大尺寸图像在两个线程之间复制的次数。

处理完成后,需要及时释放相关资源:

bitmap.close?.();
result.sr.dispose?.();

对于视频任务,资源清理非常重要。如果 Bitmap、Canvas 或 Tensor 长时间保留,处理较长视频时内存占用会持续增加。

使用 WebGPU 执行 ONNX 推理

项目通过 ONNX Runtime Web 创建 WebGPU 推理会话:

const session = await ort.InferenceSession.create(model, {
   
  executionProviders: ["webgpu"],
  graphOptimizationLevel: "all",
});

同时设置高性能设备偏好:

ort.env.webgpu.powerPreference = "high-performance";

运行任务前会检查浏览器是否支持 WebGPU:

if (!self.navigator?.gpu) {
   
  throw new Error("This feature requires WebGPU");
}

视频超分辨率的计算量明显高于普通图像分类任务。明确检查运行条件,可以避免在不合适的执行环境中启动耗时任务。

保持原视频的宽高比

模型输入尺寸固定为 320 乘 180,但用户导入的视频可能是横屏、竖屏或正方形。

如果直接把画面拉伸到固定尺寸,会改变人物和物体的比例。

项目采用等比例包含方式计算绘制区域:

const scale = Math.min(
  INPUT_WIDTH / width,
  INPUT_HEIGHT / height,
);

const drawWidth = width * scale;
const drawHeight = height * scale;

画面会等比例绘制到 320 乘 180 的模型画布中,剩余区域填充为黑色。

模型输出 1280 乘 720 的结果后,再按照预处理阶段记录的位置裁切有效区域。这样可以保持原素材的宽高比,也不会裁掉原始内容。

构造模型输入张量

每个输入帧会先绘制到 OffscreenCanvas,然后读取像素数据:

const pixels = context.getImageData(
  0,
  0,
  INPUT_WIDTH,
  INPUT_HEIGHT,
).data;

浏览器 Canvas 返回的是 RGBA 排列,而 NanoVSR 需要 RGB 通道分离的浮点张量。

转换过程如下:

tensor[rOffset + index] = pixels[pixel] / 255;
tensor[gOffset + index] = pixels[pixel + 1] / 255;
tensor[bOffset + index] = pixels[pixel + 2] / 255;

视频模型最终接收的张量形状为:

1, 5, 3, 180, 320

各个维度分别表示:

批次数量、帧数量、颜色通道、高度、宽度

模型下载与缓存

首次运行时,浏览器需要下载 ONNX 模型。

模型使用固定版本地址,而不是会持续变化的默认分支:

const MODEL_REVISION =
  "d551be137b16ecdf12637387f2fb4776565e763f";

固定版本有几个好处:

  • 线上代码和模型始终对应
  • 缓存身份保持稳定
  • 问题更容易复现
  • 更新模型时可以明确控制版本

模型下载完成后会写入 Cache Storage:

const cache = await caches.open(
  "timeline-studio-nanovsr-v1",
);

await cache.put(cacheKey, new Response(modelBytes));

缓存键由模型仓库、版本和文件路径组成,而不是直接使用下载地址。

const cacheKey =
  "/__model-cache__/haixin/" +
  "timeline-studio-onnx-models/" +
  MODEL_REVISION +
  "/" +
  modelPath;

这样即使模型的下载节点发生变化,浏览器仍然可以识别已经存在的缓存。

创建完成的 ONNX Session 也会保存在 Worker 中:

const sessionPromises = new Map();

图片模型和视频模型分别初始化一次。用户再次使用高清修复时,可以直接复用已经加载的模型。

逐帧处理视频

项目目前以 12 帧每秒处理视频:

const frameRate = 12;

const totalFrames = Math.ceil(
  sourceDuration * frameRate,
);

每一帧通过设置 <video>currentTime 完成定位:

video.currentTime =
  sourceStart + frameIndex / frameRate;

等待 seeked 事件后,从视频元素创建 ImageBitmap

const bitmap = await createImageBitmap(video);

收集五张画面后,将它们发送给 Worker。

12 帧每秒是当前版本在处理速度、内存占用和播放效果之间采用的工程参数。对于运动速度较快的视频,后续可以增加动态帧率策略。

重新生成带音频的 MP4

NanoVSR 输出的是高清图片,而不是完整视频。

项目先把模型结果转换为 PNG Blob,然后交给 FFmpeg.wasm:

-framerate 12
-i frame-%06d.png
-c:v libx264
-preset veryfast
-crf 18
-pix_fmt yuv420p
-movflags faststart
output.mp4

输出视频使用:

  • H.264 视频编码
  • CRF 18
  • yuv420p 像素格式
  • faststart MP4

如果原视频包含音频,还会按照当前片段的开始位置和时长读取对应音轨:

-ss sourceStart
-t sourceDuration
-i original-video
-map 0:v:0
-map 1:a?
-c:a aac
-b:a 192k
-shortest

最终生成的视频同时包含高清画面和原片段音频,可以直接加入编辑器的素材库。

进度显示

一次高清修复包含多个处理阶段:

  1. 准备视频画面
  2. 下载或读取模型
  3. 初始化 WebGPU
  4. 执行模型推理
  5. 生成高清帧
  6. 加载编码器
  7. 合成 MP4
  8. 创建新素材

Worker 会发送结构化进度消息:

{
   
  progress,
  phaseKey,
  frameIndex,
  totalFrames,
  backend: "webgpu",
}

界面不仅显示百分比,还会显示真实的帧数量:

正在处理视频帧
已处理 36 / 120 帧

用户可以知道当前任务是在加载模型、执行推理还是编码视频。

任务取消

处理视频可能需要一定时间,因此功能支持取消。

主线程通过 AbortController 管理任务:

const controller = new AbortController();

取消任务后:

  • 停止继续读取视频帧
  • 通知 Worker 停止后续分组
  • 丢弃当前任务结果
  • 释放已经创建的 Bitmap
  • 编码阶段会停止 FFmpeg 实例
  • 不生成不完整的视频文件

需要注意,已经开始执行的单次 WebGPU 推理通常无法立即中断。系统会等待当前五帧分组完成,但不会继续处理下一组。

先预览,再应用

模型处理完成后,结果不会立即覆盖当前视频。

用户可以在对比界面中:

  • 同步播放处理前后的画面
  • 拖动分界线查看同一位置
  • 定位到视频中的指定时间
  • 重新生成结果
  • 确认后再应用到时间线

应用时,项目会同时保存原始素材和处理结果:

enhancement: {
   
  mode: "nanovsr-644k",
  enabled: true,
  original,
  processed,
  backend: "webgpu",
  frameRate,
  totalFrames,
}

这种方式不会直接修改原文件,用户也可以随时切换回原始版本。

当前限制和后续方向

当前实现仍有一些可以继续优化的地方:

  • 模型输入尺寸固定为 320 乘 180
  • 视频处理帧率固定为 12 帧每秒
  • 长视频会产生较多 PNG 中间数据
  • FFmpeg.wasm 的虚拟文件系统会占用额外内存
  • 运行环境需要支持 WebGPU

后续可以采用下面的流式处理方式:

VideoFrame
  |
  v
WebGPU 推理
  |
  v
VideoFrame
  |
  v
WebCodecs 编码
  |
  v
MP4 封装

这样可以减少 PNG 中间文件,避免一次保存全部处理结果,也更适合较长的视频。

对于更高分辨率素材,还可以引入:

  • 分块推理
  • 重叠区域融合
  • 动态输入尺寸
  • 根据设备能力调整处理参数
  • 高运动画面的帧率补偿

总结

浏览器端视频高清修复并不只是运行一次模型,还需要完成一整套媒体处理流程:

  • 视频定位与解码
  • 五帧时序分组
  • ONNX 张量预处理
  • WebGPU 模型推理
  • 宽高比适配
  • 模型下载和缓存
  • Bitmap 与 Tensor 资源释放
  • 原始音频保留
  • MP4 重新编码
  • 进度与取消控制
  • 处理前后对比
  • 非覆盖式结果应用

Timeline Studio 已经将这些环节组合为一个可以直接使用的视频编辑功能。

如果你正在研究 WebGPU、浏览器媒体处理或本地模型推理,可以通过下面的地址查看完整源码:

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

在线体验:https://video-editor.ai-creator.top/

相关文章
|
24天前
|
人工智能 编解码 自然语言处理
把 DeepSeek Harness 接入视频剪辑工作流:开源 Timeline Studio 插件的工程实践
开源插件 dsh-timeline-studio-plugin 将 DeepSeek Harness 接入 Timeline Studio,通过 7 个安全工具实现自然语言驱动的视频工程编辑:支持工程检查、语义预演(diff)、事务式修改(apply)与 MP4 渲染验证,严格限制文件访问边界,保障专业剪辑流程可靠可控。
|
1月前
|
机器学习/深度学习 人工智能 缓存
如何用 Agent Skill 构建一套可编辑、可验证的 AI 视频生产流水线
Timeline Studio 是开源AI视频编辑系统,支持从自然语言需求出发,自动完成素材分析、剪辑决策、时间线修改、浏览器端本地AI推理(Whisper/VITS等)及成片+可编辑`.timeline`工程双重交付,实现专业级自动化视频生产。
|
1月前
|
人工智能 编解码 缓存
Timeline Studio 重大升级:安装一个 Skill,让 AI 在一杯咖啡的时间里完成视频剪辑,并交付可编辑工程
Timeline Studio 是一款AI视频编辑工具,支持从图片/视频/网址自动生成专业视频,并交付可编辑的`.timeline`工程文件。它兼顾自动剪辑与人工精修,实现“一杯咖啡成片,随时局部修改”。开源免费,浏览器本地运行。
|
2月前
|
编解码 缓存 人工智能
从 ffmpeg.wasm 到 WebCodecs:浏览器视频编辑器导出架构的演进与实践
本文介绍了一种混合架构的浏览器视频编辑器导出方案:以Canvas+WebCodecs逐帧合成与编码、OfflineAudioContext混音为核心,ffmpeg.wasm仅负责音频提取、格式标准化及MP4转码等专项任务,兼顾预览一致性、性能与容错性。(239字)
从 ffmpeg.wasm 到 WebCodecs:浏览器视频编辑器导出架构的演进与实践
|
2月前
|
人工智能 缓存 编解码
把 AI 视频剪辑搬进浏览器:Timeline Studio 的本地优先架构与工程实践
视频剪辑、语音合成、自动字幕和 AI 视觉处理,过去往往意味着桌面客户端、服务端转码集群以及漫长的素材上传。Timeline Studio 尝试了另一条路线:以浏览器为完整运行环境,把多轨时间线、ONNX 端侧推理、Canvas 合成、离线音频混音和 WebCodecs 编码连接成一个可用的 AI 视频编辑器。本文结合项目实践,拆解这套架构为何成立、最难的三个工程问题,以及我们如何处理预览与导出一致性、模型缓存、时间线精度和兼容回退
|
9月前
|
数据采集 人工智能 安全
|
6月前
|
安全 数据建模 应用服务中间件
阿里云SSL证书阿里云HTTPS证书阿里云数字证书怎么买、怎么申请、怎么部署?价格 + 申请 + 部署一站式教程
本文详解网站SSL证书配置:对比免费DV证书(适合个人/测试)与付费OV/EV证书(适用企业/金融场景),涵盖有效期、域名支持、验证等级、价格及兼容性差异,并分步指导阿里云平台申请及Nginx服务器部署流程,助程序员快速实现HTTPS安全升级。(239字)
|
7月前
|
缓存 人工智能 自然语言处理
Prompt 缓存的四种策略:从精确匹配到语义检索
本文详解Prompt缓存四大策略(精确匹配、规范化、语义相似、分层架构),直击LLM应用成本痛点——重复调用导致API费用飙升。代码示例+架构图,助你低成本提升命中率,降本30%–90%,延迟同步优化。
809 11
Prompt 缓存的四种策略:从精确匹配到语义检索
|
8月前
|
机器学习/深度学习 人工智能 自然语言处理
AI大模型面试宝典
【AI大模型面试宝典】聚焦Transformer核心架构,拆解自注意力、多头机制、位置编码等高频考点,配代码实现与面试真题解析,助你快速掌握大模型面试关键知识点,无痛拿下offer!
475 0