【图像超分重建】Real-ESRGAN 本地推理测试及量化部署

一、背景

        最近做的一个项目需求,数据上的一个特点是需要检测的目标很小,且部分情况下由于画面不清晰导致目标与背景部分比较难区分,需要想办法提高目标区域的清晰度,加大目标特征在画面中的占比,于是想到了图像超分重建,首先粗提取目标区域,再使用超分放大目标区域。

        目前常见的一些超分模型的重建过程都相对较慢,如果想在实时视频流的推理过程中加入超分模型,还能不能满足实时性要求?或者说对帧率影响多大?如果再将其移植到算力更低的边端设备,效果又会如何?这就需要把模型运行起来并一一验证了。

        由于采集到的数据经过提取后都是低分辨率图像,所以就需要把预训练好的模型拿过来直接使用。如果想要在自己的数据集上重新训练模型,或者是微调,高分辨率图像是必不可少的,这与实际情况相悖(如果有高分辨率图像那也用不着重建了)。综合以上情况最终选择的是 Real-ESRGAN。本文内容不含模型训练和微调部分,只记录使用官方模型进行重建和模型量化后的对比测试。

二、本地测试

        Real-ESRGAN 是由腾讯 ARC 实验室发布的一个盲图像超分辨率模型,模拟图像从高分辨率到低分辩率过程中的各种退化,然后通过低清图倒推出它的高清图 [1]。Real-ESRGAN 是 ESRGAN 针对真实世界图像重建优化的升级版本,重建效果更好更稳定,二者都是基于 BasicSR (一个基于 PyTorch 的开源图像视频复原工具箱)进行训练和推理的。

仓库地址:https://github.com/xinntao/Real-ESRGAN

1. 环境安装

# BasicSR 用于训练和推理
pip install basicsr
# facexlib and gfpgan 用于面部增强
pip install facexlib
pip install gfpgan
pip install -r requirements.txt
python setup.py develop

2. 预训练模型

截止到目前为止官方提供的几类模型:

(1)适用一般图像
模型对应判别器(用于微调)描述
RealESRGAN_x4plusRealESRGAN_x4plus_netD4倍模型,默认使用
RealESRGAN_x2plusRealESRGAN_x2plus_netD2倍模型
RealESRNet_x4plus使用 MSE Loss 训练的4倍模型
official ESRGAN_x44倍 ESRGAN 模型
realesr-general-x4v3轻量化4倍模型,可调节降噪程度(最新)
realesr-general-wdn-x4v3配合 realesr-general-x4v3 使用的降噪模型

        虽然官方说 realesr-general-x4v3 也支持1-3倍,但实际这个1-3倍是对重建后的图像做resize,实际模型直接输出的还是只有4倍

(2)适用动漫图像
模型对应判别器(用于微调)描述
RealESRGAN_x4plus_anime_6BRealESRGAN_x4plus_anime_6B_netD针对动漫图像优化的4倍模型
(3)适用动漫视频
模型对应判别器(用于微调)描述
realesr-animevideov31-4倍模型

        下载之后放到 weights 文件夹下。本文只测试了 RealESRGAN_x4plus 和 realesr-general-x4v3,测试图像是真实世界图像,测试效果前者更好,后者推理速度更快,效果也还可以,后续量化使用的也是 realesr-general-x4v3。

3. 测试方式

官方提供三种测试方式:

(1)在线 Demo
  • ARC Demo(仅支持动漫图,没试)
  • Colab Demo1(支持一般图像,试了但环境问题没跑通)
  • Colab Demo2(针对动漫视频,没试)
(2)可执行文件

支持 Win/Linux/Mac,可直接安装使用,没试。

(3)python 脚本执行

本文主要使用这种方式。

执行命令,输出结果保存到 results 文件夹

# -n 模型名称;-i 测试数据所在文件夹
python inference_realesrgan.py -n RealESRGAN_x4plus -i inputs

4. 结果对比

(1)效果对比

        比较了 RealESRGAN_x4plus、realesr-general-x4v3(去噪水平 0.5) 和 realesr-general-x4v3 (以下简称 x4plus、x4v3 和 x4v3-onnx)导出为静态 onnx 的重建效果。

原图(512*256)
x4plus(2048*1024)
x4v3(2048*1024)
 x4v3-onnx(2048*1024)

 原图(566*350) 
 x4plus(2264*1400)
 x4v3(2264*1400)
x4v3-onnx(2264*1400)

        重建效果 x4plus >  x4v3 > x4v3-onnx,如果是动态 onnx 模型,效果几乎无损(肉眼观察)。

(2)速度对比

        由于 x4plus 和 x4v3 是动态输入,推理速度随图像分辨率变化,所以仅以上面的两张图为例进行测试

模型图1图2
x4plus0.88s2.31s
x4v30.08s0.31s
x4v3-onnx0.51s0.65s

        可以看到 x4v3 的确比 x4plus 快不少,重建效果也还不错,而 x4v3-onnx 使用的是 CPU 推理,速度方面逊于受 GPU 加持的 x4v3。

        x4v3 训练默认使用 prelu 激活函数,将其改为 relu 并导出 onnx,推理速度没有提升但效果明显变差。

 三、量化部署

1. pt --> onnx

        scripts/pytorch2onnx.py 为官方提供的转换脚本,但只写了 x4plus 的转换,现在需要转换 x4v3,所以要修改一下脚本。

import argparse
import torch
import torch.onnx
from basicsr.archs.rrdbnet_arch import RRDBNet
from realesrgan.archs.srvgg_arch import SRVGGNetCompact


