Spaces:
Build error
Build error
| #!/usr/bin/env python3 | |
| # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import code | |
| import glob | |
| import imageio | |
| import numpy as np | |
| import os | |
| from pathlib import PurePosixPath | |
| from scipy.ndimage.filters import convolve1d | |
| import struct | |
| import sys | |
| from constants import * | |
| # Search for pyngp in the build folder. | |
| sys.path += [os.path.dirname(pyd) for pyd in glob.iglob(os.path.join(ROOT_DIR, "build*", "**/*.pyd"), recursive=True)] | |
| sys.path += [os.path.dirname(pyd) for pyd in glob.iglob(os.path.join(ROOT_DIR, "build*", "**/*.so"), recursive=True)] | |
| def repl(testbed): | |
| print("-------------------\npress Ctrl-Z to return to gui\n---------------------------") | |
| code.InteractiveConsole(locals=locals()).interact() | |
| print("------- returning to gui...") | |
| def mse2psnr(x): return -10.*np.log(x)/np.log(10.) | |
| def sanitize_path(path): | |
| return str(PurePosixPath(path.relative_to(PAPER_FOLDER))) | |
| # from https://stackoverflow.com/questions/31638651/how-can-i-draw-lines-into-numpy-arrays | |
| def trapez(y,y0,w): | |
| return np.clip(np.minimum(y+1+w/2-y0, -y+1+w/2+y0),0,1) | |
| def weighted_line(r0, c0, r1, c1, w, rmin=0, rmax=np.inf): | |
| # The algorithm below works fine if c1 >= c0 and c1-c0 >= abs(r1-r0). | |
| # If either of these cases are violated, do some switches. | |
| if abs(c1-c0) < abs(r1-r0): | |
| # Switch x and y, and switch again when returning. | |
| xx, yy, val = weighted_line(c0, r0, c1, r1, w, rmin=rmin, rmax=rmax) | |
| return (yy, xx, val) | |
| # At this point we know that the distance in columns (x) is greater | |
| # than that in rows (y). Possibly one more switch if c0 > c1. | |
| if c0 > c1: | |
| return weighted_line(r1, c1, r0, c0, w, rmin=rmin, rmax=rmax) | |
| # The following is now always < 1 in abs | |
| slope = (r1-r0) / (c1-c0) | |
| # Adjust weight by the slope | |
| w *= np.sqrt(1+np.abs(slope)) / 2 | |
| # We write y as a function of x, because the slope is always <= 1 | |
| # (in absolute value) | |
| x = np.arange(c0, c1+1, dtype=float) | |
| y = x * slope + (c1*r0-c0*r1) / (c1-c0) | |
| # Now instead of 2 values for y, we have 2*np.ceil(w/2). | |
| # All values are 1 except the upmost and bottommost. | |
| thickness = np.ceil(w/2) | |
| yy = (np.floor(y).reshape(-1,1) + np.arange(-thickness-1,thickness+2).reshape(1,-1)) | |
| xx = np.repeat(x, yy.shape[1]) | |
| vals = trapez(yy, y.reshape(-1,1), w).flatten() | |
| yy = yy.flatten() | |
| # Exclude useless parts and those outside of the interval | |
| # to avoid parts outside of the picture | |
| mask = np.logical_and.reduce((yy >= rmin, yy < rmax, vals > 0)) | |
| return (yy[mask].astype(int), xx[mask].astype(int), vals[mask]) | |
| def diagonally_truncated_mask(shape, x_threshold, angle): | |
| result = np.zeros(shape, dtype=bool) | |
| for x in range(shape[1]): | |
| for y in range(shape[0]): | |
| thres = x_threshold * shape[1] - (angle * shape[0] / 2) + y * angle | |
| result[y, x, ...] = x < thres | |
| return result | |
| def diagonally_combine_two_images(img1, img2, x_threshold, angle, gap=0, color=1): | |
| if img2.shape != img1.shape: | |
| raise ValueError(f"img1 and img2 must have the same shape; {img1.shape} vs {img2.shape}") | |
| mask = diagonally_truncated_mask(img1.shape, x_threshold, angle) | |
| result = img2.copy() | |
| result[mask] = img1[mask] | |
| if gap > 0: | |
| rr, cc, val = weighted_line(0, int(x_threshold * img1.shape[1] - (angle * img1.shape[0] / 2)), img1.shape[0]-1, int(x_threshold * img1.shape[1] + (angle * img1.shape[0] / 2)), gap) | |
| result[rr, cc, :] = result[rr, cc, :] * (1 - val[...,np.newaxis]) + val[...,np.newaxis] * color | |
| return result | |
| def diagonally_combine_images(images, x_thresholds, angle, gap=0, color=1): | |
| result = images[0] | |
| for img, thres in zip(images[1:], x_thresholds): | |
| result = diagonally_combine_two_images(result, img, thres, angle, gap, color) | |
| return result | |
| 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]) | |
| # Premultiply alpha | |
| 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) | |
| # Unmultiply alpha | |
| 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): | |
| return 0.2126 * a[:,:,0] + 0.7152 * a[:,:,1] + 0.0722 * a[:,:,2] | |
| def SSIM(a, b): | |
| def blur(a): | |
| k = np.array([0.120078, 0.233881, 0.292082, 0.233881, 0.120078]) | |
| x = convolve1d(a, k, axis=0) | |
| return convolve1d(x, k, axis=1) | |
| a = luminance(a) | |
| b = luminance(b) | |
| mA = blur(a) | |
| mB = blur(b) | |
| sA = blur(a*a) - mA**2 | |
| sB = blur(b*b) - mB**2 | |
| sAB = blur(a*b) - mA*mB | |
| c1 = 0.01**2 | |
| c2 = 0.03**2 | |
| p1 = (2.0*mA*mB + c1)/(mA*mA + mB*mB + c1) | |
| p2 = (2.0*sAB + c2)/(sA + sB + c2) | |
| error = p1 * p2 | |
| return error | |
| 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)) | |
| elif metric == "SSIM": | |
| return SSIM(np.clip(img, 0.0, 1.0), np.clip(ref, 0.0, 1.0)) | |
| 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 | |