在浏览器中实现视频四倍高清修复: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/

相关文章
|
7天前
|
人工智能 JSON 安全
Fastjson远程代码执行漏洞,阿里云AI安全为您保驾护航
阿里云AI安全产品联动防御Fastjson攻击
2043 11
Fastjson远程代码执行漏洞,阿里云AI安全为您保驾护航
|
7天前
|
云安全 人工智能 安全
|
7天前
|
人工智能 自然语言处理 数据挖掘
Qwen3.8-Max-Preview深度全解析:2.4万亿参数旗舰MoE模型+Token Plan限时优惠完整落地指南
2026年7月,全新旗舰级混合专家大模型Qwen3.8-Max-Preview正式开放抢先体验,作为通义千问Qwen3系列规格最高、综合推理能力顶尖的新一代模型,该模型总参数量达到2.4万亿(2.4T),是当前线上可调用的原生多模态旗舰模型,综合推理水准对标海外顶级Fable 5模型,在复杂工程开发、长文档深度分析、多步骤智能体自治、跨境多语言创作、海量数据挖掘五大高难度业务场景实现跨越式性能提升。
903 1
|
7天前
|
人工智能 自然语言处理 数据挖掘
最新版通义千问(Qwen3.8-Max-Preview)功能介绍
2026年,通义千问正式推出全新旗舰级大模型 **Qwen3.8-Max-Preview 预览版**,作为首款突破万亿参数规格的新一代基座模型,该模型总参数量达到**2.4万亿**,采用全新迭代的MoE混合专家架构,综合推理性能、长文本处理、多模态理解、复杂任务规划能力全面超越前代Qwen3.7-Max版本,整体实力跻身全球第一梯队,可对标海外顶级旗舰模型,是当前面向复杂工程开发、多智能体协同、超长文档解析、专业办公自动化场景的最优国产基座模型。
911 0
|
9天前
|
人工智能
Qwen3.8抢先体验!正式版即将发布并开源!
千问Qwen3.8即将开源,参数达2.4T,进化速度以“天”计,实力媲美Fable 5。预览版Qwen3.8-Max已上线阿里Token Plan等平台,限时优惠:日间Credits低至1折,夜间更优,个人/团队版月付仅35元起!
912 39
|
5天前
|
自然语言处理 测试技术 API
通义千问Qwen3.8-Max-Preview全功能解析:2.4万亿参数旗舰模型深度使用指南
在大模型技术持续迭代的当下,通义千问推出的Qwen3.8-Max-Preview作为新一代旗舰预览版模型,凭借2.4万亿参数的超大规模、多模态融合能力与全场景适配特性,成为开发者与企业用户探索AI应用的核心工具。该模型采用稀疏混合专家(MoE)架构,是通义千问首个突破万亿参数的多模态模型,可同时处理文本、图像、视频与文档等多种数据形态,在全栈代码开发、复杂逻辑推理、长文档分析与多智能体协作等场景实现跨越式升级。本文将全面拆解Qwen3.8-Max-Preview的核心功能,详解API调用流程与配置方法,覆盖多场景实战技巧,帮助用户快速掌握这款旗舰模型的使用方法,充分释放其性能潜力。
444 1
|
8天前
|
人工智能 自然语言处理 数据挖掘
Qwen3.8-Max 预览版全解析:2.4 万亿参数旗舰模型,Token Plan 限时优惠指南
Qwen3.8-Max-Preview是通义千问Qwen3系列旗舰MoE大模型,参数达2.4万亿,综合推理能力居行业第一梯队。支持思考/快速双模式,擅长大模型五大高难场景。现于阿里云百炼Token Plan、Qoder及QoderWork上线体验,个人版低至39元/月。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
669 1
Qwen3.8-Max 预览版全解析:2.4 万亿参数旗舰模型,Token Plan 限时优惠指南