| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import imageio |
| import numpy as np |
| import os |
| import struct |
|
|
| ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) |
|
|
| def mse2psnr(x): |
| return -10.*np.log(x)/np.log(10.) |
|
|
| def write_image_imageio(img_file, img, quality): |
| img = (np.clip(img, 0.0, 1.0) * 255.0 + 0.5).astype(np.uint8) |
| kwargs = {} |
| if os.path.splitext(img_file)[1].lower() in [".jpg", ".jpeg"]: |
| if img.ndim >= 3 and img.shape[2] > 3: |
| img = img[:,:,:3] |
| kwargs["quality"] = quality |
| kwargs["subsampling"] = 0 |
| imageio.imwrite(img_file, img, **kwargs) |
|
|
| def read_image_imageio(img_file): |
| img = imageio.imread(img_file) |
| img = np.asarray(img).astype(np.float32) |
| if len(img.shape) == 2: |
| img = img[:,:,np.newaxis] |
| return img / 255.0 |
|
|
| def srgb_to_linear(img): |
| limit = 0.04045 |
| return np.where(img > limit, np.power((img + 0.055) / 1.055, 2.4), img / 12.92) |
|
|
| def linear_to_srgb(img): |
| limit = 0.0031308 |
| return np.where(img > limit, 1.055 * (img ** (1.0 / 2.4)) - 0.055, 12.92 * img) |
|
|
| def read_image(file): |
| if os.path.splitext(file)[1] == ".bin": |
| with open(file, "rb") as f: |
| bytes = f.read() |
| h, w = struct.unpack("ii", bytes[:8]) |
| img = np.frombuffer(bytes, dtype=np.float16, count=h*w*4, offset=8).astype(np.float32).reshape([h, w, 4]) |
| else: |
| img = read_image_imageio(file) |
| if img.shape[2] == 4: |
| img[...,0:3] = srgb_to_linear(img[...,0:3]) |
| |
| img[...,0:3] *= img[...,3:4] |
| else: |
| img = srgb_to_linear(img) |
| return img |
|
|
| def write_image(file, img, quality=95): |
| if os.path.splitext(file)[1] == ".bin": |
| if img.shape[2] < 4: |
| img = np.dstack((img, np.ones([img.shape[0], img.shape[1], 4 - img.shape[2]]))) |
| with open(file, "wb") as f: |
| f.write(struct.pack("ii", img.shape[0], img.shape[1])) |
| f.write(img.astype(np.float16).tobytes()) |
| else: |
| if img.shape[2] == 4: |
| img = np.copy(img) |
| |
| img[...,0:3] = np.divide(img[...,0:3], img[...,3:4], out=np.zeros_like(img[...,0:3]), where=img[...,3:4] != 0) |
| img[...,0:3] = linear_to_srgb(img[...,0:3]) |
| else: |
| img = linear_to_srgb(img) |
| write_image_imageio(file, img, quality) |
|
|
| def trim(error, skip=0.000001): |
| error = np.sort(error.flatten()) |
| size = error.size |
| skip = int(skip * size) |
| return error[skip:size-skip].mean() |
|
|
| def luminance(a): |
| a = np.maximum(0, a)**0.4545454545 |
| return 0.2126 * a[:,:,0] + 0.7152 * a[:,:,1] + 0.0722 * a[:,:,2] |
|
|
| def L1(img, ref): |
| return np.abs(img - ref) |
|
|
| def APE(img, ref): |
| return L1(img, ref) / (1e-2 + ref) |
|
|
| def SAPE(img, ref): |
| return L1(img, ref) / (1e-2 + (ref + img) / 2.) |
|
|
| def L2(img, ref): |
| return (img - ref)**2 |
|
|
| def RSE(img, ref): |
| return L2(img, ref) / (1e-2 + ref**2) |
|
|
| def rgb_mean(img): |
| return np.mean(img, axis=2) |
|
|
| def compute_error_img(metric, img, ref): |
| img[np.logical_not(np.isfinite(img))] = 0 |
| img = np.maximum(img, 0.) |
| if metric == "MAE": |
| return L1(img, ref) |
| elif metric == "MAPE": |
| return APE(img, ref) |
| elif metric == "SMAPE": |
| return SAPE(img, ref) |
| elif metric == "MSE": |
| return L2(img, ref) |
| elif metric == "MScE": |
| return L2(np.clip(img, 0.0, 1.0), np.clip(ref, 0.0, 1.0)) |
| elif metric == "MRSE": |
| return RSE(img, ref) |
| elif metric == "MtRSE": |
| return trim(RSE(img, ref)) |
| elif metric == "MRScE": |
| return RSE(np.clip(img, 0, 100), np.clip(ref, 0, 100)) |
|
|
| raise ValueError(f"Unknown metric: {metric}.") |
|
|
| def compute_error(metric, img, ref): |
| metric_map = compute_error_img(metric, img, ref) |
| metric_map[np.logical_not(np.isfinite(metric_map))] = 0 |
| if len(metric_map.shape) == 3: |
| metric_map = np.mean(metric_map, axis=2) |
| mean = np.mean(metric_map) |
| return mean |
|
|