Real-ESRGAN-OpenVINO / benchmark_models.py
HelloSun's picture
benchmark: fall back to Hub download; add sample inputs
5a1f4db verified
Raw History Blame Contribute Delete
4.02 kB
#!/usr/bin/env python3
"""Benchmark the OpenVINO Real-ESRGAN variants (quality + speed), no PyTorch needed.
Every model is compared against the FP16 model, which reproduces the original
PyTorch `RealESRGAN_x4plus` result to ~48 dB.
python benchmark_models.py # default set, synthetic input
python benchmark_models.py --images a.png b.png # real images
python benchmark_models.py --models int4 --models int8
"""
import argparse
import os
import pathlib
import time
import cv2
import numpy as np
from realesrgan_ov import MODELS, RealESRGAN, _download_model
HERE = pathlib.Path(__file__).resolve().parent
def psnr(a, b, peak=255.0):
mse = np.mean((a.astype(np.float64) - b.astype(np.float64)) ** 2)
return 99.0 if mse == 0 else 10 * np.log10(peak ** 2 / mse)
def ssim(a, b):
c1, c2 = 0.01 ** 2, 0.03 ** 2
a = cv2.cvtColor(a, cv2.COLOR_BGR2GRAY).astype(np.float64) / 255.
b = cv2.cvtColor(b, cv2.COLOR_BGR2GRAY).astype(np.float64) / 255.
mu_a, mu_b = cv2.GaussianBlur(a, (11, 11), 1.5), cv2.GaussianBlur(b, (11, 11), 1.5)
sa = cv2.GaussianBlur(a * a, (11, 11), 1.5) - mu_a ** 2
sb = cv2.GaussianBlur(b * b, (11, 11), 1.5) - mu_b ** 2
sab = cv2.GaussianBlur(a * b, (11, 11), 1.5) - mu_a * mu_b
return (((2 * mu_a * mu_b + c1) * (2 * sab + c2)) / ((mu_a ** 2 + mu_b ** 2 + c1) * (sa + sb + c2))).mean()
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--models', nargs='+', default=['fp16', 'int8', 'int4', 'int4-mixed', 'int4-w4a8'])
parser.add_argument('--reference', default='fp16')
parser.add_argument('--images', nargs='*', default=[])
parser.add_argument('--tile', type=int, default=128)
parser.add_argument('--tile-pad', type=int, default=16)
parser.add_argument('--runs', type=int, default=2)
parser.add_argument('--device', default='CPU')
parser.add_argument('--size', type=int, nargs=2, default=[512, 256], help='synthetic input WxH')
parser.add_argument('--token', default=os.environ.get('HF_TOKEN'), help='Hugging Face token')
args = parser.parse_args()
if args.images:
inputs = [cv2.imread(p) for p in args.images]
inputs = [im for im in inputs if im is not None]
else:
rng = np.random.default_rng(0)
yy, xx = np.mgrid[0:args.size[1], 0:args.size[0]]
base = (127 + 100 * np.sin(xx / 7.0) * np.cos(yy / 11.0))[..., None]
inputs = [np.clip(base + rng.normal(0, 25, base.shape), 0, 255).astype(np.uint8).repeat(3, axis=2)]
def load(key):
path = HERE / 'models' / f'{MODELS[key][0]}.xml'
if not path.exists(): # not downloaded yet -> fetch it from the Hub
path = pathlib.Path(_download_model(key, args.token))
return RealESRGAN(model=str(path), device=args.device)
ref_up = load(args.reference) if args.reference in args.models else load(args.reference)
refs = [ref_up.enhance(im, tile=args.tile, tile_pad=args.tile_pad)[0] for im in inputs]
size_mb = {}
for key in args.models:
up = load(key)
xml = pathlib.Path(up.model_path)
size_mb[key] = (xml.stat().st_size + xml.with_suffix('.bin').stat().st_size) / 1e6
ps, ss, ts, mp = [], [], [], []
for im, ref in zip(inputs, refs):
up.enhance(im, tile=args.tile, tile_pad=args.tile_pad)
best = min([_time(up, im, args) for _ in range(args.runs)])
out = up.enhance(im, tile=args.tile, tile_pad=args.tile_pad)[0]
ts.append(best)
mp.append(im.shape[0] * im.shape[1] / best / 1e6)
ps.append(psnr(ref, out))
ss.append(ssim(ref, out))
print(f'{key:12s} {size_mb[key]:6.1f} MB PSNR {np.mean(ps):6.2f} dB SSIM {np.mean(ss):.4f} '
f'{np.mean(ts):6.3f} s/img {np.mean(mp):5.2f} MPix/s')
def _time(up, im, args):
t0 = time.time()
up.enhance(im, tile=args.tile, tile_pad=args.tile_pad)
return time.time() - t0
if __name__ == '__main__':
main()