PyVSR视频超分实战:从环境搭建到训练调参避坑
简介:这套基于Python的PyVSR视频超分算法设计源码,面向图像处理与计算机视觉方向的中高级研究者和开发者,提供完整的视频超分辨率实现,可应用于视频增强、监控画质提升、存储传输优化等实际场景。资源共34个文件,约67.42MB,涵盖4个Python核心脚本、3套预训练模型、XML与yml参数配置,以及GPU/CPU处理前后的对比视频与图片、调试日志和依赖清单,目录结构清晰,便于按模块定位检索。已有347人浏览学习。通过阅读源码,可掌握视频帧间关联建模、单帧超分重建、多平台推理等关键设计思路;同时可借助开源实现和参数配置快速开展实验,并根据业务需求调整处理链路。整体算法模块划分明确,扩展性好,适合在遵守许可协议的基础上二次开发,推动视频质量提升方案落地。
1. 视频超分不是玄学:PyVSR 把论文算法变成了可跑的源码
视频超分(Video Super-Resolution)这几年从学术圈火到了工程圈,核心诉求很直接:把低分辨率的视频在时间维度上恢复出更高分辨率、更清晰的帧序列。静态图像超分只处理单帧,视频超分还要利用帧与帧之间的时序信息,让纹理在运动过程中保持稳定,不闪、不糊、不出现伪影。PyVSR 正是这样一个基于 Python 实现的研究型视频超分算法库,它把 ESRGAN、EDVR、TOF、FRVSR 等主流模型的训练和推理流程封装成了统一的工程框架,让研究者不必从零搭建数据管道和训练循环,也让工业界做算法验证时有了一份可对照的参考源码。
我最早接触 PyVSR 是因为项目里要做监控视频的清晰化,当时在 GitHub 上翻到的开源视频超分项目不少,但多数只给了模型结构,没有完整的训练流程和数据预处理,根本没法复现论文指标。PyVSR 的定位恰好补上了这个缺口:它基于 PyTorch 实现,支持多卡训练、验证和视频级推理,配置文件驱动一切,改模型、换数据集、调训练参数都不需要改动核心代码。对刚进入超分领域的新手来说,这是一条能顺着跑通的完整链路;对做过几年图像算法的人来说,它的工程组织方式也值得借鉴——尤其适合用来做自己团队的视频超分算法基线。
这篇笔记我把 PyVSR 从环境搭建、数据准备、模型选型到训练调参、推理落地整个流程拆开讲,重点落在那些文档里不会写清楚、实际跑起来一定会踩的坑上。你不需要预先精通超分理论,只要会基本的 Python 和 PyTorch 操作,跟着下面的步骤走,至少能把一个视频超分模型跑起来。
2. 环境搭建与 PyVSR 源码结构:先跑通推理再碰训练
2.1 用 conda 创建 PyVSR 专属环境,避免依赖冲突
PyVSR 依赖的 PyTorch 版本和 CUDA 版本之间耦合很强,我强烈建议不要直接往 base 环境里装,否则光一个 opencv-python 和 torchvision 的版本冲突就能耗掉半天。常见的做法是先用 conda 创建一个干净环境,指定 Python 3.7 或 3.8。PyVSR 官方模板对 Python 3.9 以上的支持不好,主要是旧版 apex 和 torch 的编译兼容问题,老老实实用 3.8 最省心。
conda create -n pyvsr python=3.8
conda activate pyvsr
conda install pytorch=1.8.0 torchvision=0.9.0 cudatoolkit=11.1 -c pytorch
conda install -c conda-forge opencv=4.5.1
创建环境后先不要急着安装其他依赖。PyVSR 的核心依赖其实很少,主要就是 PyTorch、OpenCV、numpy、tqdm、yaml 和 tensorboard。其中 opencv 用 conda 装比 pip 装好处明显,conda 会自动带上 ffmpeg 相关库,后续做视频读写时遇到编码问题的概率会小很多。如果机器是纯 CPU 环境也可以跑,但推理速度会慢到让人怀疑人生,单帧 720p 超分到 1440p 可能要好几秒,只适合验证流程,不适合做效果评估。
提示:如果你的 GPU 是 30 系列以上,CUDA 11.1 和 PyTorch 1.8 组合会出现 “no kernel image is available” 的报错,这是驱动和 CUDA 版本匹配问题。解决方法是把驱动升级到 460 以上,或者直接用 PyTorch 1.10 + CUDA 11.3 的组合。
2.2 源码目录映射:每个文件夹是干什么的,改哪里心里要有数
PyVSR 源码下载解压后,第一件事不是急着读代码,而是把目录结构在脑子里建立一张地图。标准 PyVSR 工程的目录组织如下,不同 fork 会有小差别,但骨架基本一致。
pyvsr/
├── config/ # 训练和测试的 yaml 配置文件
├── datasets/ # 数据加载逻辑,主要是 lmdb 格式和视频帧序列
├── models/ # 网络结构定义,如 edsr.py、rcan.py、edvr.py
├── losses/ # 损失函数,包含 CharbonnierLoss、PerceptualLoss 等
├── utils/ # 工具函数,指标计算、图像归一化、logger 等
├── train.py # 训练入口
├── test.py # 推理入口
├── demo.py # 轻量级演示脚本,可以直接跑单张图或视频
└── options/ # 参数解析模块,支持 yaml 覆盖默认参数
这里最容易让新手困惑的是 config 和 options 两套配置逻辑。 options 里是 argparse 定义的默认参数, config 下的 yaml 文件则用来覆盖默认值。运行时 train.py -opt config/train_edvr.yaml 会先加载命令行参数,再读 yaml 层层的合并最终得到训练配置。所以改参数时优先改 yaml,不要直接动 options 里的代码,否则后续维护多个实验时会改乱。
datasets 目录下的代码是通往数据管道的入口。超分训练需要成对的低分辨率和高分辨率素材,PyVSR 支持两种常见输入格式:一种是预先做成 LMDB 数据库,训练时随机裁剪 patch 喂给网络,速度最快;另一种是直接读取视频帧序列目录,适合小规模调试。我第一次使用时图省事直接读帧目录,结果训练速度只有 LMDB 模式的六成,后来换成 LMDB 才把 GPU 利用率拉上去,这个细节后面专门讲。
2.3 跑通内置 demo:用一张低分辨率视频帧验证环境完整性
环境搭好、目录认清后,别急着训练。先用 PyVSR 自带的 demo 脚本做一次全链路冒烟测试,确认模型加载、前向推理、图像后处理都没问题。一般仓库里会带一个预训练权重文件,如果没有,可以先用随机权重跑通流程,虽然输出是雪花噪点,但至少能证明代码管道没断。
python demo.py --model edsr --scale 4 --input ./test_data/input.png --output ./test_data/output.png
这个命令做的事情很直白:加载 EDSR 模型,把输入图像按 4 倍超分重建,最终保存输出。 --model 参数决定加载哪个网络结构定义, --scale 是放大倍数,PyVSR 支持 2、3、4 倍, --input 和 --output 就是读写路径。参数解析成功后终端会打印模型参数数量和输入张量 shape,很有辨识度。
跑通 demo 后,建议做一次快速验证:用 OpenCV 把一段低分辨率视频逐帧拆开,对每一帧调用模型前向推理,再拼接成视频。这一步不是在写生产代码,而是在确认视频超分链路中“帧读取—归一化—模型推理—反归一化—视频编码”每一环的数据格式是连续的。常见的翻车点在于输入图像是 uint8 的 0 到 255 范围,模型内部需要归一化到 0 到 1,输出后再乘回 255,任何一步遗漏都会导致画面发黑或发白。
import cv2
import torch
from models import build_model
model = build_model('edsr', scale=4)
state_dict = torch.load('pretrained/edsr_x4.pth', map_location='cpu')
model.load_state_dict(state_dict, strict=True)
model.eval()
cap = cv2.VideoCapture('low.mp4')
fps = cap.get(cv2.CAP_PROP_FPS)
frames = []
with torch.no_grad():
while True:
ret, frame = cap.read()
if not ret:
break
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
tensor = torch.from_numpy(frame).float().permute(2, 0, 1).unsqueeze(0) / 255.0
out = model(tensor).squeeze(0).permute(1, 2, 0).numpy() * 255.0
out = cv2.cvtColor(out.astype('uint8'), cv2.COLOR_RGB2BGR)
frames.append(out)
这段代码里模型输入是 NCHW 格式,通道顺序是 RGB;但 OpenCV 读出来的是 BGR,所以 cvtColor 这一步不能省。很多第一次跑视频超分的人在这里翻车,输出的视频颜色偏蓝或偏红,就是通道顺序没有处理好。另外 PyVSR 的模型前向对接的是 Tensor,所以 CPU 环境下要把模型切到 CPU 模式,GPU 环境下要确保输入张量也在 CUDA 设备上,否则跨设备操作会直接报错。
3. 数据准备与 LMDB 生成:视频超分训练集的质量决定了模型上限
3.1 视频超分数据集选型:Vimeo-90K 与 REDS 的取舍
PyVSR 官方最常用的训练数据集是 Vimeo-90K 和 REDS,两者都是视频超分领域的基准数据。Vimeo-90K 包含约 9 万个短视频片段,每个片段 7 帧,场景多样,适合训练通用视频超分模型;REDS 是 NVIDIA 和商汤联合发布的真实视频数据集,包含 240 个训练视频,分辨率和运动幅度更大,更接近真实场景。还有一个常用数据集是 Vid4,主要用于测试。
选择时我建议根据目标场景决定。如果是做通用视频超分研究,直接上 Vimeo-90K,数据量大、场景覆盖面广,训练出来的模型泛化性相对好。如果是做监控、直播或影视相关应用,REDS 这种运动剧烈的数据更合适,但 REDS 的数据量比 Vimeo-90K 小,训练之前通常会用多尺度裁剪和旋转增强来扩充样本。
PyVSR 官方在 datasets 里实现了 Vimeo-90K 的读取器,只要你把数据按指定目录结构放好就可以直接用。如果自己准备了视频,则需要把每个视频抽帧成序列,并与对应的低分辨率帧序列配对,然后打包成 LMDB。
3.2 自己造 LMDB:从视频抽帧到生成训练数据的完整脚本
LMDB 的全称是 Lightning Memory-Mapped Database,超分领域用它存储海量小图像帧,比直接读文件快几个量级。PyVSR 训练时每次要随机裁剪 256x256 的 HR patch 和对应 64x64 的 LR patch,如果从单独文件读取,随机 I/O 会拖垮训练速度。LMDB 把数据整体映射到内存,索引寻址极快,GPU 利用率瞬间拉满。
下面是一个把视频帧目录打包成 LMDB 的参考脚本,这里假设你已经把每个视频片段抽帧到了 HR/seq001/0001.png 这种结构中:
import lmdb
import cv2
import os
import pickle
import numpy as np
def create_lmdb(hr_root, lmdb_path, scale=4, quality=95):
env = lmdb.open(lmdb_path, map_size=1099511627776) # 1TB 容量
txn = env.begin(write=True)
keys = []
cnt = 0
for seq in sorted(os.listdir(hr_root)):
seq_path = os.path.join(hr_root, seq)
if not os.path.isdir(seq_path):
continue
for frame in sorted(os.listdir(seq_path)):
if not frame.endswith('.png'):
continue
hr = cv2.imread(os.path.join(seq_path, frame))
h, w = hr.shape[:2]
lr = cv2.resize(hr, (w // scale, h // scale), interpolation=cv2.INTER_CUBIC)
# 压缩成 JPEG 存储,节省空间,质量设为 95 基本无损
_, hr_enc = cv2.imencode('.jpg', hr, [cv2.IMWRITE_JPEG_QUALITY, quality])
_, lr_enc = cv2.imencode('.jpg', lr, [cv2.IMWRITE_JPEG_QUALITY, quality])
key = f'{seq}_{frame}'.encode()
txn.put(key, hr_enc.tobytes() + b'|' + lr_enc.tobytes())
keys.append(key)
cnt += 1
if cnt % 1000 == 0:
print(f'{cnt} frames processed')
txn.commit()
env.close()
if __name__ == '__main__':
create_lmdb('data/HR', 'data/train.lmdb')
这段代码里有一个关键决策:把 HR 和 LR 拼接在同一个 value 中,用竖线分隔。这样读取时一次 I/O 拿到两张图,避免两次查询带来的额外开销。 map_size 要预先根据数据量估计好,通常按每帧 200KB 到 500KB 估算,如果数据超过 map_size 会报 MDB_MAP_FULL 错误,出现这个错误不是数据坏了,而是映射空间不够。
提示:读取 LMDB 时的坑在于
lr是cv2.resize出来的,默认不做抗锯齿处理。训练前需要确认退化方式与你的目标一致,如果你期望的是模糊加下采样,那么 resize 只用立方插值是不够的,得用高斯核先模糊再采样。
3.3 数据增强策略:翻转、旋转与时间反转不能乱用
视频超分和单图像超分在数据增广上有很大不同。单图超分可以对每一帧独立做水平翻转、90 度旋转,但视频超分是输入一段连续帧,时间方向上是强相关的,有些视频超分模型(如 TOF)依赖帧间光流,如果你对时间轴做反转,光流方向也跟着反转,模型需要增加学习成本去适应。更糟糕的是,如果模型结构本身不具备时序对称性,时间反转增广反而会降低训练效果。
常见的做法是:对每个训练 clip,以 50% 概率随机做水平翻转,以概率 25% 做 90、180 或 270 度旋转,垂直翻转不推荐,因为自然界视频垂直翻转后语义变化太大。对于 Vimeo-90K 这种 7 帧片段,训练时通常是随机连续采样 5 帧或 7 帧输入网络。
PyVSR 的数据增强逻辑在 datasets/vimeo90k.py 中,默认是随机裁剪 patch 加水平翻转。你可以增加一个空间旋转操作,但要注意在模型推理时,输入帧数应该和训练时保持一致,否则模型时域建模能力会打折。我有一个血泪经验:训练 EDVR 时使用了 7 帧输入,推理时为了省显存改成 5 帧,结果画面闪烁很厉害,后来发现 EDVR 的时空注意力机制依赖帧间窗口大小,改了输入帧数后效果崩塌。
4. 模型选型与训练调参:从 EDSR 到 EDVR,PyVSR 里如何做选择
4.1 模型结构对比:EDSR、RCAN 与 EDVR 的适用边界
PyVSR 支持的模型不止一种,但核心结构都围绕残差网络展开。EDSR 是最基础的单图超分网络,把残差块堆深,去掉 BatchNorm,对视频处理时它逐帧独立处理,基本不做时序建模,速度最快,效果最弱,适合做消融基线。RCAN 引入了通道注意力机制,单图超分效果比 EDSR 高一个档次,但同样没有利用帧间信息。真正意义上的视频超分模型是 EDVR,它包含 PCD(Pyramidal Cascading Deformable)对齐模块和 TSA(Temporal and Spatial Attention)融合模块,先把相邻帧与参考帧做可变形卷积对齐,再用时空注意力融合多帧信息提升画质。
这三者的边界很清晰:如果视频内容运动幅度小,比如监控固定机位,EDSR 逐帧处理可能就够用,因为静态场景中帧间信息量不大;如果运动剧烈,比如体育比赛或电影动作镜头,EDVR 的对齐和融合能力能明显减少重影和抖动。RCAN 则更适合动画视频,因为动画画面通常细节边缘锐利,通道注意力能强化高频纹理的重建。
PyVSR 中指定网络结构的参数是 --model ,它背后调用了 models/__init__.py 中的模型构建工厂函数。你新增一个自定义模型时,只需要将网络定义放在 models/ 目录,并在 build_model 函数中加上一个 elif 分支,这就是 PyVSR 留给你扩展的典型入口。
4.2 训练参数配置:batch size、学习率和 loss 权重的工程经验
PyVSR 的训练配置集中在 yaml 文件中,下面是一份针对 8GB 显存 GPU 的 EDVR 训练配置参考:
name: EDVR_x4_Vimeo90K
model_type: VideoSRModel
num_gpu: 1
manual_seed: 42
datasets:
train:
name: Vimeo90K
type: Vimeo90KDataset
dataroot_GT: data/train.lmdb
meta_info_file: data/meta_info_Vimeo90K.txt
num_frame: 7
gt_size: 256
use_flip: true
use_rot: true
val:
name: Vid4
type: VideoRecurrentTestDataset
dataroot_GT: data/Vid4/GT
dataroot_LQ: data/Vid4/BIx4
network:
type: EDVR
num_feat: 64
num_frame: 7
deformable_groups: 8
num_extract_block: 5
center_frame_idx: 3
train:
batch_size: 8
lr: !!float 2e-4
lr_scheduler: CosineAnnealingRestartLR
loss:
type: CharbonnierLoss
weight: 1.0
perceptual_weight: 0.1
batch size 设成 8 意味着每个 GPU 上的 batch 为 8,如果显存不够,优先把 gt_size 从 256 降到 192,而不是减小 batch size,因为视频超分模型对 patch 大小敏感,太小 patch 会导致模型感受野不足,重建细节粗糙。学习率用 2e-4 是 EDVR 论文的初始设置,PyVSR 的 cosine annealing 重启调度器会自动衰减再重启,这个设计对图像增强类网络往往比 step 衰减更稳。损失函数这里用了 CharbonnierLoss 加权 1.0,加上感知损失权重 0.1,感知损失能提升视觉锐利度,但过高权重会产生假纹理和色彩偏移,不建议超过 0.5。
一个最容易被低估的参数是 center_frame_idx 。EDVR 输入 7 帧,通常以第 3 帧(0 索引)作为参考帧,其他帧向参考帧对齐。如果你的视频场景中目标对象移动速度极快,参考帧固定的策略会因为大位移导致对齐失败,这时候可以考虑减小输入帧数,比如 5 帧,让参考帧两侧的帧离得更近。
4.3 训练启动与断点续训:跑三天不出 bug 的技巧
万事俱备后,启动训练的命令非常简单:
python train.py -opt config/train_edvr_x4.yaml
PyVSR 会在控制台打印每个 iteration 的 loss 和学习率,并随机抽样一组低分辨率输入和模型输出做对比,保存到 tb_logger 指定的目录。训练过程如果中断,你只需要在 yaml 里设置 path.resume_state: experiments/EDVR_x4_Vimeo90K/training_state/latest.state ,它会从指定的状态文件恢复优化器、调度器和 epoch 进度。
断点续训的坑在于模型结构改了但状态文件是旧的,直接 resume 会报 key mismatch。常见的解决办法是删除 pretrain_network 参数,只保留 resume_state ,让模型从头构建。另外,保存检查点时注意区分 training_state 和 network_snapshot ,后者是纯网络权重,常用于后续测试,前者包含优化器状态,体积大很多。
训练过程中翻车最多的是 CUDA out of memory,除了降 batch size 或 patch 大小,还可以检查 PyVSR 是否开启了梯度检查点。如果模型实现中使用 torch.utils.checkpoint 包装了中间层,训练显存能省掉 30% 左右,代价是反向传播时多一次前向计算,训练时间会增加约 20%。PyVSR 默认不开启,你需要在模型初始化时传入 checkpoint_grad 参数。
5. 训练与推理翻车避坑:五条高频踩坑记录,每一条都真实发生
5.1 现象:训练 loss 下降但验证集 PSNR 不升反降
原因:这是典型的过拟合特征,或者验证集和训练集分布不一致。在视频超分里更隐蔽的原因是验证时输入帧数设置错误。训练时用了 7 帧输入,验证脚本里只给了 1 帧或 3 帧,模型时序信息不足,重建质量自然下降。
解决:检查验证代码中数据集加载的 num_frame 是否与训练一致。PyVSR 的测试数据集默认会按时间顺序连续取 7 帧,但如果你用自己写的 dataloader,很容易漏了滑窗逻辑。另外确认验证集 LR 的退化方式和训练时一致,比如训练用 bicubic 4 倍下采样,验证时用了 blur 加 downscale,PSNR 会掉 0.5 到 1dB,这是正常现象,不是训练出问题。
5.2 现象:视频输出帧间闪烁,单帧效果好但连续播放抖动
原因:逐帧独立推理没有利用时序一致性,或者模型虽有时序建模,但推理时输入 clip 之间没有重叠区域,导致时间上下文断裂。
解决:使用 EDVR 或 TOF 这类时序模型时,推理时滑动窗口应设置 stride 小于输入帧数。比如输入 7 帧,输出中心帧,那么每次窗口向前移动 1 帧,保证相邻两次推理的上下文有 6 帧重叠。这样做能极大缓解闪烁。代价是推理时间成倍增加,如果生产环境实时性要求高,就需要改用循环骨架的 FRVSR。
5.3 现象:训练到一半 loss 变成 nan,代码没报错
原因:最直接的原因是学习率过大导致梯度爆炸。PyVSR 的损失函数如果包含感知损失,VGG 特征层的数值范围比较大,学习率超过 1e-3 容易炸。另一个隐蔽原因是 LMDB 里存在损坏的 JPEG 数据,读取后变成全零或奇异像素,导致 loss 计算出现 inf。
解决:先降低学习率到当前一半重训;如果仍然 nan,写一个脚本遍历 LMDB,检查每帧图像的平均值和方差,发现方差为 0 的记录并删除。还可以打开 yaml 中的 gradient_clip 参数,设为 0.5 或者 1.0,PyVSR 支持对梯度裁剪,这是防止训练震荡的最后保险。
5.4 现象:模型推理速度远低于论文报告,GPU 利用率不到 50%
原因:PyVSR 默认不做通信和算子优化。视频超分模型中的可变形卷积和注意力操作小算子很多,频繁 kernel launch 会带来巨大开销。如果数据读取流程没有使用 LMDB,而是直接从磁盘读 PNG,数据加载和预处理就会成为瓶颈,GPU 大部分时间在空转。
解决:训练时务必使用 LMDB 数据源;推理时将 batch 维度填满,比如一次性输入多个视频帧的叠加 batch,利用 GPU 并行性。可变形卷积建议用编译好的 mmcv 或者 deform_conv 库替换 PyVSR 里原生的 python 实现,单帧推理时间可以缩短 20% 到 30%。同时注意测试脚本中是否开启了梯度计算,如果忘记用 torch.no_grad() 包裹推理逻辑,显存和耗时都会大幅上升。
5.5 现象:多卡训练时 loss 正常但模型性能比单卡差
原因:PyVSR 使用 DistributedDataParallel 做多卡同步,如果 batch size 是全局 64,那么每张卡只分到 64 / num_gpu 个样本。相比单卡 batch 64,每卡小 batch 计算出的 BatchNorm 统计量不准确,但超分模型一般不用 BatchNorm,所以问题更大可能是学习率没有线性缩放。
解决:多卡训练时把显存足够的单卡 batch size 保持和单卡实验一致,比如单卡 8,四卡就是全局 32,这种条件下学习率可以保持不变;如果因为显存原因被迫把单卡 batch 降到 4,那么学习率应该缩半。另外检查 --launcher pytorch 参数是否正确定义了分布式端口,PyVSR 默认使用 29500,多台机器训练时要避免端口冲突。
6. 用随机视频验证超分效果:客观指标与主观感知结合的方法
模型训练完成后,别只看测试集上 PSNR 和 SSIM。这两个指标在视频超分领域饱受诟病,因为它们无法反映时间连续性和人眼感知。我自己的习惯是训练结束后做两个层面的验证:一是固定测试集上的客观指标对比,二是随机找几个真实低分辨率视频,跑完整推理链路,观察闪烁和伪影。
客观验证用 PyVSR 提供的测试脚本即可:
python test.py -opt config/test_edvr_x4.yaml
测试配置里要写清楚模型权重路径和测试数据集类型。PyVSR 会输出每帧的 PSNR 和 SSIM,并计算整个视频的平均值。你可以把同一测试集上的 EDVR、RCAN、EDSR 都跑一遍,画一张表格对比。但要注意,同一次测试中每个模型用相同的配置才公平,特别是是否需要把 LR 图像用双三次插值预放大到 HR 尺寸——这个预处理会直接改变 PSNR 数值,很多论文里不提这里,复现时会差 0.3dB 以上。
主观验证我通常会准备一段包含大量平移运动的视频,比如汽车行驶的行车记录仪画面或体育慢动作。用超分模型处理后,重点观察画面中的物体边缘是否稳定,背景纹理是否出现摩尔纹。一个直观的验证方法是把超分后的视频逐帧抽出来,减去相邻帧的差值,在差值图上能看到静态边缘位置的高亮,说明帧间重建位置有抖动。
注意:视频超分不是直接把每个 LR 帧放大成 HR 帧。如果你的目标只是让视频看起来更清楚,务必确认输入退化方式和你实际视频的退化方式是否一致。监控摄像头通常有传感器噪声和压缩伪影,与 bicubic 退化模型差别很大,此时模型效果会打折扣,你需要考虑微调或使用盲超分方案,这是另一个话题。
7. 进阶:用 PyVSR 的 eval 模式做模型轻量化和推理加速
如果你想把这个超分算法推向实际部署,不能只停在 PyTorch 的模型权重上。PyVSR 本身不包含 ONNX/TensorRT 导出脚本,但模型结构是标准 PyTorch,于是可以借助 torch.onnx.export 把 EDVR 的骨干部分导出,然后用 TensorRT 做高精度推理。不过 EDVR 的可变形卷积模块在导出 ONNX 时是一大障碍,如果你只做前置对齐部分统一进 ONNX,会出现算子不兼容的问题。
一个性价比更高的轻量化思路是在 PyVSR 中把 EDVR 的 num_feat 从 64 降到 48 或 32,训练一个轻量版。这个改动只需要在配置文件的 network 段修改,PyVSR 会自动调整所有残差块的通道数。轻量版模型参数量大约减少 40% 到 50%,PSNR 下降约 0.2dB,但推理速度上升非常可观,在一张 1080Ti 上 720p 视频超分到 1440p 可以跑到 25 到 30 FPS。
另外 PyVSR 中 demo.py 已经支持把输入视频拆帧、超分、再合成,但你有没有注意到它合成视频时用的是 OpenCV 的 VideoWriter ,编码器默认是 MP4V,处理 4K 输出时画质损耗明显。建议在合成时用 H264 编码并提高码率,或者直接输出 PNG 序列再交给 ffmpeg 合成,这样既能保留全部重建细节,也能让最终产出文件的大小可控。
我的习惯是每个训练好的模型都固定住一套推理验证脚本,里面对应同一组视频片段,保证任何一次改动之后都能对比画面。这是最容易坚持也最容易被忽视的好做法。视频超分的评估从来不是一锤子买卖,希望这篇笔记能帮你把 PyVSR 用起来,也希望你能少踩几个我踩过的坑,把更多时间留给算法本身。
更多推荐

所有评论(0)