PyVSR视频超分算法源码全解析:从配置到推理,一条命令实现四倍视频增强
简介:一套基于Python的PyVSR视频超分算法设计源码,面向计算机视觉开发者与算法研究者,利用深度学习模型对视频进行超分辨率重建,提升清晰度与细节表现力,适用于视频增强、监控画质优化、存储传输前质量提升等场景。压缩包共34个文件、约67.42MB,核心包括4个Python源文件(main.py、VideoProcessor.py、FrameSR.py、AudioUtils.py)、4个XML配置、3套预训练模型(BasicVSR、EDVR、PP-MSVSR)及参数信息,以及3个处理前后对比视频、3张示例图片、运行日志和requirements依赖清单。目录结构清晰,脚本、模型、资源、日志分目录存放,便于快速定位和二次开发;CPU/GPU对比素材可直接检验超分效果差异。资源附带开源许可证,便于合规使用与学习分享。目前已有347人学习下载,适合希望上手视频超分、研究PyVSR原理或基于开源实现做定制改进的学习者,借助配置文件和日志还能进一步调整算法参数与排查运行问题。
1. PyVSR视频超分算法源码:一条命令跑通四倍视频增强的完整工程
PyVSR视频超分算法源码是一套基于Python与PaddlePaddle的完整视频超分辨率工程,不是零散脚本拼凑的demo。它内置BasicVSR、EDVR、PP-MSVSR三个预训练模型,带CPU与GPU双推理路径,解压后按顺序执行两条命令就能把一段普通视频提升为四倍分辨率输出。对做视频增强、算法复现或课程设计的人来说,它省掉了搭训练框架、找预训练权重的两段苦路,直接进入推理与效果调优环节。项目里源码、模型、样例视频、日志、前后对比图一应俱全,适合照着读代码学视频超分流程,也适合直接在真实素材上验证超分效果到底行不行。
2. 模型与配置:看懂三个预训练模型和configure.yml背后的选型逻辑
2.1 三个模型文件是什么:BasicVSR、EDVR、PP-MSVSR各自擅长什么
打开源码包的Models目录,能看到三个独立文件夹,每个里面都是同一套Paddle Inference标准文件组合: model.pdmodel 存网络结构, model.pdiparams 存权重参数, model.pdiparams.info 存参数元信息。三个模型的共同点是都在REDS数据集上以四倍超分目标训练,所以输入输出尺寸规则完全一致,换模型时只需改一个名字,不用动调用代码。
| 模型目录 | 来源 | 结构特点 | 适合场景 |
|---|---|---|---|
| BasicVSR_reds_x4 | BasicVSR,CVPR 2021 | 双向循环架构,参数少,逐帧递归校正 | 常规视频增强,速度优先 |
| EDVR_M_wo_tsa_SRx4 | EDVR,ECCV 2020 | 可变形卷积对齐帧间运动,去掉TSA注意力模块 | 大运动、镜头切换、模糊严重的素材 |
| PP-MSVSR_reds_x4 | PaddlePaddle官方多阶段方案 | 多阶段精修,纹理重建更细 | 对画质细节要求高的静态场景 |
BasicVSR是我个人最常用的起步模型。它的双向循环结构把前后帧的信息沿时间轴交替传播,等效感受野覆盖整段视频,对大多数监控画面、短视频素材足够稳,而且推理速度最快。EDVR_M里的可变形卷积是做帧间对齐的,遇到物体高速运动或摄像机晃动时,BasicVSR容易产生拖影,EDVR反而能压住伪影,代价是显存占用和推理时间明显上升。wo_tsa表示权重里拿掉了时空注意力模块,比完整版EDVR轻不少,但精度也会打一点折扣。PP-MSVSR走的是多阶段渐进式重建路线,每个阶段负责不同尺度的细节恢复,输出纹理最细腻,可解释性也好,调试时能清楚看到是哪一阶段拉低了效果。
这里有个容易忽略的点:三个模型虽然网络结构不同,但输入帧都要求是归一化到[0,1]的RGB浮点张量,输出也是[0,1]的RGB。这意味着预处理、后处理代码可以完全复用,代价是模型本身的输入尺寸不固定,任意分辨率的帧都能喂进去,只是越大的图显存压力越大。如果你手里的视频素材在1080P以上,又选了EDVR,最好先看一遍第五章的显存避坑。
2.2 configure.yml参数详解:device、scale与模型路径的绑定关系
项目里的 configure.yml 是全局配置文件,main.py启动时只读它一次,命令行参数可以覆盖同名项。这份YAML里的字段不多,但每个都直接决定推理能不能跑起来:
device: gpu # 推理设备,gpu 或 cpu,两者只选其一
model: BasicVSR_reds_x4 # 模型名,必须与 Models/ 下的目录名严格一致
scale: 4 # 超分倍数,当前三个模型权重固定为 4,不要改成 2 或 8
batch_size: 1 # 视频超分场景固定为 1,逐帧推理
num_workers: 2 # 数据加载线程数,CPU 推理时可适当调大
cpu_threads: 4 # Paddle CPU 数学库线程数,默认 1,多核机器建议调大
save_fps: 0 # 输出帧率,0 表示沿用源视频帧率
这段配置里最容易翻车的是 scale 。乍一看把2改大一点能获得更高倍数,但模型权重是四倍训练出来的,输入一张图它只会输出四倍尺寸,你写8它也不会给你八倍图。想换倍数只有两条路:找对应倍数的预训练模型,或者自己重训。 device 字段则决定了FrameSR.py里走 enable_use_gpu 还是 disable_gpu 分支,混着写不会报错,但可能CPU跑着跑着突然调用GPU接口,异常栈很难看。 save_fps 设0在我实践里最省心,它能保留源视频的时间戳节奏,避免出现超分后音频正常但画面明显加速的怪现象。
2.3 requirements.txt与Python版本:环境依赖的边界在哪
源码包的requirements.txt是标准的依赖锁定文件,核心依赖有四组:PaddlePaddle推理框架、OpenCV视频读写、PyYAML配置解析、NumPy张量操作。一个可用的版本组合长这样:
paddlepaddle-gpu==2.4.0
opencv-python==4.5.5.64
numpy==1.23
pyyaml==6.0
tqdm==4.64.1
需要特别说明的是paddlepaddle与paddlepaddle-gpu是两套独立安装包,不能同时存在。GPU机器装 paddlepaddle-gpu==2.4.0 ,纯CPU机器装 paddlepaddle==2.4.0 ,代码里不需要区分,Paddle Inference会按configure.yml的device字段调用对应后端。我遇到过一种典型情况:机器上原本装着CPU版paddlepaddle,为了跑GPU加速又装了paddlepaddle-gpu,两个包互相覆盖依赖,结果import paddle时直接segmentation fault。正确做法是先卸载再安装,或者干脆用虚拟环境隔离。OpenCV版本我习惯锁在4.5.x,不是新版本不好,而是4.8以后VideoWriter写mp4的码率控制参数行为有变化,同一个fourcc在不同版本上产出的文件体积差别很大,DEBUG时容易误判成超分算法的问题。Python解释器建议3.9或3.10,Paddle 2.4对这两个版本支持最稳,3.11以下某些依赖需要重新编译,没必要在环境上给自己加戏。
3. 核心代码拆解:main.py、VideoProcessor.py、FrameSR.py如何串起整条视频超分链路
3.1 main.py入口:命令行参数与YAML配置的优先级关系
main.py是整个工程的入口,代码量不大,但承担了参数解析、配置加载和异常兜底三件事。一个典型的入口实现长这样:
import argparse
import yaml
from VideoProcessor import VideoProcessor
def main():
parser = argparse.ArgumentParser(description='PyVSR video super resolution')
parser.add_argument('--input', type=str, default='VideoFile/vsr_src.mp4',
help='input video path')
parser.add_argument('--output', type=str, default='Output/output.mp4',
help='output video path')
parser.add_argument('--model', type=str, default=None,
help='model name, override configure.yml')
parser.add_argument('--device', type=str, default=None,
choices=['cpu', 'gpu'],
help='inference device, override configure.yml')
args = parser.parse_args()
with open('configure.yml', 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
model = args.model if args.model else config['model']
device = args.device if args.device else config['device']
processor = VideoProcessor(
input_path=args.input,
output_path=args.output,
model=model,
device=device
)
processor.process()
if __name__ == '__main__':
main()
这里我刻意处理了参数优先级:命令行传入的 --model 和 --device 优先,没传就回落到configure.yml里的值。这样做的工程意义是,跑批量实验时不需要反复改YAML文件,用一条命令行就能切换模型与设备。 --input 和 --output 则强制走命令行,因为这两项几乎每次运行都不一样,写死在配置里反而是负担。代码里没有做输入文件的合法性校验,这一点是欠缺的,实际使用时建议自己在VideoProcessor里加一个 os.path.exists 判断,否则文件路径写错会出现OpenCV报错但异常信息含糊不清的情况。
3.2 VideoProcessor.py:用OpenCV逐帧读写,帧率与分辨率怎么同步
VideoProcessor是整条链路的调度中枢,它负责读取源视频、逐帧调用超分模型、把结果写进新视频,并在结束时合并音频。核心逻辑集中在 process() 方法里:
import cv2
from FrameSR import FrameSR
class VideoProcessor:
def __init__(self, input_path, output_path, model, device):
self.input_path = input_path
self.output_path = output_path
self.sr = FrameSR(model, device)
self.cap = cv2.VideoCapture(input_path)
self.fps = self.cap.get(cv2.CAP_PROP_FPS)
self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
def process(self):
writer = cv2.VideoWriter(
self.output_path,
cv2.VideoWriter_fourcc(*'mp4v'),
self.fps,
(self.width * 4, self.height * 4)
)
frame_count = 0
while True:
ret, frame = self.cap.read()
if not ret:
break
sr_frame = self.sr.infer(frame)
writer.write(sr_frame)
frame_count += 1
writer.release()
self.cap.release()
print(f'processed {frame_count} frames, saved to {self.output_path}')
这个类最值得注意的地方是VideoWriter的输出尺寸必须等于输入尺寸乘以四,而不是随便写。 self.width * 4 和 self.height * 4 正好对应模型输出,如果你在这里写错成宽高不匹配,VideoWriter不会报错,但产出的视频会被拉伸变形,而且肉眼很难第一时间发现。 self.fps 直接取源视频帧率,保证输出视频的时长与源视频一致。循环里的 ret 是OpenCV的读取状态标志,读到视频尾部时返回False,必须用它做退出条件,不能靠帧数硬编码。
3.3 FrameSR.py:单帧超分背后的PaddleInference推理逻辑
FrameSR是整条链路的核心引擎,它把Paddle Inference封装成单帧推理接口。模型加载与推理的完整逻辑如下:
import os
import cv2
import numpy as np
import paddle.inference as paddle_infer
class FrameSR:
def __init__(self, model_name, device='gpu'):
model_dir = os.path.join('Models', model_name)
self.device = device
self.predictor = self._create_predictor(model_dir, device)
def _create_predictor(self, model_dir, device):
config = paddle_infer.Config(
os.path.join(model_dir, 'model.pdmodel'),
os.path.join(model_dir, 'model.pdiparams')
)
if device == 'gpu':
config.enable_use_gpu(1024, 0) # 1024MB 显存预分配,0 号卡
else:
config.disable_gpu()
config.set_cpu_math_library_num_threads(4)
return paddle_infer.create_predictor(config)
def infer(self, frame_bgr):
# OpenCV 读入的是 BGR,模型训练时用的是 RGB,必须先转换
rgb = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB)
# HWC 转 CHW,归一化到 [0,1],并增加 batch 维
tensor = rgb.transpose(2, 0, 1).astype('float32') / 255.0
tensor = np.expand_dims(tensor, axis=0)
input_names = self.predictor.get_input_names()
input_handle = self.predictor.get_input_handle(input_names[0])
input_handle.copy_from_cpu(tensor)
self.predictor.run()
output_names = self.predictor.get_output_names()
output_handle = self.predictor.get_output_handle(output_names[0])
out = output_handle.copy_to_cpu()[0] # [3, 4H, 4W]
# 反归一化,CHW 转 HWC,再转回 BGR 交给 VideoWriter
out = np.clip(out * 255.0, 0, 255).astype('uint8')
out = out.transpose(1, 2, 0)
return cv2.cvtColor(out, cv2.COLOR_RGB2BGR)
这段代码里有三个关键点。第一是 paddle_infer.Config 接收两个文件路径,第一个必须是model.pdmodel,第二个必须是model.pdiparams,顺序写反会在创建predictor时报文件格式错误。第二是 enable_use_gpu(1024, 0) 的第一个参数是显存预分配大小,不是上限,实际占用超出后会动态申请,所以写1024只是初始缓存。第三是输入输出张量的形状,输入是 [1, 3, H, W] ,输出是 [1, 3, 4H, 4W] ,中间的 copy_from_cpu 和 copy_to_cpu 是Paddle Inference的固定用法,数据必须在CPU内存与GPU显存之间显式拷贝。
后处理里我做了两件事: np.clip 把输出像素值硬截断到[0,255],防止个别像素溢出导致图像出现椒盐噪声; transpose(1, 2, 0) 把CHW维度换回HWC,因为OpenCV的VideoWriter只接受HWC排列。如果你在调试时看到输出图像是旋转90度或者颜色错乱,十有八九是这两步里某一个维度没对齐。
3.4 AudioUtils.py与Output目录:音频保留和产物落盘的实现方式
三个Python文件之外,AudioUtils.py承担着音视频合并的任务。OpenCV的VideoWriter只写视频流,不处理音频,所以超分后的mp4默认是无声的。AudioUtils.py的常规实现方式是调用ffmpeg子进程完成两件事:从源视频提取音频轨,再把音频合并进超分后的视频。
import subprocess
def extract_audio(video_path, audio_path):
# 从源视频中提取音频,保存为aac格式,后续合并用
subprocess.run([
'ffmpeg', '-i', video_path, '-vn', '-acodec', 'aac',
audio_path, '-y'
], check=True)
def merge_audio(video_path, audio_path, output_path):
# 把音频轨合并回超分后的视频,视频流直接拷贝不重新编码
subprocess.run([
'ffmpeg', '-i', video_path, '-i', audio_path,
'-map', '0:v:0', '-map', '1:a:0',
'-c:v', 'copy', '-c:a', 'aac', '-shortest',
output_path, '-y'
], check=True)
extract_audio 里的 -vn 表示不要视频流, -acodec aac 把音频统一转成AAC,这样合并时不会因为原视频音频编码格式太偏门而出问题。 merge_audio 里的 -map 0:v:0 取第一个输入的视频流, -map 1:a:0 取第二个输入的音频流, -c:v copy 是关键——视频流直接拷贝,不重新编码,避免二次画质损失。 -shortest 以较短的流为基准截断,防止音频比视频长导致尾部静音。
4. 完整复现:从解压源码包到生成四倍超分视频的六个执行步骤
4.1 目录结构核对:源码包解压后先看Models与VideoFile
拿到源码包后先别急着跑命令,花两分钟核对目录结构。源码包解压后应该是这样的布局:根目录放着main.py、VideoProcessor.py、FrameSR.py、AudioUtils.py四个Python文件,Models目录下三个模型子目录,每个子目录里必须有且仅有三个模型文件,VideoFile目录放着示例视频vsr_src.mp4,Output目录是产物输出位置,根目录还有configure.yml、requirements.txt、run.log、error.log。
ls -la
find Models -maxdepth 2 -type f | sort
核对目录的意义在于,三个Python文件内部全部使用相对路径拼接模型目录,一旦你把Models文件夹挪了位置或者改了名字,main.py运行时会直接报模型文件找不到,而且报错信息只显示相对路径,排查起来很费劲。我一般习惯在跑任何命令之前先执行一次 find 命令,确认每个模型目录下都有完整的三个文件,缺了再想办法补,不要抱侥幸心理。
4.2 创建Python环境并安装依赖(CPU与GPU两套路径)
PyVSR对Python版本有明确要求,建议用conda创建独立环境,避免和系统Python搅在一起:
conda create -n pyvsr python=3.9 -y
conda activate pyvsr
pip install -r requirements.txt
如果你的机器没有GPU,必须把requirements.txt里的 paddlepaddle-gpu 改成 paddlepaddle 再安装,两个包二选一,否则Paddle Inference初始化时会去找CUDA动态库,找不到就崩。GPU机器还要确认驱动与CUDA版本匹配,Paddle 2.4要求CUDA 11.2及以上,用 nvidia-smi 看一眼驱动版本就能判断。这里有一条血泪经验:装依赖前先确认pip的源,默认PyPI源在国内下载Paddle这个几百MB的包容易断,换国内镜像源能省一半时间,但这不是项目本身的问题,只是环境加速手段。
4.3 检查模型权重文件是否齐全
模型文件是超分效果的核心,缺一个字节都跑不起来。执行完目录核对后,再单独验证一下三个模型的文件完整性:
ls -lh Models/BasicVSR_reds_x4/
正常情况下 model.pdmodel 和 model.pdiparams 两个文件都不应为0字节,且 model.pdiparams 的文件大小远大于 model.pdmodel ,因为权重参数表动辄几十上百MB。 model.pdiparams.info 在纯推理阶段不参与加载,但如果你后续要用PaddleSlim做量化压缩,这个文件必须保留。我在一次部署时图省事把info文件删了,结果跑普通推理一切正常,上了量化工具就报元信息缺失,来回折腾半小时才定位到原因,从那以后我默认不删任何模型文件。
4.4 修改configure.yml与启动推理
确认环境与模型都没问题后,先看一眼configure.yml里的 device 和 model 两个字段,再执行推理命令。默认配置是GPU推理加BasicVSR模型:
python main.py \
--input VideoFile/vsr_src.mp4 \
--output Output/output.mp4 \
--model BasicVSR_reds_x4 \
--device gpu
--device cpu 切换到CPU推理, --model EDVR_M_wo_tsa_SRx4 换模型。第一次跑建议用短视频素材,VideoFile目录下的vsr_src.mp4本来就是为验证准备的,直接拿它跑通全流程再换自己的素材。如果显存比较紧张,先把源视频分辨率降一半再喂给模型,输出尺寸会相应缩小,但超分倍数不变。命令行参数的优先级高于YAML配置,所以这里显式传了 --model 和 --device ,不传也行,会落到configure.yml上。
4.5 日志确认与产物验证
推理结束后,先看run.log确认每帧处理耗时和总帧数,再看error.log是否为空。正常情况下error.log应该什么都没有,run.log末尾会出现类似 processed N frames, saved to Output/output.mp4 的提示。然后用ffprobe验证输出视频的分辨率和时长:
ffprobe Output/output.mp4
如果源视频是1920x1080,输出应该是3840x2160,帧率保持不变。这里有一个容易忽略的细节:ffprobe显示的 fps 字段如果出现 30000/1001 这种分数,说明源视频是29.97帧率的NTSC素材,VideoWriter写入时也会保留这个分数,属于正常现象,不用强行转成整数30。
4.6 音频缺失时的标准补救流程
AudioUtils.py里的音频合并函数在process()结束时会被调用,但如果ffmpeg没装,这一步会静默失败,得到的output.mp4就没有声音。这是一个非常隐蔽的坑,因为你可能跑完整条流程都没有任何报错,等到播放时才发现视频是无声的。补救方式也很直接,手动执行一次合并:
ffmpeg -i Output/output.mp4 -i VideoFile/vsr_src.mp4 \
-map 0:v:0 -map 1:a:0 \
-c:v copy -c:a aac -shortest \
Output/output_with_audio.mp4 -y
这条命令用 -map 0:v:0 从超分视频取视频流, -map 1:a:0 从源视频取音频流,视频流用 -c:v copy 直接拷贝不重新编码,音频转成AAC编码。执行后务必再用ffprobe确认输出文件里有 Audio: aac 这一行,否则就是ffmpeg版本太老,不支持 -shortest 与 -map 的组合,需要升级ffmpeg。
5. 避坑记录:跑PyVSR项目时我踩过的五个实用坑
5.1 模型加载失败:InvalidArgumentError与目录名拼写不一致
现象:运行main.py时,日志弹出 InvalidArgumentError ,提示无法打开 model.pdmodel 文件,程序直接中断。
原因:Models目录下的模型文件夹名与configure.yml里的 model 字段拼写不一致。比如模型目录实际叫 BasicVSR_reds_x4 ,配置里却写了 barsicVS_r4 ,大小写和下划线差一个字符,Paddle Inference拼接出的路径就不存在了。
解决:先执行 ls Models 确认目录真实名字,再把configure.yml或命令行参数的模型名改成与目录名完全一致。我自己的习惯是直接复制目录名粘贴到配置文件里,不手敲,从根上杜绝拼写错误。
5.2 GPU推理显存溢出:EDVR在6GB显存显卡上直接崩溃
现象:切换EDVR模型后,推理执行到第几十帧时抛出 CUDA out of memory ,进程被杀,显存占用曲线骤增。
原因:EDVR_M_wo_tsa_SRx4虽然去掉了TSA注意力模块,但可变形卷积需要同时保存多张特征图做offset回归,显存占用是BasicVSR的两到三倍。1080P输入、batch_size为1的情况下,6GB显存刚好卡在临界点。
解决:优先换回BasicVSR或PP-MSVSR做对比;如果必须用EDVR,先把输入视频用cv2.resize缩小一半,输出尺寸会变小但超分倍数不变;最后的办法是把 num_workers 调成0,减少数据加载线程对显存的额外占用。显存问题属于物理资源限制,靠改参数只能缓解,不能根治。
5.3 输出视频没有声音:VideoWriter只写视频流不写音频轨
现象:超分后的output.mp4画面清晰,但播放器没有声音,文件体积也明显小于源视频。
原因:OpenCV的VideoWriter从设计上就不支持音频写入,它在初始化时根本没有音频编码器。AudioUtils.py里的合并逻辑依赖ffmpeg子进程,如果环境里没装ffmpeg,合并步骤会静默失败,不抛异常。
解决:先确认ffmpeg有没有装好: ffmpeg -version 。装好后重新执行一次章节4.6里的合并命令,把音频轨手动加回去。如果你希望后续每次跑完自动出带声视频,可以在VideoProcessor.process()里对 merge_audio 的subprocess调用加一个返回值检查,失败了就打印一条明显的警告,而不是继续静默。
5.4 CPU推理慢到怀疑人生:线程数没设满或者选错了模型
现象:一个30秒的1080P片段,用默认配置在CPU上跑了三个小时还没结束,帧率只有零点几。
原因:FrameSR里 set_cpu_math_library_num_threads(4) 设的是4线程,但很多机器是8核16线程,4线程只吃了一半算力;另一个原因是你选了EDVR这种重型模型,它本身就是为大算力设计的,CPU硬扛不现实。
解决:先执行 python -c "import os; print(os.cpu_count())" 看本机核心数,把configure.yml里的 cpu_threads 改成核心数;CPU推理强制用BasicVSR,不要选EDVR;还可以做抽帧超分——原视频30帧每秒,你只超分其中10帧,剩下的帧直接用最近邻复制填充,观感上差别不大,但推理时间直接降到三分之一。这是工程上常见的性能取舍,不是算法本身的缺陷。
5.5 输出颜色偏绿或偏红:BGR与RGB通道顺序在链路里被写反
现象:超分后的视频整体偏色,红色变成了蓝色,人脸肤色发青,看起来像调色板被翻转了。
原因:OpenCV读入的帧是BGR排列,而三个模型训练时用的都是RGB输入。预处理阶段漏了 cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2RGB) ,或者后处理多转了一次,都会导致通道错位。这种问题在单帧测试时不容易察觉,因为单帧颜色偏差不明显,但视频连续播放时异常感会被放大。
解决:在FrameSR.py的infer方法里,确认输入侧有且只有一次BGR到RGB的转换,输出侧有且只有一次RGB到BGR的转换。如果颜色还是不对,用源码包里的 cpu_before_sr.png 和 cpu_after_sr.png 两张图做基准,先对单张图片跑推理,对比通道顺序是否正确,再回到视频流程。通道问题是视频超分项目里最玄学的毛病之一,但根因永远在这两行转换代码上。
6. 进阶验证:用run.log、error.log与前后对比图,把超分效果量化出来
6.1 先读日志再调参数:两个日志文件的分工
run.log记录的是推理性能数据,包括模型加载耗时、每帧平均处理时间、总处理时长。error.log只记录异常堆栈,为空说明这一次运行没有致命错误。跑完一遍后先看error.log有没有内容,再看run.log里的平均帧耗时——用总耗时除以总帧数,如果CPU推理超过了200毫秒每帧,这个素材就不适合全量跑,要么缩小输入,要么换模型。
6.2 用PSNR/SSIM做有参考评估,用拉普拉斯方差做无参考评估
源码包里没有配对的高清底版,所以PSNR在这里只能作为一个参考值。真正有落地价值的评估方式是横向对比:
import cv2
import numpy as np
def laplacian_variance(img):
"""无参考清晰度评估,值越高说明边缘越锐利"""
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return cv2.Laplacian(gray, cv2.CV_64F).var()
before = cv2.imread('cpu_before_sr.png')
after = cv2.imread('cpu_after_sr.png')
print(f'before: {laplacian_variance(before):.2f}')
print(f'after : {laplacian_variance(after):.2f}')
拉普拉斯方差是工程上常用的无参考清晰度指标,它统计图像二阶导数的离散程度——边缘越锐利,方差越大。拿源码包自带的对比图跑一下,超分后的数值应该是超分前的两倍以上。如果你手里有同一段视频的高清底版,可以用PSNR和SSIM做有参考评估,需要注意两张图尺寸必须一致,先把低清图用双线性插值放大到相同尺寸再算,否则数值没有意义。
6.3 把三个模型各跑一遍,同一段素材横向比较
调参的最终目的是选出最适合当前素材的模型。我会把一段10秒素材分别用三个模型跑一遍,记录各自的平均帧耗时和拉普拉斯方差,再做一次主观观感对比。BasicVSR耗时最短但文字边缘容易发虚,EDVR对运动区域更友好,PP-MSVSR的静态纹理细节最扎实。这个对比结果不是通用的,换了素材结论可能反过来,所以每次接新项目我都会重新跑一遍这个对比流程。从那以后,我每次跑视频超分项目都强制把素材先裁成10秒片段,分别在CPU和GPU上各跑一遍小模型确认链路通,再换最终模型全量跑。这个习惯帮我提前拦下了至少三次把机器卡死的尴尬,也让我对配置参数的理解从黑匣子变成了可预测的输入输出关系。希望帮到你。
更多推荐

所有评论(0)