def dni(net_a, net_b, dni_weight, key='params', loc='cpu'):
    """Deep network interpolation.

    ``Paper: Deep Network Interpolation for Continuous Imagery Effect Transition``
    """
    net_a = torch.load(net_a, map_location=torch.device(loc))
    net_b = torch.load(net_b, map_location=torch.device(loc))
    for k, v_a in net_a[key].items():
        net_a[key][k] = dni_weight[0] * v_a + dni_weight[1] * net_b[key][k]
    return net_a


def main(args):
    # x4v3使用SRVGGNetCompact
    if 'v3' in args.input:
        model = SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='prelu')
        keyname = 'params'
        dni_weight = [args.denoise_strength, 1 - args.denoise_strength]
        pth = dni(args.input, args.input2, dni_weight)
    else:
        model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4)
        keyname = 'params_ema'
        pth = torch.load(args.input)

    state_dict = pth[keyname]
    model.load_state_dict(state_dict)

    model.train(False)
    model.cpu().eval()

    # 固定输入大小,导出为静态模型
    x = torch.rand(1, 3, 256, 256)
    # 导出
    with torch.no_grad():
        torch.onnx.export(model,
                          x,
                          args.output,
                          opset_version=11,
                          )

    print('Export ONNX model successful!')


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--input', type=str, default='', help='Input model path')
    parser.add_argument(
        '--input2', type=str, default='', help='Input denoise model path')
    parser.add_argument('--output', type=str, default='', help='Output onnx path')
    parser.add_argument(
        '-dn',
        '--denoise_strength',
        type=float,
        default=0.5,
        help=('Denoise strength. 0 for weak denoise (keep noise), 1 for strong denoise ability. '
              'Only used for the realesr-general-x4v3 model'))
    args = parser.parse_args()

    main(args)

        如果导出动态模型,需要在导出前添加动态参数,这样导出的模型可支持任意宽高图像输入。

x = torch.rand(1, 3, 256, 256)
dynamic = {'images': {0: 'batch_size', 1: 'channel', 2: 'height', 3: 'width'},
           'output': {0: 'batch_size', 1: 'channel', 2: 'height', 3: 'width'}}
# Export the model
with torch.no_grad():
    torch.onnx.export(model,
                      x,
                      args.output,
                      opset_version=11,
                      input_names=['images'],
                      output_names=['output'],
                      dynamic_axes=dynamic,
                      )

如果把 prelu 改为 relu,需要修改 act_type 和权重文件(还是不要改了,效果不好 - -)

model = SRVGGNetCompact(num_in_ch=3, num_out_ch=3, num_feat=64, num_conv=32, upscale=4, act_type='relu')
state_dict = pth[keyname]
# 需要删除部分层
state_dict_copy = state_dict.copy()
del_key = [str(i) for i in range(1, 66, 2)]
for k, v in state_dict.items():
    k2 = k.split('.')[1]
    k3 = k.split('.')[2]
    if k3 == 'weight':
        if k2 in del_key:
            del state_dict_copy[k]
model.load_state_dict(state_dict_copy)

 2. onnx --> rknn

        一开始是想把 onnx 模型直接放到盒子上,使用 onnxruntime 推理,这样没法调用 npu 加速,实测推理速度在1.7s,有点惨不忍睹,所以要用起来就得想办法转成rknn。RK 官方的 Model Zoo 里没有 Real-ESRGAN,只能尝试一下看能不能转换成功,没想到使用现有脚本(可参考RK官方脚本或看我之前的文章)直接就能转,也可以直接做量化。

        下面记录一下对 x4v3 做转换和量化后的模型(分别记作 x4v3-rknn 和 x4v3-rknn-q)测试重建效果和推理速度。

(1)重建效果
x4v3-rknn 
x4v3-rknn-q 

x4v3-rknn 
x4v3-rknn-q 

        可以看到没有量化的 x4v3-rknn 与 x4v3-onnx 的重建效果没有肉眼可见的差别,但量化后的 x4v3-rknn-q 对第一张图像的重建效果与 x4v3-rknn 相比就比较明显,背景没有 x4v3-rknn 平滑,将降噪水平调到最高,也没有改善很多,可能是量化对模型精度的损失较大,但第二张图的效果差别又不明显了。

 (2)推理速度

以下测试均在 RK3588 上进行。

模型图一图二
x4v3-onnx1.71s1.70s
x4v3-rknn0.14s (91.8%↑)0.15s (91.1%↑)
x4v3-rknn-q0.10s (94.1%↑)0.11s (93.5%↑)

经过 NPU 加速后推理速度提升很明显。

四、总结 

        经过一番折腾实现了 Real-ESRGAN 的边端推理,过程比预想的顺利,最终的效果和速度(能力有限,目前只能做到这个速度)基本满足需求。除了 Real-ESRGAN,也测试了 EGVSR 和CAMixerSR,都是主打速度快。

        在 GPU 上对同样 13 张不同分辨率的图像做4倍重建,x4plus 总耗时 16s,x4v3 总耗时 4.59s,CMixerSRx4 总耗时 12.68s,TecoGAN_BD_iter50000 总耗时 5.23s,效果上 CMixerSRx4 还是比 x4v3 差一些,EGVSR 可能是我用法不对,重建出来的图像没法看,后面有时间再搞搞清楚。

参考资料

[1] 盲图像超分辨率模型 Real-ESRGAN 使用教程-知乎

[2] 图像超分综述:超长文一网打尽图像超分的前世今生 (附核心代码)-CSDN博客

[3] 超分辨率重建——CAMixerSR网络训练与推理测试(详细图文教程)-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值